1#![warn(missing_docs)]
9
10mod engine;
11#[cfg(feature = "postgres")]
13pub mod postgres;
14pub mod spec;
16#[cfg(feature = "sqlite")]
18pub mod sqlite;
19
20pub use spec::{
21 Action, Check, Field, Filter, Key, Link, Mass, Name, Only, Op, Order, Page, Pick, Query, Rule,
22 Run, Schema, Sort, Table, Tree,
23};
24
25use std::{fmt, future::Future, pin::Pin, str::FromStr, sync::Arc};
26
27use chrono::{DateTime, NaiveDate, NaiveDateTime, NaiveTime, Utc};
28use rust_decimal::Decimal;
29use serde::Serialize;
30
31#[derive(Clone, Debug, PartialEq)]
33pub enum Value {
34 Null,
36 Int(i64),
38 Float(f64),
40 Str(String),
42 Bool(bool),
44 DateTime(DateTime<Utc>),
46 Decimal(Decimal),
48}
49
50impl Serialize for Value {
51 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
52 where
53 S: serde::Serializer,
54 {
55 match self {
56 Self::Null => serializer.serialize_none(),
57 Self::Int(value) => serializer.serialize_i64(*value),
58 Self::Float(value) => serializer.serialize_f64(*value),
59 Self::Str(value) => serializer.serialize_str(value),
60 Self::Bool(value) => serializer.serialize_bool(*value),
61 Self::DateTime(at) => serializer.serialize_str(&at.to_rfc3339()),
62 Self::Decimal(value) => serializer.serialize_str(&value.to_string()),
63 }
64 }
65}
66
67impl Value {
68 pub fn str(s: impl Into<String>) -> Self {
70 Self::Str(s.into())
71 }
72
73 pub fn int(v: i64) -> Self {
75 Self::Int(v)
76 }
77
78 pub fn float(v: f64) -> Self {
80 Self::Float(v)
81 }
82
83 pub fn bool(v: bool) -> Self {
85 Self::Bool(v)
86 }
87
88 pub fn datetime(at: DateTime<Utc>) -> Self {
90 Self::DateTime(at)
91 }
92
93 pub fn decimal(v: Decimal) -> Self {
95 Self::Decimal(v)
96 }
97}
98
99impl From<&String> for Value {
100 fn from(value: &String) -> Self {
101 Self::Str(value.clone())
102 }
103}
104
105impl From<&Option<String>> for Value {
106 fn from(value: &Option<String>) -> Self {
107 match value {
108 Some(value) => Self::Str(value.clone()),
109 None => Self::Null,
110 }
111 }
112}
113
114#[derive(Clone, Copy, Debug, PartialEq, Serialize)]
116pub enum ColumnKind {
117 Integer,
119 Real,
121 Text,
123}
124
125#[derive(Clone, Debug, PartialEq, Serialize)]
127pub struct Column {
128 pub name: String,
130 pub kind: ColumnKind,
132}
133
134#[derive(Clone, Debug, PartialEq, Serialize)]
136pub struct Row {
137 pub values: Vec<Value>,
139}
140
141impl Row {
142 pub fn get(&self, i: usize) -> Option<&Value> {
144 self.values.get(i)
145 }
146
147 pub fn int(&self, i: usize) -> Result<i64, StoreError> {
149 match self.values.get(i) {
150 Some(Value::Int(value)) => Ok(*value),
151 _ => Err(StoreError::Value(format!("row column {i} not int"))),
152 }
153 }
154
155 pub fn float(&self, i: usize) -> Result<f64, StoreError> {
157 match self.values.get(i) {
158 Some(Value::Float(value)) => Ok(*value),
159 _ => Err(StoreError::Value(format!("row column {i} not float"))),
160 }
161 }
162
163 pub fn str(&self, i: usize) -> Result<String, StoreError> {
165 match self.values.get(i) {
166 Some(Value::Str(value)) => Ok(value.clone()),
167 _ => Err(StoreError::Value(format!("row column {i} not str"))),
168 }
169 }
170
171 pub fn opt_str(&self, i: usize) -> Option<String> {
173 match self.values.get(i) {
174 Some(Value::Str(value)) => Some(value.clone()),
175 _ => None,
176 }
177 }
178
179 pub fn bool(&self, i: usize) -> Result<bool, StoreError> {
181 match self.values.get(i) {
182 Some(Value::Bool(value)) => Ok(*value),
183 Some(Value::Int(value)) => Ok(*value != 0),
184 _ => Err(StoreError::Value(format!("row column {i} not bool"))),
185 }
186 }
187
188 pub fn datetime(&self, i: usize) -> Result<DateTime<Utc>, StoreError> {
190 let bad = || StoreError::Value(format!("row column {i} not datetime"));
191 match self.values.get(i) {
192 Some(Value::DateTime(at)) => Ok(*at),
193 Some(Value::Int(stamp)) => DateTime::from_timestamp(*stamp, 0).ok_or_else(bad),
194 Some(Value::Str(text)) => text.parse::<DateTime<Utc>>().map_err(|_| bad()),
195 _ => Err(bad()),
196 }
197 }
198
199 pub fn decimal(&self, i: usize) -> Result<Decimal, StoreError> {
201 let bad = || StoreError::Value(format!("row column {i} not decimal"));
202 match self.values.get(i) {
203 Some(Value::Decimal(value)) => Ok(*value),
204 Some(Value::Str(text)) => text.parse::<Decimal>().map_err(|_| bad()),
205 _ => Err(bad()),
206 }
207 }
208
209 pub fn cells(&self) -> Cells<'_> {
211 Cells::new(&self.values)
212 }
213}
214
215pub type Rows = Vec<Row>;
217
218#[derive(Debug)]
220pub enum StoreError {
221 Sql(String),
223 Value(String),
225 Channel(String),
227 Io(String),
229 Unsupported(String),
231}
232
233impl fmt::Display for StoreError {
234 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
235 match self {
236 Self::Sql(msg)
237 | Self::Value(msg)
238 | Self::Channel(msg)
239 | Self::Io(msg)
240 | Self::Unsupported(msg) => write!(f, "{msg}"),
241 }
242 }
243}
244
245impl std::error::Error for StoreError {}
246
247#[derive(Clone, Copy, Debug, PartialEq, Eq)]
249pub enum Widget {
250 Text,
252 Int,
254 Flt,
256 Money,
258 Date,
260 Check,
262}
263
264pub trait Writer {
266 fn nothing(&mut self);
268 fn int(&mut self, value: i64);
270 fn flt(&mut self, value: f64);
272 fn str(&mut self, value: &str);
274}
275
276pub trait Reader {
278 fn nothing(&mut self) -> bool;
280 fn int(&mut self) -> Result<i64, StoreError>;
282 fn flt(&mut self) -> Result<f64, StoreError>;
284 fn str(&mut self) -> Result<String, StoreError>;
286}
287
288pub trait Storable: Send + Sync + 'static {
290 fn put(&self, w: &mut dyn Writer);
292 fn take(r: &mut dyn Reader) -> Result<Self, StoreError>
294 where
295 Self: Sized;
296 fn dtype() -> &'static str;
298}
299
300pub trait Show: Sized {
302 fn widget() -> Widget;
304 fn parse(raw: &str) -> Result<Self, StoreError>;
306 fn text(&self) -> String;
308}
309
310macro_rules! cells {
311 ($($cell:ty),*) => {$(
312 impl Storable for $cell {
313 fn put(&self, w: &mut dyn Writer) {
314 w.int(*self as i64);
315 }
316
317 fn take(r: &mut dyn Reader) -> Result<Self, StoreError> {
318 Ok(r.int()? as $cell)
319 }
320
321 fn dtype() -> &'static str {
322 "INTEGER"
323 }
324 }
325
326 impl Show for $cell {
327 fn widget() -> Widget {
328 Widget::Int
329 }
330
331 fn parse(raw: &str) -> Result<Self, StoreError> {
332 raw.parse::<$cell>()
333 .map_err(|_| StoreError::Value(format!("bad int {raw}")))
334 }
335
336 fn text(&self) -> String {
337 self.to_string()
338 }
339 }
340 )*};
341}
342
343cells!(i64, i32, i16, i8, u64, u32, u16, u8, isize, usize);
344
345impl Storable for String {
346 fn put(&self, w: &mut dyn Writer) {
347 w.str(self);
348 }
349
350 fn take(r: &mut dyn Reader) -> Result<Self, StoreError> {
351 r.str()
352 }
353
354 fn dtype() -> &'static str {
355 "TEXT"
356 }
357}
358
359impl Show for String {
360 fn widget() -> Widget {
361 Widget::Text
362 }
363
364 fn parse(raw: &str) -> Result<Self, StoreError> {
365 Ok(raw.into())
366 }
367
368 fn text(&self) -> String {
369 self.clone()
370 }
371}
372
373impl Storable for f64 {
374 fn put(&self, w: &mut dyn Writer) {
375 w.flt(*self);
376 }
377
378 fn take(r: &mut dyn Reader) -> Result<Self, StoreError> {
379 r.flt()
380 }
381
382 fn dtype() -> &'static str {
383 "REAL"
384 }
385}
386
387impl Show for f64 {
388 fn widget() -> Widget {
389 Widget::Flt
390 }
391
392 fn parse(raw: &str) -> Result<Self, StoreError> {
393 raw.parse::<f64>()
394 .map_err(|_| StoreError::Value(format!("bad float {raw}")))
395 }
396
397 fn text(&self) -> String {
398 self.to_string()
399 }
400}
401
402impl Storable for bool {
403 fn put(&self, w: &mut dyn Writer) {
404 w.int(if *self { 1 } else { 0 });
405 }
406
407 fn take(r: &mut dyn Reader) -> Result<Self, StoreError> {
408 Ok(r.int()? != 0)
409 }
410
411 fn dtype() -> &'static str {
412 "INTEGER"
413 }
414}
415
416impl Show for bool {
417 fn widget() -> Widget {
418 Widget::Check
419 }
420
421 fn parse(raw: &str) -> Result<Self, StoreError> {
422 match raw {
423 "1" | "true" | "yes" | "on" => Ok(true),
424 "0" | "false" | "no" | "off" | "" => Ok(false),
425 _ => Err(StoreError::Value(format!("bad bool {raw}"))),
426 }
427 }
428
429 fn text(&self) -> String {
430 self.to_string()
431 }
432}
433
434impl Storable for Decimal {
435 fn put(&self, w: &mut dyn Writer) {
436 w.str(&self.to_string());
437 }
438
439 fn take(r: &mut dyn Reader) -> Result<Self, StoreError> {
440 Decimal::from_str(&r.str()?).map_err(|_| StoreError::Value("bad decimal".into()))
441 }
442
443 fn dtype() -> &'static str {
444 "TEXT"
445 }
446}
447
448impl Show for Decimal {
449 fn widget() -> Widget {
450 Widget::Money
451 }
452
453 fn parse(raw: &str) -> Result<Self, StoreError> {
454 Decimal::from_str(raw).map_err(|_| StoreError::Value(format!("bad decimal {raw}")))
455 }
456
457 fn text(&self) -> String {
458 self.to_string()
459 }
460}
461
462impl Storable for DateTime<Utc> {
463 fn put(&self, w: &mut dyn Writer) {
464 w.int(self.timestamp());
465 }
466
467 fn take(r: &mut dyn Reader) -> Result<Self, StoreError> {
468 DateTime::from_timestamp(r.int()?, 0)
469 .ok_or_else(|| StoreError::Value("bad datetime".into()))
470 }
471
472 fn dtype() -> &'static str {
473 "INTEGER"
474 }
475}
476
477impl Show for DateTime<Utc> {
478 fn widget() -> Widget {
479 Widget::Date
480 }
481
482 fn parse(raw: &str) -> Result<Self, StoreError> {
483 let bad = || StoreError::Value(format!("bad date {raw}"));
484 if let Ok(at) = DateTime::parse_from_rfc3339(raw) {
485 return Ok(at.with_timezone(&Utc));
486 }
487 if let Ok(at) = NaiveDateTime::parse_from_str(raw, "%Y-%m-%dT%H:%M") {
488 return Ok(at.and_utc());
489 }
490 NaiveDate::parse_from_str(raw, "%Y-%m-%d")
491 .map(|day| day.and_time(NaiveTime::MIN).and_utc())
492 .map_err(|_| bad())
493 }
494
495 fn text(&self) -> String {
496 self.format("%Y-%m-%d %H:%M").to_string()
497 }
498}
499
500impl Storable for NaiveDate {
501 fn put(&self, w: &mut dyn Writer) {
502 w.str(&self.format("%Y-%m-%d").to_string());
503 }
504
505 fn take(r: &mut dyn Reader) -> Result<Self, StoreError> {
506 NaiveDate::parse_from_str(&r.str()?, "%Y-%m-%d")
507 .map_err(|_| StoreError::Value("bad date".into()))
508 }
509
510 fn dtype() -> &'static str {
511 "TEXT"
512 }
513}
514
515impl Show for NaiveDate {
516 fn widget() -> Widget {
517 Widget::Date
518 }
519
520 fn parse(raw: &str) -> Result<Self, StoreError> {
521 NaiveDate::parse_from_str(raw, "%Y-%m-%d")
522 .map_err(|_| StoreError::Value(format!("bad date {raw}")))
523 }
524
525 fn text(&self) -> String {
526 self.format("%Y-%m-%d").to_string()
527 }
528}
529
530impl Storable for NaiveTime {
531 fn put(&self, w: &mut dyn Writer) {
532 w.str(&self.format("%H:%M:%S").to_string());
533 }
534
535 fn take(r: &mut dyn Reader) -> Result<Self, StoreError> {
536 NaiveTime::parse_from_str(&r.str()?, "%H:%M:%S")
537 .map_err(|_| StoreError::Value("bad time".into()))
538 }
539
540 fn dtype() -> &'static str {
541 "TEXT"
542 }
543}
544
545impl Show for NaiveTime {
546 fn widget() -> Widget {
547 Widget::Text
548 }
549
550 fn parse(raw: &str) -> Result<Self, StoreError> {
551 NaiveTime::parse_from_str(raw, "%H:%M:%S")
552 .map_err(|_| StoreError::Value(format!("bad time {raw}")))
553 }
554
555 fn text(&self) -> String {
556 self.format("%H:%M").to_string()
557 }
558}
559
560impl<T: Storable> Storable for Option<T> {
561 fn put(&self, w: &mut dyn Writer) {
562 match self {
563 Some(value) => value.put(w),
564 None => w.nothing(),
565 }
566 }
567
568 fn take(r: &mut dyn Reader) -> Result<Self, StoreError> {
569 if r.nothing() {
570 Ok(None)
571 } else {
572 Ok(Some(T::take(r)?))
573 }
574 }
575
576 fn dtype() -> &'static str {
577 T::dtype()
578 }
579}
580
581pub struct Gather {
583 value: Value,
584}
585
586impl Gather {
587 pub fn new() -> Self {
589 Self { value: Value::Null }
590 }
591
592 pub fn value(self) -> Value {
594 self.value
595 }
596}
597
598impl Default for Gather {
599 fn default() -> Self {
600 Self::new()
601 }
602}
603
604impl Writer for Gather {
605 fn nothing(&mut self) {
606 self.value = Value::Null;
607 }
608
609 fn int(&mut self, value: i64) {
610 self.value = Value::Int(value);
611 }
612
613 fn flt(&mut self, value: f64) {
614 self.value = Value::Float(value);
615 }
616
617 fn str(&mut self, value: &str) {
618 self.value = Value::Str(value.into());
619 }
620}
621
622pub struct Slots {
624 values: Vec<Value>,
625}
626
627impl Slots {
628 pub fn new() -> Self {
630 Self { values: Vec::new() }
631 }
632
633 pub fn values(self) -> Vec<Value> {
635 self.values
636 }
637}
638
639impl Default for Slots {
640 fn default() -> Self {
641 Self::new()
642 }
643}
644
645impl Writer for Slots {
646 fn nothing(&mut self) {
647 self.values.push(Value::Null);
648 }
649
650 fn int(&mut self, value: i64) {
651 self.values.push(Value::Int(value));
652 }
653
654 fn flt(&mut self, value: f64) {
655 self.values.push(Value::Float(value));
656 }
657
658 fn str(&mut self, value: &str) {
659 self.values.push(Value::Str(value.into()));
660 }
661}
662
663pub struct Cells<'a> {
665 values: &'a [Value],
666 at: usize,
667}
668
669impl<'a> Cells<'a> {
670 pub fn new(values: &'a [Value]) -> Self {
672 Self { values, at: 0 }
673 }
674}
675
676impl Reader for Cells<'_> {
677 fn nothing(&mut self) -> bool {
678 match self.values.get(self.at) {
679 Some(Value::Null) => {
680 self.at += 1;
681 true
682 }
683 None => true,
684 Some(_) => false,
685 }
686 }
687
688 fn int(&mut self) -> Result<i64, StoreError> {
689 let bad = || StoreError::Value(format!("cell {} not int", self.at));
690 match self.values.get(self.at) {
691 Some(Value::Int(value)) => {
692 self.at += 1;
693 Ok(*value)
694 }
695 Some(Value::Bool(value)) => {
696 self.at += 1;
697 Ok(*value as i64)
698 }
699 Some(Value::DateTime(at)) => {
700 self.at += 1;
701 Ok(at.timestamp())
702 }
703 _ => Err(bad()),
704 }
705 }
706
707 fn flt(&mut self) -> Result<f64, StoreError> {
708 let at = self.at;
709 let bad = || StoreError::Value(format!("cell {at} not float"));
710 match self.values.get(at) {
711 Some(Value::Float(value)) => {
712 self.at += 1;
713 Ok(*value)
714 }
715 Some(Value::Int(value)) => {
716 self.at += 1;
717 Ok(*value as f64)
718 }
719 Some(Value::Bool(value)) => {
720 self.at += 1;
721 Ok(*value as i64 as f64)
722 }
723 Some(Value::Decimal(value)) => {
724 self.at += 1;
725 value.to_string().parse::<f64>().map_err(|_| bad())
726 }
727 _ => Err(bad()),
728 }
729 }
730
731 fn str(&mut self) -> Result<String, StoreError> {
732 let at = self.at;
733 let bad = || StoreError::Value(format!("cell {at} not str"));
734 match self.values.get(at) {
735 Some(Value::Str(value)) => {
736 self.at += 1;
737 Ok(value.clone())
738 }
739 Some(Value::Decimal(value)) => {
740 self.at += 1;
741 Ok(value.to_string())
742 }
743 Some(Value::DateTime(value)) => {
744 self.at += 1;
745 Ok(value.to_rfc3339())
746 }
747 _ => Err(bad()),
748 }
749 }
750}
751
752pub type BoxFuture<'a, T> = Pin<Box<dyn Future<Output = T> + Send + 'a>>;
754
755pub trait Store: Send + Sync + 'static {
757 fn execute<'a>(
759 &'a self,
760 sql: &'a str,
761 params: &'a [Value],
762 ) -> BoxFuture<'a, Result<usize, StoreError>>;
763
764 fn fetch<'a>(
766 &'a self,
767 sql: &'a str,
768 params: &'a [Value],
769 kinds: &'a [ColumnKind],
770 ) -> BoxFuture<'a, Result<Rows, StoreError>>;
771
772 fn columns<'a>(&'a self, table: &'a str) -> BoxFuture<'a, Result<Vec<Column>, StoreError>> {
774 let _ = table;
775 Box::pin(async { Err(StoreError::Unsupported("columns".into())) })
776 }
777
778 fn scan_query<'a>(
780 &'a self,
781 schema: &'a Schema,
782 query: &'a Query,
783 ) -> BoxFuture<'a, Result<Rows, StoreError>>;
784
785 fn total_query<'a>(
787 &'a self,
788 schema: &'a Schema,
789 query: &'a Query,
790 ) -> BoxFuture<'a, Result<usize, StoreError>>;
791
792 fn define<'a>(&'a self, schema: &'a Schema) -> BoxFuture<'a, Result<(), StoreError>>;
794
795 fn create<'a>(
797 &'a self,
798 schema: &'a Schema,
799 batch: &'a [Vec<(Name, Value)>],
800 ) -> BoxFuture<'a, Result<Vec<Key>, StoreError>>;
801
802 fn replace<'a>(
804 &'a self,
805 schema: &'a Schema,
806 key: &'a Key,
807 cells: &'a [(Name, Value)],
808 ) -> BoxFuture<'a, Result<(), StoreError>>;
809
810 fn upsert<'a>(
812 &'a self,
813 schema: &'a Schema,
814 batch: &'a [Vec<(Name, Value)>],
815 ) -> BoxFuture<'a, Result<usize, StoreError>>;
816
817 fn remove<'a>(
819 &'a self,
820 schema: &'a Schema,
821 key: &'a Key,
822 ) -> BoxFuture<'a, Result<(), StoreError>>;
823
824 fn evolve<'a>(
826 &'a self,
827 schema: &'a Schema,
828 drop: bool,
829 ) -> BoxFuture<'a, Result<usize, StoreError>>;
830
831 fn mass<'a>(
833 &'a self,
834 schema: &'a Schema,
835 query: &'a Query,
836 ) -> BoxFuture<'a, Result<Value, StoreError>>;
837
838 fn last_id<'a>(&'a self, table: &'a str) -> BoxFuture<'a, Result<i64, StoreError>>;
840
841 fn insert<'a>(
843 &'a self,
844 table: &'a str,
845 columns: &'a [String],
846 values: &'a [Value],
847 ) -> BoxFuture<'a, Result<i64, StoreError>> {
848 Box::pin(async move {
849 let cols = columns
850 .iter()
851 .map(|name| format!("\"{name}\""))
852 .collect::<Vec<_>>()
853 .join(", ");
854 let marks = vec!["?"; columns.len()].join(", ");
855 let sql = format!("INSERT INTO \"{table}\" ({cols}) VALUES ({marks})");
856 self.execute(&sql, values).await?;
857 self.last_id(table).await
858 })
859 }
860
861 fn deal<'a>(&'a self) -> BoxFuture<'a, Result<Arc<dyn Store>, StoreError>> {
863 Box::pin(async { Err(StoreError::Unsupported("deal".into())) })
864 }
865
866 fn settle(self: Arc<Self>, commit: bool) -> BoxFuture<'static, Result<(), StoreError>> {
868 Box::pin(async move {
869 let _ = commit;
870 Ok(())
871 })
872 }
873}
874
875#[cfg(test)]
876mod tests {
877 use super::*;
878
879 #[test]
880 fn opt() {
881 let row = Row {
882 values: vec![Value::Str("a".into()), Value::Null, Value::Int(3)],
883 };
884 assert_eq!(row.opt_str(0), Some("a".into()));
885 assert_eq!(row.opt_str(1), None);
886 assert_eq!(row.opt_str(2), None);
887 assert_eq!(row.opt_str(9), None);
888 assert_eq!(Value::from(&"a".to_string()), Value::Str("a".into()));
889 assert_eq!(Value::from(&Some("a".to_string())), Value::Str("a".into()));
890 let none: Option<String> = None;
891 assert_eq!(Value::from(&none), Value::Null);
892 }
893
894 #[test]
895 fn json() {
896 assert_eq!(serde_json::to_string(&Value::Null).unwrap(), "null");
897 assert_eq!(serde_json::to_string(&Value::Int(12)).unwrap(), "12");
898 assert_eq!(serde_json::to_string(&Value::Float(1.5)).unwrap(), "1.5");
899 assert_eq!(
900 serde_json::to_string(&Value::Str("hi".into())).unwrap(),
901 "\"hi\""
902 );
903 assert_eq!(serde_json::to_string(&Value::Bool(true)).unwrap(), "true");
904 let at = DateTime::from_timestamp(0, 0).unwrap();
905 assert_eq!(
906 serde_json::to_string(&Value::datetime(at)).unwrap(),
907 "\"1970-01-01T00:00:00+00:00\""
908 );
909 let money = Decimal::from_str_exact("1.50").unwrap();
910 assert_eq!(
911 serde_json::to_string(&Value::decimal(money)).unwrap(),
912 "\"1.50\""
913 );
914 assert_eq!(
915 serde_json::to_string(&ColumnKind::Integer).unwrap(),
916 "\"Integer\""
917 );
918 let column = Column {
919 name: "posts".into(),
920 kind: ColumnKind::Text,
921 };
922 assert_eq!(
923 serde_json::to_string(&column).unwrap(),
924 "{\"name\":\"posts\",\"kind\":\"Text\"}"
925 );
926 let row = Row {
927 values: vec![Value::Int(1), Value::Null],
928 };
929 assert_eq!(
930 serde_json::to_string(&row).unwrap(),
931 "{\"values\":[1,null]}"
932 );
933 }
934}