1use crate::{
2 DataSetError, DataSetResult, HashMap, OrderedSet, SchemaFingerprint, SchemaRecord, Value,
3};
4use crate::{NullOverride, SchemaSet};
5use hydrate_schema::Schema;
6use std::hash::{Hash, Hasher};
7use std::str::FromStr;
8use std::string::ToString;
9use uuid::Uuid;
10
11#[derive(Clone, Debug)]
14pub struct SingleObject {
15 schema: SchemaRecord,
16 properties: HashMap<String, Value>,
17 property_null_overrides: HashMap<String, NullOverride>,
18 dynamic_collection_entries: HashMap<String, OrderedSet<Uuid>>,
19}
20
21impl Hash for SingleObject {
22 fn hash<H: Hasher>(
23 &self,
24 state: &mut H,
25 ) {
26 let schema = &self.schema;
27
28 schema.fingerprint().hash(state);
29
30 let mut properties_hash = 0;
32 for (key, value) in &self.properties {
33 let mut inner_hasher = siphasher::sip::SipHasher::default();
34 key.hash(&mut inner_hasher);
35 value.hash(&mut inner_hasher);
36 properties_hash = properties_hash ^ inner_hasher.finish();
37 }
38 properties_hash.hash(state);
39
40 let mut property_null_overrides_hash = 0;
42 for (key, value) in &self.property_null_overrides {
43 let mut inner_hasher = siphasher::sip::SipHasher::default();
44 key.hash(&mut inner_hasher);
45 value.hash(&mut inner_hasher);
46 property_null_overrides_hash = property_null_overrides_hash ^ inner_hasher.finish();
47 }
48 property_null_overrides_hash.hash(state);
49
50 let mut dynamic_collection_entries_hash = 0;
52 for (key, value) in &self.dynamic_collection_entries {
53 let mut inner_hasher = siphasher::sip::SipHasher::default();
54 key.hash(&mut inner_hasher);
55
56 let mut uuid_set_hash = 0;
57 for id in value {
58 let mut inner_hasher2 = siphasher::sip::SipHasher::default();
59 id.hash(&mut inner_hasher2);
60 uuid_set_hash = uuid_set_hash ^ inner_hasher2.finish();
61 }
62 uuid_set_hash.hash(&mut inner_hasher);
63
64 dynamic_collection_entries_hash =
65 dynamic_collection_entries_hash ^ inner_hasher.finish();
66 }
67 dynamic_collection_entries_hash.hash(state);
68 }
69}
70
71impl SingleObject {
72 pub fn new(schema: &SchemaRecord) -> Self {
73 SingleObject {
74 schema: schema.clone(),
75 properties: Default::default(),
76 property_null_overrides: Default::default(),
77 dynamic_collection_entries: Default::default(),
78 }
79 }
80
81 pub fn restore(
82 schema_set: &SchemaSet,
83 schema: SchemaFingerprint,
84 properties: HashMap<String, Value>,
85 property_null_overrides: HashMap<String, NullOverride>,
86 dynamic_collection_entries: HashMap<String, OrderedSet<Uuid>>,
87 ) -> SingleObject {
88 let schema = schema_set.schemas().get(&schema).unwrap();
89 let schema_record = schema.as_record().cloned().unwrap();
90 SingleObject {
91 schema: schema_record,
92 properties,
93 property_null_overrides,
94 dynamic_collection_entries,
95 }
96 }
97
98 pub fn schema(&self) -> &SchemaRecord {
99 &self.schema
100 }
101
102 pub fn properties(&self) -> &HashMap<String, Value> {
103 &self.properties
104 }
105
106 pub fn property_null_overrides(&self) -> &HashMap<String, NullOverride> {
107 &self.property_null_overrides
108 }
109
110 pub fn dynamic_collection_entries(&self) -> &HashMap<String, OrderedSet<Uuid>> {
111 &self.dynamic_collection_entries
112 }
113
114 pub fn get_null_override(
117 &self,
118 schema_set: &SchemaSet,
119 path: impl AsRef<str>,
120 ) -> DataSetResult<NullOverride> {
121 let property_schema = self
122 .schema
123 .find_property_schema(&path, schema_set.schemas())
124 .ok_or(DataSetError::SchemaNotFound)?;
125
126 if property_schema.is_nullable() {
127 Ok(self
129 .property_null_overrides
130 .get(path.as_ref())
131 .copied()
132 .unwrap_or(NullOverride::Unset))
133 } else {
134 Err(DataSetError::InvalidSchema)?
135 }
136 }
137
138 pub fn set_null_override(
139 &mut self,
140 schema_set: &SchemaSet,
141 path: impl AsRef<str>,
142 null_override: NullOverride,
143 ) -> DataSetResult<()> {
144 let property_schema = self
145 .schema
146 .find_property_schema(&path, schema_set.schemas())
147 .ok_or(DataSetError::SchemaNotFound)?;
148
149 if property_schema.is_nullable() {
150 if null_override != NullOverride::Unset {
151 self.property_null_overrides
152 .insert(path.as_ref().to_string(), null_override);
153 } else {
154 self.property_null_overrides.remove(path.as_ref());
155 }
156 Ok(())
157 } else {
158 Err(DataSetError::InvalidSchema)?
159 }
160 }
161
162 fn validate_parent_paths(
163 &self,
164 schema_set: &SchemaSet,
165 path: impl AsRef<str>,
166 ) -> DataSetResult<Schema> {
167 let mut accessed_nullable_keys = vec![];
169 let mut accessed_dynamic_array_keys = vec![];
171 let mut accessed_static_array_keys = vec![];
172 let mut accessed_map_keys = vec![];
173
174 let property_schema = super::property_schema_and_path_ancestors_to_check(
177 &self.schema,
178 &path,
179 schema_set.schemas(),
180 &mut accessed_nullable_keys,
181 &mut accessed_dynamic_array_keys,
182 &mut accessed_static_array_keys,
183 &mut accessed_map_keys,
184 )?;
185
186 for checked_property in &accessed_nullable_keys {
188 if self.resolve_null_override(schema_set, checked_property)? != NullOverride::SetNonNull
189 {
190 return Err(DataSetError::PathParentIsNull)?;
191 }
192 }
193
194 for (path, key) in &accessed_dynamic_array_keys {
197 let dynamic_collection_entries =
198 self.resolve_dynamic_array_entries(schema_set, path)?;
199 if !dynamic_collection_entries
200 .contains(&Uuid::from_str(key).map_err(|_| DataSetError::UuidParseError)?)
201 {
202 return Err(DataSetError::PathDynamicArrayEntryDoesNotExist)?;
203 }
204 }
205
206 Ok(property_schema)
207 }
208
209 pub fn resolve_null_override(
212 &self,
213 schema_set: &SchemaSet,
214 path: impl AsRef<str>,
215 ) -> DataSetResult<NullOverride> {
216 let property_schema = self.validate_parent_paths(schema_set, path.as_ref())?;
217
218 if !property_schema.is_nullable() {
220 return Err(DataSetError::InvalidSchema)?;
221 }
222
223 Ok(self
224 .property_null_overrides
225 .get(path.as_ref())
226 .copied()
227 .unwrap_or(NullOverride::Unset))
228 }
229
230 pub fn has_property_override(
231 &self,
232 path: impl AsRef<str>,
233 ) -> bool {
234 self.get_property_override(path).is_some()
235 }
236
237 pub fn get_property_override(
240 &self,
241 path: impl AsRef<str>,
242 ) -> Option<&Value> {
243 self.properties.get(path.as_ref())
244 }
245
246 pub fn set_property_override(
248 &mut self,
249 schema_set: &SchemaSet,
250 path: impl AsRef<str>,
251 value: Option<Value>,
252 ) -> DataSetResult<Option<Value>> {
253 let property_schema = self.validate_parent_paths(schema_set, path.as_ref())?;
254
255 if let Some(value) = &value {
256 if !value.matches_schema(&property_schema, schema_set.schemas()) {
257 log::debug!(
258 "Value {:?} doesn't match schema {:?} on schema {:?} path {:?}",
259 value,
260 property_schema,
261 self.schema.name(),
262 path.as_ref()
263 );
264 return Err(DataSetError::ValueDoesNotMatchSchema)?;
265 }
266 }
267
268 let old_value = if let Some(value) = value {
269 self.properties.insert(path.as_ref().to_string(), value)
270 } else {
271 self.properties.remove(path.as_ref())
272 };
273 Ok(old_value)
274 }
275
276 pub fn resolve_property<'a>(
277 &'a self,
278 schema_set: &'a SchemaSet,
279 path: impl AsRef<str>,
280 ) -> DataSetResult<&'a Value> {
281 let property_schema = self.validate_parent_paths(schema_set, path.as_ref())?;
282
283 if let Some(value) = self.properties.get(path.as_ref()) {
284 return Ok(value);
285 }
286
287 Ok(Value::default_for_schema(&property_schema, schema_set))
288 }
289
290 fn get_dynamic_collection_entries(
291 &self,
292 path: impl AsRef<str>,
293 ) -> DataSetResult<std::slice::Iter<Uuid>> {
294 if let Some(overrides) = self.dynamic_collection_entries.get(path.as_ref()) {
295 Ok(overrides.iter())
296 } else {
297 Ok(std::slice::Iter::default())
298 }
299 }
300
301 pub fn get_dynamic_array_entries(
302 &self,
303 schema_set: &SchemaSet,
304 path: impl AsRef<str>,
305 ) -> DataSetResult<std::slice::Iter<Uuid>> {
306 let property_schema = self
307 .schema
308 .find_property_schema(&path, schema_set.schemas())
309 .ok_or(DataSetError::SchemaNotFound)?;
310
311 if !property_schema.is_dynamic_array() {
312 return Err(DataSetError::InvalidSchema)?;
313 }
314
315 self.get_dynamic_collection_entries(path)
316 }
317
318 pub fn get_map_entries(
319 &self,
320 schema_set: &SchemaSet,
321 path: impl AsRef<str>,
322 ) -> DataSetResult<std::slice::Iter<Uuid>> {
323 let property_schema = self
324 .schema
325 .find_property_schema(&path, schema_set.schemas())
326 .ok_or(DataSetError::SchemaNotFound)?;
327
328 if !property_schema.is_map() {
329 return Err(DataSetError::InvalidSchema)?;
330 }
331
332 self.get_dynamic_collection_entries(path)
333 }
334
335 fn add_dynamic_collection_entry(
336 &mut self,
337 path: impl AsRef<str>,
338 ) -> DataSetResult<Uuid> {
339 let entry = self
340 .dynamic_collection_entries
341 .entry(path.as_ref().to_string())
342 .or_insert(Default::default());
343 let new_uuid = Uuid::new_v4();
344 let newly_inserted = entry.try_insert_at_end(new_uuid);
345 if !newly_inserted {
346 panic!("Created a new random UUID but it matched an existing UUID");
347 }
348 Ok(new_uuid)
349 }
350
351 pub fn add_dynamic_array_entry(
352 &mut self,
353 schema_set: &SchemaSet,
354 path: impl AsRef<str>,
355 ) -> DataSetResult<Uuid> {
356 let property_schema = self
357 .schema
358 .find_property_schema(&path, schema_set.schemas())
359 .ok_or(DataSetError::SchemaNotFound)?;
360
361 if !property_schema.is_dynamic_array() {
362 return Err(DataSetError::InvalidSchema)?;
363 }
364
365 self.add_dynamic_collection_entry(path)
366 }
367
368 pub fn add_map_entry(
369 &mut self,
370 schema_set: &SchemaSet,
371 path: impl AsRef<str>,
372 ) -> DataSetResult<Uuid> {
373 let property_schema = self
374 .schema
375 .find_property_schema(&path, schema_set.schemas())
376 .ok_or(DataSetError::SchemaNotFound)?;
377
378 if !property_schema.is_map() {
379 return Err(DataSetError::InvalidSchema)?;
380 }
381
382 self.add_dynamic_collection_entry(path)
383 }
384
385 pub fn insert_dynamic_array_entry(
386 &mut self,
387 schema_set: &SchemaSet,
388 path: impl AsRef<str>,
389 index: usize,
390 entry_uuid: Uuid,
391 ) -> DataSetResult<()> {
392 let property_schema = self
393 .schema
394 .find_property_schema(&path, schema_set.schemas())
395 .ok_or(DataSetError::SchemaNotFound)?;
396
397 if !property_schema.is_dynamic_array() {
398 return Err(DataSetError::InvalidSchema)?;
399 }
400
401 if !property_schema.is_dynamic_array() {
402 return Err(DataSetError::InvalidSchema)?;
403 }
404
405 let entry = self
406 .dynamic_collection_entries
407 .entry(path.as_ref().to_string())
408 .or_insert(Default::default());
409 if entry.try_insert_at_position(index, entry_uuid) {
410 Ok(())
411 } else {
412 Err(DataSetError::DuplicateEntryKey)?
413 }
414 }
415
416 fn remove_dynamic_collection_entry(
417 &mut self,
418 path: impl AsRef<str>,
419 element_id: Uuid,
420 ) -> DataSetResult<bool> {
421 if let Some(override_list) = self.dynamic_collection_entries.get_mut(path.as_ref()) {
422 let was_removed = override_list.remove(&element_id);
424 Ok(was_removed)
425 } else {
426 Ok(false)
428 }
429 }
430
431 pub fn remove_dynamic_array_entry(
432 &mut self,
433 schema_set: &SchemaSet,
434 path: impl AsRef<str>,
435 element_id: Uuid,
436 ) -> DataSetResult<bool> {
437 let property_schema = self
438 .schema
439 .find_property_schema(&path, schema_set.schemas())
440 .ok_or(DataSetError::SchemaNotFound)?;
441
442 if !property_schema.is_dynamic_array() {
443 return Err(DataSetError::InvalidSchema)?;
444 }
445
446 self.remove_dynamic_collection_entry(path, element_id)
447 }
448
449 pub fn remove_map_entry(
450 &mut self,
451 schema_set: &SchemaSet,
452 path: impl AsRef<str>,
453 element_id: Uuid,
454 ) -> DataSetResult<bool> {
455 let property_schema = self
456 .schema
457 .find_property_schema(&path, schema_set.schemas())
458 .ok_or(DataSetError::SchemaNotFound)?;
459
460 if !property_schema.is_map() {
461 return Err(DataSetError::InvalidSchema)?;
462 }
463
464 self.remove_dynamic_collection_entry(path, element_id)
465 }
466
467 fn do_resolve_dynamic_collection_entries(
468 &self,
469 path: &str,
470 resolved_entries: &mut Vec<Uuid>,
471 ) {
472 if let Some(entries) = self.dynamic_collection_entries.get(path) {
473 for entry in entries {
474 resolved_entries.push(*entry);
475 }
476 }
477 }
478
479 pub fn resolve_dynamic_array_entries(
480 &self,
481 schema_set: &SchemaSet,
482 path: impl AsRef<str>,
483 ) -> DataSetResult<Box<[Uuid]>> {
484 let property_schema = self.validate_parent_paths(schema_set, path.as_ref())?;
485 if !property_schema.is_dynamic_array() {
486 return Err(DataSetError::InvalidSchema)?;
487 }
488
489 let mut resolved_entries = vec![];
490 self.do_resolve_dynamic_collection_entries(path.as_ref(), &mut resolved_entries);
491 Ok(resolved_entries.into_boxed_slice())
492 }
493
494 pub fn resolve_map_entries(
495 &self,
496 schema_set: &SchemaSet,
497 path: impl AsRef<str>,
498 ) -> DataSetResult<Box<[Uuid]>> {
499 let property_schema = self.validate_parent_paths(schema_set, path.as_ref())?;
500 if !property_schema.is_map() {
501 return Err(DataSetError::InvalidSchema)?;
502 }
503
504 let mut resolved_entries = vec![];
505 self.do_resolve_dynamic_collection_entries(path.as_ref(), &mut resolved_entries);
506 Ok(resolved_entries.into_boxed_slice())
507 }
508}