1use std::borrow::Cow;
24
25use crate::model::{
26 CreateEntity, CreateRelation, DeleteEntity, DeleteRelation,
27 Edit, Id, Op, PropertyValue, RestoreEntity, RestoreRelation, UnsetRelationField,
28 UnsetLanguage, UnsetValue, UpdateEntity, UpdateRelation, Value,
29};
30
31#[derive(Debug, Clone)]
33pub struct EditBuilder<'a> {
34 id: Id,
35 name: Cow<'a, str>,
36 authors: Vec<Id>,
37 created_at: i64,
38 ops: Vec<Op<'a>>,
39}
40
41impl<'a> EditBuilder<'a> {
42 pub fn new(id: Id) -> Self {
44 Self {
45 id,
46 name: Cow::Borrowed(""),
47 authors: Vec::new(),
48 created_at: 0,
49 ops: Vec::new(),
50 }
51 }
52
53 pub fn name(mut self, name: impl Into<Cow<'a, str>>) -> Self {
55 self.name = name.into();
56 self
57 }
58
59 pub fn author(mut self, author_id: Id) -> Self {
61 self.authors.push(author_id);
62 self
63 }
64
65 pub fn authors(mut self, author_ids: impl IntoIterator<Item = Id>) -> Self {
67 self.authors.extend(author_ids);
68 self
69 }
70
71 pub fn created_at(mut self, timestamp: i64) -> Self {
73 self.created_at = timestamp;
74 self
75 }
76
77 pub fn created_now(mut self) -> Self {
79 use std::time::{SystemTime, UNIX_EPOCH};
80 let micros = SystemTime::now()
81 .duration_since(UNIX_EPOCH)
82 .map(|d| d.as_micros() as i64)
83 .unwrap_or(0);
84 self.created_at = micros;
85 self
86 }
87
88 pub fn create_entity<F>(mut self, id: Id, f: F) -> Self
94 where
95 F: FnOnce(EntityBuilder<'a>) -> EntityBuilder<'a>,
96 {
97 let builder = f(EntityBuilder::new());
98 self.ops.push(Op::CreateEntity(CreateEntity {
99 id,
100 values: builder.values,
101 context: None,
102 }));
103 self
104 }
105
106 pub fn create_empty_entity(mut self, id: Id) -> Self {
108 self.ops.push(Op::CreateEntity(CreateEntity {
109 id,
110 values: Vec::new(),
111 context: None,
112 }));
113 self
114 }
115
116 pub fn update_entity<F>(mut self, id: Id, f: F) -> Self
118 where
119 F: FnOnce(UpdateEntityBuilder<'a>) -> UpdateEntityBuilder<'a>,
120 {
121 let builder = f(UpdateEntityBuilder::new(id));
122 self.ops.push(Op::UpdateEntity(UpdateEntity {
123 id: builder.id,
124 set_properties: builder.set_properties,
125 unset_values: builder.unset_values,
126 context: None,
127 }));
128 self
129 }
130
131 pub fn delete_entity(mut self, id: Id) -> Self {
133 self.ops.push(Op::DeleteEntity(DeleteEntity { id, context: None }));
134 self
135 }
136
137 pub fn restore_entity(mut self, id: Id) -> Self {
139 self.ops.push(Op::RestoreEntity(RestoreEntity { id, context: None }));
140 self
141 }
142
143 pub fn create_relation_simple(
149 mut self,
150 id: Id,
151 from: Id,
152 to: Id,
153 relation_type: Id,
154 ) -> Self {
155 self.ops.push(Op::CreateRelation(CreateRelation {
156 id,
157 relation_type,
158 from,
159 from_is_value_ref: false,
160 to,
161 to_is_value_ref: false,
162 entity: None,
163 position: None,
164 from_space: None,
165 from_version: None,
166 to_space: None,
167 to_version: None,
168 context: None,
169 }));
170 self
171 }
172
173 pub fn create_relation<F>(mut self, f: F) -> Self
175 where
176 F: FnOnce(RelationBuilder<'a>) -> RelationBuilder<'a>,
177 {
178 let builder = f(RelationBuilder::new());
179 if let Some(relation) = builder.build() {
180 self.ops.push(Op::CreateRelation(relation));
181 }
182 self
183 }
184
185 pub fn update_relation<F>(mut self, id: Id, f: F) -> Self
187 where
188 F: FnOnce(UpdateRelationBuilder<'a>) -> UpdateRelationBuilder<'a>,
189 {
190 let builder = f(UpdateRelationBuilder::new(id));
191 self.ops.push(Op::UpdateRelation(UpdateRelation {
192 id: builder.id,
193 from_space: builder.from_space,
194 from_version: builder.from_version,
195 to_space: builder.to_space,
196 to_version: builder.to_version,
197 position: builder.position,
198 unset: builder.unset,
199 context: None,
200 }));
201 self
202 }
203
204 pub fn update_relation_position(mut self, id: Id, position: Option<Cow<'a, str>>) -> Self {
206 self.ops.push(Op::UpdateRelation(UpdateRelation {
207 id,
208 from_space: None,
209 from_version: None,
210 to_space: None,
211 to_version: None,
212 position,
213 unset: vec![],
214 context: None,
215 }));
216 self
217 }
218
219 pub fn delete_relation(mut self, id: Id) -> Self {
221 self.ops.push(Op::DeleteRelation(DeleteRelation { id, context: None }));
222 self
223 }
224
225 pub fn restore_relation(mut self, id: Id) -> Self {
227 self.ops.push(Op::RestoreRelation(RestoreRelation { id, context: None }));
228 self
229 }
230
231 pub fn op(mut self, op: Op<'a>) -> Self {
237 self.ops.push(op);
238 self
239 }
240
241 pub fn ops(mut self, ops: impl IntoIterator<Item = Op<'a>>) -> Self {
243 self.ops.extend(ops);
244 self
245 }
246
247 pub fn build(self) -> Edit<'a> {
253 Edit {
254 id: self.id,
255 name: self.name,
256 authors: self.authors,
257 created_at: self.created_at,
258 ops: self.ops,
259 }
260 }
261
262 pub fn op_count(&self) -> usize {
264 self.ops.len()
265 }
266}
267
268#[derive(Debug, Clone, Default)]
270pub struct EntityBuilder<'a> {
271 values: Vec<PropertyValue<'a>>,
272}
273
274impl<'a> EntityBuilder<'a> {
275 pub fn new() -> Self {
277 Self::default()
278 }
279
280 pub fn value(mut self, property: Id, value: Value<'a>) -> Self {
282 self.values.push(PropertyValue { property, value });
283 self
284 }
285
286 pub fn text(
288 mut self,
289 property: Id,
290 value: impl Into<Cow<'a, str>>,
291 language: Option<Id>,
292 ) -> Self {
293 self.values.push(PropertyValue {
294 property,
295 value: Value::Text {
296 value: value.into(),
297 language,
298 },
299 });
300 self
301 }
302
303 pub fn int64(mut self, property: Id, value: i64, unit: Option<Id>) -> Self {
305 self.values.push(PropertyValue {
306 property,
307 value: Value::Int64 { value, unit },
308 });
309 self
310 }
311
312 pub fn float64(mut self, property: Id, value: f64, unit: Option<Id>) -> Self {
314 self.values.push(PropertyValue {
315 property,
316 value: Value::Float64 { value, unit },
317 });
318 self
319 }
320
321 pub fn bool(mut self, property: Id, value: bool) -> Self {
323 self.values.push(PropertyValue {
324 property,
325 value: Value::Bool(value),
326 });
327 self
328 }
329
330 pub fn bytes(mut self, property: Id, value: impl Into<Cow<'a, [u8]>>) -> Self {
332 self.values.push(PropertyValue {
333 property,
334 value: Value::Bytes(value.into()),
335 });
336 self
337 }
338
339 pub fn point(mut self, property: Id, lon: f64, lat: f64, alt: Option<f64>) -> Self {
341 self.values.push(PropertyValue {
342 property,
343 value: Value::Point { lon, lat, alt },
344 });
345 self
346 }
347
348 pub fn date(mut self, property: Id, value: impl Into<Cow<'a, str>>) -> Self {
353 self.values.push(PropertyValue {
354 property,
355 value: Value::Date(value.into()),
356 });
357 self
358 }
359
360 pub fn time(mut self, property: Id, value: impl Into<Cow<'a, str>>) -> Self {
365 self.values.push(PropertyValue {
366 property,
367 value: Value::Time(value.into()),
368 });
369 self
370 }
371
372 pub fn datetime(mut self, property: Id, value: impl Into<Cow<'a, str>>) -> Self {
377 self.values.push(PropertyValue {
378 property,
379 value: Value::Datetime(value.into()),
380 });
381 self
382 }
383
384 pub fn schedule(mut self, property: Id, value: impl Into<Cow<'a, str>>) -> Self {
386 self.values.push(PropertyValue {
387 property,
388 value: Value::Schedule(value.into()),
389 });
390 self
391 }
392
393 pub fn decimal(
395 mut self,
396 property: Id,
397 exponent: i32,
398 mantissa: crate::model::DecimalMantissa<'a>,
399 unit: Option<Id>,
400 ) -> Self {
401 self.values.push(PropertyValue {
402 property,
403 value: Value::Decimal { exponent, mantissa, unit },
404 });
405 self
406 }
407
408 pub fn embedding(
410 mut self,
411 property: Id,
412 sub_type: crate::model::EmbeddingSubType,
413 dims: usize,
414 data: impl Into<Cow<'a, [u8]>>,
415 ) -> Self {
416 self.values.push(PropertyValue {
417 property,
418 value: Value::Embedding {
419 sub_type,
420 dims,
421 data: data.into(),
422 },
423 });
424 self
425 }
426}
427
428#[derive(Debug, Clone)]
430pub struct UpdateEntityBuilder<'a> {
431 id: Id,
432 set_properties: Vec<PropertyValue<'a>>,
433 unset_values: Vec<UnsetValue>,
434}
435
436impl<'a> UpdateEntityBuilder<'a> {
437 pub fn new(id: Id) -> Self {
439 Self {
440 id,
441 set_properties: Vec::new(),
442 unset_values: Vec::new(),
443 }
444 }
445
446 pub fn set(mut self, property: Id, value: Value<'a>) -> Self {
448 self.set_properties.push(PropertyValue { property, value });
449 self
450 }
451
452 pub fn set_text(
454 mut self,
455 property: Id,
456 value: impl Into<Cow<'a, str>>,
457 language: Option<Id>,
458 ) -> Self {
459 self.set_properties.push(PropertyValue {
460 property,
461 value: Value::Text {
462 value: value.into(),
463 language,
464 },
465 });
466 self
467 }
468
469 pub fn set_int64(mut self, property: Id, value: i64, unit: Option<Id>) -> Self {
471 self.set_properties.push(PropertyValue {
472 property,
473 value: Value::Int64 { value, unit },
474 });
475 self
476 }
477
478 pub fn set_float64(mut self, property: Id, value: f64, unit: Option<Id>) -> Self {
480 self.set_properties.push(PropertyValue {
481 property,
482 value: Value::Float64 { value, unit },
483 });
484 self
485 }
486
487 pub fn set_bool(mut self, property: Id, value: bool) -> Self {
489 self.set_properties.push(PropertyValue {
490 property,
491 value: Value::Bool(value),
492 });
493 self
494 }
495
496 pub fn set_point(mut self, property: Id, lon: f64, lat: f64, alt: Option<f64>) -> Self {
498 self.set_properties.push(PropertyValue {
499 property,
500 value: Value::Point { lon, lat, alt },
501 });
502 self
503 }
504
505 pub fn set_date(mut self, property: Id, value: impl Into<Cow<'a, str>>) -> Self {
510 self.set_properties.push(PropertyValue {
511 property,
512 value: Value::Date(value.into()),
513 });
514 self
515 }
516
517 pub fn set_time(mut self, property: Id, value: impl Into<Cow<'a, str>>) -> Self {
522 self.set_properties.push(PropertyValue {
523 property,
524 value: Value::Time(value.into()),
525 });
526 self
527 }
528
529 pub fn set_datetime(mut self, property: Id, value: impl Into<Cow<'a, str>>) -> Self {
534 self.set_properties.push(PropertyValue {
535 property,
536 value: Value::Datetime(value.into()),
537 });
538 self
539 }
540
541 pub fn set_schedule(mut self, property: Id, value: impl Into<Cow<'a, str>>) -> Self {
543 self.set_properties.push(PropertyValue {
544 property,
545 value: Value::Schedule(value.into()),
546 });
547 self
548 }
549
550 pub fn set_bytes(mut self, property: Id, value: impl Into<Cow<'a, [u8]>>) -> Self {
552 self.set_properties.push(PropertyValue {
553 property,
554 value: Value::Bytes(value.into()),
555 });
556 self
557 }
558
559 pub fn set_decimal(
561 mut self,
562 property: Id,
563 exponent: i32,
564 mantissa: crate::model::DecimalMantissa<'a>,
565 unit: Option<Id>,
566 ) -> Self {
567 self.set_properties.push(PropertyValue {
568 property,
569 value: Value::Decimal { exponent, mantissa, unit },
570 });
571 self
572 }
573
574 pub fn set_embedding(
576 mut self,
577 property: Id,
578 sub_type: crate::model::EmbeddingSubType,
579 dims: usize,
580 data: impl Into<Cow<'a, [u8]>>,
581 ) -> Self {
582 self.set_properties.push(PropertyValue {
583 property,
584 value: Value::Embedding {
585 sub_type,
586 dims,
587 data: data.into(),
588 },
589 });
590 self
591 }
592
593 pub fn unset(mut self, property: Id, language: UnsetLanguage) -> Self {
595 self.unset_values.push(UnsetValue { property, language });
596 self
597 }
598
599 pub fn unset_all(mut self, property: Id) -> Self {
601 self.unset_values.push(UnsetValue {
602 property,
603 language: UnsetLanguage::All,
604 });
605 self
606 }
607
608 pub fn unset_english(mut self, property: Id) -> Self {
610 self.unset_values.push(UnsetValue {
611 property,
612 language: UnsetLanguage::English,
613 });
614 self
615 }
616
617 pub fn unset_language(mut self, property: Id, language: Id) -> Self {
619 self.unset_values.push(UnsetValue {
620 property,
621 language: UnsetLanguage::Specific(language),
622 });
623 self
624 }
625}
626
627#[derive(Debug, Clone, Default)]
629pub struct RelationBuilder<'a> {
630 id: Option<Id>,
631 relation_type: Option<Id>,
632 from: Option<Id>,
633 from_is_value_ref: bool,
634 to: Option<Id>,
635 to_is_value_ref: bool,
636 entity: Option<Id>,
637 position: Option<Cow<'a, str>>,
638 from_space: Option<Id>,
639 from_version: Option<Id>,
640 to_space: Option<Id>,
641 to_version: Option<Id>,
642}
643
644impl<'a> RelationBuilder<'a> {
645 pub fn new() -> Self {
647 Self::default()
648 }
649
650 pub fn id(mut self, id: Id) -> Self {
652 self.id = Some(id);
653 self
654 }
655
656 pub fn relation_type(mut self, id: Id) -> Self {
658 self.relation_type = Some(id);
659 self
660 }
661
662 pub fn from(mut self, id: Id) -> Self {
664 self.from = Some(id);
665 self
666 }
667
668 pub fn to(mut self, id: Id) -> Self {
670 self.to = Some(id);
671 self
672 }
673
674 pub fn entity(mut self, id: Id) -> Self {
676 self.entity = Some(id);
677 self
678 }
679
680 pub fn position(mut self, pos: impl Into<Cow<'a, str>>) -> Self {
682 self.position = Some(pos.into());
683 self
684 }
685
686 pub fn from_space(mut self, space_id: Id) -> Self {
688 self.from_space = Some(space_id);
689 self
690 }
691
692 pub fn from_version(mut self, version_id: Id) -> Self {
694 self.from_version = Some(version_id);
695 self
696 }
697
698 pub fn to_space(mut self, space_id: Id) -> Self {
700 self.to_space = Some(space_id);
701 self
702 }
703
704 pub fn to_version(mut self, version_id: Id) -> Self {
706 self.to_version = Some(version_id);
707 self
708 }
709
710 pub fn build(self) -> Option<CreateRelation<'a>> {
712 Some(CreateRelation {
713 id: self.id?,
714 relation_type: self.relation_type?,
715 from: self.from?,
716 from_is_value_ref: self.from_is_value_ref,
717 to: self.to?,
718 to_is_value_ref: self.to_is_value_ref,
719 entity: self.entity,
720 position: self.position,
721 from_space: self.from_space,
722 from_version: self.from_version,
723 to_space: self.to_space,
724 to_version: self.to_version,
725 context: None,
726 })
727 }
728
729 pub fn from_value_ref(mut self, id: Id) -> Self {
731 self.from = Some(id);
732 self.from_is_value_ref = true;
733 self
734 }
735
736 pub fn to_value_ref(mut self, id: Id) -> Self {
738 self.to = Some(id);
739 self.to_is_value_ref = true;
740 self
741 }
742}
743
744#[derive(Debug, Clone)]
746pub struct UpdateRelationBuilder<'a> {
747 id: Id,
748 from_space: Option<Id>,
749 from_version: Option<Id>,
750 to_space: Option<Id>,
751 to_version: Option<Id>,
752 position: Option<Cow<'a, str>>,
753 unset: Vec<UnsetRelationField>,
754}
755
756impl<'a> UpdateRelationBuilder<'a> {
757 pub fn new(id: Id) -> Self {
759 Self {
760 id,
761 from_space: None,
762 from_version: None,
763 to_space: None,
764 to_version: None,
765 position: None,
766 unset: Vec::new(),
767 }
768 }
769
770 pub fn set_from_space(mut self, space_id: Id) -> Self {
772 self.from_space = Some(space_id);
773 self
774 }
775
776 pub fn set_from_version(mut self, version_id: Id) -> Self {
778 self.from_version = Some(version_id);
779 self
780 }
781
782 pub fn set_to_space(mut self, space_id: Id) -> Self {
784 self.to_space = Some(space_id);
785 self
786 }
787
788 pub fn set_to_version(mut self, version_id: Id) -> Self {
790 self.to_version = Some(version_id);
791 self
792 }
793
794 pub fn set_position(mut self, pos: impl Into<Cow<'a, str>>) -> Self {
796 self.position = Some(pos.into());
797 self
798 }
799
800 pub fn unset_from_space(mut self) -> Self {
802 self.unset.push(UnsetRelationField::FromSpace);
803 self
804 }
805
806 pub fn unset_from_version(mut self) -> Self {
808 self.unset.push(UnsetRelationField::FromVersion);
809 self
810 }
811
812 pub fn unset_to_space(mut self) -> Self {
814 self.unset.push(UnsetRelationField::ToSpace);
815 self
816 }
817
818 pub fn unset_to_version(mut self) -> Self {
820 self.unset.push(UnsetRelationField::ToVersion);
821 self
822 }
823
824 pub fn unset_position(mut self) -> Self {
826 self.unset.push(UnsetRelationField::Position);
827 self
828 }
829}
830
831#[cfg(test)]
832mod tests {
833 use super::*;
834
835 #[test]
836 fn test_edit_builder_basic() {
837 let edit_id = [1u8; 16];
838 let author_id = [2u8; 16];
839 let entity_id = [3u8; 16];
840 let prop_id = [4u8; 16];
841
842 let edit = EditBuilder::new(edit_id)
843 .name("Test Edit")
844 .author(author_id)
845 .created_at(1234567890)
846 .create_entity(entity_id, |e| {
847 e.text(prop_id, "Hello", None)
848 .int64([5u8; 16], 42, None)
849 })
850 .build();
851
852 assert_eq!(edit.id, edit_id);
853 assert_eq!(edit.name, "Test Edit");
854 assert_eq!(edit.authors, vec![author_id]);
855 assert_eq!(edit.created_at, 1234567890);
856 assert_eq!(edit.ops.len(), 1);
857
858 match &edit.ops[0] {
859 Op::CreateEntity(ce) => {
860 assert_eq!(ce.id, entity_id);
861 assert_eq!(ce.values.len(), 2);
862 }
863 _ => panic!("Expected CreateEntity"),
864 }
865 }
866
867 #[test]
868 fn test_edit_builder_relations() {
869 let edit = EditBuilder::new([1u8; 16])
870 .create_relation_simple([5u8; 16], [2u8; 16], [3u8; 16], [4u8; 16])
871 .create_relation_simple([6u8; 16], [2u8; 16], [3u8; 16], [4u8; 16])
872 .build();
873
874 assert_eq!(edit.ops.len(), 2);
875
876 match &edit.ops[0] {
877 Op::CreateRelation(cr) => {
878 assert_eq!(cr.id, [5u8; 16]);
879 }
880 _ => panic!("Expected CreateRelation"),
881 }
882
883 match &edit.ops[1] {
884 Op::CreateRelation(cr) => {
885 assert_eq!(cr.id, [6u8; 16]);
886 }
887 _ => panic!("Expected CreateRelation"),
888 }
889 }
890
891 #[test]
892 fn test_update_entity_builder() {
893 let entity_id = [1u8; 16];
894 let prop_id = [2u8; 16];
895
896 let edit = EditBuilder::new([0u8; 16])
897 .update_entity(entity_id, |u| {
898 u.set_text(prop_id, "New value", None)
899 .unset_all([3u8; 16])
900 })
901 .build();
902
903 assert_eq!(edit.ops.len(), 1);
904
905 match &edit.ops[0] {
906 Op::UpdateEntity(ue) => {
907 assert_eq!(ue.id, entity_id);
908 assert_eq!(ue.set_properties.len(), 1);
909 assert_eq!(ue.unset_values.len(), 1);
910 }
911 _ => panic!("Expected UpdateEntity"),
912 }
913 }
914
915 #[test]
916 fn test_relation_builder_full() {
917 let edit = EditBuilder::new([0u8; 16])
918 .create_relation(|r| {
919 r.id([1u8; 16])
920 .from([2u8; 16])
921 .to([3u8; 16])
922 .relation_type([4u8; 16])
923 .entity([5u8; 16])
924 .position("aaa")
925 .from_space([6u8; 16])
926 })
927 .build();
928
929 assert_eq!(edit.ops.len(), 1);
930
931 match &edit.ops[0] {
932 Op::CreateRelation(cr) => {
933 assert_eq!(cr.id, [1u8; 16]);
934 assert_eq!(cr.entity, Some([5u8; 16]));
935 assert_eq!(cr.position.as_deref(), Some("aaa"));
936 assert_eq!(cr.from_space, Some([6u8; 16]));
937 }
938 _ => panic!("Expected CreateRelation"),
939 }
940 }
941
942 #[test]
943 fn test_entity_builder_all_types() {
944 let edit = EditBuilder::new([0u8; 16])
945 .create_entity([1u8; 16], |e| {
946 e.text([2u8; 16], "text", None)
947 .int64([3u8; 16], 123, None)
948 .float64([4u8; 16], 3.14, None)
949 .bool([5u8; 16], true)
950 .point([6u8; 16], -74.0060, 40.7128, None)
951 .date([7u8; 16], "2024-01-15Z") .time([10u8; 16], "14:30:00Z") .datetime([11u8; 16], "2024-01-15T14:30:00Z") .schedule([8u8; 16], "BEGIN:VEVENT\r\nDTSTART:20240315T090000Z\r\nEND:VEVENT")
955 .bytes([9u8; 16], vec![1, 2, 3, 4])
956 })
957 .build();
958
959 match &edit.ops[0] {
960 Op::CreateEntity(ce) => {
961 assert_eq!(ce.values.len(), 10);
962 }
963 _ => panic!("Expected CreateEntity"),
964 }
965 }
966}