| Crates.io | exemplar |
| lib.rs | exemplar |
| version | 0.37.0 |
| created_at | 2023-10-09 23:32:32.286984+00 |
| updated_at | 2025-09-05 20:02:50.282878+00 |
| description | A boilerplate eliminator for rusqlite. |
| homepage | |
| repository | https://github.com/Colonial-Dev/exemplar/ |
| max_upload_size | |
| id | 998593 |
| size | 68,428 |
rusqlite.
A taste of what you can do:
#[derive(Debug, PartialEq, Model)]
#[table("users")]
#[check("../tests/schema.sql")]
struct User {
username: String,
#[bind(bind_path)]
#[extr(extr_path)]
home_dir: PathBuf,
#[column("pwd")]
password: Vec<u8>,
}
fn main() -> Result<()> {
let conn = Connection::open_in_memory()?;
conn.execute_batch(
include_str!("../tests/schema.sql")
)?;
let alice = User {
username: "Alice".to_owned(),
home_dir: "/var/home/alice".into(),
password: b"hunter2".to_vec()
};
let bob = User {
username: "Bob".to_owned(),
home_dir: "/var/home/robert".into(),
password: b"password".to_vec()
};
alice.insert(&conn)?;
bob.insert(&conn)?;
let mut stmt = conn.prepare("
SELECT * FROM users ORDER BY username ASC
")?;
let mut iter = stmt.query_and_then([], User::from_row)?;
assert_eq!(alice, iter.next().unwrap()?);
assert_eq!(bob, iter.next().unwrap()?);
Ok(())
}
Exemplar is based around the Model trait, which has its own derive macro.
enums in models, check out the sql_enum macro.record macro.Model trait, which gets inlined and monomorphized away before runtime. The resulting code is roughly what you'd write by hand when using pure rusqlite.rusqlite's existing types where possible, including its Result type alias.Deref's to rusqlite::Connection, such as transactions or pooled connections.enums and "anonymous" record types that map to ad-hoc queries.dyn Models at runtime.If you just need to CRUD some Rust data with sqlite and don't want a whole ORM or enterprise-grade DBMS, then Exemplar is for you!
A few key things:
diesel or sqlx/seaorm, which both support SQLite.INSERT.)rusqlite is supported.Yes. On my machine (according to these benchmarks) Exemplar can:
SELECT * LIMIT 1)Obviously the credit for this speed goes to the SQLite and rusqlite developers, but I can confidently say that I didn't slow things down!
serde-rusqlite?"serde_rusqlite is a clever hack, but it still involved too much contorting and boilerplate for my taste - that's why I created Exemplar.
The pain points I tried to fix were:
String column names to efficiently deserialize rows - probably due to serde limitations?
from_row requires no extra inputs and makes no superfluous allocations.enums - they are inefficiently serialized as TEXT instead of INTEGER. This was nice for debugging, but I figured the faster option should be Exemplar's default.to_params_named(&row1).unwrap().to_slice().as_slice()
row1.insert(&conn) or row1.insert_with(&stmt) in Exemplar.serde overhead popping up, both at compile and runtime.
serde_rusqlite is ~25% slower on insert operations compared to Exemplar.rusqlite, for providing the foundation on which this library is built.