1use crate::path_reference::CanonicalPathReference;
2use crate::{
3 AssetId, HashMap, HashSet, OrderedSet, PathReference, PathReferenceHash, Schema,
4 SchemaFingerprint, SchemaRecord, SingleObject, Value,
5};
6pub use crate::{DataSetError, DataSetResult};
7use crate::{NullOverride, SchemaSet};
8use siphasher::sip::SipHasher;
9use std::cmp::Ordering;
10use std::hash::{Hash, Hasher};
11use std::str::FromStr;
12use std::string::ToString;
13use uuid::Uuid;
14
15#[derive(Copy, Clone, PartialEq)]
16pub enum HashObjectMode {
17 PropertiesOnly,
23
24 FullObjectWithLocationId,
27 FullObjectWithLocationChainNames,
28}
29
30#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd)]
31pub struct AssetName(String);
32
33impl AssetName {
34 pub fn new<T: Into<String>>(name: T) -> Self {
35 AssetName(name.into())
36 }
37
38 pub fn empty() -> Self {
39 AssetName(String::default())
40 }
41
42 pub fn is_empty(&self) -> bool {
43 self.0.is_empty()
44 }
45
46 pub fn as_string(&self) -> Option<&String> {
47 if self.0.is_empty() {
48 None
49 } else {
50 Some(&self.0)
51 }
52 }
53}
54
55impl Ord for AssetName {
56 fn cmp(
57 &self,
58 other: &Self,
59 ) -> Ordering {
60 self.0.to_lowercase().cmp(&other.0.to_lowercase())
61 }
62}
63
64#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)]
65pub struct AssetLocation {
66 path_node_id: AssetId,
67}
68
69impl AssetLocation {
70 pub fn new(path_node_id: AssetId) -> Self {
71 AssetLocation { path_node_id }
72 }
73
74 pub fn null() -> AssetLocation {
75 AssetLocation {
76 path_node_id: AssetId::null(),
77 }
78 }
79
80 pub fn path_node_id(&self) -> AssetId {
81 self.path_node_id
82 }
83
84 pub fn is_null(&self) -> bool {
85 self.path_node_id.is_null()
86 }
87}
88
89#[derive(Debug, Copy, Clone, PartialEq)]
90pub enum OverrideBehavior {
91 Append,
92 Replace,
93}
94
95#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
96pub struct ImporterId(pub Uuid);
97
98#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
99pub struct BuilderId(pub usize);
100
101#[derive(Clone, Debug, PartialEq, Eq, Hash, Default)]
103pub struct ImportableName(String);
104
105impl ImportableName {
106 pub fn new(name: String) -> Self {
109 ImportableName(name)
110 }
111
112 pub fn new_optional(name: Option<String>) -> Self {
115 if let Some(name) = name {
116 assert!(!name.is_empty());
117 ImportableName(name)
118 } else {
119 ImportableName(String::default())
120 }
121 }
122
123 pub fn name(&self) -> Option<&str> {
124 if self.0.is_empty() {
125 None
126 } else {
127 Some(&self.0)
128 }
129 }
130
131 pub fn is_default(&self) -> bool {
132 self.0.is_empty()
133 }
134}
135
136#[derive(Clone, Debug)]
138pub struct ImportInfo {
139 importer_id: ImporterId,
141
142 source_file: CanonicalPathReference,
153
154 path_references: HashMap<PathReferenceHash, CanonicalPathReference>,
157
158 source_file_modified_timestamp: u64,
160 source_file_size: u64,
161
162 import_data_contents_hash: u64,
164}
165
166impl ImportInfo {
167 pub fn new(
168 importer_id: ImporterId,
169 source_file: CanonicalPathReference,
170 path_references: HashMap<PathReferenceHash, CanonicalPathReference>,
171 source_file_modified_timestamp: u64,
172 source_file_size: u64,
173 import_data_contents_hash: u64,
174 ) -> Self {
175 ImportInfo {
176 importer_id,
177 source_file,
178 path_references,
179 source_file_modified_timestamp,
180 source_file_size,
181 import_data_contents_hash,
182 }
183 }
184
185 pub fn importer_id(&self) -> ImporterId {
186 self.importer_id
187 }
188
189 pub fn source_file(&self) -> &CanonicalPathReference {
190 &self.source_file
191 }
192
193 pub fn importable_name(&self) -> &ImportableName {
194 self.source_file.importable_name()
195 }
196
197 pub fn path_references(&self) -> &HashMap<PathReferenceHash, CanonicalPathReference> {
198 &self.path_references
199 }
200
201 pub fn source_file_modified_timestamp(&self) -> u64 {
202 self.source_file_modified_timestamp
203 }
204
205 pub fn source_file_size(&self) -> u64 {
206 self.source_file_size
207 }
208
209 pub fn import_data_contents_hash(&self) -> u64 {
210 self.import_data_contents_hash
211 }
212}
213
214impl Hash for ImportInfo {
215 fn hash<H: Hasher>(
216 &self,
217 state: &mut H,
218 ) {
219 self.importer_id.hash(state);
220 self.source_file.hash(state);
221
222 let mut path_references_hashes = 0u64;
223 for (k, v) in &self.path_references {
224 let mut inner_hasher = SipHasher::new();
225 k.hash(&mut inner_hasher);
226 v.hash(&mut inner_hasher);
227 path_references_hashes = path_references_hashes ^ inner_hasher.finish();
228 }
229
230 self.source_file_modified_timestamp.hash(state);
231 self.source_file_size.hash(state);
232 self.import_data_contents_hash.hash(state);
233 }
234}
235
236#[derive(Clone, Debug, Default)]
239pub struct BuildInfo {
240 pub path_reference_overrides: HashMap<CanonicalPathReference, AssetId>,
244}
245
246impl Hash for BuildInfo {
247 fn hash<H: Hasher>(
248 &self,
249 state: &mut H,
250 ) {
251 let mut path_references_overrides_hashes = 0u64;
252 for (k, v) in &self.path_reference_overrides {
253 let mut inner_hasher = SipHasher::new();
254 k.hash(&mut inner_hasher);
255 v.hash(&mut inner_hasher);
256 path_references_overrides_hashes =
257 path_references_overrides_hashes ^ inner_hasher.finish();
258 }
259
260 path_references_overrides_hashes.hash(state);
261 }
262}
263
264pub struct PropertiesBundle {
266 schema: Schema,
267 properties: HashMap<String, Value>,
268 property_null_overrides: HashMap<String, NullOverride>,
269 properties_in_replace_mode: HashSet<String>,
270 dynamic_collection_entries: HashMap<String, OrderedSet<Uuid>>,
271}
272
273impl PropertiesBundle {
274 fn read(
275 asset_info: &DataSetAssetInfo,
276 path_prefix: impl AsRef<str>,
277 schema_set: &SchemaSet,
278 ) -> DataSetResult<PropertiesBundle> {
279 let path_prefix_str = path_prefix.as_ref();
280 let prefix_string = if path_prefix_str.is_empty() {
281 Default::default()
282 } else {
283 format!("{}", path_prefix_str)
284 };
285
286 let schema = asset_info
287 .schema()
288 .find_property_schema(path_prefix_str, schema_set.schemas())
289 .ok_or(DataSetError::SchemaNotFound)?;
290
291 let mut properties = HashMap::<String, Value>::default();
292 println!("Look for property {:?}", path_prefix_str);
293 for (k, v) in &asset_info.properties {
294 println!(" property {:?}", k);
295 if k.starts_with(&prefix_string) {
296 properties.insert(k[prefix_string.len()..].to_string(), v.clone());
297 }
298 }
299
300 let mut property_null_overrides = HashMap::<String, NullOverride>::default();
301 for (k, v) in &asset_info.property_null_overrides {
302 if k.starts_with(&prefix_string) {
303 property_null_overrides.insert(k[prefix_string.len()..].to_string(), v.clone());
304 }
305 }
306
307 let mut properties_in_replace_mode = HashSet::<String>::default();
308 for k in &asset_info.properties_in_replace_mode {
309 if k.starts_with(&prefix_string) {
310 properties_in_replace_mode.insert(k[prefix_string.len()..].to_string());
311 }
312 }
313
314 let mut dynamic_collection_entries = HashMap::<String, OrderedSet<Uuid>>::default();
315 for (k, v) in &asset_info.dynamic_collection_entries {
316 if k.starts_with(&prefix_string) {
317 dynamic_collection_entries.insert(k[prefix_string.len()..].to_string(), v.clone());
318 }
319 }
320
321 Ok(PropertiesBundle {
322 schema,
323 properties,
324 property_null_overrides,
325 properties_in_replace_mode,
326 dynamic_collection_entries,
327 })
328 }
329
330 fn write(
331 &self,
332 asset_info: &mut DataSetAssetInfo,
333 path_prefix: impl AsRef<str>,
334 schema_set: &SchemaSet,
335 ) -> DataSetResult<()> {
336 let path_prefix_str = path_prefix.as_ref();
337 let prefix_string = if path_prefix_str.is_empty() {
338 Default::default()
339 } else {
340 format!("{}", path_prefix_str)
341 };
342
343 let schema = asset_info
347 .schema()
348 .find_property_schema(path_prefix_str, schema_set.schemas())
349 .ok_or(DataSetError::SchemaNotFound)?;
350 assert_eq!(schema, self.schema);
351
352 asset_info
356 .properties
357 .retain(|k, _| !k.starts_with(&prefix_string));
358 asset_info
359 .property_null_overrides
360 .retain(|k, _| !k.starts_with(&prefix_string));
361 asset_info
362 .properties_in_replace_mode
363 .retain(|k| !k.starts_with(&prefix_string));
364 asset_info
365 .dynamic_collection_entries
366 .retain(|k, _| !k.starts_with(&prefix_string));
367
368 for (k, v) in &self.properties {
372 asset_info
373 .properties
374 .insert(format!("{}{}", prefix_string, k), v.clone());
375 }
376
377 for (k, v) in &self.property_null_overrides {
378 asset_info
379 .property_null_overrides
380 .insert(format!("{}{}", prefix_string, k), v.clone());
381 }
382
383 for k in &self.properties_in_replace_mode {
384 asset_info
385 .properties_in_replace_mode
386 .insert(format!("{}{}", prefix_string, k));
387 }
388
389 for (k, v) in &self.dynamic_collection_entries {
390 asset_info
391 .dynamic_collection_entries
392 .insert(format!("{}{}", prefix_string, k), v.clone());
393 }
394
395 Ok(())
396 }
397}
398
399#[derive(Clone, Debug)]
401pub struct DataSetAssetInfo {
402 schema: SchemaRecord,
403
404 pub(super) asset_name: AssetName,
405 pub(super) asset_location: AssetLocation,
406
407 pub(super) import_info: Option<ImportInfo>,
409 pub(super) build_info: BuildInfo,
410
411 pub(super) prototype: Option<AssetId>,
412 pub(super) properties: HashMap<String, Value>,
413 pub(super) property_null_overrides: HashMap<String, NullOverride>,
414 pub(super) properties_in_replace_mode: HashSet<String>,
415 pub(super) dynamic_collection_entries: HashMap<String, OrderedSet<Uuid>>,
416}
417
418impl DataSetAssetInfo {
419 pub fn schema(&self) -> &SchemaRecord {
420 &self.schema
421 }
422
423 pub fn asset_name(&self) -> &AssetName {
424 &self.asset_name
425 }
426
427 pub fn asset_location(&self) -> AssetLocation {
428 self.asset_location
429 }
430
431 pub fn import_info(&self) -> &Option<ImportInfo> {
432 &self.import_info
433 }
434
435 pub fn build_info(&self) -> &BuildInfo {
436 &self.build_info
437 }
438
439 pub fn prototype(&self) -> Option<AssetId> {
440 self.prototype
441 }
442
443 pub fn properties(&self) -> &HashMap<String, Value> {
444 &self.properties
445 }
446
447 pub fn property_null_overrides(&self) -> &HashMap<String, NullOverride> {
448 &self.property_null_overrides
449 }
450
451 pub fn properties_in_replace_mode(&self) -> &HashSet<String> {
452 &self.properties_in_replace_mode
453 }
454
455 pub fn dynamic_collection_entries(&self) -> &HashMap<String, OrderedSet<Uuid>> {
456 &self.dynamic_collection_entries
457 }
458}
459
460#[derive(Default, Clone)]
463pub struct DataSet {
464 assets: HashMap<AssetId, DataSetAssetInfo>,
465}
466
467impl DataSet {
468 pub fn assets(&self) -> &HashMap<AssetId, DataSetAssetInfo> {
469 &self.assets
470 }
471
472 pub(super) fn assets_mut(&mut self) -> &mut HashMap<AssetId, DataSetAssetInfo> {
474 &mut self.assets
475 }
476
477 pub fn take_assets(self) -> HashMap<AssetId, DataSetAssetInfo> {
478 self.assets
479 }
480
481 fn insert_asset(
483 &mut self,
484 id: AssetId,
485 obj_info: DataSetAssetInfo,
486 ) -> DataSetResult<()> {
487 if self.assets.contains_key(&id) {
488 Err(DataSetError::DuplicateAssetId)?
489 } else {
490 let old = self.assets.insert(id, obj_info);
491 assert!(old.is_none());
492 Ok(())
493 }
494 }
495
496 pub fn restore_asset(
498 &mut self,
499 asset_id: AssetId,
500 asset_name: AssetName,
501 asset_location: AssetLocation,
502 import_info: Option<ImportInfo>,
503 build_info: BuildInfo,
504 schema_set: &SchemaSet,
505 prototype: Option<AssetId>,
506 schema: SchemaFingerprint,
507 properties: HashMap<String, Value>,
508 property_null_overrides: HashMap<String, NullOverride>,
509 properties_in_replace_mode: HashSet<String>,
510 dynamic_collection_entries: HashMap<String, OrderedSet<Uuid>>,
511 ) -> DataSetResult<()> {
512 let schema = schema_set
513 .schemas()
514 .get(&schema)
515 .ok_or(DataSetError::SchemaNotFound)?;
516 let schema_record = schema.as_record().cloned()?;
517 let obj = DataSetAssetInfo {
518 schema: schema_record,
519 asset_name,
520 asset_location,
521 import_info,
522 build_info,
523 prototype,
524 properties,
525 property_null_overrides,
526 properties_in_replace_mode,
527 dynamic_collection_entries,
528 };
529
530 self.assets.insert(asset_id, obj);
531 Ok(())
532 }
533
534 pub fn new_asset_with_id(
537 &mut self,
538 asset_id: AssetId,
539 asset_name: AssetName,
540 asset_location: AssetLocation,
541 schema: &SchemaRecord,
542 ) -> DataSetResult<()> {
543 let obj = DataSetAssetInfo {
544 schema: schema.clone(),
545 asset_name: asset_name,
546 asset_location: asset_location,
547 import_info: None,
548 build_info: Default::default(),
549 prototype: None,
550 properties: Default::default(),
551 property_null_overrides: Default::default(),
552 properties_in_replace_mode: Default::default(),
553 dynamic_collection_entries: Default::default(),
554 };
555
556 self.insert_asset(asset_id, obj)
557 }
558
559 pub fn new_asset(
561 &mut self,
562 asset_name: AssetName,
563 asset_location: AssetLocation,
564 schema: &SchemaRecord,
565 ) -> AssetId {
566 let id = AssetId::from_uuid(Uuid::new_v4());
567
568 self.new_asset_with_id(id, asset_name, asset_location, schema)
570 .expect("Randomly created UUID collided with existing UUID");
571
572 id
573 }
574
575 pub fn new_asset_from_prototype(
578 &mut self,
579 asset_name: AssetName,
580 asset_location: AssetLocation,
581 prototype_asset_id: AssetId,
582 ) -> DataSetResult<AssetId> {
583 let prototype_schema = self
584 .assets
585 .get(&prototype_asset_id)
586 .ok_or(DataSetError::AssetNotFound)?;
587
588 let id = self.new_asset(
589 asset_name,
590 asset_location,
591 &prototype_schema.schema().clone(),
592 );
593 self.assets
594 .get_mut(&id)
595 .expect("Newly created asset was not found")
596 .prototype = Some(prototype_asset_id);
597 Ok(id)
598 }
599
600 pub fn copy_from_single_object(
603 &mut self,
604 asset_id: AssetId,
605 single_object: &SingleObject,
606 ) -> DataSetResult<()> {
607 let asset = self
608 .assets
609 .get_mut(&asset_id)
610 .ok_or(DataSetError::AssetNotFound)?;
611
612 if asset.schema.fingerprint() != single_object.schema().fingerprint() {
613 return Err(DataSetError::SingleObjectDoesNotMatchSchema)?;
614 };
615
616 asset.prototype = None;
618 asset.properties.clear();
619 asset.property_null_overrides.clear();
620 asset.properties_in_replace_mode.clear();
621 asset.dynamic_collection_entries.clear();
622
623 for (property, value) in single_object.properties() {
624 asset.properties.insert(property.clone(), value.clone());
625 }
626
627 for (property, null_override) in single_object.property_null_overrides() {
628 asset
629 .property_null_overrides
630 .insert(property.clone(), *null_override);
631 }
632
633 for (property, dynamic_collection_entries) in single_object.dynamic_collection_entries() {
634 let property_entry = asset
635 .dynamic_collection_entries
636 .entry(property.clone())
637 .or_default();
638 for element in &*dynamic_collection_entries {
639 let is_newly_inserted = property_entry.try_insert_at_end(*element);
640 assert!(is_newly_inserted);
642 }
643 }
644
645 Ok(())
646 }
647
648 pub fn duplicate_asset(
649 &mut self,
650 asset_id: AssetId,
651 schema_set: &SchemaSet,
652 ) -> DataSetResult<AssetId> {
653 let old_asset = self
654 .assets
655 .get(&asset_id)
656 .ok_or(DataSetError::AssetNotFound)?;
657
658 let new_asset_id = AssetId(Uuid::new_v4());
659 let mut name_count = 1;
660 let new_name = loop {
661 let new_name = if name_count == 1 {
662 AssetName::new(format!("Copy of {}", old_asset.asset_name.0))
663 } else {
664 AssetName::new(format!("Copy of {} {}", old_asset.asset_name.0, name_count))
665 };
666
667 let mut is_duplicate = false;
668 for (_, asset_info) in &self.assets {
669 if asset_info.asset_name == new_name
670 && asset_info.asset_location == old_asset.asset_location
671 {
672 is_duplicate = true;
673 break;
674 }
675 }
676
677 if !is_duplicate {
678 break new_name;
679 }
680
681 name_count += 1;
682 };
683
684 self.restore_asset(
685 new_asset_id,
686 new_name,
687 old_asset.asset_location,
688 old_asset.import_info.clone(),
689 old_asset.build_info.clone(),
690 schema_set,
691 old_asset.prototype,
692 old_asset.schema.fingerprint(),
693 old_asset.properties.clone(),
694 old_asset.property_null_overrides.clone(),
695 old_asset.properties_in_replace_mode.clone(),
696 old_asset.dynamic_collection_entries.clone(),
697 )?;
698 Ok(new_asset_id)
699 }
700
701 pub fn delete_asset(
703 &mut self,
704 asset_id: AssetId,
705 ) -> DataSetResult<()> {
706 if self.assets.remove(&asset_id).is_none() {
707 Err(DataSetError::AssetNotFound)?
708 } else {
709 Ok(())
710 }
711 }
712
713 pub fn set_asset_location(
715 &mut self,
716 asset_id: AssetId,
717 new_location: AssetLocation,
718 ) -> DataSetResult<()> {
719 let mut new_parent_asset_id_iter = Some(new_location.path_node_id());
720 while let Some(new_parent_asset_id) = new_parent_asset_id_iter {
721 if new_parent_asset_id == asset_id {
722 return Err(DataSetError::NewLocationIsChildOfCurrentAsset)?;
724 }
725 new_parent_asset_id_iter = self
726 .asset_location(new_parent_asset_id)
727 .map(|x| x.path_node_id())
728 }
729
730 let asset = self
731 .assets
732 .get_mut(&asset_id)
733 .ok_or(DataSetError::AssetNotFound)?;
734
735 asset.asset_location = new_location;
736 Ok(())
737 }
738
739 pub fn set_import_info(
741 &mut self,
742 asset_id: AssetId,
743 import_info: ImportInfo,
744 ) -> DataSetResult<()> {
745 let asset = self
746 .assets
747 .get_mut(&asset_id)
748 .ok_or(DataSetError::AssetNotFound)?;
749
750 asset.import_info = Some(import_info);
751 Ok(())
752 }
753
754 pub fn copy_from(
758 &mut self,
759 other: &DataSet,
760 asset_id: AssetId,
761 ) -> DataSetResult<()> {
762 let asset = other
763 .assets
764 .get(&asset_id)
765 .ok_or(DataSetError::AssetNotFound)?;
766
767 self.assets.insert(asset_id, asset.clone());
768 Ok(())
769 }
770
771 pub fn asset_name(
773 &self,
774 asset_id: AssetId,
775 ) -> DataSetResult<&AssetName> {
776 Ok(self
777 .assets
778 .get(&asset_id)
779 .ok_or(DataSetError::AssetNotFound)?
780 .asset_name())
781 }
782
783 pub fn set_asset_name(
785 &mut self,
786 asset_id: AssetId,
787 asset_name: AssetName,
788 ) -> DataSetResult<()> {
789 let asset = self
790 .assets
791 .get_mut(&asset_id)
792 .ok_or(DataSetError::AssetNotFound)?;
793
794 asset.asset_name = asset_name;
795 Ok(())
796 }
797
798 pub fn asset_location(
800 &self,
801 asset_id: AssetId,
802 ) -> Option<AssetLocation> {
803 self.assets
804 .get(&asset_id)
805 .map(|x| &x.asset_location)
806 .copied()
807 }
808
809 pub fn asset_location_chain(
812 &self,
813 asset_id: AssetId,
814 ) -> DataSetResult<Vec<AssetLocation>> {
815 let mut asset_location_chain = Vec::default();
816
817 let Some(mut obj_iter) = self.asset_location(asset_id) else {
819 return Ok(asset_location_chain);
820 };
821
822 while !obj_iter.path_node_id.is_null() {
824 if asset_location_chain.contains(&obj_iter) {
825 return Err(DataSetError::LocationCycleDetected)?;
827 }
828
829 asset_location_chain.push(obj_iter.clone());
830 obj_iter = if let Some(location) = self.asset_location(obj_iter.path_node_id) {
831 location
833 } else {
834 return Err(DataSetError::LocationParentNotFound)?;
836 };
837 }
838
839 Ok(asset_location_chain)
840 }
841
842 pub fn import_info(
845 &self,
846 asset_id: AssetId,
847 ) -> Option<&ImportInfo> {
848 self.assets
849 .get(&asset_id)
850 .map(|x| x.import_info.as_ref())
851 .flatten()
852 }
853
854 fn do_resolve_path_reference_into_canonical_path_reference<'a>(
855 &'a self,
856 asset: &'a DataSetAssetInfo,
857 path_reference_hash: PathReferenceHash,
858 ) -> Option<&'a CanonicalPathReference> {
859 if let Some(import_info) = &asset.import_info {
861 if let Some(canonical_path) = import_info.path_references.get(&path_reference_hash) {
862 return Some(canonical_path);
863 }
864 }
865
866 if let Some(prototype) = asset.prototype {
868 if let Some(prototype_asset) = self.assets.get(&prototype) {
871 return self.do_resolve_path_reference_into_canonical_path_reference(
872 prototype_asset,
873 path_reference_hash,
874 );
875 }
876 }
877
878 None
879 }
880
881 fn do_resolve_canonical_path_reference_into_asset_id(
882 &self,
883 asset: &DataSetAssetInfo,
884 canonical_path: &CanonicalPathReference,
885 ) -> Option<AssetId> {
886 if let Some(referenced_asset_id) = asset
888 .build_info
889 .path_reference_overrides
890 .get(canonical_path)
891 {
892 return Some(*referenced_asset_id);
893 }
894
895 if let Some(prototype) = asset.prototype {
897 if let Some(prototype_asset) = self.assets.get(&prototype) {
900 return self.do_resolve_canonical_path_reference_into_asset_id(
901 prototype_asset,
902 canonical_path,
903 );
904 }
905 }
906
907 None
908 }
909
910 fn do_resolve_all_path_references_into_canonical_path_references(
911 &self,
912 asset: &DataSetAssetInfo,
913 all_references: &mut HashMap<PathReferenceHash, CanonicalPathReference>,
914 ) -> DataSetResult<()> {
915 if let Some(prototype) = asset.prototype {
916 if let Some(prototype_asset) = self.assets.get(&prototype) {
919 self.do_resolve_all_path_references_into_canonical_path_references(
920 prototype_asset,
921 all_references,
922 )?;
923 }
924 }
925
926 if let Some(import_info) = &asset.import_info {
927 for (k, v) in &import_info.path_references {
928 all_references.insert(*k, v.clone());
929 }
930 }
931
932 Ok(())
933 }
934
935 fn do_resolve_all_canonical_path_references_into_asset_id(
936 &self,
937 asset: &DataSetAssetInfo,
938 all_references: &mut HashMap<CanonicalPathReference, AssetId>,
939 ) -> DataSetResult<()> {
940 if let Some(prototype) = asset.prototype {
941 if let Some(prototype_asset) = self.assets.get(&prototype) {
944 self.do_resolve_all_canonical_path_references_into_asset_id(
945 prototype_asset,
946 all_references,
947 )?;
948 }
949 }
950
951 for (k, v) in &asset.build_info.path_reference_overrides {
952 all_references.insert(k.clone(), *v);
953 }
954
955 Ok(())
956 }
957
958 pub fn resolve_path_reference<P: Into<PathReference>>(
959 &self,
960 asset_id: AssetId,
961 path: P,
962 ) -> DataSetResult<Option<AssetId>> {
963 let asset = self
964 .assets
965 .get(&asset_id)
966 .ok_or(DataSetError::AssetNotFound)?;
967 let canonical_path = self.do_resolve_path_reference_into_canonical_path_reference(
968 asset,
969 path.into().path_reference_hash(),
970 );
971 Ok(if let Some(canonical_path) = canonical_path {
972 self.do_resolve_canonical_path_reference_into_asset_id(asset, canonical_path)
973 } else {
974 None
975 })
976 }
977
978 pub fn resolve_canonical_path_reference(
979 &self,
980 asset_id: AssetId,
981 canonical_path: &CanonicalPathReference,
982 ) -> DataSetResult<Option<AssetId>> {
983 let asset = self
984 .assets
985 .get(&asset_id)
986 .ok_or(DataSetError::AssetNotFound)?;
987 Ok(self.do_resolve_canonical_path_reference_into_asset_id(asset, canonical_path))
988 }
989
990 pub fn resolve_all_hashed_path_references(
991 &self,
992 asset_id: AssetId,
993 ) -> DataSetResult<HashMap<PathReferenceHash, CanonicalPathReference>> {
994 let asset = self
995 .assets
996 .get(&asset_id)
997 .ok_or(DataSetError::AssetNotFound)?;
998 let mut all_references = HashMap::default();
999 self.do_resolve_all_path_references_into_canonical_path_references(
1000 asset,
1001 &mut all_references,
1002 )?;
1003 Ok(all_references)
1004 }
1005
1006 pub fn resolve_all_path_reference_overrides(
1007 &self,
1008 asset_id: AssetId,
1009 ) -> DataSetResult<HashMap<CanonicalPathReference, AssetId>> {
1010 let asset = self
1011 .assets
1012 .get(&asset_id)
1013 .ok_or(DataSetError::AssetNotFound)?;
1014 let mut all_references = HashMap::default();
1015 self.do_resolve_all_canonical_path_references_into_asset_id(asset, &mut all_references)?;
1016 Ok(all_references)
1017 }
1018
1019 pub fn get_all_path_reference_overrides(
1020 &mut self,
1021 asset_id: AssetId,
1022 ) -> Option<&HashMap<CanonicalPathReference, AssetId>> {
1023 self.assets
1024 .get(&asset_id)
1025 .map(|x| &x.build_info.path_reference_overrides)
1026 }
1027
1028 pub fn set_path_reference_override(
1029 &mut self,
1030 asset_id: AssetId,
1031 path: CanonicalPathReference,
1032 referenced_asset_id: AssetId,
1033 ) -> DataSetResult<()> {
1034 let asset = self
1035 .assets
1036 .get_mut(&asset_id)
1037 .ok_or(DataSetError::AssetNotFound)?;
1038
1039 asset
1040 .build_info
1041 .path_reference_overrides
1042 .insert(path, referenced_asset_id);
1043 Ok(())
1044 }
1045
1046 pub fn asset_prototype(
1047 &self,
1048 asset_id: AssetId,
1049 ) -> Option<AssetId> {
1050 self.assets.get(&asset_id).map(|x| x.prototype).flatten()
1051 }
1052
1053 pub fn asset_schema(
1054 &self,
1055 asset_id: AssetId,
1056 ) -> Option<&SchemaRecord> {
1057 self.assets.get(&asset_id).map(|x| &x.schema)
1058 }
1059
1060 fn hash_property_data(
1061 hasher: &mut SipHasher,
1062 properties: &HashMap<String, Value>,
1063 property_null_overrides: &HashMap<String, NullOverride>,
1064 properties_in_replace_mode: &HashSet<String>,
1065 dynamic_collection_entries: &HashMap<String, OrderedSet<Uuid>>,
1066 ) {
1067 let mut properties_hash = 0;
1069 for (key, value) in properties {
1070 let mut inner_hasher = siphasher::sip::SipHasher::default();
1071 key.hash(&mut inner_hasher);
1072 value.hash(&mut inner_hasher);
1073 properties_hash = properties_hash ^ inner_hasher.finish();
1074 }
1075 properties_hash.hash(hasher);
1076
1077 let mut property_null_overrides_hash = 0;
1079 for (key, value) in property_null_overrides {
1080 let mut inner_hasher = siphasher::sip::SipHasher::default();
1081 key.hash(&mut inner_hasher);
1082 value.hash(&mut inner_hasher);
1083 property_null_overrides_hash = property_null_overrides_hash ^ inner_hasher.finish();
1084 }
1085 property_null_overrides_hash.hash(hasher);
1086
1087 let mut properties_in_replace_mode_hash = 0;
1089 for value in properties_in_replace_mode {
1090 let mut inner_hasher = siphasher::sip::SipHasher::default();
1091 value.hash(&mut inner_hasher);
1092 properties_in_replace_mode_hash =
1093 properties_in_replace_mode_hash ^ inner_hasher.finish();
1094 }
1095 properties_in_replace_mode_hash.hash(hasher);
1096
1097 let mut dynamic_collection_entries_hash = 0;
1099 for (key, value) in dynamic_collection_entries {
1100 let mut inner_hasher = siphasher::sip::SipHasher::default();
1101 key.hash(&mut inner_hasher);
1102
1103 let mut uuid_set_hash = 0;
1104 for id in value.iter() {
1105 let mut inner_hasher2 = siphasher::sip::SipHasher::default();
1106 id.hash(&mut inner_hasher2);
1107 uuid_set_hash = uuid_set_hash ^ inner_hasher2.finish();
1108 }
1109 uuid_set_hash.hash(&mut inner_hasher);
1110
1111 dynamic_collection_entries_hash =
1112 dynamic_collection_entries_hash ^ inner_hasher.finish();
1113 }
1114 dynamic_collection_entries_hash.hash(hasher);
1115 }
1116
1117 pub fn hash_object(
1118 &self,
1119 asset_id: AssetId,
1120 hash_object_mode: HashObjectMode,
1121 ) -> DataSetResult<u64> {
1122 let asset = self
1123 .assets
1124 .get(&asset_id)
1125 .ok_or(DataSetError::AssetNotFound)?;
1126
1127 let mut hasher = SipHasher::default();
1128
1129 match hash_object_mode {
1131 HashObjectMode::PropertiesOnly => {
1132 }
1134 HashObjectMode::FullObjectWithLocationId => {
1135 asset.asset_location.path_node_id.hash(&mut hasher);
1137 }
1138 HashObjectMode::FullObjectWithLocationChainNames => {
1139 let location_chain = self.asset_location_chain(asset_id)?;
1141 for location in location_chain {
1142 let location_asset = self
1143 .assets
1144 .get(&location.path_node_id)
1145 .ok_or(DataSetError::AssetNotFound)?;
1146 location_asset.asset_name.hash(&mut hasher);
1147 }
1148 }
1149 };
1150
1151 match hash_object_mode {
1153 HashObjectMode::FullObjectWithLocationId
1154 | HashObjectMode::FullObjectWithLocationChainNames => {
1155 asset.asset_name.hash(&mut hasher);
1156 asset.import_info.hash(&mut hasher);
1157 asset.build_info.hash(&mut hasher);
1158 asset.prototype.hash(&mut hasher);
1159 }
1160 _ => {}
1161 }
1162
1163 let schema = &asset.schema;
1165 schema.fingerprint().hash(&mut hasher);
1166
1167 Self::hash_property_data(
1169 &mut hasher,
1170 &asset.properties,
1171 &asset.property_null_overrides,
1172 &asset.properties_in_replace_mode,
1173 &asset.dynamic_collection_entries,
1174 );
1175
1176 if hash_object_mode == HashObjectMode::PropertiesOnly {
1178 if let Some(prototype) = asset.prototype {
1179 self.hash_object(prototype, HashObjectMode::PropertiesOnly)?
1183 .hash(&mut hasher);
1184 }
1185 }
1186
1187 let asset_hash = hasher.finish();
1188 Ok(asset_hash)
1189 }
1190
1191 pub fn get_null_override(
1195 &self,
1196 schema_set: &SchemaSet,
1197 asset_id: AssetId,
1198 path: impl AsRef<str>,
1199 ) -> DataSetResult<NullOverride> {
1200 let asset = self
1201 .assets
1202 .get(&asset_id)
1203 .ok_or(DataSetError::AssetNotFound)?;
1204 let property_schema = asset
1205 .schema
1206 .find_property_schema(&path, schema_set.schemas())
1207 .ok_or(DataSetError::SchemaNotFound)?;
1208
1209 if property_schema.is_nullable() {
1210 Ok(asset
1212 .property_null_overrides
1213 .get(path.as_ref())
1214 .copied()
1215 .unwrap_or(NullOverride::Unset))
1216 } else {
1217 Err(DataSetError::InvalidSchema)?
1218 }
1219 }
1220
1221 pub fn set_null_override(
1223 &mut self,
1224 schema_set: &SchemaSet,
1225 asset_id: AssetId,
1226 path: impl AsRef<str>,
1227 null_override: NullOverride,
1228 ) -> DataSetResult<()> {
1229 let asset = self
1230 .assets
1231 .get_mut(&asset_id)
1232 .ok_or(DataSetError::AssetNotFound)?;
1233 let property_schema = asset
1234 .schema
1235 .find_property_schema(&path, schema_set.schemas())
1236 .ok_or(DataSetError::SchemaNotFound)?;
1237
1238 if property_schema.is_nullable() {
1239 if null_override != NullOverride::Unset {
1240 asset
1241 .property_null_overrides
1242 .insert(path.as_ref().to_string(), null_override);
1243 } else {
1244 asset.property_null_overrides.remove(path.as_ref());
1246 }
1247 Ok(())
1248 } else {
1249 Err(DataSetError::InvalidSchema)?
1250 }
1251 }
1252
1253 fn validate_parent_paths(
1254 &self,
1255 schema_set: &SchemaSet,
1256 asset_id: AssetId,
1257 path: impl AsRef<str>,
1258 ) -> DataSetResult<Schema> {
1259 let asset_schema = self
1260 .asset_schema(asset_id)
1261 .ok_or(DataSetError::AssetNotFound)?;
1262
1263 let mut accessed_nullable_keys = vec![];
1265 let mut accessed_dynamic_array_keys = vec![];
1267 let mut accessed_static_array_keys = vec![];
1268 let mut accessed_map_keys = vec![];
1269
1270 let schema = super::property_schema_and_path_ancestors_to_check(
1271 asset_schema,
1272 &path,
1273 schema_set.schemas(),
1274 &mut accessed_nullable_keys,
1275 &mut accessed_dynamic_array_keys,
1276 &mut accessed_static_array_keys,
1277 &mut accessed_map_keys,
1278 )?;
1279
1280 for checked_property in &accessed_nullable_keys {
1282 if self.resolve_null_override(schema_set, asset_id, checked_property)?
1283 != NullOverride::SetNonNull
1284 {
1285 return Err(DataSetError::PathParentIsNull)?;
1286 }
1287 }
1288
1289 for (path, key) in &accessed_dynamic_array_keys {
1292 let dynamic_collection_entries =
1293 self.resolve_dynamic_array_entries(schema_set, asset_id, path)?;
1294 if !dynamic_collection_entries
1295 .contains(&Uuid::from_str(key).map_err(|_| DataSetError::UuidParseError)?)
1296 {
1297 return Err(DataSetError::PathDynamicArrayEntryDoesNotExist)?;
1298 }
1299 }
1300
1301 Ok(schema)
1302 }
1303
1304 pub fn resolve_null_override(
1306 &self,
1307 schema_set: &SchemaSet,
1308 asset_id: AssetId,
1309 path: impl AsRef<str>,
1310 ) -> DataSetResult<NullOverride> {
1311 let property_schema = self.validate_parent_paths(schema_set, asset_id, path.as_ref())?;
1312
1313 if !property_schema.is_nullable() {
1315 return Err(DataSetError::InvalidSchema)?;
1316 }
1317
1318 let mut prototype_id = Some(asset_id);
1320 while let Some(prototype_id_iter) = prototype_id {
1321 let obj = self
1322 .assets
1323 .get(&prototype_id_iter)
1324 .ok_or(DataSetError::AssetNotFound)?;
1325
1326 if let Some(value) = obj.property_null_overrides.get(path.as_ref()) {
1327 match value {
1328 NullOverride::Unset => unreachable!(),
1330 NullOverride::SetNull => return Ok(NullOverride::SetNull),
1331 NullOverride::SetNonNull => return Ok(NullOverride::SetNonNull),
1332 }
1333 }
1334
1335 prototype_id = obj.prototype;
1336 }
1337
1338 Ok(NullOverride::Unset)
1340 }
1341
1342 pub fn has_property_override(
1343 &self,
1344 asset_id: AssetId,
1345 path: impl AsRef<str>,
1346 ) -> DataSetResult<bool> {
1347 Ok(self.get_property_override(asset_id, path)?.is_some())
1348 }
1349
1350 pub fn get_property_override(
1353 &self,
1354 asset_id: AssetId,
1355 path: impl AsRef<str>,
1356 ) -> DataSetResult<Option<&Value>> {
1357 let asset = self
1358 .assets
1359 .get(&asset_id)
1360 .ok_or(DataSetError::AssetNotFound)?;
1361 Ok(asset.properties.get(path.as_ref()))
1362 }
1363
1364 pub fn set_property_override(
1366 &mut self,
1367 schema_set: &SchemaSet,
1368 asset_id: AssetId,
1369 path: impl AsRef<str>,
1370 value: Option<Value>,
1371 ) -> DataSetResult<Option<Value>> {
1372 let asset_schema = self
1373 .asset_schema(asset_id)
1374 .ok_or(DataSetError::AssetNotFound)?;
1375 let property_schema = asset_schema
1376 .find_property_schema(&path, schema_set.schemas())
1377 .ok_or(DataSetError::SchemaNotFound)?;
1378
1379 if let Some(value) = &value {
1380 if !value.matches_schema(&property_schema, schema_set.schemas()) {
1381 log::debug!(
1382 "Value {:?} doesn't match schema {:?} on schema {:?} path {:?}",
1383 value,
1384 property_schema,
1385 asset_schema.name(),
1386 path.as_ref()
1387 );
1388 return Err(DataSetError::ValueDoesNotMatchSchema)?;
1389 }
1390 }
1391
1392 let _ = self.validate_parent_paths(schema_set, asset_id, path.as_ref())?;
1393
1394 let obj = self
1395 .assets
1396 .get_mut(&asset_id)
1397 .ok_or(DataSetError::AssetNotFound)?;
1398 let old_value = if let Some(value) = value {
1399 obj.properties.insert(path.as_ref().to_string(), value)
1400 } else {
1401 obj.properties.remove(path.as_ref())
1402 };
1403 Ok(old_value)
1404 }
1405
1406 pub fn apply_property_override_to_prototype(
1407 &mut self,
1408 schema_set: &SchemaSet,
1409 asset_id: AssetId,
1410 path: impl AsRef<str>,
1411 ) -> DataSetResult<()> {
1412 let asset = self
1413 .assets
1414 .get(&asset_id)
1415 .ok_or(DataSetError::AssetNotFound)?;
1416 let prototype_id = asset.prototype;
1417
1418 if let Some(prototype_id) = prototype_id {
1419 let v = self.set_property_override(schema_set, asset_id, path.as_ref(), None)?;
1420 if let Some(v) = v {
1421 self.set_property_override(schema_set, prototype_id, path, Some(v))?;
1423 } else {
1424 }
1426 } else {
1427 }
1429
1430 Ok(())
1431 }
1432
1433 pub fn resolve_property<'a>(
1434 &'a self,
1435 schema_set: &'a SchemaSet,
1436 asset_id: AssetId,
1437 path: impl AsRef<str>,
1438 ) -> DataSetResult<&'a Value> {
1439 let property_schema = self.validate_parent_paths(schema_set, asset_id, path.as_ref())?;
1440
1441 let mut prototype_id = Some(asset_id);
1442 while let Some(prototype_id_iter) = prototype_id {
1443 let obj = self.assets.get(&prototype_id_iter);
1444 if let Some(obj) = obj {
1445 if let Some(value) = obj.properties.get(path.as_ref()) {
1446 return Ok(value);
1447 }
1448
1449 prototype_id = obj.prototype;
1450 } else {
1451 prototype_id = None;
1454 }
1455 }
1456
1457 Ok(Value::default_for_schema(&property_schema, schema_set))
1458 }
1459
1460 fn get_dynamic_collection_entries(
1461 asset: &DataSetAssetInfo,
1462 path: impl AsRef<str>,
1463 ) -> DataSetResult<std::slice::Iter<Uuid>> {
1464 if let Some(overrides) = asset.dynamic_collection_entries.get(path.as_ref()) {
1465 Ok(overrides.iter())
1466 } else {
1467 Ok(std::slice::Iter::default())
1468 }
1469 }
1470
1471 pub fn get_dynamic_array_entries(
1472 &self,
1473 schema_set: &SchemaSet,
1474 asset_id: AssetId,
1475 path: impl AsRef<str>,
1476 ) -> DataSetResult<std::slice::Iter<Uuid>> {
1477 let asset = self
1478 .assets
1479 .get(&asset_id)
1480 .ok_or(DataSetError::AssetNotFound)?;
1481 let property_schema = asset
1482 .schema
1483 .find_property_schema(&path, schema_set.schemas())
1484 .ok_or(DataSetError::SchemaNotFound)?;
1485
1486 if !property_schema.is_dynamic_array() {
1487 return Err(DataSetError::InvalidSchema)?;
1488 }
1489
1490 Self::get_dynamic_collection_entries(asset, path)
1491 }
1492
1493 pub fn get_map_entries(
1494 &self,
1495 schema_set: &SchemaSet,
1496 asset_id: AssetId,
1497 path: impl AsRef<str>,
1498 ) -> DataSetResult<std::slice::Iter<Uuid>> {
1499 let asset = self
1500 .assets
1501 .get(&asset_id)
1502 .ok_or(DataSetError::AssetNotFound)?;
1503 let property_schema = asset
1504 .schema
1505 .find_property_schema(&path, schema_set.schemas())
1506 .ok_or(DataSetError::SchemaNotFound)?;
1507
1508 if !property_schema.is_map() {
1509 return Err(DataSetError::InvalidSchema)?;
1510 }
1511
1512 Self::get_dynamic_collection_entries(asset, path)
1513 }
1514
1515 fn add_dynamic_collection_entry(
1516 asset: &mut DataSetAssetInfo,
1517 path: impl AsRef<str>,
1518 ) -> DataSetResult<Uuid> {
1519 let entry = asset
1520 .dynamic_collection_entries
1521 .entry(path.as_ref().to_string())
1522 .or_insert(Default::default());
1523 let new_uuid = Uuid::new_v4();
1524 let newly_inserted = entry.try_insert_at_end(new_uuid);
1525 if !newly_inserted {
1526 panic!("Created a new random UUID but it matched an existing UUID");
1527 }
1528 Ok(new_uuid)
1529 }
1530
1531 pub fn add_dynamic_array_entry(
1532 &mut self,
1533 schema_set: &SchemaSet,
1534 asset_id: AssetId,
1535 path: impl AsRef<str>,
1536 ) -> DataSetResult<Uuid> {
1537 let asset = self
1538 .assets
1539 .get_mut(&asset_id)
1540 .ok_or(DataSetError::AssetNotFound)?;
1541 let property_schema = asset
1542 .schema
1543 .find_property_schema(&path, schema_set.schemas())
1544 .ok_or(DataSetError::SchemaNotFound)?;
1545
1546 if !property_schema.is_dynamic_array() {
1547 return Err(DataSetError::InvalidSchema)?;
1548 }
1549
1550 Self::add_dynamic_collection_entry(asset, path)
1551 }
1552
1553 pub fn add_map_entry(
1554 &mut self,
1555 schema_set: &SchemaSet,
1556 asset_id: AssetId,
1557 path: impl AsRef<str>,
1558 ) -> DataSetResult<Uuid> {
1559 let asset = self
1560 .assets
1561 .get_mut(&asset_id)
1562 .ok_or(DataSetError::AssetNotFound)?;
1563 let property_schema = asset
1564 .schema
1565 .find_property_schema(&path, schema_set.schemas())
1566 .ok_or(DataSetError::SchemaNotFound)?;
1567
1568 if !property_schema.is_map() {
1569 return Err(DataSetError::InvalidSchema)?;
1570 }
1571
1572 Self::add_dynamic_collection_entry(asset, path)
1573 }
1574
1575 pub fn insert_dynamic_array_entry(
1576 &mut self,
1577 schema_set: &SchemaSet,
1578 asset_id: AssetId,
1579 path: impl AsRef<str>,
1580 index: usize,
1581 entry_uuid: Uuid,
1582 ) -> DataSetResult<()> {
1583 let asset = self
1584 .assets
1585 .get_mut(&asset_id)
1586 .ok_or(DataSetError::AssetNotFound)?;
1587 let property_schema = asset
1588 .schema
1589 .find_property_schema(&path, schema_set.schemas())
1590 .ok_or(DataSetError::SchemaNotFound)?;
1591
1592 if !property_schema.is_dynamic_array() {
1593 return Err(DataSetError::InvalidSchema)?;
1594 }
1595
1596 let entry = asset
1597 .dynamic_collection_entries
1598 .entry(path.as_ref().to_string())
1599 .or_insert(Default::default());
1600 if entry.try_insert_at_position(index, entry_uuid) {
1601 Ok(())
1602 } else {
1603 Err(DataSetError::DuplicateEntryKey)?
1604 }
1605 }
1606
1607 fn remove_dynamic_collection_entry(
1608 asset: &mut DataSetAssetInfo,
1609 path: impl AsRef<str>,
1610 element_id: Uuid,
1611 ) -> DataSetResult<bool> {
1612 if let Some(override_list) = asset.dynamic_collection_entries.get_mut(path.as_ref()) {
1613 let was_removed = override_list.remove(&element_id);
1615 Ok(was_removed)
1616 } else {
1617 Ok(false)
1619 }
1620 }
1621
1622 pub fn remove_dynamic_array_entry(
1623 &mut self,
1624 schema_set: &SchemaSet,
1625 asset_id: AssetId,
1626 path: impl AsRef<str>,
1627 element_id: Uuid,
1628 ) -> DataSetResult<bool> {
1629 let asset = self
1630 .assets
1631 .get_mut(&asset_id)
1632 .ok_or(DataSetError::AssetNotFound)?;
1633 let property_schema = asset
1634 .schema
1635 .find_property_schema(&path, schema_set.schemas())
1636 .ok_or(DataSetError::SchemaNotFound)?;
1637
1638 if !property_schema.is_dynamic_array() {
1639 return Err(DataSetError::InvalidSchema)?;
1640 }
1641
1642 Self::remove_dynamic_collection_entry(asset, path, element_id)
1643 }
1644
1645 pub fn remove_map_entry(
1646 &mut self,
1647 schema_set: &SchemaSet,
1648 asset_id: AssetId,
1649 path: impl AsRef<str>,
1650 element_id: Uuid,
1651 ) -> DataSetResult<bool> {
1652 let asset = self
1653 .assets
1654 .get_mut(&asset_id)
1655 .ok_or(DataSetError::AssetNotFound)?;
1656 let property_schema = asset
1657 .schema
1658 .find_property_schema(&path, schema_set.schemas())
1659 .ok_or(DataSetError::SchemaNotFound)?;
1660
1661 if !property_schema.is_map() {
1662 return Err(DataSetError::InvalidSchema)?;
1663 }
1664
1665 Self::remove_dynamic_collection_entry(asset, path, element_id)
1666 }
1667
1668 fn do_resolve_dynamic_collection_entries(
1669 &self,
1670 asset_id: AssetId,
1671 path: &str,
1672 resolved_entries: &mut Vec<Uuid>,
1673 ) -> DataSetResult<()> {
1674 let obj = self
1675 .assets
1676 .get(&asset_id)
1677 .ok_or(DataSetError::AssetNotFound)?;
1678
1679 let mut check_parents = true;
1681
1682 if obj.properties_in_replace_mode.contains(path) {
1683 check_parents = false;
1684 }
1685
1686 if check_parents {
1688 if let Some(prototype) = obj.prototype {
1689 if self.assets.contains_key(&prototype) {
1691 self.do_resolve_dynamic_collection_entries(prototype, path, resolved_entries)?;
1692 }
1693 }
1694 }
1695
1696 if let Some(entries) = obj.dynamic_collection_entries.get(path) {
1697 for entry in entries {
1698 resolved_entries.push(*entry);
1699 }
1700 }
1701
1702 Ok(())
1703 }
1704
1705 pub fn resolve_dynamic_array_entries(
1706 &self,
1707 schema_set: &SchemaSet,
1708 asset_id: AssetId,
1709 path: impl AsRef<str>,
1710 ) -> DataSetResult<Box<[Uuid]>> {
1711 let property_schema = self.validate_parent_paths(schema_set, asset_id, path.as_ref())?;
1712 if !property_schema.is_dynamic_array() {
1713 return Err(DataSetError::InvalidSchema)?;
1714 }
1715
1716 let mut resolved_entries = vec![];
1717 self.do_resolve_dynamic_collection_entries(asset_id, path.as_ref(), &mut resolved_entries)?;
1718 Ok(resolved_entries.into_boxed_slice())
1719 }
1720
1721 pub fn resolve_map_entries(
1722 &self,
1723 schema_set: &SchemaSet,
1724 asset_id: AssetId,
1725 path: impl AsRef<str>,
1726 ) -> DataSetResult<Box<[Uuid]>> {
1727 let property_schema = self.validate_parent_paths(schema_set, asset_id, path.as_ref())?;
1728 if !property_schema.is_map() {
1729 return Err(DataSetError::InvalidSchema)?;
1730 }
1731
1732 let mut resolved_entries = vec![];
1733 self.do_resolve_dynamic_collection_entries(asset_id, path.as_ref(), &mut resolved_entries)?;
1734 Ok(resolved_entries.into_boxed_slice())
1735 }
1736
1737 pub fn get_override_behavior(
1738 &self,
1739 schema_set: &SchemaSet,
1740 asset_id: AssetId,
1741 path: impl AsRef<str>,
1742 ) -> DataSetResult<OverrideBehavior> {
1743 let asset = self
1744 .assets
1745 .get(&asset_id)
1746 .ok_or(DataSetError::AssetNotFound)?;
1747 let property_schema = asset
1748 .schema
1749 .find_property_schema(&path, schema_set.schemas())
1750 .ok_or(DataSetError::SchemaNotFound)?;
1751
1752 Ok(match property_schema {
1753 Schema::DynamicArray(_) | Schema::Map(_) => {
1754 if asset.properties_in_replace_mode.contains(path.as_ref()) {
1755 OverrideBehavior::Replace
1756 } else {
1757 OverrideBehavior::Append
1758 }
1759 }
1760 _ => OverrideBehavior::Replace,
1761 })
1762 }
1763
1764 pub fn set_override_behavior(
1765 &mut self,
1766 schema_set: &SchemaSet,
1767 asset_id: AssetId,
1768 path: impl AsRef<str>,
1769 behavior: OverrideBehavior,
1770 ) -> DataSetResult<()> {
1771 let asset = self
1772 .assets
1773 .get_mut(&asset_id)
1774 .ok_or(DataSetError::AssetNotFound)?;
1775 let property_schema = asset
1776 .schema
1777 .find_property_schema(&path, schema_set.schemas())
1778 .ok_or(DataSetError::SchemaNotFound)?;
1779
1780 match property_schema {
1781 Schema::DynamicArray(_) | Schema::Map(_) => {
1782 let _ = match behavior {
1783 OverrideBehavior::Append => {
1784 asset.properties_in_replace_mode.remove(path.as_ref())
1785 }
1786 OverrideBehavior::Replace => asset
1787 .properties_in_replace_mode
1788 .insert(path.as_ref().to_string()),
1789 };
1790 Ok(())
1791 }
1792 _ => Err(DataSetError::InvalidSchema)?,
1793 }
1794 }
1795
1796 pub fn read_properties_bundle(
1797 &self,
1798 schema_set: &SchemaSet,
1799 asset_id: AssetId,
1800 path: impl AsRef<str>,
1801 ) -> DataSetResult<PropertiesBundle> {
1802 let asset = self
1803 .assets
1804 .get(&asset_id)
1805 .ok_or(DataSetError::AssetNotFound)?;
1806 Ok(PropertiesBundle::read(asset, path, schema_set)?)
1807 }
1808
1809 pub fn write_properties_bundle(
1810 &mut self,
1811 schema_set: &SchemaSet,
1812 asset_id: AssetId,
1813 path: impl AsRef<str>,
1814 properties_bundle: &PropertiesBundle,
1815 ) -> DataSetResult<()> {
1816 let asset = self
1817 .assets
1818 .get_mut(&asset_id)
1819 .ok_or(DataSetError::AssetNotFound)?;
1820 properties_bundle.write(asset, path, schema_set)
1821 }
1822}