1use core::marker::PhantomData;
102use core::ops::{Bound, RangeBounds};
103
104use yo_common::{Code, Error, Result};
105use yo_shape::{Shape, Tag};
106
107use crate::db::Handle;
108
109pub use yo_doc::{Builder, Doc, IndexKind, Key};
110
111pub trait Field: Shape + Sized {
118 fn write(&self, b: &mut Builder) -> Result<()>;
125
126 fn read(d: Doc<'_>) -> Result<Self>;
133
134 fn missing(name: &str) -> Result<Self> {
145 Err(Error::fmt(
146 Code::Corrupt,
147 format_args!(
148 "this document has no {name}, and the field is not an Option. Either the collection holds something written under another shape, or the field was added without a default"
149 ),
150 ))
151 }
152}
153
154pub trait Query {
160 fn key(&self, kind: IndexKind) -> Option<Key>;
163}
164
165pub trait Asked: Query {
172 type Ask: Query + ?Sized;
175}
176
177macro_rules! asks_for_itself {
178 ($($t:ty),* $(,)?) => {
179 $(impl Asked for $t {
180 type Ask = $t;
181 })*
182 };
183}
184
185asks_for_itself!(i8, i16, i32, i64, u8, u16, u32, u64, f32, f64, bool);
186
187impl Asked for String {
188 type Ask = str;
189}
190
191#[diagnostic::on_unimplemented(
195 message = "`{Self}` is not a document",
196 label = "this type has no id",
197 note = "add `#[derive(Yo)]` to it and mark one field `#[yo(id)]`, which is what a document is stored under"
198)]
199pub trait Document: Field + Indexed {
200 type Id: Field + Asked;
202
203 fn id(&self) -> &Self::Id;
205}
206
207pub trait Indexed {
214 const INDEXES: &'static [(&'static str, IndexKind)];
216}
217
218pub struct Path<T, V> {
226 path: &'static str,
227 kind: IndexKind,
228 marker: PhantomData<fn() -> (T, V)>,
231}
232
233impl<T, V> Clone for Path<T, V> {
234 fn clone(&self) -> Path<T, V> {
235 *self
236 }
237}
238
239impl<T, V> Copy for Path<T, V> {}
240
241impl<T, V> core::fmt::Debug for Path<T, V> {
242 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
243 f.debug_struct("Path")
244 .field("path", &self.path)
245 .field("kind", &self.kind)
246 .finish()
247 }
248}
249
250impl<T, V> Path<T, V> {
251 #[must_use]
258 pub const fn new(path: &'static str, kind: IndexKind) -> Path<T, V> {
259 Path {
260 path,
261 kind,
262 marker: PhantomData,
263 }
264 }
265
266 #[must_use]
268 pub const fn path(&self) -> &'static str {
269 self.path
270 }
271
272 #[must_use]
274 pub const fn kind(&self) -> IndexKind {
275 self.kind
276 }
277}
278
279pub struct Ordered<T, V> {
288 path: Path<T, V>,
289}
290
291impl<T, V> Clone for Ordered<T, V> {
292 fn clone(&self) -> Ordered<T, V> {
293 *self
294 }
295}
296
297impl<T, V> Copy for Ordered<T, V> {}
298
299impl<T, V> core::fmt::Debug for Ordered<T, V> {
300 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
301 f.debug_struct("Ordered")
302 .field("path", &self.path.path)
303 .finish()
304 }
305}
306
307impl<T, V> Ordered<T, V> {
308 #[must_use]
312 pub const fn new(path: &'static str) -> Ordered<T, V> {
313 Ordered {
314 path: Path::new(path, IndexKind::Ordered),
315 }
316 }
317
318 #[must_use]
320 pub const fn path(&self) -> &'static str {
321 self.path.path
322 }
323}
324
325impl<T, V> From<Ordered<T, V>> for Path<T, V> {
328 fn from(o: Ordered<T, V>) -> Path<T, V> {
329 o.path
330 }
331}
332
333pub struct Docs<T> {
338 db: Handle,
339 at: usize,
340 tag: Tag,
341 marker: PhantomData<fn() -> T>,
342}
343
344impl<T> Clone for Docs<T> {
345 fn clone(&self) -> Docs<T> {
346 Docs {
347 db: self.db.clone(),
348 at: self.at,
349 tag: self.tag,
350 marker: PhantomData,
351 }
352 }
353}
354
355impl<T> core::fmt::Debug for Docs<T> {
356 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
357 let name = self
358 .db
359 .read(|inner| Ok(inner.collections[self.at].name.clone()))
360 .unwrap_or_else(|_| "?".to_owned());
361 f.debug_struct("Docs").field("name", &name).finish()
362 }
363}
364
365impl<T: Document> Docs<T> {
366 pub(crate) fn new(db: Handle, at: usize, tag: Tag) -> Docs<T> {
367 Docs {
368 db,
369 at,
370 tag,
371 marker: PhantomData,
372 }
373 }
374
375 pub fn name(&self) -> Result<String> {
382 self.db
383 .read(|inner| Ok(inner.collections[self.at].name.clone()))
384 }
385
386 #[must_use]
388 pub fn tag(&self) -> Tag {
389 self.tag
390 }
391
392 pub fn put(&self, doc: &T) -> Result<bool> {
403 let id = key_of(doc.id(), IndexKind::Equality, "the id")?;
404 self.write(|c| {
405 c.scratch.clear();
406 Field::write(doc, &mut c.scratch)?;
407 let bytes = c.scratch.finish()?;
408 c.docs.put_bytes(id.as_bytes(), bytes)
409 })
410 }
411
412 pub fn get(&self, id: &<T::Id as Asked>::Ask) -> Result<Option<T>> {
418 let id = key_of(id, IndexKind::Equality, "the id")?;
419 self.read(|docs| match docs.get(id.as_bytes()) {
420 Some(doc) => T::read(doc).map(Some),
421 None => Ok(None),
422 })
423 }
424
425 pub fn contains(&self, id: &<T::Id as Asked>::Ask) -> Result<bool> {
431 let id = key_of(id, IndexKind::Equality, "the id")?;
432 self.read(|docs| Ok(docs.contains(id.as_bytes())))
433 }
434
435 pub fn remove(&self, id: &<T::Id as Asked>::Ask) -> Result<bool> {
441 let id = key_of(id, IndexKind::Equality, "the id")?;
442 self.write(|c| Ok(c.docs.remove(id.as_bytes())))
443 }
444
445 pub fn len(&self) -> Result<usize> {
452 self.read(|docs| Ok(docs.len()))
453 }
454
455 pub fn is_empty(&self) -> Result<bool> {
461 self.read(|docs| Ok(docs.is_empty()))
462 }
463
464 pub fn all(&self) -> Result<Vec<T>> {
473 self.read(|docs| {
474 let mut out = Vec::with_capacity(docs.len());
475 for (_, doc) in docs.iter() {
476 out.push(T::read(doc)?);
477 }
478 Ok(out)
479 })
480 }
481
482 pub fn find<V: Asked>(&self, path: impl Into<Path<T, V>>, value: &V::Ask) -> Result<Vec<T>> {
494 let path = path.into();
495 let key = key_of(value, path.kind, path.path)?;
496 self.read(|docs| {
497 let mut out = Vec::new();
498 let mut bad = Ok(());
499 docs.find(path.path, &key, |_, doc| {
500 if bad.is_ok() {
501 match T::read(doc) {
502 Ok(v) => out.push(v),
503 Err(e) => bad = Err(e),
504 }
505 }
506 })?;
507 bad?;
508 Ok(out)
509 })
510 }
511
512 pub fn count<V: Asked>(&self, path: impl Into<Path<T, V>>, value: &V::Ask) -> Result<usize> {
521 let path = path.into();
522 let key = key_of(value, path.kind, path.path)?;
523 self.read(|docs| docs.count(path.path, &key))
524 }
525
526 pub fn range<V: Asked, R: RangeBounds<V::Ask>>(
537 &self,
538 path: Ordered<T, V>,
539 range: R,
540 ) -> Result<Vec<T>> {
541 let path = path.path();
542 let (lo, hi) = bounds(&range, path)?;
543 self.read(|docs| {
544 let mut out = Vec::new();
545 let mut bad = Ok(());
546 docs.range(path, as_ref(&lo), as_ref(&hi), |_, doc| {
547 if bad.is_ok() {
548 match T::read(doc) {
549 Ok(v) => out.push(v),
550 Err(e) => bad = Err(e),
551 }
552 }
553 })?;
554 bad?;
555 Ok(out)
556 })
557 }
558
559 pub fn range_rev<V: Asked, R: RangeBounds<V::Ask>>(
565 &self,
566 path: Ordered<T, V>,
567 range: R,
568 ) -> Result<Vec<T>> {
569 let path = path.path();
570 let (lo, hi) = bounds(&range, path)?;
571 self.read(|docs| {
572 let mut out = Vec::new();
573 let mut bad = Ok(());
574 docs.range_rev(path, as_ref(&lo), as_ref(&hi), |_, doc| {
575 if bad.is_ok() {
576 match T::read(doc) {
577 Ok(v) => out.push(v),
578 Err(e) => bad = Err(e),
579 }
580 }
581 })?;
582 bad?;
583 Ok(out)
584 })
585 }
586
587 pub fn count_range<V: Asked, R: RangeBounds<V::Ask>>(
597 &self,
598 path: Ordered<T, V>,
599 range: R,
600 ) -> Result<usize> {
601 let path = path.path();
602 let (lo, hi) = bounds(&range, path)?;
603 self.read(|docs| docs.count_range(path, as_ref(&lo), as_ref(&hi)))
604 }
605
606 pub fn memory_bytes(&self) -> Result<usize> {
613 self.read(|docs| Ok(docs.memory_bytes()))
614 }
615
616 fn read<R>(&self, f: impl FnOnce(&yo_doc::Docs) -> Result<R>) -> Result<R> {
617 self.db
618 .read(|inner| f(inner.collections[self.at].data.docs()))
619 }
620
621 fn write<R>(&self, f: impl FnOnce(&mut Documents) -> Result<R>) -> Result<R> {
622 self.db
623 .write(|inner| f(inner.collections[self.at].data.docs_mut()))
624 }
625}
626
627pub(crate) struct Documents {
632 pub(crate) docs: yo_doc::Docs,
633 pub(crate) scratch: Builder,
634}
635
636impl Documents {
637 pub(crate) fn new() -> Documents {
638 Documents {
639 docs: yo_doc::Docs::new(),
640 scratch: Builder::new(),
641 }
642 }
643}
644
645pub(crate) fn key_of<Q: Query + ?Sized>(value: &Q, kind: IndexKind, what: &str) -> Result<Key> {
647 let key = value.key(kind).ok_or_else(|| {
651 let why = if kind == IndexKind::Text {
652 "a text index holds one word at a time, and this is not one word"
653 } else {
654 "an index does not file this type, so it cannot be looked up"
655 };
656 Error::fmt(Code::Invalid, format_args!("{what}: {why}"))
657 })?;
658 if key.is_too_long() {
659 return Err(Error::fmt(
660 Code::Full,
661 format_args!(
662 "{what} is longer than {} bytes, which is as long as a key can be",
663 yo_doc::KEY_MAX
664 ),
665 ));
666 }
667 Ok(key)
668}
669
670fn bounds<Q, R>(range: &R, path: &str) -> Result<(Bound<Key>, Bound<Key>)>
673where
674 Q: Query + ?Sized,
675 R: RangeBounds<Q>,
676{
677 Ok((
678 one(range.start_bound(), path)?,
679 one(range.end_bound(), path)?,
680 ))
681}
682
683fn one<Q: Query + ?Sized>(b: Bound<&Q>, path: &str) -> Result<Bound<Key>> {
684 Ok(match b {
685 Bound::Included(v) => Bound::Included(key_of(v, IndexKind::Ordered, path)?),
686 Bound::Excluded(v) => Bound::Excluded(key_of(v, IndexKind::Ordered, path)?),
687 Bound::Unbounded => Bound::Unbounded,
688 })
689}
690
691fn as_ref(b: &Bound<Key>) -> Bound<&Key> {
692 match b {
693 Bound::Included(k) => Bound::Included(k),
694 Bound::Excluded(k) => Bound::Excluded(k),
695 Bound::Unbounded => Bound::Unbounded,
696 }
697}
698
699pub fn at<V: Field>(d: Doc<'_>, name: &str) -> Result<V> {
706 match d.get(name.as_bytes()) {
707 Some(at) => V::read(at),
708 None => V::missing(name),
709 }
710}
711
712pub fn expect_object(d: Doc<'_>, name: &str) -> Result<()> {
719 if d.kind() == yo_doc::Kind::Object {
720 return Ok(());
721 }
722 Err(Error::fmt(
723 Code::Corrupt,
724 format_args!("a {name} in this collection is stored as {:?}", d.kind()),
725 ))
726}
727
728fn not_a(want: &str, d: Doc<'_>) -> Error {
729 Error::fmt(
730 Code::Corrupt,
731 format_args!(
732 "this field should be a {want} and is stored as {:?}",
733 d.kind()
734 ),
735 )
736}
737
738macro_rules! ints {
739 ($($t:ty),* $(,)?) => {
740 $(
741 impl Field for $t {
742 fn write(&self, b: &mut Builder) -> Result<()> {
743 b.int(i64::from(*self))
744 }
745
746 fn read(d: Doc<'_>) -> Result<$t> {
747 let n = d.as_int().ok_or_else(|| not_a(stringify!($t), d))?;
748 <$t>::try_from(n).map_err(|_| {
749 Error::fmt(
750 Code::Corrupt,
751 format_args!("{n} does not fit in a {}", stringify!($t)),
752 )
753 })
754 }
755 }
756
757 impl Query for $t {
758 fn key(&self, _kind: IndexKind) -> Option<Key> {
759 Some(Key::int(i64::from(*self)))
760 }
761 }
762 )*
763 };
764}
765
766ints!(i8, i16, i32, i64, u8, u16, u32);
767
768impl Field for u64 {
772 fn write(&self, b: &mut Builder) -> Result<()> {
773 match i64::try_from(*self) {
774 Ok(n) => b.int(n),
775 Err(_) => Err(Error::fmt(
776 Code::Invalid,
777 format_args!(
778 "{self} is past i64::MAX, and a document holds one number type, which is signed"
779 ),
780 )),
781 }
782 }
783
784 fn read(d: Doc<'_>) -> Result<u64> {
785 let n = d.as_int().ok_or_else(|| not_a("u64", d))?;
786 u64::try_from(n).map_err(|_| {
787 Error::fmt(
788 Code::Corrupt,
789 format_args!("{n} is negative and this field is a u64"),
790 )
791 })
792 }
793}
794
795impl Query for u64 {
796 fn key(&self, _kind: IndexKind) -> Option<Key> {
797 i64::try_from(*self).ok().map(Key::int)
798 }
799}
800
801macro_rules! floats {
802 ($($t:ty),* $(,)?) => {
803 $(
804 impl Field for $t {
805 fn write(&self, b: &mut Builder) -> Result<()> {
806 b.float(f64::from(*self))
807 }
808
809 fn read(d: Doc<'_>) -> Result<$t> {
810 match (d.as_float(), d.as_int()) {
814 (Some(v), _) => Ok(v as $t),
815 (None, Some(n)) => Ok(n as $t),
816 (None, None) => Err(not_a(stringify!($t), d)),
817 }
818 }
819 }
820
821 impl Query for $t {
822 fn key(&self, _kind: IndexKind) -> Option<Key> {
823 Some(Key::float(f64::from(*self)))
824 }
825 }
826 )*
827 };
828}
829
830floats!(f32, f64);
831
832impl Field for bool {
833 fn write(&self, b: &mut Builder) -> Result<()> {
834 b.bool(*self)
835 }
836
837 fn read(d: Doc<'_>) -> Result<bool> {
838 d.as_bool().ok_or_else(|| not_a("bool", d))
839 }
840}
841
842impl Query for bool {
843 fn key(&self, _kind: IndexKind) -> Option<Key> {
844 Some(Key::bool(*self))
845 }
846}
847
848impl Field for String {
849 fn write(&self, b: &mut Builder) -> Result<()> {
850 b.text(self)
851 }
852
853 fn read(d: Doc<'_>) -> Result<String> {
854 d.as_text()
855 .map(str::to_owned)
856 .ok_or_else(|| not_a("string", d))
857 }
858}
859
860impl Query for String {
861 fn key(&self, kind: IndexKind) -> Option<Key> {
862 self.as_str().key(kind)
863 }
864}
865
866impl Query for str {
868 fn key(&self, kind: IndexKind) -> Option<Key> {
869 match kind {
870 IndexKind::Text => Key::word(self),
873 _ => Some(Key::text(self)),
874 }
875 }
876}
877
878impl<T: Field> Field for Option<T> {
882 fn write(&self, b: &mut Builder) -> Result<()> {
883 match self {
884 Some(v) => v.write(b),
885 None => b.null(),
886 }
887 }
888
889 fn read(d: Doc<'_>) -> Result<Option<T>> {
890 if d.is_null() {
891 return Ok(None);
892 }
893 T::read(d).map(Some)
894 }
895
896 fn missing(_name: &str) -> Result<Option<T>> {
897 Ok(None)
898 }
899}
900
901impl<T: Field> Field for Vec<T> {
902 fn write(&self, b: &mut Builder) -> Result<()> {
903 b.begin_array()?;
904 for v in self {
905 v.write(b)?;
906 }
907 b.end_array()
908 }
909
910 fn read(d: Doc<'_>) -> Result<Vec<T>> {
911 if d.kind() != yo_doc::Kind::Array {
912 return Err(not_a("list", d));
913 }
914 let mut out = Vec::with_capacity(d.len());
915 for elem in d.iter() {
916 out.push(T::read(elem)?);
917 }
918 Ok(out)
919 }
920}
921
922#[cfg(test)]
923mod tests {
924 use super::*;
925 use crate::{Yo, open};
926
927 #[derive(Yo, Debug, Clone, PartialEq)]
928 struct Order {
929 #[yo(id)]
930 id: u64,
931 #[yo(index)]
932 status: String,
933 #[yo(ordered)]
934 total: f64,
935 #[yo(array)]
936 tags: Vec<String>,
937 #[yo(text)]
938 note: String,
939 sent: Option<String>,
940 }
941
942 fn order(id: u64, status: &str, total: f64) -> Order {
943 Order {
944 id,
945 status: status.to_owned(),
946 total,
947 tags: Vec::new(),
948 note: String::new(),
949 sent: None,
950 }
951 }
952
953 fn three() -> (crate::Db, Docs<Order>) {
955 let db = open(crate::MEMORY).expect("a database in memory");
956 let orders = db.docs::<Order>("orders").expect("a new collection");
957 for o in [
958 order(1, "open", 12.5),
959 order(2, "shipped", 99.0),
960 order(3, "open", 40.0),
961 ] {
962 orders.put(&o).expect("a document that fits");
963 }
964 (db, orders)
965 }
966
967 #[test]
968 fn a_document_comes_back_as_the_struct_that_went_in() {
969 let (_db, orders) = three();
970 assert_eq!(
971 orders.get(&1).expect("a read"),
972 Some(order(1, "open", 12.5))
973 );
974 assert_eq!(orders.get(&9).expect("a read"), None);
975 assert_eq!(orders.len().expect("a count"), 3);
976 assert!(orders.contains(&2).expect("a read"));
977 }
978
979 #[test]
980 fn every_field_kind_survives_the_round_trip() {
981 let db = open(crate::MEMORY).expect("a database in memory");
982 let orders = db.docs::<Order>("orders").expect("a new collection");
983 let o = Order {
984 id: 7,
985 status: "open".to_owned(),
986 total: -0.5,
987 tags: vec!["red".to_owned(), "small".to_owned()],
988 note: "A red kite".to_owned(),
989 sent: Some("tuesday".to_owned()),
990 };
991 orders.put(&o).expect("a document that fits");
992 assert_eq!(orders.get(&7).expect("a read"), Some(o));
993 }
994
995 #[test]
996 fn putting_the_same_id_twice_replaces_it() {
997 let (_db, orders) = three();
998 assert!(!orders.put(&order(1, "shut", 1.0)).expect("a write"));
999 assert_eq!(orders.len().expect("a count"), 3);
1000 assert_eq!(
1001 orders.get(&1).expect("a read").expect("it is there").status,
1002 "shut"
1003 );
1004 assert_eq!(orders.count(Order::STATUS, "open").expect("a count"), 1);
1006 }
1007
1008 #[test]
1009 fn removing_a_document_takes_it_out_of_its_indexes() {
1010 let (_db, orders) = three();
1011 assert!(orders.remove(&1).expect("a write"));
1012 assert!(!orders.remove(&1).expect("a write"));
1013 assert_eq!(orders.len().expect("a count"), 2);
1014 assert_eq!(orders.count(Order::STATUS, "open").expect("a count"), 1);
1015 assert!(orders.find(Order::TOTAL, &12.5).expect("a read").is_empty());
1016 }
1017
1018 #[test]
1019 fn an_equality_index_answers_with_the_documents() {
1020 let (_db, orders) = three();
1021 let mut open = orders.find(Order::STATUS, "open").expect("a read");
1022 open.sort_by_key(|o| o.id);
1023 assert_eq!(open, [order(1, "open", 12.5), order(3, "open", 40.0)]);
1024 assert_eq!(orders.count(Order::STATUS, "gone").expect("a count"), 0);
1025 }
1026
1027 #[test]
1031 fn a_range_over_a_float_field_is_in_numeric_order() {
1032 let (_db, orders) = three();
1033 let cheap = orders.range(Order::TOTAL, 0.0..50.0).expect("a read");
1034 assert_eq!(
1035 cheap.iter().map(|o| o.total).collect::<Vec<_>>(),
1036 [12.5, 40.0]
1037 );
1038
1039 let all = orders.range(Order::TOTAL, ..).expect("a read");
1040 assert_eq!(
1041 all.iter().map(|o| o.total).collect::<Vec<_>>(),
1042 [12.5, 40.0, 99.0]
1043 );
1044
1045 let down = orders.range_rev(Order::TOTAL, ..).expect("a read");
1046 assert_eq!(
1047 down.iter().map(|o| o.total).collect::<Vec<_>>(),
1048 [99.0, 40.0, 12.5]
1049 );
1050
1051 assert_eq!(
1052 orders
1053 .count_range(Order::TOTAL, 12.5..=40.0)
1054 .expect("a count"),
1055 2
1056 );
1057 }
1058
1059 #[test]
1060 fn an_ordered_path_can_still_be_asked_for_equality() {
1061 let (_db, orders) = three();
1062 assert_eq!(orders.find(Order::TOTAL, &40.0).expect("a read").len(), 1);
1063 assert_eq!(orders.count(Order::TOTAL, &99.0).expect("a count"), 1);
1064 }
1065
1066 #[test]
1067 fn a_range_over_a_string_field_takes_a_pair_of_bounds() {
1068 let db = open(crate::MEMORY).expect("a database in memory");
1069 let names = db.docs::<Named>("names").expect("a new collection");
1070 for (id, name) in [(1u64, "banana"), (2, "apple"), (3, "quince")] {
1071 names
1072 .put(&Named {
1073 id,
1074 name: name.to_owned(),
1075 })
1076 .expect("a document that fits");
1077 }
1078 let early = names
1079 .range(Named::NAME, (Bound::Included("a"), Bound::Excluded("m")))
1080 .expect("a read");
1081 assert_eq!(
1082 early.iter().map(|n| n.name.as_str()).collect::<Vec<_>>(),
1083 ["apple", "banana"]
1084 );
1085 }
1086
1087 #[derive(Yo, Debug, PartialEq)]
1088 struct Named {
1089 #[yo(id)]
1090 id: u64,
1091 #[yo(ordered)]
1092 name: String,
1093 }
1094
1095 #[test]
1096 fn an_array_index_files_a_document_under_every_element() {
1097 let db = open(crate::MEMORY).expect("a database in memory");
1098 let orders = db.docs::<Order>("orders").expect("a new collection");
1099 let mut o = order(1, "open", 1.0);
1100 o.tags = vec!["red".to_owned(), "small".to_owned()];
1101 orders.put(&o).expect("a document that fits");
1102
1103 assert_eq!(orders.find(Order::TAGS, "red").expect("a read").len(), 1);
1104 assert_eq!(orders.find(Order::TAGS, "small").expect("a read").len(), 1);
1105 assert_eq!(orders.count(Order::TAGS, "large").expect("a count"), 0);
1106 }
1107
1108 #[test]
1109 fn a_text_index_files_a_document_under_every_word() {
1110 let db = open(crate::MEMORY).expect("a database in memory");
1111 let orders = db.docs::<Order>("orders").expect("a new collection");
1112 let mut o = order(1, "open", 1.0);
1113 o.note = "A red kite".to_owned();
1114 orders.put(&o).expect("a document that fits");
1115
1116 assert_eq!(orders.find(Order::NOTE, "RED").expect("a read").len(), 1);
1119 assert_eq!(orders.find(Order::NOTE, "kite").expect("a read").len(), 1);
1120 assert_eq!(orders.count(Order::NOTE, "blue").expect("a count"), 0);
1121 }
1122
1123 #[test]
1124 fn asking_a_text_index_for_a_phrase_says_so() {
1125 let (_db, orders) = three();
1126 let e = orders
1127 .find(Order::NOTE, "red kite")
1128 .expect_err("not one word");
1129 assert_eq!(e.code(), crate::Code::Invalid);
1130 assert!(e.message().contains("one word"), "{}", e.message());
1131 }
1132
1133 #[test]
1134 fn an_absent_field_reads_back_as_none() {
1135 let (_db, orders) = three();
1136 assert_eq!(
1137 orders.get(&1).expect("a read").expect("it is there").sent,
1138 None
1139 );
1140 }
1141
1142 #[test]
1143 fn a_nested_struct_is_a_field() {
1144 #[derive(Yo, Debug, PartialEq)]
1145 struct Where {
1146 city: String,
1147 postcode: String,
1148 }
1149
1150 #[derive(Yo, Debug, PartialEq)]
1151 struct Person {
1152 #[yo(id)]
1153 id: u64,
1154 home: Where,
1155 }
1156
1157 let db = open(crate::MEMORY).expect("a database in memory");
1158 let people = db.docs::<Person>("people").expect("a new collection");
1159 let p = Person {
1160 id: 1,
1161 home: Where {
1162 city: "Hanoi".to_owned(),
1163 postcode: "100000".to_owned(),
1164 },
1165 };
1166 people.put(&p).expect("a document that fits");
1167 assert_eq!(people.get(&1).expect("a read"), Some(p));
1168 }
1169
1170 #[test]
1171 fn all_walks_every_document() {
1172 let (_db, orders) = three();
1173 let mut ids: Vec<u64> = orders.all().expect("a read").iter().map(|o| o.id).collect();
1174 ids.sort_unstable();
1175 assert_eq!(ids, [1, 2, 3]);
1176 }
1177
1178 #[test]
1179 fn opening_a_collection_as_the_wrong_thing_is_refused() {
1180 let db = open(crate::MEMORY).expect("a database in memory");
1181 let _orders = db.docs::<Order>("orders").expect("a new collection");
1182 let e = db
1183 .map::<String, u64>("orders")
1184 .expect_err("a different shape");
1185 assert_eq!(e.code(), crate::Code::ShapeMismatch);
1186 let e = db.docs::<Named>("orders").expect_err("a different struct");
1187 assert_eq!(e.code(), crate::Code::ShapeMismatch);
1188 }
1189
1190 #[test]
1191 fn reopening_a_collection_hands_back_the_same_documents() {
1192 let (db, orders) = three();
1193 let again = db.docs::<Order>("orders").expect("the same collection");
1194 assert_eq!(again.len().expect("a count"), 3);
1195 assert_eq!(again.count(Order::STATUS, "open").expect("a count"), 2);
1196 drop(orders);
1197 }
1198
1199 #[test]
1200 fn a_u64_past_what_json_can_hold_is_refused() {
1201 let db = open(crate::MEMORY).expect("a database in memory");
1202 let orders = db.docs::<Order>("orders").expect("a new collection");
1203 let e = orders
1204 .put(&order(u64::MAX, "open", 1.0))
1205 .expect_err("too big");
1206 assert_eq!(e.code(), crate::Code::Invalid);
1207 }
1208}