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, days: i32, offset_min: i16) -> Self {
354 self.values.push(PropertyValue {
355 property,
356 value: Value::Date { days, offset_min },
357 });
358 self
359 }
360
361 pub fn time(mut self, property: Id, time_us: i64, offset_min: i16) -> Self {
367 self.values.push(PropertyValue {
368 property,
369 value: Value::Time { time_us, offset_min },
370 });
371 self
372 }
373
374 pub fn datetime(mut self, property: Id, epoch_us: i64, offset_min: i16) -> Self {
380 self.values.push(PropertyValue {
381 property,
382 value: Value::Datetime { epoch_us, offset_min },
383 });
384 self
385 }
386
387 pub fn schedule(mut self, property: Id, value: impl Into<Cow<'a, str>>) -> Self {
389 self.values.push(PropertyValue {
390 property,
391 value: Value::Schedule(value.into()),
392 });
393 self
394 }
395
396 pub fn decimal(
398 mut self,
399 property: Id,
400 exponent: i32,
401 mantissa: crate::model::DecimalMantissa<'a>,
402 unit: Option<Id>,
403 ) -> Self {
404 self.values.push(PropertyValue {
405 property,
406 value: Value::Decimal { exponent, mantissa, unit },
407 });
408 self
409 }
410
411 pub fn embedding(
413 mut self,
414 property: Id,
415 sub_type: crate::model::EmbeddingSubType,
416 dims: usize,
417 data: impl Into<Cow<'a, [u8]>>,
418 ) -> Self {
419 self.values.push(PropertyValue {
420 property,
421 value: Value::Embedding {
422 sub_type,
423 dims,
424 data: data.into(),
425 },
426 });
427 self
428 }
429}
430
431#[derive(Debug, Clone)]
433pub struct UpdateEntityBuilder<'a> {
434 id: Id,
435 set_properties: Vec<PropertyValue<'a>>,
436 unset_values: Vec<UnsetValue>,
437}
438
439impl<'a> UpdateEntityBuilder<'a> {
440 pub fn new(id: Id) -> Self {
442 Self {
443 id,
444 set_properties: Vec::new(),
445 unset_values: Vec::new(),
446 }
447 }
448
449 pub fn set(mut self, property: Id, value: Value<'a>) -> Self {
451 self.set_properties.push(PropertyValue { property, value });
452 self
453 }
454
455 pub fn set_text(
457 mut self,
458 property: Id,
459 value: impl Into<Cow<'a, str>>,
460 language: Option<Id>,
461 ) -> Self {
462 self.set_properties.push(PropertyValue {
463 property,
464 value: Value::Text {
465 value: value.into(),
466 language,
467 },
468 });
469 self
470 }
471
472 pub fn set_int64(mut self, property: Id, value: i64, unit: Option<Id>) -> Self {
474 self.set_properties.push(PropertyValue {
475 property,
476 value: Value::Int64 { value, unit },
477 });
478 self
479 }
480
481 pub fn set_float64(mut self, property: Id, value: f64, unit: Option<Id>) -> Self {
483 self.set_properties.push(PropertyValue {
484 property,
485 value: Value::Float64 { value, unit },
486 });
487 self
488 }
489
490 pub fn set_bool(mut self, property: Id, value: bool) -> Self {
492 self.set_properties.push(PropertyValue {
493 property,
494 value: Value::Bool(value),
495 });
496 self
497 }
498
499 pub fn set_point(mut self, property: Id, lon: f64, lat: f64, alt: Option<f64>) -> Self {
501 self.set_properties.push(PropertyValue {
502 property,
503 value: Value::Point { lon, lat, alt },
504 });
505 self
506 }
507
508 pub fn set_date(mut self, property: Id, days: i32, offset_min: i16) -> Self {
514 self.set_properties.push(PropertyValue {
515 property,
516 value: Value::Date { days, offset_min },
517 });
518 self
519 }
520
521 pub fn set_time(mut self, property: Id, time_us: i64, offset_min: i16) -> Self {
527 self.set_properties.push(PropertyValue {
528 property,
529 value: Value::Time { time_us, offset_min },
530 });
531 self
532 }
533
534 pub fn set_datetime(mut self, property: Id, epoch_us: i64, offset_min: i16) -> Self {
540 self.set_properties.push(PropertyValue {
541 property,
542 value: Value::Datetime { epoch_us, offset_min },
543 });
544 self
545 }
546
547 pub fn set_schedule(mut self, property: Id, value: impl Into<Cow<'a, str>>) -> Self {
549 self.set_properties.push(PropertyValue {
550 property,
551 value: Value::Schedule(value.into()),
552 });
553 self
554 }
555
556 pub fn set_bytes(mut self, property: Id, value: impl Into<Cow<'a, [u8]>>) -> Self {
558 self.set_properties.push(PropertyValue {
559 property,
560 value: Value::Bytes(value.into()),
561 });
562 self
563 }
564
565 pub fn set_decimal(
567 mut self,
568 property: Id,
569 exponent: i32,
570 mantissa: crate::model::DecimalMantissa<'a>,
571 unit: Option<Id>,
572 ) -> Self {
573 self.set_properties.push(PropertyValue {
574 property,
575 value: Value::Decimal { exponent, mantissa, unit },
576 });
577 self
578 }
579
580 pub fn set_embedding(
582 mut self,
583 property: Id,
584 sub_type: crate::model::EmbeddingSubType,
585 dims: usize,
586 data: impl Into<Cow<'a, [u8]>>,
587 ) -> Self {
588 self.set_properties.push(PropertyValue {
589 property,
590 value: Value::Embedding {
591 sub_type,
592 dims,
593 data: data.into(),
594 },
595 });
596 self
597 }
598
599 pub fn unset(mut self, property: Id, language: UnsetLanguage) -> Self {
601 self.unset_values.push(UnsetValue { property, language });
602 self
603 }
604
605 pub fn unset_all(mut self, property: Id) -> Self {
607 self.unset_values.push(UnsetValue {
608 property,
609 language: UnsetLanguage::All,
610 });
611 self
612 }
613
614 pub fn unset_english(mut self, property: Id) -> Self {
616 self.unset_values.push(UnsetValue {
617 property,
618 language: UnsetLanguage::English,
619 });
620 self
621 }
622
623 pub fn unset_language(mut self, property: Id, language: Id) -> Self {
625 self.unset_values.push(UnsetValue {
626 property,
627 language: UnsetLanguage::Specific(language),
628 });
629 self
630 }
631}
632
633#[derive(Debug, Clone, Default)]
635pub struct RelationBuilder<'a> {
636 id: Option<Id>,
637 relation_type: Option<Id>,
638 from: Option<Id>,
639 from_is_value_ref: bool,
640 to: Option<Id>,
641 to_is_value_ref: bool,
642 entity: Option<Id>,
643 position: Option<Cow<'a, str>>,
644 from_space: Option<Id>,
645 from_version: Option<Id>,
646 to_space: Option<Id>,
647 to_version: Option<Id>,
648}
649
650impl<'a> RelationBuilder<'a> {
651 pub fn new() -> Self {
653 Self::default()
654 }
655
656 pub fn id(mut self, id: Id) -> Self {
658 self.id = Some(id);
659 self
660 }
661
662 pub fn relation_type(mut self, id: Id) -> Self {
664 self.relation_type = Some(id);
665 self
666 }
667
668 pub fn from(mut self, id: Id) -> Self {
670 self.from = Some(id);
671 self
672 }
673
674 pub fn to(mut self, id: Id) -> Self {
676 self.to = Some(id);
677 self
678 }
679
680 pub fn entity(mut self, id: Id) -> Self {
682 self.entity = Some(id);
683 self
684 }
685
686 pub fn position(mut self, pos: impl Into<Cow<'a, str>>) -> Self {
688 self.position = Some(pos.into());
689 self
690 }
691
692 pub fn from_space(mut self, space_id: Id) -> Self {
694 self.from_space = Some(space_id);
695 self
696 }
697
698 pub fn from_version(mut self, version_id: Id) -> Self {
700 self.from_version = Some(version_id);
701 self
702 }
703
704 pub fn to_space(mut self, space_id: Id) -> Self {
706 self.to_space = Some(space_id);
707 self
708 }
709
710 pub fn to_version(mut self, version_id: Id) -> Self {
712 self.to_version = Some(version_id);
713 self
714 }
715
716 pub fn build(self) -> Option<CreateRelation<'a>> {
718 Some(CreateRelation {
719 id: self.id?,
720 relation_type: self.relation_type?,
721 from: self.from?,
722 from_is_value_ref: self.from_is_value_ref,
723 to: self.to?,
724 to_is_value_ref: self.to_is_value_ref,
725 entity: self.entity,
726 position: self.position,
727 from_space: self.from_space,
728 from_version: self.from_version,
729 to_space: self.to_space,
730 to_version: self.to_version,
731 context: None,
732 })
733 }
734
735 pub fn from_value_ref(mut self, id: Id) -> Self {
737 self.from = Some(id);
738 self.from_is_value_ref = true;
739 self
740 }
741
742 pub fn to_value_ref(mut self, id: Id) -> Self {
744 self.to = Some(id);
745 self.to_is_value_ref = true;
746 self
747 }
748}
749
750#[derive(Debug, Clone)]
752pub struct UpdateRelationBuilder<'a> {
753 id: Id,
754 from_space: Option<Id>,
755 from_version: Option<Id>,
756 to_space: Option<Id>,
757 to_version: Option<Id>,
758 position: Option<Cow<'a, str>>,
759 unset: Vec<UnsetRelationField>,
760}
761
762impl<'a> UpdateRelationBuilder<'a> {
763 pub fn new(id: Id) -> Self {
765 Self {
766 id,
767 from_space: None,
768 from_version: None,
769 to_space: None,
770 to_version: None,
771 position: None,
772 unset: Vec::new(),
773 }
774 }
775
776 pub fn set_from_space(mut self, space_id: Id) -> Self {
778 self.from_space = Some(space_id);
779 self
780 }
781
782 pub fn set_from_version(mut self, version_id: Id) -> Self {
784 self.from_version = Some(version_id);
785 self
786 }
787
788 pub fn set_to_space(mut self, space_id: Id) -> Self {
790 self.to_space = Some(space_id);
791 self
792 }
793
794 pub fn set_to_version(mut self, version_id: Id) -> Self {
796 self.to_version = Some(version_id);
797 self
798 }
799
800 pub fn set_position(mut self, pos: impl Into<Cow<'a, str>>) -> Self {
802 self.position = Some(pos.into());
803 self
804 }
805
806 pub fn unset_from_space(mut self) -> Self {
808 self.unset.push(UnsetRelationField::FromSpace);
809 self
810 }
811
812 pub fn unset_from_version(mut self) -> Self {
814 self.unset.push(UnsetRelationField::FromVersion);
815 self
816 }
817
818 pub fn unset_to_space(mut self) -> Self {
820 self.unset.push(UnsetRelationField::ToSpace);
821 self
822 }
823
824 pub fn unset_to_version(mut self) -> Self {
826 self.unset.push(UnsetRelationField::ToVersion);
827 self
828 }
829
830 pub fn unset_position(mut self) -> Self {
832 self.unset.push(UnsetRelationField::Position);
833 self
834 }
835}
836
837#[cfg(test)]
838mod tests {
839 use super::*;
840
841 #[test]
842 fn test_edit_builder_basic() {
843 let edit_id = [1u8; 16];
844 let author_id = [2u8; 16];
845 let entity_id = [3u8; 16];
846 let prop_id = [4u8; 16];
847
848 let edit = EditBuilder::new(edit_id)
849 .name("Test Edit")
850 .author(author_id)
851 .created_at(1234567890)
852 .create_entity(entity_id, |e| {
853 e.text(prop_id, "Hello", None)
854 .int64([5u8; 16], 42, None)
855 })
856 .build();
857
858 assert_eq!(edit.id, edit_id);
859 assert_eq!(edit.name, "Test Edit");
860 assert_eq!(edit.authors, vec![author_id]);
861 assert_eq!(edit.created_at, 1234567890);
862 assert_eq!(edit.ops.len(), 1);
863
864 match &edit.ops[0] {
865 Op::CreateEntity(ce) => {
866 assert_eq!(ce.id, entity_id);
867 assert_eq!(ce.values.len(), 2);
868 }
869 _ => panic!("Expected CreateEntity"),
870 }
871 }
872
873 #[test]
874 fn test_edit_builder_relations() {
875 let edit = EditBuilder::new([1u8; 16])
876 .create_relation_simple([5u8; 16], [2u8; 16], [3u8; 16], [4u8; 16])
877 .create_relation_simple([6u8; 16], [2u8; 16], [3u8; 16], [4u8; 16])
878 .build();
879
880 assert_eq!(edit.ops.len(), 2);
881
882 match &edit.ops[0] {
883 Op::CreateRelation(cr) => {
884 assert_eq!(cr.id, [5u8; 16]);
885 }
886 _ => panic!("Expected CreateRelation"),
887 }
888
889 match &edit.ops[1] {
890 Op::CreateRelation(cr) => {
891 assert_eq!(cr.id, [6u8; 16]);
892 }
893 _ => panic!("Expected CreateRelation"),
894 }
895 }
896
897 #[test]
898 fn test_update_entity_builder() {
899 let entity_id = [1u8; 16];
900 let prop_id = [2u8; 16];
901
902 let edit = EditBuilder::new([0u8; 16])
903 .update_entity(entity_id, |u| {
904 u.set_text(prop_id, "New value", None)
905 .unset_all([3u8; 16])
906 })
907 .build();
908
909 assert_eq!(edit.ops.len(), 1);
910
911 match &edit.ops[0] {
912 Op::UpdateEntity(ue) => {
913 assert_eq!(ue.id, entity_id);
914 assert_eq!(ue.set_properties.len(), 1);
915 assert_eq!(ue.unset_values.len(), 1);
916 }
917 _ => panic!("Expected UpdateEntity"),
918 }
919 }
920
921 #[test]
922 fn test_relation_builder_full() {
923 let edit = EditBuilder::new([0u8; 16])
924 .create_relation(|r| {
925 r.id([1u8; 16])
926 .from([2u8; 16])
927 .to([3u8; 16])
928 .relation_type([4u8; 16])
929 .entity([5u8; 16])
930 .position("aaa")
931 .from_space([6u8; 16])
932 })
933 .build();
934
935 assert_eq!(edit.ops.len(), 1);
936
937 match &edit.ops[0] {
938 Op::CreateRelation(cr) => {
939 assert_eq!(cr.id, [1u8; 16]);
940 assert_eq!(cr.entity, Some([5u8; 16]));
941 assert_eq!(cr.position.as_deref(), Some("aaa"));
942 assert_eq!(cr.from_space, Some([6u8; 16]));
943 }
944 _ => panic!("Expected CreateRelation"),
945 }
946 }
947
948 #[test]
949 fn test_entity_builder_all_types() {
950 let edit = EditBuilder::new([0u8; 16])
951 .create_entity([1u8; 16], |e| {
952 e.text([2u8; 16], "text", None)
953 .int64([3u8; 16], 123, None)
954 .float64([4u8; 16], 3.14, None)
955 .bool([5u8; 16], true)
956 .point([6u8; 16], -74.0060, 40.7128, None)
957 .date([7u8; 16], 19738, 0) .schedule([8u8; 16], "BEGIN:VEVENT\r\nDTSTART:20240315T090000Z\r\nEND:VEVENT")
959 .bytes([9u8; 16], vec![1, 2, 3, 4])
960 })
961 .build();
962
963 match &edit.ops[0] {
964 Op::CreateEntity(ce) => {
965 assert_eq!(ce.values.len(), 8);
966 }
967 _ => panic!("Expected CreateEntity"),
968 }
969 }
970}