capnweb_server/
server_wire_handler.rs

1// Wire protocol handler for the server
2// This module adds wire protocol support to the existing server
3
4use capnweb_core::{PropertyKey, WireExpression};
5use serde_json::Value;
6use std::collections::HashMap;
7use tracing::{debug, warn};
8
9/// Convert WireExpression arguments to JSON Values for RPC calls
10pub fn wire_expr_to_values(expr: &WireExpression) -> Vec<Value> {
11    match expr {
12        WireExpression::Array(items) => items.iter().map(wire_expr_to_value).collect(),
13        single => vec![wire_expr_to_value(single)],
14    }
15}
16
17/// Convert WireExpression arguments to JSON Values with pipeline evaluation
18pub fn wire_expr_to_values_with_evaluation(
19    expr: &WireExpression,
20    results: &HashMap<i64, WireExpression>,
21) -> Vec<Value> {
22    match expr {
23        WireExpression::Array(items) => items
24            .iter()
25            .map(|e| wire_expr_to_value_with_evaluation(e, results))
26            .collect(),
27        single => vec![wire_expr_to_value_with_evaluation(single, results)],
28    }
29}
30
31/// Convert a single WireExpression to a JSON Value
32pub fn wire_expr_to_value(expr: &WireExpression) -> Value {
33    match expr {
34        WireExpression::Null => Value::Null,
35        WireExpression::Bool(b) => Value::Bool(*b),
36        WireExpression::Number(n) => Value::Number(n.clone()),
37        WireExpression::String(s) => Value::String(s.clone()),
38        WireExpression::Array(items) => {
39            Value::Array(items.iter().map(wire_expr_to_value).collect())
40        }
41        WireExpression::Object(map) => Value::Object(
42            map.iter()
43                .map(|(k, v)| (k.clone(), wire_expr_to_value(v)))
44                .collect(),
45        ),
46        WireExpression::CapRef(id) => {
47            // Marshal capability reference as special JSON object
48            // This follows TypeScript implementation pattern
49            serde_json::json!({
50                "_type": "capability",
51                "id": id
52            })
53        }
54        _ => {
55            warn!("Unsupported WireExpression type: {:?}", expr);
56            Value::String(format!("Unsupported: {:?}", expr))
57        }
58    }
59}
60
61/// Convert a single WireExpression to a JSON Value with pipeline evaluation
62pub fn wire_expr_to_value_with_evaluation(
63    expr: &WireExpression,
64    results: &HashMap<i64, WireExpression>,
65) -> Value {
66    match expr {
67        // Handle pipeline expressions by evaluating them
68        WireExpression::Pipeline {
69            import_id,
70            property_path,
71            args: _,
72        } => {
73            debug!(
74                "Evaluating pipeline: import_id={}, path={:?}",
75                import_id, property_path
76            );
77
78            // Look up the result for this import_id
79            if let Some(result_expr) = results.get(import_id) {
80                debug!(
81                    "Found result for import_id {}: {:?}",
82                    import_id, result_expr
83                );
84
85                // Navigate the property path if present
86                if let Some(path) = property_path {
87                    let result_value = wire_expr_to_value(result_expr);
88                    navigate_property_path(&result_value, path)
89                } else {
90                    // No path, return the whole result
91                    wire_expr_to_value(result_expr)
92                }
93            } else {
94                warn!(
95                    "No result found for import_id {} during pipeline evaluation",
96                    import_id
97                );
98                Value::Null
99            }
100        }
101        // For non-pipeline expressions, use the regular conversion
102        other => wire_expr_to_value(other),
103    }
104}
105
106/// Navigate a property path through a JSON value
107fn navigate_property_path(value: &Value, path: &[PropertyKey]) -> Value {
108    let mut current = value.clone();
109
110    for key in path {
111        match key {
112            PropertyKey::String(s) => {
113                if let Value::Object(map) = current {
114                    current = map.get(s).cloned().unwrap_or(Value::Null);
115                } else {
116                    return Value::Null;
117                }
118            }
119            PropertyKey::Number(n) => {
120                if let Value::Array(arr) = current {
121                    let index = *n; // n is already a usize
122                    current = arr.get(index).cloned().unwrap_or(Value::Null);
123                } else {
124                    return Value::Null;
125                }
126            }
127        }
128    }
129
130    current
131}
132
133/// Convert a JSON Value back to WireExpression
134pub fn value_to_wire_expr(value: Value) -> WireExpression {
135    match value {
136        Value::Null => WireExpression::Null,
137        Value::Bool(b) => WireExpression::Bool(b),
138        Value::Number(n) => WireExpression::Number(n),
139        Value::String(s) => WireExpression::String(s),
140        Value::Array(items) => {
141            WireExpression::Array(items.into_iter().map(value_to_wire_expr).collect())
142        }
143        Value::Object(map) => {
144            // Check if this is a capability reference
145            if let (Some(type_val), Some(id_val)) = (map.get("_type"), map.get("id")) {
146                if type_val.as_str() == Some("capability") {
147                    if let Some(id) = id_val.as_i64() {
148                        return WireExpression::CapRef(id);
149                    }
150                }
151            }
152
153            // Regular object
154            WireExpression::Object(
155                map.into_iter()
156                    .map(|(k, v)| (k, value_to_wire_expr(v)))
157                    .collect(),
158            )
159        }
160    }
161}