The fnsql crate provides simple type-safe optional wrappers around SQL queries. Instead of calling type-less .query() and .execute(), you call to auto-generated unique wrappers that are strongly typed, .query_<name>() and .execute_<name>(). However, you manually specify the input and output types, but only once, with the query, and in separation with the code that uses the query.

It's a very simple implementation that doesn't force any schema or ORM down your throat, so if you are already using the rusqlite or postgres crates, you can gradually replace your type-less queries with the type-ful wrappers, or migrate from an opinionated ORM.

The way to generate these wrappers is to specify input and output types for each one of the queries. For example, consider the following definitions specified with fnsql, based on the rusqlite example:

```rust fnsql::fnsql! { #[rusqlite, test] createtablepet() { "CREATE TABLE pet ( id INTEGER PRIMARY KEY, name TEXT NOT NULL, data BLOB )" }

#[rusqlite, test(with=[create_table_pet])]
insert_new_pet(name: String, data: Option<Vec<u8>>) {
    "INSERT INTO pet (name, data) VALUES (:name, :data)"
}

#[rusqlite, test(with=[create_table_pet])]
get_pet_id_data(name: Option<String>) -> [(i32, Option<Vec<u8>>, String)] {
    "SELECT id, data, name FROM pet WHERE pet.name = :name"
}

} ```

The definitions can be used as such (commented out is how the previous type-less interfaces were used):

```rust ignore let mut conn = rusqlite::Connection::openinmemory()?;

conn.executecreatetable_pet()?; // conn.execute( // "CREATE TABLE pet ( // id INTEGER PRIMARY KEY, // name TEXT NOT NULL, // data BLOB // )", // [], // )?;

conn.executeinsertnew_pet(&me.name, &me.data)?; // conn.execute( // "INSERT INTO pet (name, data) VALUES (?1, ?2)", // params![me.name, me.data], // )?;

let mut stmt = conn.preparegetpetiddata()?; // let mut stmt = conn.prepare("SELECT id, data, name FROM pet WHERE pet.name = :name")?;

let petiter = stmt.querymap(&Some("Max".tostring()), |id, data, name| { Ok::<_, rusqlite::Error>(Pet { id, data, name, }) })?; // let petiter = stmt.querymap([(":name", "Max".tostring())], |row| { // Ok(Pet { // id: row.get(0)?, // name: row.get(1)?, // data: row.get(2)?, // }) // })?; ```

Technical discussion

The idea with this crate is to allow direct SQL usage but never use inline queries or have type inference at the call-site. Instead, we declare each query on top-level, giving each a name and designated accessor methods that derive from the name.

text running 3 tests test auto_create_table_pet ... ok test auto_insert_new_pet ... ok test auto_get_pet_id_data ... ok

The following is for allowing generated query tests to compile:

toml [dev-dependencies] arbitrary = { version = "1", features = ["derive"] }

Limitations