2 unstable releases
Uses new Rust 2024
| 0.2.0 | Jul 11, 2025 |
|---|---|
| 0.1.0 | Jul 10, 2025 |
#2579 in Rust patterns
39 downloads per month
16KB
250 lines
turso-mappers
Row mappers for turso
See the published crate and the documentation for more information.
- Allows you to map turso rows to structs more easily
- Provides a
MapRowstrait with amap_rowsmethod for easily mapping overturso::Rows - Defines a
TryFromRowtrait forturso::Row - Supports deriving the
TryFromRowtraits for structs via the turso-mappers-derive crate - The derive macro currently requires the columns in the SQL query to be in the same order as the struct fields
- The derive macro currently supports INTEGER (i64), TEXT (String), REAL (f64), and BLOB (Vec) types
- The derive macro now supports NULL values via Option types
Usage
This is a work in progress. Currently, the following functionality is implemented.
map_rowsfromMapRowsis implemented to allow mapping over rows- The
TryFromRowderive macro is implemented with support for:- Basic types: INTEGER (i64), TEXT (String), REAL (f64), and BLOB (Vec)
- Option types for handling NULL values
use turso_mappers::MapRows;
use turso_mappers::TryFromRow;
use turso_mappers::TursoMapperResult;
use turso_mappers::TursoMapperError;
use turso_core::types::Text;
use turso::Row;
use turso::Builder;
#[derive(TryFromRow)]
pub struct Customer {
pub id: i64,
pub name: String,
pub value: f64,
pub image: Vec<u8>,
// Option<T> types are supported for handling NULL values
pub description: Option<String>,
}
#[tokio::main]
async fn main() -> TursoMapperResult<()> {
let db = Builder::new_local(":memory:").build().await?;
let conn = db.connect()?;
// Create a table with columns for all our struct fields and insert some data
conn.execute("CREATE TABLE customer (id INTEGER PRIMARY KEY, name TEXT NOT NULL, value REAL NOT NULL, image BLOB NOT NULL, description TEXT);", ()).await?;
conn.execute("INSERT INTO customer (name, value, image, description) VALUES ('Charlie', 3.12, x'00010203', 'First customer');", ()).await?;
conn.execute("INSERT INTO customer (name, value, image) VALUES ('Sarah', 0.99, x'09080706');", ()).await?;
// Query all customers and map the rows to our Customer struct
let customers = conn
.query("SELECT id, name, value, image, description FROM customer;", ())
.await?
.map_rows(Customer::try_from_row)
.await?;
// Verify we got both customers
assert_eq!(customers.len(), 2);
// Verify first customer (Charlie) has correct data
assert_eq!(customers[0].id, 1);
assert_eq!(customers[0].name, "Charlie");
assert_eq!(customers[0].value, 3.12);
assert_eq!(customers[0].image, vec![0, 1, 2, 3]);
assert_eq!(customers[0].description, Some("First customer".to_string()));
// Verify second customer (Sarah) has correct data
assert_eq!(customers[1].id, 2);
assert_eq!(customers[1].name, "Sarah");
assert_eq!(customers[1].value, 0.99);
assert_eq!(customers[1].image, vec![9, 8, 7, 6]);
assert_eq!(customers[1].description, None);
Ok(())
}
TODO
- Add an option to use named mapping (by column name) instead of index-based mapping
- Improve error messages and error handling
- Reduce duplicated code in derive macro implementation
Dependencies
~24–35MB
~610K SLoC