1use std::collections::{BTreeMap, HashMap, HashSet};
5use std::sync::Arc;
6
7use crate::{DataType, Value};
8
9#[derive(Debug, Clone, PartialEq, Eq)]
10pub struct DynamicFieldError {
11 pub code: &'static str,
12 pub field: String,
13}
14
15impl std::fmt::Display for DynamicFieldError {
16 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
17 write!(formatter, "{}: {}", self.code, self.field)
18 }
19}
20
21impl std::error::Error for DynamicFieldError {}
22
23fn error(code: &'static str, field: impl Into<String>) -> DynamicFieldError {
24 DynamicFieldError {
25 code,
26 field: field.into(),
27 }
28}
29
30#[derive(Debug, Clone, PartialEq, Eq)]
32pub enum DynamicFieldSelection {
33 All,
34 Fields(BTreeMap<String, DataType>),
35}
36
37impl DynamicFieldSelection {
38 pub fn fields(
39 fields: impl IntoIterator<Item = (String, DataType)>,
40 ) -> Result<Self, DynamicFieldError> {
41 let mut selected = BTreeMap::new();
42 for (code, data_type) in fields {
43 if code.trim().is_empty() || code.starts_with(['#', '_']) {
44 return Err(error("DYNAMIC_FIELD_INVALID_CODE", code));
45 }
46 if selected.insert(code.clone(), data_type).is_some() {
47 return Err(error("DYNAMIC_FIELD_DUPLICATE_CODE", code));
48 }
49 }
50 Ok(Self::Fields(selected))
51 }
52
53 pub fn validate(&self, definitions: &DynamicFieldDefinitions) -> Result<(), DynamicFieldError> {
54 if let Self::Fields(fields) = self {
55 for (code, selected_type) in fields {
56 if definitions.data_type(code)? != *selected_type {
57 return Err(error("DYNAMIC_FIELD_TYPE_MISMATCH", code));
58 }
59 }
60 }
61 Ok(())
62 }
63
64 pub fn contains(&self, code: &str) -> bool {
65 match self {
66 Self::All => true,
67 Self::Fields(fields) => fields.contains_key(code),
68 }
69 }
70}
71
72#[derive(Debug, PartialEq, Eq)]
74pub struct DynamicFieldDefinitions {
75 owner_type: String,
76 revision: String,
77 fields: Arc<HashMap<String, DataType>>,
78 storage_binding: Option<DynamicFieldStorageBinding>,
79}
80
81#[derive(PartialEq, Eq)]
82struct DynamicFieldStorageBinding {
83 profile: String,
84 executor: u64,
85}
86
87impl std::fmt::Debug for DynamicFieldStorageBinding {
88 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
89 formatter.write_str("DynamicFieldStorageBinding(<opaque>)")
90 }
91}
92
93impl DynamicFieldDefinitions {
94 pub fn new(
95 owner_type: impl Into<String>,
96 revision: impl Into<String>,
97 fields: impl IntoIterator<Item = (String, DataType)>,
98 ) -> Result<Arc<Self>, DynamicFieldError> {
99 let owner_type = owner_type.into();
100 let revision = revision.into();
101 if owner_type.trim().is_empty() || revision.trim().is_empty() {
102 return Err(error("DYNAMIC_FIELD_INVALID_DEFINITIONS", owner_type));
103 }
104 let mut definitions = HashMap::new();
105 for (code, data_type) in fields {
106 if code.trim().is_empty() || code.starts_with(['#', '_']) {
107 return Err(error("DYNAMIC_FIELD_INVALID_CODE", code));
108 }
109 if definitions.insert(code.clone(), data_type).is_some() {
110 return Err(error("DYNAMIC_FIELD_DUPLICATE_CODE", code));
111 }
112 }
113 Ok(Arc::new(Self {
114 owner_type,
115 revision,
116 fields: Arc::new(definitions),
117 storage_binding: None,
118 }))
119 }
120
121 pub fn owner_type(&self) -> &str {
122 &self.owner_type
123 }
124 pub fn revision(&self) -> &str {
125 &self.revision
126 }
127 pub fn fields(&self) -> &HashMap<String, DataType> {
128 &self.fields
129 }
130
131 #[doc(hidden)]
133 pub fn with_storage_binding(self: &Arc<Self>, profile: &str, executor: u64) -> Arc<Self> {
134 if self.storage_binding_matches(profile, executor) {
135 return self.clone();
136 }
137 Arc::new(Self {
138 owner_type: self.owner_type.clone(),
139 revision: self.revision.clone(),
140 fields: self.fields.clone(),
141 storage_binding: Some(DynamicFieldStorageBinding {
142 profile: profile.into(),
143 executor,
144 }),
145 })
146 }
147
148 #[doc(hidden)]
149 pub fn storage_binding_matches(&self, profile: &str, executor: u64) -> bool {
150 self.storage_binding
151 .as_ref()
152 .is_some_and(|binding| binding.profile == profile && binding.executor == executor)
153 }
154
155 #[doc(hidden)]
156 pub fn same_schema(&self, other: &Self) -> bool {
157 self.owner_type == other.owner_type
158 && self.revision == other.revision
159 && self.fields == other.fields
160 }
161
162 pub fn data_type(&self, code: &str) -> Result<DataType, DynamicFieldError> {
163 self.fields
164 .get(code)
165 .copied()
166 .ok_or_else(|| error("DYNAMIC_FIELD_NOT_FOUND", code))
167 }
168
169 pub fn validate_value(&self, code: &str, value: &Value) -> Result<(), DynamicFieldError> {
172 validate_field(self, code, value)
173 }
174}
175
176#[derive(Debug, Clone, Copy, PartialEq, Eq)]
177pub enum DynamicFieldState {
178 Value,
179 Null,
180 NotLoaded,
181}
182
183#[derive(Debug, Clone, PartialEq)]
185pub enum DynamicFieldMutation {
186 Set(Value),
187 Delete,
188}
189
190#[derive(Debug, Clone, Copy)]
192pub struct DynamicFieldValue<'a> {
193 data_type: DataType,
194 stored: Option<&'a Value>,
195}
196
197impl<'a> DynamicFieldValue<'a> {
198 pub fn data_type(&self) -> DataType {
199 self.data_type
200 }
201 pub fn state(&self) -> DynamicFieldState {
202 match self.stored {
203 None => DynamicFieldState::NotLoaded,
204 Some(Value::Null | Value::TypedNull(_)) => DynamicFieldState::Null,
205 Some(_) => DynamicFieldState::Value,
206 }
207 }
208 pub fn value(&self) -> Option<&'a Value> {
210 (self.state() == DynamicFieldState::Value)
211 .then_some(self.stored)
212 .flatten()
213 }
214 pub fn is_loaded(&self) -> bool {
215 self.state() != DynamicFieldState::NotLoaded
216 }
217}
218
219#[derive(Debug, Clone, PartialEq)]
221pub struct DynamicFieldValues {
222 definitions: Arc<DynamicFieldDefinitions>,
223 selected_codes: Arc<HashSet<String>>,
224 values: HashMap<String, Value>,
225}
226
227#[doc(hidden)]
230#[derive(Default)]
231pub struct DynamicFieldMergeShapes {
232 #[allow(clippy::type_complexity)]
233 unions: HashMap<
234 (usize, usize),
235 (
236 Arc<HashSet<String>>,
237 Arc<HashSet<String>>,
238 Arc<HashSet<String>>,
239 ),
240 >,
241}
242
243impl DynamicFieldMergeShapes {
244 fn union(
245 &mut self,
246 left: &Arc<HashSet<String>>,
247 right: &Arc<HashSet<String>>,
248 ) -> Arc<HashSet<String>> {
249 if right.is_subset(left) {
250 return left.clone();
251 }
252 if left.is_subset(right) {
253 return right.clone();
254 }
255 let key = (Arc::as_ptr(left) as usize, Arc::as_ptr(right) as usize);
256 self.unions
257 .entry(key)
258 .or_insert_with(|| {
259 (
260 left.clone(),
261 right.clone(),
262 Arc::new(left.union(right).cloned().collect()),
263 )
264 })
265 .2
266 .clone()
267 }
268}
269
270impl DynamicFieldValues {
271 #[doc(hidden)]
272 pub fn validate_merge(&self, other: &Self) -> Result<(), DynamicFieldError> {
273 if !Arc::ptr_eq(&self.definitions, &other.definitions)
274 && self.definitions != other.definitions
275 {
276 return Err(error(
277 "DYNAMIC_FIELD_INCOMPATIBLE_VIEW",
278 self.definitions.owner_type(),
279 ));
280 }
281 Ok(())
282 }
283
284 #[doc(hidden)]
287 pub fn merge_loaded(
288 &mut self,
289 other: Self,
290 shapes: &mut DynamicFieldMergeShapes,
291 ) -> Result<(), DynamicFieldError> {
292 self.validate_merge(&other)?;
293 self.selected_codes = shapes.union(&self.selected_codes, &other.selected_codes);
294 self.values.extend(other.values);
295 Ok(())
296 }
297 pub fn from_values(
298 definitions: Arc<DynamicFieldDefinitions>,
299 values: HashMap<String, Value>,
300 ) -> Result<Self, DynamicFieldError> {
301 validate_values(&definitions, &values)?;
302 let selected_codes = Arc::new(values.keys().cloned().collect());
303 Ok(Self {
304 definitions,
305 selected_codes,
306 values,
307 })
308 }
309
310 pub fn from_batch(
313 definitions: Arc<DynamicFieldDefinitions>,
314 rows: impl IntoIterator<Item = HashMap<String, Value>>,
315 ) -> Result<Vec<Self>, DynamicFieldError> {
316 use std::hash::{Hash, Hasher};
317 let mut shapes: HashMap<u64, Vec<Arc<HashSet<String>>>> = HashMap::new();
318 let mut result = Vec::new();
319 for values in rows {
320 validate_values(&definitions, &values)?;
321 let hash = values.keys().fold(0_u64, |total, code| {
322 let mut hasher = std::collections::hash_map::DefaultHasher::new();
323 code.hash(&mut hasher);
324 total.wrapping_add(hasher.finish())
325 });
326 let bucket = shapes.entry(hash).or_default();
327 let selected_codes = match bucket.iter().find(|shape| {
328 shape.len() == values.len() && values.keys().all(|code| shape.contains(code))
329 }) {
330 Some(shape) => shape.clone(),
331 None => {
332 let shape: Arc<HashSet<String>> = Arc::new(values.keys().cloned().collect());
333 bucket.push(shape.clone());
334 shape
335 }
336 };
337 result.push(Self {
338 definitions: definitions.clone(),
339 selected_codes,
340 values,
341 });
342 }
343 Ok(result)
344 }
345
346 pub fn definitions(&self) -> &Arc<DynamicFieldDefinitions> {
347 &self.definitions
348 }
349 pub fn selected_codes(&self) -> &Arc<HashSet<String>> {
350 &self.selected_codes
351 }
352 pub fn values(&self) -> &HashMap<String, Value> {
353 &self.values
354 }
355
356 pub fn into_values(self) -> HashMap<String, Value> {
357 self.values
358 }
359
360 pub fn assign(&mut self, code: &str, value: Value) -> Result<(), DynamicFieldError> {
362 validate_field(&self.definitions, code, &value)?;
363 self.values.insert(code.to_owned(), value);
364 if !self.selected_codes.contains(code) {
365 Arc::make_mut(&mut self.selected_codes).insert(code.to_owned());
366 }
367 Ok(())
368 }
369
370 pub fn delete(&mut self, code: &str) -> Result<(), DynamicFieldError> {
372 self.definitions.data_type(code)?;
373 self.values.remove(code);
374 if self.selected_codes.contains(code) {
375 Arc::make_mut(&mut self.selected_codes).remove(code);
376 }
377 Ok(())
378 }
379
380 pub fn field(&self, code: &str) -> Result<DynamicFieldValue<'_>, DynamicFieldError> {
381 Ok(DynamicFieldValue {
382 data_type: self.definitions.data_type(code)?,
383 stored: self.values.get(code),
384 })
385 }
386}
387
388fn validate_values(
389 definitions: &DynamicFieldDefinitions,
390 values: &HashMap<String, Value>,
391) -> Result<(), DynamicFieldError> {
392 for (code, value) in values {
393 validate_field(definitions, code, value)?;
394 }
395 Ok(())
396}
397
398fn validate_field(
399 definitions: &DynamicFieldDefinitions,
400 code: &str,
401 value: &Value,
402) -> Result<(), DynamicFieldError> {
403 let declared = definitions.data_type(code)?;
404 let matches = match value {
405 Value::Null => true,
406 Value::TypedNull(data_type) => *data_type == declared,
407 Value::Bool(_) => declared == DataType::Bool,
408 Value::I64(_) => declared == DataType::I64,
409 Value::U64(_) => declared == DataType::U64,
410 Value::F64(_) => declared == DataType::F64,
411 Value::Decimal(_) => declared == DataType::Decimal,
412 Value::Text(_) => matches!(declared, DataType::Text | DataType::LargeText),
413 Value::Json(_) => declared == DataType::Json,
414 Value::Date(_) => declared == DataType::Date,
415 Value::Timestamp(_) => declared == DataType::Timestamp,
416 Value::Object(_) | Value::List(_) => false,
417 };
418 if !matches {
419 return Err(error("DYNAMIC_FIELD_TYPE_MISMATCH", code));
420 }
421 Ok(())
422}