1use hydrate_data::json_storage::RestoreAssetFromStorageImpl;
2use hydrate_data::{
3 CanonicalPathReference, OrderedSet, PathReference, PathReferenceNamespaceResolver,
4 PropertiesBundle, SingleObject,
5};
6use hydrate_pipeline::{DynEditContext, HydrateProjectConfiguration};
7use std::path::{Path, PathBuf};
8use uuid::Uuid;
9
10use crate::editor::undo::{UndoContext, UndoStack};
11use crate::{
12 AssetId, AssetLocation, AssetName, BuildInfo, DataSet, DataSetAssetInfo, DataSetDiff,
13 DataSetResult, EditContextKey, EndContextBehavior, HashMap, HashSet, ImportInfo, NullOverride,
14 OverrideBehavior, SchemaFingerprint, SchemaNamedType, SchemaRecord, SchemaSet, Value,
15};
16
17pub struct EditContext {
45 project_config: HydrateProjectConfiguration,
46 schema_set: SchemaSet,
47 pub(super) data_set: DataSet,
48 undo_context: UndoContext,
49}
50
51impl PathReferenceNamespaceResolver for EditContext {
52 fn namespace_root(
53 &self,
54 namespace: &str,
55 ) -> Option<PathBuf> {
56 self.project_config.namespace_root(namespace)
57 }
58
59 fn simplify_path(
60 &self,
61 path: &Path,
62 ) -> Option<(String, PathBuf)> {
63 self.project_config.simplify_path(path)
64 }
65}
66
67impl RestoreAssetFromStorageImpl for EditContext {
68 fn restore_asset(
69 &mut self,
70 asset_id: AssetId,
71 asset_name: AssetName,
72 asset_location: AssetLocation,
73 import_info: Option<ImportInfo>,
74 build_info: BuildInfo,
75 prototype: Option<AssetId>,
76 schema: SchemaFingerprint,
77 properties: HashMap<String, Value>,
78 property_null_overrides: HashMap<String, NullOverride>,
79 properties_in_replace_mode: HashSet<String>,
80 dynamic_collection_entries: HashMap<String, OrderedSet<Uuid>>,
81 ) -> DataSetResult<()> {
82 self.restore_asset(
83 asset_id,
84 asset_name,
85 asset_location,
86 import_info,
87 build_info,
88 prototype,
89 schema,
90 properties,
91 property_null_overrides,
92 properties_in_replace_mode,
93 dynamic_collection_entries,
94 )
95 }
96
97 fn namespace_resolver(&self) -> &dyn PathReferenceNamespaceResolver {
98 self
99 }
100}
101
102impl DynEditContext for EditContext {
103 fn data_set(&self) -> &DataSet {
104 &self.data_set
105 }
106
107 fn schema_set(&self) -> &SchemaSet {
108 &self.schema_set
109 }
110}
111
112impl EditContext {
113 fn track_new_asset(
115 &mut self,
116 asset_id: AssetId,
117 ) {
118 if self.undo_context.has_open_context() {
119 self.undo_context.track_new_asset(asset_id);
121 }
122 }
123
124 fn track_existing_asset(
126 &mut self,
127 asset_id: AssetId,
128 ) -> DataSetResult<()> {
129 if self.undo_context.has_open_context() {
130 self.undo_context
132 .track_existing_asset(&mut self.data_set, asset_id)?;
133 }
134
135 Ok(())
136 }
137
138 pub fn apply_diff(
139 &mut self,
140 diff: &DataSetDiff,
141 ) -> DataSetResult<()> {
142 diff.apply(&mut self.data_set, &self.schema_set)?;
143 Ok(())
144 }
145
146 pub fn new(
147 project_config: &HydrateProjectConfiguration,
148 edit_context_key: EditContextKey,
149 schema_set: SchemaSet,
150 undo_stack: &UndoStack,
151 ) -> Self {
152 EditContext {
153 project_config: project_config.clone(),
154 schema_set,
155 data_set: Default::default(),
156 undo_context: UndoContext::new(undo_stack, edit_context_key),
157 }
158 }
159
160 pub fn new_with_data(
161 project_config: &HydrateProjectConfiguration,
162 edit_context_key: EditContextKey,
163 schema_set: SchemaSet,
164 undo_stack: &UndoStack,
165 ) -> Self {
166 EditContext {
167 project_config: project_config.clone(),
168 schema_set,
169 data_set: Default::default(),
170 undo_context: UndoContext::new(undo_stack, edit_context_key),
171 }
172 }
173
174 pub fn with_undo_context<F: FnOnce(&mut Self) -> EndContextBehavior>(
175 &mut self,
176 name: &'static str,
177 f: F,
178 ) {
179 self.undo_context.begin_context(&self.data_set, name);
180 let end_context_behavior = (f)(self);
181 self.undo_context
182 .end_context(&self.data_set, end_context_behavior);
183 }
184
185 pub fn commit_pending_undo_context(&mut self) {
186 self.undo_context.commit_context(&mut self.data_set);
187 }
188
189 pub fn cancel_pending_undo_context(&mut self) -> DataSetResult<()> {
190 self.undo_context.cancel_context(&mut self.data_set)
191 }
192
193 pub fn schema_set(&self) -> &SchemaSet {
201 &self.schema_set
202 }
203
204 pub fn schemas(&self) -> &HashMap<SchemaFingerprint, SchemaNamedType> {
205 &self.schema_set.schemas()
206 }
207
208 pub fn data_set(&self) -> &DataSet {
212 &self.data_set
213 }
214
215 pub fn assets(&self) -> &HashMap<AssetId, DataSetAssetInfo> {
220 self.data_set.assets()
221 }
222
223 pub fn has_asset(
224 &self,
225 asset_id: AssetId,
226 ) -> bool {
227 self.assets().contains_key(&asset_id)
228 }
229
230 pub fn new_asset_with_id(
231 &mut self,
232 asset_id: AssetId,
233 asset_name: &AssetName,
234 asset_location: &AssetLocation,
235 schema: &SchemaRecord,
236 ) -> DataSetResult<()> {
237 self.data_set.new_asset_with_id(
238 asset_id,
239 asset_name.clone(),
240 asset_location.clone(),
241 schema,
242 )?;
243 self.track_new_asset(asset_id);
244 Ok(())
245 }
246
247 pub fn new_asset(
248 &mut self,
249 asset_name: &AssetName,
250 asset_location: &AssetLocation,
251 schema: &SchemaRecord,
252 ) -> AssetId {
253 let asset_id = self
254 .data_set
255 .new_asset(asset_name.clone(), asset_location.clone(), schema);
256 self.track_new_asset(asset_id);
257 asset_id
258 }
259
260 pub fn new_asset_from_prototype(
261 &mut self,
262 asset_name: &AssetName,
263 asset_location: &AssetLocation,
264 prototype: AssetId,
265 ) -> DataSetResult<AssetId> {
266 let asset_id = self.data_set.new_asset_from_prototype(
267 asset_name.clone(),
268 asset_location.clone(),
269 prototype,
270 )?;
271 self.track_new_asset(asset_id);
272 Ok(asset_id)
273 }
274
275 pub fn init_from_single_object(
276 &mut self,
277 asset_id: AssetId,
278 asset_name: AssetName,
279 asset_location: AssetLocation,
280 single_object: &SingleObject,
281 ) -> DataSetResult<()> {
282 self.track_new_asset(asset_id);
283 self.data_set.new_asset_with_id(
284 asset_id,
285 asset_name,
286 asset_location,
287 single_object.schema(),
288 )?;
289 self.data_set
290 .copy_from_single_object(asset_id, single_object)
291 }
292
293 pub fn restore_assets_from(
294 &mut self,
295 data_set: DataSet,
296 ) -> DataSetResult<()> {
297 for (k, v) in data_set.take_assets() {
298 self.restore_asset(
299 k,
300 v.asset_name().clone(),
301 v.asset_location().clone(),
302 v.import_info().clone().clone(),
303 v.build_info().clone(),
304 v.prototype(),
305 v.schema().fingerprint(),
306 v.properties().clone(),
307 v.property_null_overrides().clone(),
308 v.properties_in_replace_mode().clone(),
309 v.dynamic_collection_entries().clone(),
310 )?;
311 }
312
313 Ok(())
314 }
315
316 pub(crate) fn restore_asset(
317 &mut self,
318 asset_id: AssetId,
319 asset_name: AssetName,
320 asset_location: AssetLocation,
321 import_info: Option<ImportInfo>,
322 build_info: BuildInfo,
323 prototype: Option<AssetId>,
324 schema: SchemaFingerprint,
325 properties: HashMap<String, Value>,
326 property_null_overrides: HashMap<String, NullOverride>,
327 properties_in_replace_mode: HashSet<String>,
328 dynamic_collection_entries: HashMap<String, OrderedSet<Uuid>>,
329 ) -> DataSetResult<()> {
330 self.track_new_asset(asset_id);
331 self.data_set.restore_asset(
332 asset_id,
333 asset_name,
334 asset_location,
335 import_info,
336 build_info,
337 &self.schema_set,
338 prototype,
339 schema,
340 properties,
341 property_null_overrides,
342 properties_in_replace_mode,
343 dynamic_collection_entries,
344 )
345 }
346
347 pub fn duplicate_asset(
348 &mut self,
349 asset_id: AssetId,
350 ) -> DataSetResult<AssetId> {
351 let new_asset_id = self.data_set.duplicate_asset(asset_id, &self.schema_set)?;
352 self.track_new_asset(new_asset_id);
353 Ok(new_asset_id)
354 }
355
356 pub fn delete_asset(
357 &mut self,
358 asset_id: AssetId,
359 ) -> DataSetResult<()> {
360 self.track_existing_asset(asset_id)?;
361 self.data_set.delete_asset(asset_id)
362 }
363
364 pub fn set_asset_location(
365 &mut self,
366 asset_id: AssetId,
367 new_location: AssetLocation,
368 ) -> DataSetResult<()> {
369 self.track_existing_asset(asset_id)?;
370 self.data_set.set_asset_location(asset_id, new_location)?;
371 self.track_existing_asset(asset_id)?;
373 Ok(())
374 }
375
376 pub fn set_import_info(
377 &mut self,
378 asset_id: AssetId,
379 import_info: ImportInfo,
380 ) -> DataSetResult<()> {
381 self.data_set.set_import_info(asset_id, import_info)?;
382 self.track_existing_asset(asset_id)?;
383 Ok(())
384 }
385
386 pub fn asset_name(
387 &self,
388 asset_id: AssetId,
389 ) -> DataSetResult<&AssetName> {
390 self.data_set.asset_name(asset_id)
391 }
392
393 pub fn asset_name_or_id_string(
394 &self,
395 asset_id: AssetId,
396 ) -> DataSetResult<String> {
397 let asset_name = self.data_set.asset_name(asset_id)?;
398 Ok(if let Some(name) = asset_name.as_string() {
399 name.to_string()
400 } else {
401 asset_id.as_uuid().to_string()
402 })
403 }
404
405 pub fn set_asset_name(
406 &mut self,
407 asset_id: AssetId,
408 asset_name: AssetName,
409 ) -> DataSetResult<()> {
410 self.track_existing_asset(asset_id)?;
411 self.data_set.set_asset_name(asset_id, asset_name)
412 }
413
414 pub fn asset_location(
415 &self,
416 asset_id: AssetId,
417 ) -> Option<AssetLocation> {
418 self.data_set.asset_location(asset_id)
419 }
420
421 pub fn asset_location_chain(
422 &self,
423 asset_id: AssetId,
424 ) -> DataSetResult<Vec<AssetLocation>> {
425 self.data_set.asset_location_chain(asset_id)
426 }
427
428 pub fn import_info(
429 &self,
430 asset_id: AssetId,
431 ) -> Option<&ImportInfo> {
432 self.data_set.import_info(asset_id)
433 }
434
435 pub fn resolve_path_reference<P: Into<PathReference>>(
436 &self,
437 asset_id: AssetId,
438 path: P,
439 ) -> DataSetResult<Option<AssetId>> {
440 self.data_set.resolve_path_reference(asset_id, path)
441 }
442
443 pub fn resolve_canonical_path_reference(
444 &self,
445 asset_id: AssetId,
446 canonical_path: &CanonicalPathReference,
447 ) -> DataSetResult<Option<AssetId>> {
448 self.data_set
449 .resolve_canonical_path_reference(asset_id, canonical_path)
450 }
451
452 pub fn resolve_all_path_reference_overrides(
453 &self,
454 asset_id: AssetId,
455 ) -> DataSetResult<HashMap<CanonicalPathReference, AssetId>> {
456 self.data_set.resolve_all_path_reference_overrides(asset_id)
457 }
458
459 pub fn get_all_path_reference_overrides(
460 &mut self,
461 asset_id: AssetId,
462 ) -> Option<&HashMap<CanonicalPathReference, AssetId>> {
463 self.data_set.get_all_path_reference_overrides(asset_id)
464 }
465
466 pub fn set_path_reference_override(
467 &mut self,
468 asset_id: AssetId,
469 path: CanonicalPathReference,
470 referenced_asset_id: AssetId,
471 ) -> DataSetResult<()> {
472 self.track_existing_asset(asset_id)?;
473 self.data_set
474 .set_path_reference_override(asset_id, path, referenced_asset_id)
475 }
476
477 pub fn asset_prototype(
478 &self,
479 asset_id: AssetId,
480 ) -> Option<AssetId> {
481 self.data_set.asset_prototype(asset_id)
482 }
483
484 pub fn asset_schema(
485 &self,
486 asset_id: AssetId,
487 ) -> Option<&SchemaRecord> {
488 self.data_set.asset_schema(asset_id)
489 }
490
491 pub fn get_null_override(
492 &self,
493 asset_id: AssetId,
494 path: impl AsRef<str>,
495 ) -> DataSetResult<NullOverride> {
496 self.data_set
497 .get_null_override(&self.schema_set, asset_id, path)
498 }
499
500 pub fn set_null_override(
501 &mut self,
502 asset_id: AssetId,
503 path: impl AsRef<str>,
504 null_override: NullOverride,
505 ) -> DataSetResult<()> {
506 self.track_existing_asset(asset_id)?;
507 self.data_set
508 .set_null_override(&self.schema_set, asset_id, path, null_override)
509 }
510
511 pub fn resolve_null_override(
512 &self,
513 asset_id: AssetId,
514 path: impl AsRef<str>,
515 ) -> DataSetResult<NullOverride> {
516 self.data_set
517 .resolve_null_override(&self.schema_set, asset_id, path)
518 }
519
520 pub fn has_property_override(
521 &self,
522 asset_id: AssetId,
523 path: impl AsRef<str>,
524 ) -> DataSetResult<bool> {
525 self.data_set.has_property_override(asset_id, path)
526 }
527
528 pub fn get_property_override(
531 &self,
532 asset_id: AssetId,
533 path: impl AsRef<str>,
534 ) -> DataSetResult<Option<&Value>> {
535 self.data_set.get_property_override(asset_id, path)
536 }
537
538 pub fn set_property_override(
540 &mut self,
541 asset_id: AssetId,
542 path: impl AsRef<str>,
543 value: Option<Value>,
544 ) -> DataSetResult<Option<Value>> {
545 self.track_existing_asset(asset_id)?;
546 self.data_set
547 .set_property_override(&self.schema_set, asset_id, path, value)
548 }
549
550 pub fn apply_property_override_to_prototype(
551 &mut self,
552 asset_id: AssetId,
553 path: impl AsRef<str>,
554 ) -> DataSetResult<()> {
555 self.track_existing_asset(asset_id)?;
556 if let Some(prototype) = self.asset_prototype(asset_id) {
557 self.track_existing_asset(prototype)?;
558 }
559
560 self.data_set
561 .apply_property_override_to_prototype(&self.schema_set, asset_id, path)
562 }
563
564 pub fn resolve_property(
565 &self,
566 asset_id: AssetId,
567 path: impl AsRef<str>,
568 ) -> DataSetResult<&Value> {
569 self.data_set
570 .resolve_property(&self.schema_set, asset_id, path)
571 }
572
573 pub fn get_dynamic_array_entries(
574 &self,
575 asset_id: AssetId,
576 path: impl AsRef<str>,
577 ) -> DataSetResult<std::slice::Iter<Uuid>> {
578 self.data_set
579 .get_dynamic_array_entries(&self.schema_set, asset_id, path)
580 }
581
582 pub fn get_map_entries(
583 &self,
584 asset_id: AssetId,
585 path: impl AsRef<str>,
586 ) -> DataSetResult<std::slice::Iter<Uuid>> {
587 self.data_set
588 .get_map_entries(&self.schema_set, asset_id, path)
589 }
590
591 pub fn add_dynamic_array_entry(
592 &mut self,
593 asset_id: AssetId,
594 path: impl AsRef<str>,
595 ) -> DataSetResult<Uuid> {
596 self.track_existing_asset(asset_id)?;
597 self.data_set
598 .add_dynamic_array_entry(&self.schema_set, asset_id, path)
599 }
600
601 pub fn add_map_entry(
602 &mut self,
603 asset_id: AssetId,
604 path: impl AsRef<str>,
605 ) -> DataSetResult<Uuid> {
606 self.track_existing_asset(asset_id)?;
607 self.data_set
608 .add_map_entry(&self.schema_set, asset_id, path)
609 }
610
611 pub fn insert_dynamic_array_entry(
612 &mut self,
613 asset_id: AssetId,
614 path: impl AsRef<str>,
615 index: usize,
616 entry_uuid: Uuid,
617 ) -> DataSetResult<()> {
618 self.track_existing_asset(asset_id)?;
619 self.data_set.insert_dynamic_array_entry(
620 &self.schema_set,
621 asset_id,
622 path,
623 index,
624 entry_uuid,
625 )
626 }
627
628 pub fn remove_dynamic_array_entry(
629 &mut self,
630 asset_id: AssetId,
631 path: impl AsRef<str>,
632 element_id: Uuid,
633 ) -> DataSetResult<bool> {
634 self.track_existing_asset(asset_id)?;
635 self.data_set
636 .remove_dynamic_array_entry(&self.schema_set, asset_id, path, element_id)
637 }
638
639 pub fn remove_map_entry(
640 &mut self,
641 asset_id: AssetId,
642 path: impl AsRef<str>,
643 element_id: Uuid,
644 ) -> DataSetResult<bool> {
645 self.track_existing_asset(asset_id)?;
646 self.data_set
647 .remove_map_entry(&self.schema_set, asset_id, path, element_id)
648 }
649
650 pub fn resolve_dynamic_array_entries(
651 &self,
652 asset_id: AssetId,
653 path: impl AsRef<str>,
654 ) -> DataSetResult<Box<[Uuid]>> {
655 self.data_set
656 .resolve_dynamic_array_entries(&self.schema_set, asset_id, path)
657 }
658
659 pub fn resolve_map_entries(
660 &self,
661 asset_id: AssetId,
662 path: impl AsRef<str>,
663 ) -> DataSetResult<Box<[Uuid]>> {
664 self.data_set
665 .resolve_map_entries(&self.schema_set, asset_id, path)
666 }
667
668 pub fn get_override_behavior(
669 &self,
670 asset_id: AssetId,
671 path: impl AsRef<str>,
672 ) -> DataSetResult<OverrideBehavior> {
673 self.data_set
674 .get_override_behavior(&self.schema_set, asset_id, path)
675 }
676
677 pub fn set_override_behavior(
678 &mut self,
679 asset_id: AssetId,
680 path: impl AsRef<str>,
681 behavior: OverrideBehavior,
682 ) -> DataSetResult<()> {
683 self.track_existing_asset(asset_id)?;
684 self.data_set
685 .set_override_behavior(&self.schema_set, asset_id, path, behavior)
686 }
687
688 pub fn read_properties_bundle(
689 &self,
690 schema_set: &SchemaSet,
691 asset_id: AssetId,
692 path: impl AsRef<str>,
693 ) -> DataSetResult<PropertiesBundle> {
694 self.data_set
695 .read_properties_bundle(schema_set, asset_id, path)
696 }
697
698 pub fn write_properties_bundle(
699 &mut self,
700 schema_set: &SchemaSet,
701 asset_id: AssetId,
702 path: impl AsRef<str>,
703 properties_bundle: &PropertiesBundle,
704 ) -> DataSetResult<()> {
705 self.track_existing_asset(asset_id)?;
706 self.data_set
707 .write_properties_bundle(schema_set, asset_id, path, properties_bundle)
708 }
709}