capnweb_core/protocol/
expression.rs

1// use serde::{Deserialize, Serialize}; // TODO: Remove when serialization is implemented
2use super::ids::{ExportId, ImportId};
3use serde_json::{Number, Value as JsonValue};
4use std::collections::HashMap;
5
6/// Cap'n Web expressions represent values and operations in the protocol
7#[derive(Debug, Clone, PartialEq)]
8pub enum Expression {
9    // Literal JSON values
10    Null,
11    Bool(bool),
12    Number(Number),
13    String(String),
14    Array(Vec<Expression>),
15    Object(std::collections::HashMap<String, Box<Expression>>),
16
17    // Special typed expressions (parsed from arrays)
18    EscapedArray(Vec<Expression>),
19    Date(f64),
20    Error(ErrorExpression),
21    Import(ImportExpression),
22    Pipeline(PipelineExpression),
23    Remap(RemapExpression),
24    Export(ExportExpression),
25    Promise(PromiseExpression),
26}
27
28impl Expression {
29    /// Parse an expression from a JSON value
30    pub fn from_json(value: &JsonValue) -> Result<Self, ExpressionError> {
31        match value {
32            JsonValue::Null => Ok(Expression::Null),
33            JsonValue::Bool(b) => Ok(Expression::Bool(*b)),
34            JsonValue::Number(n) => Ok(Expression::Number(n.clone())),
35            JsonValue::String(s) => Ok(Expression::String(s.clone())),
36
37            JsonValue::Array(arr) if arr.is_empty() => Ok(Expression::Array(Vec::new())),
38
39            JsonValue::Array(arr) => {
40                // Check if this is a special typed array
41                if let Some(JsonValue::String(type_code)) = arr.first() {
42                    Self::parse_typed_array(type_code, arr)
43                } else if let Some(JsonValue::Array(inner)) = arr.first() {
44                    // This is an escaped array: [[...]]
45                    if arr.len() == 1 {
46                        let elements = inner
47                            .iter()
48                            .map(Self::from_json)
49                            .collect::<Result<Vec<_>, _>>()?;
50                        Ok(Expression::EscapedArray(elements))
51                    } else {
52                        // Regular array with array as first element
53                        let elements = arr
54                            .iter()
55                            .map(Self::from_json)
56                            .collect::<Result<Vec<_>, _>>()?;
57                        Ok(Expression::Array(elements))
58                    }
59                } else {
60                    // Regular array
61                    let elements = arr
62                        .iter()
63                        .map(Self::from_json)
64                        .collect::<Result<Vec<_>, _>>()?;
65                    Ok(Expression::Array(elements))
66                }
67            }
68
69            JsonValue::Object(obj) => {
70                let mut map = HashMap::new();
71                for (key, val) in obj {
72                    map.insert(key.clone(), Box::new(Self::from_json(val)?));
73                }
74                Ok(Expression::Object(map))
75            }
76        }
77    }
78
79    /// Convert the expression to a JSON value
80    pub fn to_json(&self) -> JsonValue {
81        match self {
82            Expression::Null => JsonValue::Null,
83            Expression::Bool(b) => JsonValue::Bool(*b),
84            Expression::Number(n) => JsonValue::Number(n.clone()),
85            Expression::String(s) => JsonValue::String(s.clone()),
86
87            Expression::Array(elements) => {
88                JsonValue::Array(elements.iter().map(|e| e.to_json()).collect())
89            }
90
91            Expression::Object(map) => {
92                let mut obj = serde_json::Map::new();
93                for (key, val) in map {
94                    obj.insert(key.clone(), val.to_json());
95                }
96                JsonValue::Object(obj)
97            }
98
99            Expression::EscapedArray(elements) => {
100                // Wrap in outer array for escaping
101                let inner = elements.iter().map(|e| e.to_json()).collect();
102                JsonValue::Array(vec![JsonValue::Array(inner)])
103            }
104
105            Expression::Date(millis) => {
106                serde_json::json!(["date", millis])
107            }
108
109            Expression::Error(err) => {
110                if let Some(stack) = &err.stack {
111                    serde_json::json!(["error", &err.error_type, &err.message, stack])
112                } else {
113                    serde_json::json!(["error", &err.error_type, &err.message])
114                }
115            }
116
117            Expression::Import(import) => import.to_json(),
118            Expression::Pipeline(pipeline) => pipeline.to_json(),
119            Expression::Remap(remap) => remap.to_json(),
120            Expression::Export(export) => export.to_json(),
121            Expression::Promise(promise) => promise.to_json(),
122        }
123    }
124
125    fn parse_typed_array(type_code: &str, arr: &[JsonValue]) -> Result<Self, ExpressionError> {
126        match type_code {
127            "date" => {
128                if arr.len() != 2 {
129                    return Err(ExpressionError::InvalidDate);
130                }
131                let millis = arr[1].as_f64().ok_or(ExpressionError::InvalidDate)?;
132                Ok(Expression::Date(millis))
133            }
134
135            "error" => {
136                if arr.len() < 3 || arr.len() > 4 {
137                    return Err(ExpressionError::InvalidError);
138                }
139                let error_type = arr[1]
140                    .as_str()
141                    .ok_or(ExpressionError::InvalidError)?
142                    .to_string();
143                let message = arr[2]
144                    .as_str()
145                    .ok_or(ExpressionError::InvalidError)?
146                    .to_string();
147                let stack = arr.get(3).and_then(|v| v.as_str()).map(String::from);
148
149                Ok(Expression::Error(ErrorExpression {
150                    error_type,
151                    message,
152                    stack,
153                }))
154            }
155
156            "import" => ImportExpression::from_array(arr).map(Expression::Import),
157
158            "pipeline" => PipelineExpression::from_array(arr).map(Expression::Pipeline),
159
160            "remap" => RemapExpression::from_array(arr).map(Expression::Remap),
161
162            "export" => ExportExpression::from_array(arr).map(Expression::Export),
163
164            "promise" => PromiseExpression::from_array(arr).map(Expression::Promise),
165
166            _ => {
167                // Unknown type code, treat as regular array
168                let elements = arr
169                    .iter()
170                    .map(Self::from_json)
171                    .collect::<Result<Vec<_>, _>>()?;
172                Ok(Expression::Array(elements))
173            }
174        }
175    }
176}
177
178#[derive(Debug, Clone, PartialEq)]
179pub struct ErrorExpression {
180    pub error_type: String,
181    pub message: String,
182    pub stack: Option<String>,
183}
184
185#[derive(Debug, Clone, PartialEq)]
186pub struct ImportExpression {
187    pub import_id: ImportId,
188    pub property_path: Option<Vec<PropertyKey>>,
189    pub call_arguments: Option<Box<Expression>>,
190}
191
192impl ImportExpression {
193    fn from_array(arr: &[JsonValue]) -> Result<Self, ExpressionError> {
194        if arr.len() < 2 || arr.len() > 4 {
195            return Err(ExpressionError::InvalidImport);
196        }
197
198        let import_id = arr[1].as_i64().ok_or(ExpressionError::InvalidImport)?;
199
200        let property_path = arr.get(2).map(PropertyKey::parse_path).transpose()?;
201
202        let call_arguments = arr
203            .get(3)
204            .map(|v| Expression::from_json(v).map(Box::new))
205            .transpose()?;
206
207        Ok(ImportExpression {
208            import_id: ImportId(import_id),
209            property_path,
210            call_arguments,
211        })
212    }
213
214    fn to_json(&self) -> JsonValue {
215        let mut arr = vec![
216            JsonValue::String("import".to_string()),
217            JsonValue::Number(Number::from(self.import_id.0)),
218        ];
219
220        if let Some(path) = &self.property_path {
221            arr.push(PropertyKey::path_to_json(path));
222        }
223
224        if let Some(args) = &self.call_arguments {
225            arr.push(args.to_json());
226        }
227
228        JsonValue::Array(arr)
229    }
230}
231
232#[derive(Debug, Clone, PartialEq)]
233pub struct PipelineExpression {
234    pub import_id: ImportId,
235    pub property_path: Option<Vec<PropertyKey>>,
236    pub call_arguments: Option<Box<Expression>>,
237}
238
239impl PipelineExpression {
240    fn from_array(arr: &[JsonValue]) -> Result<Self, ExpressionError> {
241        if arr.len() < 2 || arr.len() > 4 {
242            return Err(ExpressionError::InvalidPipeline);
243        }
244
245        let import_id = arr[1].as_i64().ok_or(ExpressionError::InvalidPipeline)?;
246
247        let property_path = arr.get(2).map(PropertyKey::parse_path).transpose()?;
248
249        let call_arguments = arr
250            .get(3)
251            .map(|v| Expression::from_json(v).map(Box::new))
252            .transpose()?;
253
254        Ok(PipelineExpression {
255            import_id: ImportId(import_id),
256            property_path,
257            call_arguments,
258        })
259    }
260
261    fn to_json(&self) -> JsonValue {
262        let mut arr = vec![
263            JsonValue::String("pipeline".to_string()),
264            JsonValue::Number(Number::from(self.import_id.0)),
265        ];
266
267        if let Some(path) = &self.property_path {
268            arr.push(PropertyKey::path_to_json(path));
269        }
270
271        if let Some(args) = &self.call_arguments {
272            arr.push(args.to_json());
273        }
274
275        JsonValue::Array(arr)
276    }
277}
278
279#[derive(Debug, Clone, PartialEq)]
280pub struct RemapExpression {
281    pub import_id: ImportId,
282    pub property_path: Option<Vec<PropertyKey>>,
283    pub captures: Vec<CaptureRef>,
284    pub instructions: Vec<Expression>,
285}
286
287impl RemapExpression {
288    fn from_array(arr: &[JsonValue]) -> Result<Self, ExpressionError> {
289        if arr.len() != 5 {
290            return Err(ExpressionError::InvalidRemap);
291        }
292
293        let import_id = arr[1].as_i64().ok_or(ExpressionError::InvalidRemap)?;
294
295        let property_path = if !arr[2].is_null() {
296            Some(PropertyKey::parse_path(&arr[2])?)
297        } else {
298            None
299        };
300
301        let captures = arr[3]
302            .as_array()
303            .ok_or(ExpressionError::InvalidRemap)?
304            .iter()
305            .map(CaptureRef::from_json)
306            .collect::<Result<Vec<_>, _>>()?;
307
308        let instructions = arr[4]
309            .as_array()
310            .ok_or(ExpressionError::InvalidRemap)?
311            .iter()
312            .map(Expression::from_json)
313            .collect::<Result<Vec<_>, _>>()?;
314
315        Ok(RemapExpression {
316            import_id: ImportId(import_id),
317            property_path,
318            captures,
319            instructions,
320        })
321    }
322
323    fn to_json(&self) -> JsonValue {
324        let path = self
325            .property_path
326            .as_ref()
327            .map(|p| PropertyKey::path_to_json(p))
328            .unwrap_or(JsonValue::Null);
329
330        let captures: Vec<JsonValue> = self.captures.iter().map(|c| c.to_json()).collect();
331
332        let instructions: Vec<JsonValue> = self.instructions.iter().map(|i| i.to_json()).collect();
333
334        serde_json::json!(["remap", self.import_id.0, path, captures, instructions])
335    }
336}
337
338#[derive(Debug, Clone, PartialEq)]
339pub struct ExportExpression {
340    pub export_id: ExportId,
341}
342
343impl ExportExpression {
344    fn from_array(arr: &[JsonValue]) -> Result<Self, ExpressionError> {
345        if arr.len() != 2 {
346            return Err(ExpressionError::InvalidExport);
347        }
348
349        let export_id = arr[1].as_i64().ok_or(ExpressionError::InvalidExport)?;
350
351        Ok(ExportExpression {
352            export_id: ExportId(export_id),
353        })
354    }
355
356    fn to_json(&self) -> JsonValue {
357        serde_json::json!(["export", self.export_id.0])
358    }
359}
360
361#[derive(Debug, Clone, PartialEq)]
362pub struct PromiseExpression {
363    pub export_id: ExportId,
364}
365
366impl PromiseExpression {
367    fn from_array(arr: &[JsonValue]) -> Result<Self, ExpressionError> {
368        if arr.len() != 2 {
369            return Err(ExpressionError::InvalidPromise);
370        }
371
372        let export_id = arr[1].as_i64().ok_or(ExpressionError::InvalidPromise)?;
373
374        Ok(PromiseExpression {
375            export_id: ExportId(export_id),
376        })
377    }
378
379    fn to_json(&self) -> JsonValue {
380        serde_json::json!(["promise", self.export_id.0])
381    }
382}
383
384#[derive(Debug, Clone, PartialEq)]
385pub enum PropertyKey {
386    String(String),
387    Number(usize),
388}
389
390impl PropertyKey {
391    fn parse_path(value: &JsonValue) -> Result<Vec<PropertyKey>, ExpressionError> {
392        let arr = value
393            .as_array()
394            .ok_or(ExpressionError::InvalidPropertyPath)?;
395
396        arr.iter()
397            .map(|v| {
398                if let Some(s) = v.as_str() {
399                    Ok(PropertyKey::String(s.to_string()))
400                } else if let Some(n) = v.as_u64() {
401                    Ok(PropertyKey::Number(n as usize))
402                } else {
403                    Err(ExpressionError::InvalidPropertyPath)
404                }
405            })
406            .collect()
407    }
408
409    fn path_to_json(path: &[PropertyKey]) -> JsonValue {
410        let elements: Vec<JsonValue> = path
411            .iter()
412            .map(|key| match key {
413                PropertyKey::String(s) => JsonValue::String(s.clone()),
414                PropertyKey::Number(n) => JsonValue::Number(Number::from(*n)),
415            })
416            .collect();
417
418        JsonValue::Array(elements)
419    }
420}
421
422#[derive(Debug, Clone, PartialEq)]
423pub enum CaptureRef {
424    Import(ImportId),
425    Export(ExportId),
426}
427
428impl CaptureRef {
429    fn from_json(value: &JsonValue) -> Result<Self, ExpressionError> {
430        let arr = value.as_array().ok_or(ExpressionError::InvalidCapture)?;
431
432        if arr.len() != 2 {
433            return Err(ExpressionError::InvalidCapture);
434        }
435
436        let type_str = arr[0].as_str().ok_or(ExpressionError::InvalidCapture)?;
437
438        let id = arr[1].as_i64().ok_or(ExpressionError::InvalidCapture)?;
439
440        match type_str {
441            "import" => Ok(CaptureRef::Import(ImportId(id))),
442            "export" => Ok(CaptureRef::Export(ExportId(id))),
443            _ => Err(ExpressionError::InvalidCapture),
444        }
445    }
446
447    fn to_json(&self) -> JsonValue {
448        match self {
449            CaptureRef::Import(id) => serde_json::json!(["import", id.0]),
450            CaptureRef::Export(id) => serde_json::json!(["export", id.0]),
451        }
452    }
453}
454
455#[derive(Debug, thiserror::Error)]
456pub enum ExpressionError {
457    #[error("Invalid date expression")]
458    InvalidDate,
459
460    #[error("Invalid error expression")]
461    InvalidError,
462
463    #[error("Invalid import expression")]
464    InvalidImport,
465
466    #[error("Invalid pipeline expression")]
467    InvalidPipeline,
468
469    #[error("Invalid remap expression")]
470    InvalidRemap,
471
472    #[error("Invalid export expression")]
473    InvalidExport,
474
475    #[error("Invalid promise expression")]
476    InvalidPromise,
477
478    #[error("Invalid property path")]
479    InvalidPropertyPath,
480
481    #[error("Invalid capture reference")]
482    InvalidCapture,
483}
484
485#[cfg(test)]
486mod tests {
487    use super::*;
488    use serde_json::json;
489
490    #[test]
491    fn test_literal_expressions() {
492        assert_eq!(
493            Expression::from_json(&json!(null)).unwrap(),
494            Expression::Null
495        );
496
497        assert_eq!(
498            Expression::from_json(&json!(true)).unwrap(),
499            Expression::Bool(true)
500        );
501
502        assert_eq!(
503            Expression::from_json(&json!(42)).unwrap(),
504            Expression::Number(Number::from(42))
505        );
506
507        assert_eq!(
508            Expression::from_json(&json!("hello")).unwrap(),
509            Expression::String("hello".to_string())
510        );
511    }
512
513    #[test]
514    fn test_date_expression() {
515        let json = json!(["date", 1234567890.0]);
516        let expr = Expression::from_json(&json).unwrap();
517
518        match expr {
519            Expression::Date(millis) => assert_eq!(millis, 1234567890.0),
520            _ => panic!("Expected Date expression"),
521        }
522
523        assert_eq!(expr.to_json(), json);
524    }
525
526    #[test]
527    fn test_error_expression() {
528        let json = json!(["error", "TypeError", "Something went wrong", "stack trace"]);
529        let expr = Expression::from_json(&json).unwrap();
530
531        match expr {
532            Expression::Error(err) => {
533                assert_eq!(err.error_type, "TypeError");
534                assert_eq!(err.message, "Something went wrong");
535                assert_eq!(err.stack, Some("stack trace".to_string()));
536            }
537            _ => panic!("Expected Error expression"),
538        }
539    }
540
541    #[test]
542    fn test_import_expression() {
543        let json = json!(["import", 42, ["method"], [1, 2, 3]]);
544        let expr = Expression::from_json(&json).unwrap();
545
546        match expr {
547            Expression::Import(import) => {
548                assert_eq!(import.import_id, ImportId(42));
549                assert_eq!(
550                    import.property_path,
551                    Some(vec![PropertyKey::String("method".to_string())])
552                );
553                assert!(import.call_arguments.is_some());
554            }
555            _ => panic!("Expected Import expression"),
556        }
557    }
558
559    #[test]
560    fn test_escaped_array() {
561        let json = json!([["just", "an", "array"]]);
562        let expr = Expression::from_json(&json).unwrap();
563
564        match expr {
565            Expression::EscapedArray(elements) => {
566                assert_eq!(elements.len(), 3);
567                assert_eq!(elements[0], Expression::String("just".to_string()));
568            }
569            _ => panic!("Expected EscapedArray expression"),
570        }
571    }
572}