Crates.io | exemplar |
lib.rs | exemplar |
version | 0.11.0 |
source | src |
created_at | 2023-10-09 23:32:32.286984 |
updated_at | 2024-08-18 02:07:11.448322 |
description | A boilerplate eliminator for rusqlite. |
homepage | |
repository | https://github.com/Colonial-Dev/exemplar/ |
max_upload_size | |
id | 998593 |
size | 49,077 |
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.
enum
s 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.enum
s and "anonymous" record types that map to ad-hoc queries.dyn Model
s 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.enum
s - 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.