Repository adapters
Sometimes a Team should hold team data and nothing else. You can put database operations on a separate type without copying the fields into it. Canyon calls this a repository adapter.
Let's use a table named team, Canyon's default name for the Rust type Team. Only the row model is a Canyon entity:
#![allow(unused)] fn main() { use canyon_sql::macros::{canyon_entity, CanyonMapper}; #[derive(Debug, CanyonMapper)] #[canyon_entity] pub struct Team { #[primary_key] pub id: i64, pub name: String, } }
CanyonMapper reads rows into Team. The #[primary_key] marker tells generated operations which field identifies a row; #[canyon_entity] processes that marker on the model.
Now define a type for writes. It has no database fields of its own:
#![allow(unused)] fn main() { use canyon_sql::macros::{EntityDelete, EntityInsert, EntityUpdate}; #[derive(EntityInsert, EntityUpdate, EntityDelete)] #[canyon_crud(maps_to = Team)] pub struct TeamWriter { marker: (), } }
maps_to = Team says which type the operations receive. It does not turn TeamWriter into another entity. With the operation traits in scope, you can write:
#![allow(unused)] fn main() { use canyon_sql::crud::{EntityDelete as _, EntityInsert as _, EntityUpdate as _}; TeamWriter::insert_entity(&mut team).await?; let affected: u64 = TeamWriter::update_entity(&team).await?; TeamWriter::delete_entity(&team).await?; }
There is no need to construct a TeamWriter here. The generated methods take a Team as an argument. You can derive just one or two operations if that is all the adapter should expose. Deriving all three also gives it the composite EntityCrud trait.
As with model methods, each operation has a _with form for a named datasource or compatible connection. The adapter does not start a transaction for you.
Before using an adapter with another model
The example above works with the conventional table name team. Check these two cases before copying the pattern:
- Your table has a custom name. Earlier in this book,
Teammaps toteams. Today's adapter derives still generate SQL forteam; they do not pick upTeam'stable_name. Write those repository operations explicitly for now. - You want to derive
Readon a fieldless adapter. Its generatedfind_allselects the adapter's fields, notTeam's. Amarker: ()field would become a selected column. KeepReadon the model or implement the repository read yourself.
Don't annotate the adapter as an entity. Adding
#[canyon_entity(table_name = "teams")]toTeamWriterhappens to point its SQL at the right table, but it also registers the adapter as an entity. That is a macro limitation to fix, not a pattern to copy.