1use std::{marker::PhantomData, sync::Arc};
4
5use axum::{extract::FromRequestParts, http::request::Parts};
6
7use crate::{
8 error::Error,
9 store::{Cells, Gather, Reader, Row, Slots, Store, StoreError, Value, Widget, Writer},
10};
11
12pub use crate::store::{
13 Action, Check, Field, Filter, Key, Link, Mass, Name, Only, Op, Order, Page, Pick, Query, Rule,
14 Run, Schema, Sort, Table, Tree,
15};
16
17pub trait Model: Clone + Send + Sync + 'static {
19 fn table() -> Table;
21 fn fields() -> Vec<Field>;
23 fn write(&self, w: &mut dyn Writer);
25 fn read(r: &mut dyn Reader) -> Result<Self, StoreError>;
27 fn write_id(&self, w: &mut dyn Writer);
29 fn read_id(&mut self, r: &mut dyn Reader) -> Result<(), StoreError>;
31
32 fn columns() -> Vec<Name> {
34 Self::fields()
35 .into_iter()
36 .filter(|field| !field.id && !field.keyed && !field.many)
37 .map(|field| field.name)
38 .collect()
39 }
40
41 fn search() -> Vec<Name> {
43 Self::fields()
44 .into_iter()
45 .filter(|field| field.load() == Widget::Text)
46 .map(|field| field.name)
47 .collect()
48 }
49
50 fn readonly() -> Vec<Name> {
52 Vec::new()
53 }
54
55 fn actions() -> Vec<Action> {
57 vec![Action::wipe()]
58 }
59
60 fn schema() -> Schema {
62 Schema {
63 table: Self::table(),
64 fields: Self::fields(),
65 rules: Vec::new(),
66 }
67 }
68
69 fn spec() -> Schema {
71 Self::schema()
72 }
73
74 fn row(&self) -> Vec<Value> {
76 let mut slots = Slots::new();
77 self.write(&mut slots);
78 slots.values()
79 }
80
81 fn id(&self) -> Value {
83 let mut gather = Gather::new();
84 self.write_id(&mut gather);
85 gather.value()
86 }
87}
88
89pub fn id_column<M: Model>() -> Name {
91 M::schema().key()
92}
93
94pub fn key<M: Model>(raw: &str) -> Value {
96 Key::parse(raw, &M::schema()).value()
97}
98
99pub async fn related<M: Model>(
101 store: &Arc<dyn Store>,
102 schemas: &[Schema],
103 model: &M,
104 field: Name,
105) -> Result<Vec<Row>, StoreError> {
106 let bad = |msg: &str| StoreError::Value(msg.into());
107 let here = M::schema();
108 let many = here
109 .fields
110 .iter()
111 .find(|entry| entry.name == field)
112 .ok_or_else(|| bad("unknown field"))?;
113 if !many.many {
114 return Err(StoreError::Unsupported("single field needs filter".into()));
115 }
116 let (through_table, mine, theirs) = match &many.link {
117 Some(Link::Via(through, mine, theirs)) => (*through, *mine, *theirs),
118 _ => return Err(bad("many field needs via")),
119 };
120 let through = schemas
121 .iter()
122 .find(|spec| spec.table == through_table)
123 .ok_or_else(|| bad("unknown through"))?;
124 let their = through
125 .fields
126 .iter()
127 .find(|entry| entry.name == theirs)
128 .ok_or_else(|| bad("unknown through column"))?;
129 let (target_table, target_col) = match &their.link {
130 Some(Link::To(table, name)) => (*table, *name),
131 _ => return Err(bad("through column needs references")),
132 };
133 let target = schemas
134 .iter()
135 .find(|spec| spec.table == target_table)
136 .ok_or_else(|| bad("unknown target"))?;
137 store.define(through).await?;
138 store.define(target).await?;
139 let links = store
140 .scan_query(
141 through,
142 &Query {
143 tree: Tree::Leaf(Filter {
144 field: mine,
145 op: Op::Eq,
146 value: model.id(),
147 }),
148 sort: Vec::new(),
149 page: Page::all(),
150 only: Only::Some(vec![theirs]),
151 mass: None,
152 },
153 )
154 .await?;
155 let mut ids = Vec::new();
156 for row in &links {
157 if let Some(id) = row.get(0)
158 && *id != Value::Null
159 && !ids.contains(id)
160 {
161 ids.push(id.clone());
162 }
163 }
164 if ids.is_empty() {
165 return Ok(Vec::new());
166 }
167 let leaves = ids
168 .into_iter()
169 .map(|id| {
170 Tree::Leaf(Filter {
171 field: target_col,
172 op: Op::Eq,
173 value: id,
174 })
175 })
176 .collect();
177 store
178 .scan_query(
179 target,
180 &Query {
181 tree: Tree::Or(leaves),
182 sort: vec![Sort {
183 field: target.key(),
184 order: Order::Asc,
185 }],
186 page: Page::all(),
187 only: Only::All,
188 mass: None,
189 },
190 )
191 .await
192}
193
194pub struct Repository<M = ()> {
196 store: Arc<dyn Store>,
197 marker: PhantomData<M>,
198}
199
200fn pairs<M: Model>(model: &M) -> Vec<(Name, Value)> {
201 let mut values = model.row().into_iter();
202 M::fields()
203 .into_iter()
204 .filter(|field| !field.id && !field.many)
205 .map(|field| {
206 let value = values.next().unwrap_or(Value::Null);
207 let value = match value {
208 Value::Null => field.initial(),
209 other => other,
210 };
211 (field.name, value)
212 })
213 .collect()
214}
215
216impl<M: Model> Repository<M> {
217 pub fn new(store: Arc<dyn Store>) -> Self {
219 Self {
220 store,
221 marker: PhantomData,
222 }
223 }
224
225 async fn ensure(&self) -> Result<(), StoreError> {
226 self.store.define(&M::schema()).await
227 }
228
229 pub async fn save(&self, model: &mut M) -> Result<(), StoreError> {
231 self.save_many(std::slice::from_mut(model)).await
232 }
233
234 pub async fn save_many(&self, models: &mut [M]) -> Result<(), StoreError> {
236 if models.is_empty() {
237 return Ok(());
238 }
239 self.ensure().await?;
240 let batch = models.iter().map(pairs).collect::<Vec<_>>();
241 let keys = self.store.create(&M::schema(), &batch).await?;
242 for (model, key) in models.iter_mut().zip(keys) {
243 let value = key.value();
244 model.read_id(&mut Cells::new(std::slice::from_ref(&value)))?;
245 }
246 Ok(())
247 }
248
249 pub async fn get(&self, id: &Value) -> Result<Option<M>, StoreError> {
251 let schema = M::schema();
252 let mut rows = self
253 .scan_query(&Query {
254 tree: Tree::Leaf(Filter {
255 field: schema.key(),
256 op: Op::Eq,
257 value: id.clone(),
258 }),
259 sort: vec![Sort {
260 field: schema.key(),
261 order: Order::Asc,
262 }],
263 page: Page::all(),
264 only: Only::Lone,
265 mass: None,
266 })
267 .await?;
268 Ok(rows.pop())
269 }
270
271 pub async fn all(&self) -> Result<Vec<M>, StoreError> {
273 let schema = M::schema();
274 self.scan_query(&Query {
275 tree: Tree::And(Vec::new()),
276 sort: vec![Sort {
277 field: schema.key(),
278 order: Order::Asc,
279 }],
280 page: Page::all(),
281 only: Only::All,
282 mass: None,
283 })
284 .await
285 }
286
287 pub async fn filter(&self, field: Name, value: &Value) -> Result<Vec<M>, StoreError> {
289 let schema = M::schema();
290 self.scan_query(&Query {
291 tree: Tree::Leaf(Filter {
292 field,
293 op: Op::Eq,
294 value: value.clone(),
295 }),
296 sort: vec![Sort {
297 field: schema.key(),
298 order: Order::Asc,
299 }],
300 page: Page::all(),
301 only: Only::All,
302 mass: None,
303 })
304 .await
305 }
306
307 pub async fn ordered(&self, sort: Sort) -> Result<Vec<M>, StoreError> {
309 self.scan_query(&Query {
310 tree: Tree::And(Vec::new()),
311 sort: vec![sort],
312 page: Page::all(),
313 only: Only::All,
314 mass: None,
315 })
316 .await
317 }
318
319 pub async fn scan_query(&self, query: &Query) -> Result<Vec<M>, StoreError> {
321 if matches!(query.only, Only::Some(_)) {
322 return Err(StoreError::Unsupported("projected rows need rows()".into()));
323 }
324 let rows = self.rows(query).await?;
325 rows.iter().map(|row| M::read(&mut row.cells())).collect()
326 }
327
328 pub async fn rows(&self, query: &Query) -> Result<Vec<Row>, StoreError> {
330 self.ensure().await?;
331 self.store.scan_query(&M::schema(), query).await
332 }
333
334 pub async fn total_query(&self, query: &Query) -> Result<usize, StoreError> {
336 self.ensure().await?;
337 self.store.total_query(&M::schema(), query).await
338 }
339
340 pub async fn mass(&self, query: &Query) -> Result<Value, StoreError> {
342 self.ensure().await?;
343 self.store.mass(&M::schema(), query).await
344 }
345
346 pub async fn update(&self, model: &M) -> Result<(), StoreError> {
348 self.ensure().await?;
349 self.store
350 .replace(&M::schema(), &Key::of(&model.id())?, &pairs(model))
351 .await
352 }
353
354 pub async fn delete(&self, id: &Value) -> Result<(), StoreError> {
356 self.ensure().await?;
357 self.store.remove(&M::schema(), &Key::of(id)?).await
358 }
359}
360
361impl<M: Model> FromRequestParts<()> for Repository<M> {
362 type Rejection = Error;
363
364 async fn from_request_parts(parts: &mut Parts, _state: &()) -> Result<Self, Self::Rejection> {
365 let store = parts
366 .extensions
367 .get::<Arc<dyn Store>>()
368 .cloned()
369 .ok_or_else(|| Error::Server("no store configured".into()))?;
370 Ok(Self::new(store))
371 }
372}
373
374#[cfg(test)]
375mod tests {
376 use super::*;
377 use crate::store::Storable;
378
379 #[derive(Clone)]
380 struct Post {
381 id: i64,
382 title: String,
383 }
384
385 impl Model for Post {
386 fn table() -> Table {
387 Table("posts")
388 }
389
390 fn fields() -> Vec<Field> {
391 vec![Field::id(), Field::str("title")]
392 }
393
394 fn write(&self, w: &mut dyn Writer) {
395 Storable::put(&self.title, w);
396 }
397
398 fn read(r: &mut dyn Reader) -> Result<Self, StoreError> {
399 Ok(Self {
400 id: Storable::take(r)?,
401 title: Storable::take(r)?,
402 })
403 }
404
405 fn write_id(&self, w: &mut dyn Writer) {
406 Storable::put(&self.id, w);
407 }
408
409 fn read_id(&mut self, r: &mut dyn Reader) -> Result<(), StoreError> {
410 self.id = Storable::take(r)?;
411 Ok(())
412 }
413 }
414
415 #[derive(Clone)]
416 struct Product {
417 sku: String,
418 price: rust_decimal::Decimal,
419 }
420
421 impl Model for Product {
422 fn table() -> Table {
423 Table("products")
424 }
425
426 fn fields() -> Vec<Field> {
427 vec![
428 Field::key::<String>("sku"),
429 Field::cell::<rust_decimal::Decimal>("price"),
430 ]
431 }
432
433 fn write(&self, w: &mut dyn Writer) {
434 Storable::put(&self.sku, w);
435 Storable::put(&self.price, w);
436 }
437
438 fn read(r: &mut dyn Reader) -> Result<Self, StoreError> {
439 Ok(Self {
440 sku: Storable::take(r)?,
441 price: Storable::take(r)?,
442 })
443 }
444
445 fn write_id(&self, w: &mut dyn Writer) {
446 Storable::put(&self.sku, w);
447 }
448
449 fn read_id(&mut self, r: &mut dyn Reader) -> Result<(), StoreError> {
450 self.sku = Storable::take(r)?;
451 Ok(())
452 }
453 }
454
455 #[test]
456 fn spec() {
457 let spec = Post::spec();
458 assert_eq!(spec.table, Table("posts"));
459 assert_eq!(spec.rules, Vec::new());
460 assert_eq!(Post::spec().key(), Name("id"));
461 assert_eq!(Product::spec().key(), Name("sku"));
462 assert_eq!(Post::columns(), vec![Name("title")]);
463 assert_eq!(Post::search(), vec![Name("title")]);
464 }
465
466 #[test]
467 fn sorts() {
468 let schema = Post::spec();
469 assert_eq!(
470 Sort::parse("title", &schema),
471 Sort {
472 field: Name("title"),
473 order: Order::Asc,
474 }
475 );
476 assert_eq!(
477 Sort::parse("-title", &schema),
478 Sort {
479 field: Name("title"),
480 order: Order::Desc,
481 }
482 );
483 assert_eq!(
484 Sort::parse("junk", &schema),
485 Sort {
486 field: Name("id"),
487 order: Order::Asc,
488 }
489 );
490 }
491
492 #[test]
493 fn keys() {
494 assert_eq!(Key::parse("7", &Post::spec()).value(), Value::int(7));
495 assert_eq!(Key::parse("7", &Product::spec()).value(), Value::str("7"));
496 assert_eq!(Key::parse("x", &Post::spec()).value(), Value::str("x"));
497 }
498
499 #[tokio::test]
500 async fn gets() {
501 let path =
502 std::env::temp_dir().join(format!("rango-test-{}-gets.sqlite", std::process::id()));
503 let _ = std::fs::remove_file(&path);
504 let store = crate::store::sqlite::open(&path).await.unwrap();
505 let repo = Repository::<Post>::new(store);
506 let mut post = Post {
507 id: 0,
508 title: "hello".into(),
509 };
510 repo.save(&mut post).await.unwrap();
511 let found = repo.get(&Value::int(post.id)).await.unwrap().unwrap();
512 assert_eq!(found.title, "hello");
513 assert!(repo.get(&Value::int(999)).await.unwrap().is_none());
514 let projected = repo
515 .scan_query(&Query {
516 tree: Tree::And(Vec::new()),
517 sort: Vec::new(),
518 page: Page::all(),
519 only: Only::Some(vec![Name("title")]),
520 mass: None,
521 })
522 .await;
523 assert!(matches!(projected, Err(StoreError::Unsupported(_))));
524 }
525}