1use std::sync::Arc;
2
3use crate::{BoxFuture, Gather, Reader, Show, Storable, Store, StoreError, Value, Widget, Writer};
4
5#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
7pub struct Table(pub &'static str);
8
9#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
11pub struct Name(pub &'static str);
12
13impl Table {
14 pub fn as_str(&self) -> &'static str {
16 self.0
17 }
18}
19
20impl Name {
21 pub fn as_str(&self) -> &'static str {
23 self.0
24 }
25}
26
27impl std::fmt::Display for Table {
28 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
29 f.write_str(self.0)
30 }
31}
32
33impl std::fmt::Display for Name {
34 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
35 f.write_str(self.0)
36 }
37}
38
39#[derive(Clone, Debug, PartialEq)]
41pub struct Pick {
42 pub options: &'static [(&'static str, &'static str)],
44}
45
46#[derive(Clone, Debug, PartialEq)]
48pub struct Check(pub &'static str);
49
50#[derive(Clone, Debug, PartialEq)]
52pub enum Link {
53 To(Table, Name),
55 Via(Table, Name, Name),
57}
58
59#[derive(Clone, Debug, PartialEq)]
61pub enum Rule {
62 Same(Vec<Name>),
64 Hold(Name),
66 Said(Check),
68}
69
70#[derive(Clone, Copy, Debug, PartialEq, Eq)]
72pub enum Op {
73 Eq,
75 Ne,
77 More,
79 Less,
81 At,
83 Like,
85 Bare,
87}
88
89#[derive(Clone, Debug, PartialEq)]
91pub struct Filter {
92 pub field: Name,
94 pub op: Op,
96 pub value: Value,
98}
99
100#[derive(Clone, Debug, PartialEq)]
102pub enum Tree {
103 Leaf(Filter),
105 And(Vec<Tree>),
107 Or(Vec<Tree>),
109 Cut(Box<Tree>),
111}
112
113#[derive(Clone, Copy, Debug, PartialEq, Eq)]
115pub enum Order {
116 Asc,
118 Desc,
120}
121
122#[derive(Clone, Copy, Debug, PartialEq, Eq)]
124pub struct Sort {
125 pub field: Name,
127 pub order: Order,
129}
130
131impl Sort {
132 pub fn parse(raw: &str, schema: &Schema) -> Self {
134 let (name, order) = match raw.strip_prefix('-') {
135 Some(name) => (name, Order::Desc),
136 None => (raw, Order::Asc),
137 };
138 let known = schema
139 .fields
140 .iter()
141 .any(|field| field.name.as_str() == name);
142 if known {
143 for field in &schema.fields {
144 if field.name.as_str() == name {
145 return Sort {
146 field: field.name,
147 order,
148 };
149 }
150 }
151 }
152 Sort {
153 field: schema.key(),
154 order: Order::Asc,
155 }
156 }
157}
158
159#[derive(Clone, Copy, Debug, PartialEq, Eq)]
161pub struct Page {
162 pub count: usize,
164 pub offset: usize,
166}
167
168impl Page {
169 pub fn all() -> Self {
171 Self {
172 count: 0,
173 offset: 0,
174 }
175 }
176}
177
178#[derive(Clone, Debug, PartialEq, Eq)]
180pub enum Only {
181 All,
183 Some(Vec<Name>),
185 Lone,
187}
188
189#[derive(Clone, Copy, Debug, PartialEq, Eq)]
191pub enum Mass {
192 Count,
194 Sum(Name),
196 Mean(Name),
198 Low(Name),
200 High(Name),
202}
203
204#[derive(Clone, Debug, PartialEq)]
206pub struct Query {
207 pub tree: Tree,
209 pub sort: Vec<Sort>,
211 pub page: Page,
213 pub only: Only,
215 pub mass: Option<Mass>,
217}
218
219#[derive(Clone, Debug, PartialEq)]
221pub enum Key {
222 Int(i64),
224 Text(String),
226}
227
228impl Key {
229 pub fn of(value: &Value) -> Result<Self, StoreError> {
231 match value {
232 Value::Int(id) => Ok(Key::Int(*id)),
233 Value::Str(text) => Ok(Key::Text(text.clone())),
234 _ => Err(StoreError::Value("bad key".into())),
235 }
236 }
237
238 pub fn parse(raw: &str, schema: &Schema) -> Self {
240 let keyed = schema
241 .fields
242 .iter()
243 .any(|field| field.keyed && !field.id && field.name == schema.key());
244 if keyed {
245 return Key::Text(raw.into());
246 }
247 match raw.parse::<i64>() {
248 Ok(id) => Key::Int(id),
249 Err(_) => Key::Text(raw.into()),
250 }
251 }
252
253 pub fn value(&self) -> Value {
255 match self {
256 Key::Int(id) => Value::int(*id),
257 Key::Text(text) => Value::str(text),
258 }
259 }
260}
261
262#[derive(Clone)]
264pub struct Field {
265 pub name: Name,
267 pub dtype: &'static str,
269 pub id: bool,
271 pub keyed: bool,
273 pub many: bool,
275 pub optional: bool,
277 pub unique: bool,
279 pub index: bool,
281 pub pick: Option<Pick>,
283 pub default: Option<DefaultFn>,
285 pub link: Option<Link>,
287 pub widget: fn() -> Widget,
289 pub parse: fn(&str, &mut dyn Writer) -> Result<(), StoreError>,
291 pub text: fn(&mut dyn Reader) -> String,
293}
294
295pub type DefaultFn = Arc<dyn Fn(&mut dyn Writer) + Send + Sync + 'static>;
297
298impl Field {
299 pub fn cell<T: Storable + Show>(name: &'static str) -> Self {
301 Self {
302 name: Name(name),
303 dtype: T::dtype(),
304 id: false,
305 keyed: false,
306 many: false,
307 optional: false,
308 unique: false,
309 index: false,
310 pick: None,
311 default: None,
312 link: None,
313 widget: widget_of::<T>,
314 parse: parse_of::<T>,
315 text: text_of::<T>,
316 }
317 }
318
319 pub fn id() -> Self {
321 let field = Self::cell::<i64>("id");
322 Self {
323 id: true,
324 keyed: true,
325 ..field
326 }
327 }
328
329 pub fn key<T: Storable + Show>(name: &'static str) -> Self {
331 let field = Self::cell::<T>(name);
332 Self {
333 keyed: true,
334 ..field
335 }
336 }
337
338 pub fn str(name: &'static str) -> Self {
340 Self::cell::<String>(name)
341 }
342
343 pub fn check(name: &'static str) -> Self {
345 Self::cell::<bool>(name)
346 }
347
348 pub fn many(name: &'static str) -> Self {
350 Self {
351 name: Name(name),
352 dtype: "TEXT",
353 id: false,
354 keyed: false,
355 many: true,
356 optional: false,
357 unique: false,
358 index: false,
359 pick: None,
360 default: None,
361 link: None,
362 widget: nop_widget,
363 parse: nop_parse,
364 text: nop_text,
365 }
366 }
367
368 pub fn optional(mut self) -> Self {
370 self.optional = true;
371 self
372 }
373
374 pub fn unique(mut self) -> Self {
376 self.unique = true;
377 self
378 }
379
380 pub fn indexed(mut self) -> Self {
382 self.index = true;
383 self
384 }
385
386 pub fn default_value(mut self, value: impl Storable) -> Self {
388 self.default = Some(Arc::new(move |w| value.put(w)));
389 self
390 }
391
392 pub fn default(mut self, make: impl Fn(&mut dyn Writer) + Send + Sync + 'static) -> Self {
394 self.default = Some(Arc::new(make));
395 self
396 }
397
398 pub fn references(mut self, target: &'static str) -> Self {
400 let (table, column) = match target.split_once('.') {
401 Some((table, column)) => (table, column),
402 None => (target, "id"),
403 };
404 self.link = Some(Link::To(Table(table), Name(column)));
405 self
406 }
407
408 pub fn link(mut self, link: Link) -> Self {
410 self.link = Some(link);
411 self
412 }
413
414 pub fn reference(&self) -> Option<(Table, Name)> {
416 match self.link {
417 Some(Link::To(table, name)) => Some((table, name)),
418 _ => None,
419 }
420 }
421
422 pub fn load(&self) -> Widget {
424 (self.widget)()
425 }
426
427 pub fn initial(&self) -> Value {
429 match &self.default {
430 Some(make) => {
431 let mut gather = Gather::new();
432 make(&mut gather);
433 gather.value()
434 }
435 None => Value::Null,
436 }
437 }
438}
439
440fn widget_of<T: Show>() -> Widget {
441 T::widget()
442}
443
444fn parse_of<T: Storable + Show>(raw: &str, w: &mut dyn Writer) -> Result<(), StoreError> {
445 let value = T::parse(raw)?;
446 T::put(&value, w);
447 Ok(())
448}
449
450fn text_of<T: Storable + Show>(r: &mut dyn Reader) -> String {
451 match T::take(r) {
452 Ok(value) => T::text(&value),
453 Err(_) => String::new(),
454 }
455}
456
457fn nop_widget() -> Widget {
458 Widget::Text
459}
460
461fn nop_parse(_raw: &str, _w: &mut dyn Writer) -> Result<(), StoreError> {
462 Ok(())
463}
464
465fn nop_text(_r: &mut dyn Reader) -> String {
466 String::new()
467}
468
469#[derive(Clone)]
471pub struct Schema {
472 pub table: Table,
474 pub fields: Vec<Field>,
476 pub rules: Vec<Rule>,
478}
479
480impl Schema {
481 pub fn key(&self) -> Name {
483 self.fields
484 .iter()
485 .find(|field| field.id || field.keyed)
486 .map(|field| field.name)
487 .unwrap_or(Name("id"))
488 }
489}
490
491#[derive(Clone, Copy, Debug)]
493pub struct Action {
494 pub name: Name,
496 pub title: &'static str,
498 pub run: Run,
500 pub logged: bool,
502 pub row: bool,
504}
505
506pub type Run =
508 fn(Arc<dyn Store>, Schema, Vec<Key>) -> BoxFuture<'static, Result<String, StoreError>>;
509
510impl Action {
511 pub fn wipe() -> Self {
513 Self {
514 name: Name("wipe"),
515 title: "Delete",
516 run: wipe,
517 logged: true,
518 row: true,
519 }
520 }
521}
522
523fn wipe(
524 store: Arc<dyn Store>,
525 schema: Schema,
526 keys: Vec<Key>,
527) -> BoxFuture<'static, Result<String, StoreError>> {
528 Box::pin(async move {
529 for key in &keys {
530 store.remove(&schema, key).await?;
531 }
532 if keys.len() == 1 {
533 Ok("Deleted 1 row.".into())
534 } else {
535 Ok(format!("Deleted {} rows.", keys.len()))
536 }
537 })
538}
539
540#[cfg(test)]
541mod tests {
542 use super::*;
543 use rust_decimal::Decimal;
544
545 fn schema() -> Schema {
546 Schema {
547 table: Table("posts"),
548 fields: vec![Field::id(), Field::str("title")],
549 rules: Vec::new(),
550 }
551 }
552
553 fn keyed() -> Schema {
554 Schema {
555 table: Table("products"),
556 fields: vec![Field::key::<String>("sku"), Field::cell::<Decimal>("price")],
557 rules: Vec::new(),
558 }
559 }
560
561 #[test]
562 fn keys() {
563 assert_eq!(schema().key(), Name("id"));
564 assert_eq!(keyed().key(), Name("sku"));
565 assert_eq!(Key::parse("7", &schema()).value(), Value::int(7));
566 assert_eq!(Key::parse("7", &keyed()).value(), Value::str("7"));
567 assert_eq!(Key::parse("x", &schema()).value(), Value::str("x"));
568 }
569
570 #[test]
571 fn key_of() {
572 assert_eq!(Key::of(&Value::int(3)).unwrap(), Key::Int(3));
573 assert_eq!(Key::of(&Value::str("x")).unwrap(), Key::Text("x".into()));
574 assert!(Key::of(&Value::Null).is_err());
575 }
576
577 #[test]
578 fn sorts() {
579 let schema = schema();
580 assert_eq!(
581 Sort::parse("title", &schema),
582 Sort {
583 field: Name("title"),
584 order: Order::Asc,
585 }
586 );
587 assert_eq!(
588 Sort::parse("-title", &schema),
589 Sort {
590 field: Name("title"),
591 order: Order::Desc,
592 }
593 );
594 assert_eq!(
595 Sort::parse("junk", &schema),
596 Sort {
597 field: Name("id"),
598 order: Order::Asc,
599 }
600 );
601 }
602}