xerv_core/schema/
defaults.rs

1//! Default value handling for schema fields.
2//!
3//! Provides infrastructure for defining and retrieving default values
4//! for schema fields, enabling safe schema evolution with new fields.
5
6use parking_lot::RwLock;
7use std::collections::HashMap;
8use std::sync::Arc;
9
10/// Default value provider for schema fields.
11///
12/// Implementations provide default values for fields in a schema,
13/// which are used when migrating data from older versions that
14/// lack certain fields.
15pub trait DefaultProvider: Send + Sync {
16    /// Get the default value for a field as bytes.
17    ///
18    /// # Arguments
19    ///
20    /// * `field` - The field name
21    /// * `type_name` - The field's type name
22    ///
23    /// # Returns
24    ///
25    /// The default value as bytes (for arena storage), or None if no default.
26    fn default_bytes(&self, field: &str, type_name: &str) -> Option<Vec<u8>>;
27
28    /// Get the default value for a field as a string representation.
29    ///
30    /// This is useful for debugging and display purposes.
31    fn default_string(&self, field: &str, type_name: &str) -> Option<String>;
32
33    /// Check if a field has a default value.
34    fn has_default(&self, field: &str) -> bool;
35
36    /// Get all fields that have defaults.
37    fn fields_with_defaults(&self) -> Vec<String>;
38}
39
40/// A simple default provider backed by a map.
41#[derive(Default)]
42pub struct MapDefaultProvider {
43    /// Field name -> (type_name, bytes, string_repr)
44    defaults: HashMap<String, (String, Vec<u8>, String)>,
45}
46
47impl MapDefaultProvider {
48    /// Create a new empty provider.
49    pub fn new() -> Self {
50        Self {
51            defaults: HashMap::new(),
52        }
53    }
54
55    /// Add a string default.
56    pub fn with_string(mut self, field: &str, value: &str) -> Self {
57        let bytes = value.as_bytes().to_vec();
58        self.defaults.insert(
59            field.to_string(),
60            ("String".to_string(), bytes, value.to_string()),
61        );
62        self
63    }
64
65    /// Add an i32 default.
66    pub fn with_i32(mut self, field: &str, value: i32) -> Self {
67        let bytes = value.to_le_bytes().to_vec();
68        self.defaults.insert(
69            field.to_string(),
70            ("i32".to_string(), bytes, value.to_string()),
71        );
72        self
73    }
74
75    /// Add an i64 default.
76    pub fn with_i64(mut self, field: &str, value: i64) -> Self {
77        let bytes = value.to_le_bytes().to_vec();
78        self.defaults.insert(
79            field.to_string(),
80            ("i64".to_string(), bytes, value.to_string()),
81        );
82        self
83    }
84
85    /// Add a u32 default.
86    pub fn with_u32(mut self, field: &str, value: u32) -> Self {
87        let bytes = value.to_le_bytes().to_vec();
88        self.defaults.insert(
89            field.to_string(),
90            ("u32".to_string(), bytes, value.to_string()),
91        );
92        self
93    }
94
95    /// Add a u64 default.
96    pub fn with_u64(mut self, field: &str, value: u64) -> Self {
97        let bytes = value.to_le_bytes().to_vec();
98        self.defaults.insert(
99            field.to_string(),
100            ("u64".to_string(), bytes, value.to_string()),
101        );
102        self
103    }
104
105    /// Add an f32 default.
106    pub fn with_f32(mut self, field: &str, value: f32) -> Self {
107        let bytes = value.to_le_bytes().to_vec();
108        self.defaults.insert(
109            field.to_string(),
110            ("f32".to_string(), bytes, value.to_string()),
111        );
112        self
113    }
114
115    /// Add an f64 default.
116    pub fn with_f64(mut self, field: &str, value: f64) -> Self {
117        let bytes = value.to_le_bytes().to_vec();
118        self.defaults.insert(
119            field.to_string(),
120            ("f64".to_string(), bytes, value.to_string()),
121        );
122        self
123    }
124
125    /// Add a bool default.
126    pub fn with_bool(mut self, field: &str, value: bool) -> Self {
127        let bytes = vec![if value { 1u8 } else { 0u8 }];
128        self.defaults.insert(
129            field.to_string(),
130            ("bool".to_string(), bytes, value.to_string()),
131        );
132        self
133    }
134
135    /// Add raw bytes default.
136    pub fn with_bytes(mut self, field: &str, type_name: &str, bytes: Vec<u8>, repr: &str) -> Self {
137        self.defaults.insert(
138            field.to_string(),
139            (type_name.to_string(), bytes, repr.to_string()),
140        );
141        self
142    }
143}
144
145impl DefaultProvider for MapDefaultProvider {
146    fn default_bytes(&self, field: &str, _type_name: &str) -> Option<Vec<u8>> {
147        self.defaults.get(field).map(|(_, bytes, _)| bytes.clone())
148    }
149
150    fn default_string(&self, field: &str, _type_name: &str) -> Option<String> {
151        self.defaults.get(field).map(|(_, _, repr)| repr.clone())
152    }
153
154    fn has_default(&self, field: &str) -> bool {
155        self.defaults.contains_key(field)
156    }
157
158    fn fields_with_defaults(&self) -> Vec<String> {
159        self.defaults.keys().cloned().collect()
160    }
161}
162
163/// Registry of default value providers.
164///
165/// Stores default providers for different schemas, enabling
166/// runtime lookup of field defaults during migration.
167pub struct DefaultRegistry {
168    /// Providers keyed by schema name.
169    providers: RwLock<HashMap<String, Arc<dyn DefaultProvider>>>,
170}
171
172impl DefaultRegistry {
173    /// Create a new empty registry.
174    pub fn new() -> Self {
175        Self {
176            providers: RwLock::new(HashMap::new()),
177        }
178    }
179
180    /// Register a default provider for a schema.
181    pub fn register(&self, schema: &str, provider: impl DefaultProvider + 'static) {
182        let mut providers = self.providers.write();
183        providers.insert(schema.to_string(), Arc::new(provider));
184    }
185
186    /// Register a shared default provider for a schema.
187    pub fn register_shared(&self, schema: &str, provider: Arc<dyn DefaultProvider>) {
188        let mut providers = self.providers.write();
189        providers.insert(schema.to_string(), provider);
190    }
191
192    /// Get the default provider for a schema.
193    pub fn get_provider(&self, schema: &str) -> Option<Arc<dyn DefaultProvider>> {
194        let providers = self.providers.read();
195        providers.get(schema).cloned()
196    }
197
198    /// Get a default value for a field in a schema.
199    pub fn get_default(&self, schema: &str, field: &str, type_name: &str) -> Option<Vec<u8>> {
200        self.get_provider(schema)
201            .and_then(|p| p.default_bytes(field, type_name))
202    }
203
204    /// Get a default value as string representation.
205    pub fn get_default_string(&self, schema: &str, field: &str, type_name: &str) -> Option<String> {
206        self.get_provider(schema)
207            .and_then(|p| p.default_string(field, type_name))
208    }
209
210    /// Check if a field has a default in a schema.
211    pub fn has_default(&self, schema: &str, field: &str) -> bool {
212        self.get_provider(schema)
213            .map(|p| p.has_default(field))
214            .unwrap_or(false)
215    }
216
217    /// Get all schemas with registered providers.
218    pub fn schemas(&self) -> Vec<String> {
219        let providers = self.providers.read();
220        providers.keys().cloned().collect()
221    }
222
223    /// Unregister a default provider.
224    pub fn unregister(&self, schema: &str) -> bool {
225        let mut providers = self.providers.write();
226        providers.remove(schema).is_some()
227    }
228}
229
230impl Default for DefaultRegistry {
231    fn default() -> Self {
232        Self::new()
233    }
234}
235
236/// Field default specification for macro generation.
237///
238/// This struct captures default value information that can be
239/// used by the `#[xerv::schema]` macro.
240#[derive(Debug, Clone)]
241pub struct FieldDefault {
242    /// The name of the field.
243    pub field: String,
244    /// The type name of the field.
245    pub type_name: String,
246    /// Default value expression as a code string (used for code generation).
247    pub expression: String,
248    /// Serialized bytes of the default value (for runtime use).
249    pub bytes: Option<Vec<u8>>,
250}
251
252impl FieldDefault {
253    /// Create a new field default specification.
254    pub fn new(
255        field: impl Into<String>,
256        type_name: impl Into<String>,
257        expression: impl Into<String>,
258    ) -> Self {
259        Self {
260            field: field.into(),
261            type_name: type_name.into(),
262            expression: expression.into(),
263            bytes: None,
264        }
265    }
266
267    /// Set the serialized bytes.
268    pub fn with_bytes(mut self, bytes: Vec<u8>) -> Self {
269        self.bytes = Some(bytes);
270        self
271    }
272}
273
274#[cfg(test)]
275mod tests {
276    use super::*;
277
278    #[test]
279    fn map_provider_string_default() {
280        let provider = MapDefaultProvider::new().with_string("currency", "USD");
281
282        assert!(provider.has_default("currency"));
283        assert!(!provider.has_default("other"));
284
285        let bytes = provider.default_bytes("currency", "String").unwrap();
286        assert_eq!(String::from_utf8(bytes).unwrap(), "USD");
287
288        let repr = provider.default_string("currency", "String").unwrap();
289        assert_eq!(repr, "USD");
290    }
291
292    #[test]
293    fn map_provider_numeric_defaults() {
294        let provider = MapDefaultProvider::new()
295            .with_i32("count", 42)
296            .with_f64("rate", 0.05)
297            .with_bool("active", true);
298
299        let count_bytes = provider.default_bytes("count", "i32").unwrap();
300        assert_eq!(i32::from_le_bytes(count_bytes.try_into().unwrap()), 42);
301
302        let rate_bytes = provider.default_bytes("rate", "f64").unwrap();
303        assert_eq!(f64::from_le_bytes(rate_bytes.try_into().unwrap()), 0.05);
304
305        let active_bytes = provider.default_bytes("active", "bool").unwrap();
306        assert_eq!(active_bytes[0], 1);
307    }
308
309    #[test]
310    fn registry_register_and_get() {
311        let registry = DefaultRegistry::new();
312
313        let provider = MapDefaultProvider::new()
314            .with_string("currency", "USD")
315            .with_f64("amount", 0.0);
316
317        registry.register("OrderInput@v2", provider);
318
319        assert!(registry.has_default("OrderInput@v2", "currency"));
320        assert!(!registry.has_default("OrderInput@v2", "other"));
321        assert!(!registry.has_default("OrderInput@v1", "currency"));
322
323        let default = registry.get_default("OrderInput@v2", "currency", "String");
324        assert!(default.is_some());
325
326        let repr = registry.get_default_string("OrderInput@v2", "currency", "String");
327        assert_eq!(repr, Some("USD".to_string()));
328    }
329
330    #[test]
331    fn registry_schemas() {
332        let registry = DefaultRegistry::new();
333
334        registry.register("Schema1", MapDefaultProvider::new());
335        registry.register("Schema2", MapDefaultProvider::new());
336
337        let schemas = registry.schemas();
338        assert_eq!(schemas.len(), 2);
339        assert!(schemas.contains(&"Schema1".to_string()));
340        assert!(schemas.contains(&"Schema2".to_string()));
341    }
342
343    #[test]
344    fn registry_unregister() {
345        let registry = DefaultRegistry::new();
346
347        registry.register("Schema1", MapDefaultProvider::new());
348        assert!(registry.unregister("Schema1"));
349        assert!(!registry.unregister("Schema1"));
350        assert!(registry.schemas().is_empty());
351    }
352
353    #[test]
354    fn fields_with_defaults() {
355        let provider = MapDefaultProvider::new()
356            .with_string("a", "value")
357            .with_i32("b", 0)
358            .with_bool("c", false);
359
360        let fields = provider.fields_with_defaults();
361        assert_eq!(fields.len(), 3);
362        assert!(fields.contains(&"a".to_string()));
363        assert!(fields.contains(&"b".to_string()));
364        assert!(fields.contains(&"c".to_string()));
365    }
366}