Skip to main content

shape_wire/
envelope.rs

1//! Value envelope - combines value with metadata
2//!
3//! The envelope is the primary data structure for exchanging Shape values
4//! between components. It bundles the raw value with type information and
5//! available format options.
6
7use crate::metadata::{TypeInfo, TypeKind, TypeRegistry};
8use crate::value::WireValue;
9use serde::{Deserialize, Serialize};
10
11/// Complete value envelope with metadata
12///
13/// This is the primary exchange format for Shape values.
14/// It contains:
15/// - The raw value (for lossless data transfer)
16/// - Type information (for proper interpretation)
17/// - Type registry (for metadata and display options)
18#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
19pub struct ValueEnvelope {
20    /// The wire-format value
21    pub value: WireValue,
22
23    /// Type information
24    pub type_info: TypeInfo,
25
26    /// Available metadata items for display/parsing
27    pub type_registry: TypeRegistry,
28}
29
30impl ValueEnvelope {
31    /// Create a new envelope
32    pub fn new(value: WireValue, type_info: TypeInfo, type_registry: TypeRegistry) -> Self {
33        ValueEnvelope {
34            value,
35            type_info,
36            type_registry,
37        }
38    }
39
40    /// Create an envelope with default metadata inferred from the value
41    pub fn from_value(value: WireValue) -> Self {
42        let (type_info, type_registry) = Self::infer_metadata(&value);
43        ValueEnvelope {
44            value,
45            type_info,
46            type_registry,
47        }
48    }
49
50    /// Infer type and registry metadata from a value
51    fn infer_metadata(value: &WireValue) -> (TypeInfo, TypeRegistry) {
52        match value {
53            WireValue::Null => (TypeInfo::null(), TypeRegistry::default_for_primitives()),
54
55            WireValue::Bool(_) => (TypeInfo::bool(), TypeRegistry::default_for_primitives()),
56
57            WireValue::Number(_) => (TypeInfo::number(), TypeRegistry::for_number()),
58
59            WireValue::Integer(_) => (TypeInfo::integer(), TypeRegistry::for_number()),
60
61            WireValue::I8(_) => (TypeInfo::primitive("i8"), TypeRegistry::for_number()),
62            WireValue::U8(_) => (TypeInfo::primitive("u8"), TypeRegistry::for_number()),
63            WireValue::I16(_) => (TypeInfo::primitive("i16"), TypeRegistry::for_number()),
64            WireValue::U16(_) => (TypeInfo::primitive("u16"), TypeRegistry::for_number()),
65            WireValue::I32(_) => (TypeInfo::primitive("i32"), TypeRegistry::for_number()),
66            WireValue::U32(_) => (TypeInfo::primitive("u32"), TypeRegistry::for_number()),
67            WireValue::I64(_) => (TypeInfo::primitive("i64"), TypeRegistry::for_number()),
68            WireValue::U64(_) => (TypeInfo::primitive("u64"), TypeRegistry::for_number()),
69            WireValue::Isize(_) => (TypeInfo::primitive("isize"), TypeRegistry::for_number()),
70            WireValue::Usize(_) => (TypeInfo::primitive("usize"), TypeRegistry::for_number()),
71            WireValue::Ptr(_) => (
72                TypeInfo::primitive("ptr"),
73                TypeRegistry::default_for_primitives(),
74            ),
75            WireValue::F32(_) => (TypeInfo::primitive("f32"), TypeRegistry::for_number()),
76
77            WireValue::String(_) => (TypeInfo::string(), TypeRegistry::default_for_primitives()),
78
79            WireValue::Timestamp(_) => (TypeInfo::timestamp(), TypeRegistry::for_timestamp()),
80
81            WireValue::Duration { .. } => (
82                TypeInfo::primitive("Duration"),
83                TypeRegistry::default_for_primitives(),
84            ),
85
86            WireValue::Array(items) => {
87                let element_type = if items.is_empty() {
88                    TypeInfo::primitive("Unknown")
89                } else {
90                    Self::infer_metadata(&items[0]).0
91                };
92                (
93                    TypeInfo::array(element_type),
94                    TypeRegistry::default_for_primitives(),
95                )
96            }
97
98            WireValue::Object(fields) => {
99                let field_infos: Vec<_> = fields
100                    .iter()
101                    .map(|(name, v)| {
102                        let (field_type, _) = Self::infer_metadata(v);
103                        crate::metadata::FieldInfo::required(name, field_type)
104                    })
105                    .collect();
106                (
107                    TypeInfo::object("Object", field_infos),
108                    TypeRegistry::default_for_primitives(),
109                )
110            }
111
112            WireValue::Table(series) => {
113                let element_type = series
114                    .type_name
115                    .as_ref()
116                    .map(|n| TypeInfo::primitive(n.clone()))
117                    .unwrap_or_else(|| TypeInfo::primitive("Row"));
118                (
119                    TypeInfo::table(element_type),
120                    TypeRegistry::default_for_primitives(),
121                )
122            }
123
124            WireValue::Result { ok, value } => {
125                let (inner_type, _) = Self::infer_metadata(value);
126                let name = if *ok {
127                    format!("Ok<{}>", inner_type.name)
128                } else {
129                    format!("Err<{}>", inner_type.name)
130                };
131                (
132                    TypeInfo {
133                        name,
134                        kind: TypeKind::Result,
135                        fields: None,
136                        generic_params: Some(vec![inner_type]),
137                        variants: None,
138                        description: None,
139                        metadata: None,
140                    },
141                    TypeRegistry::default_for_primitives(),
142                )
143            }
144
145            WireValue::Range { .. } => (
146                TypeInfo::primitive("Range"),
147                TypeRegistry::default_for_primitives(),
148            ),
149
150            WireValue::FunctionRef { name } => (
151                TypeInfo {
152                    name: format!("Function<{}>", name),
153                    kind: TypeKind::Function,
154                    fields: None,
155                    generic_params: None,
156                    variants: None,
157                    description: None,
158                    metadata: None,
159                },
160                TypeRegistry::default_for_primitives(),
161            ),
162
163            WireValue::PrintResult(_) => (
164                TypeInfo::primitive("PrintResult"),
165                TypeRegistry::default_for_primitives(),
166            ),
167
168            WireValue::Content(_) => (
169                TypeInfo::primitive("Content"),
170                TypeRegistry::default_for_primitives(),
171            ),
172        }
173    }
174
175    /// Get the default format name
176    pub fn default_format(&self) -> &str {
177        &self.type_registry.default_item
178    }
179
180    /// Get available metadata/format names
181    pub fn available_formats(&self) -> Vec<&str> {
182        self.type_registry
183            .items
184            .iter()
185            .map(|f| f.name.as_str())
186            .collect()
187    }
188
189    /// Check if a metadata item is available
190    pub fn has_format(&self, name: &str) -> bool {
191        self.type_registry.items.iter().any(|f| f.name == name)
192    }
193
194    /// Format the value using the default format
195    pub fn format_default(&self) -> crate::error::Result<String> {
196        self.format(
197            &self.type_registry.default_item,
198            &std::collections::HashMap::new(),
199        )
200    }
201
202    /// Format the value using a specific metadata item
203    pub fn format(
204        &self,
205        format_name: &str,
206        params: &std::collections::HashMap<String, serde_json::Value>,
207    ) -> crate::error::Result<String> {
208        crate::formatter::format_value(&self.value, format_name, params)
209    }
210
211    /// Format the value with specific metadata and parameters as JSON
212    pub fn format_with_json_params(
213        &self,
214        format_name: &str,
215        params: &serde_json::Value,
216    ) -> crate::error::Result<String> {
217        let params_map: std::collections::HashMap<String, serde_json::Value> = match params {
218            serde_json::Value::Object(map) => {
219                map.iter().map(|(k, v)| (k.clone(), v.clone())).collect()
220            }
221            _ => std::collections::HashMap::new(),
222        };
223        self.format(format_name, &params_map)
224    }
225}
226
227// Convenience constructors for common types
228impl ValueEnvelope {
229    /// Create a null envelope
230    pub fn null() -> Self {
231        Self::from_value(WireValue::Null)
232    }
233
234    /// Create a number envelope
235    pub fn number(n: f64) -> Self {
236        Self::from_value(WireValue::Number(n))
237    }
238
239    /// Create a string envelope
240    pub fn string(s: impl Into<String>) -> Self {
241        Self::from_value(WireValue::String(s.into()))
242    }
243
244    /// Create a boolean envelope
245    pub fn bool(b: bool) -> Self {
246        Self::from_value(WireValue::Bool(b))
247    }
248
249    /// Create a timestamp envelope
250    pub fn timestamp(millis: i64) -> Self {
251        Self::from_value(WireValue::Timestamp(millis))
252    }
253}
254
255#[cfg(test)]
256mod tests {
257    use super::*;
258
259    #[test]
260    fn test_envelope_from_value() {
261        let env = ValueEnvelope::from_value(WireValue::Number(42.0));
262        assert_eq!(env.type_info.name, "Number");
263        assert!(env.has_format("Default"));
264        assert!(env.has_format("Fixed"));
265    }
266
267    #[test]
268    fn test_envelope_timestamp() {
269        let env = ValueEnvelope::timestamp(1704067200000);
270        assert_eq!(env.type_info.name, "Timestamp");
271        assert_eq!(env.default_format(), "ISO8601");
272        assert!(env.has_format("Unix"));
273        assert!(env.has_format("Relative"));
274    }
275
276    #[test]
277    fn test_envelope_array() {
278        let env = ValueEnvelope::from_value(WireValue::Array(vec![
279            WireValue::Number(1.0),
280            WireValue::Number(2.0),
281        ]));
282        assert_eq!(env.type_info.name, "Array<Number>");
283    }
284
285    #[test]
286    fn test_envelope_convenience() {
287        let env = ValueEnvelope::number(3.14);
288        assert_eq!(env.value.as_number(), Some(3.14));
289
290        let env = ValueEnvelope::string("hello");
291        assert_eq!(env.value.as_str(), Some("hello"));
292    }
293}