Skip to main content

orpc_procedure/
input.rs

1use serde::de::DeserializeOwned;
2
3use crate::error::{DeserializeError, ProcedureError};
4
5/// Type-erased input — wraps raw data that can be deserialized on demand.
6///
7/// Used by the type-erased execution layer; the typed layer deserializes into `TInput`.
8///
9/// Two modes:
10/// - `Deserializer`: lazy deserialization from raw bytes (HTTP body, query string, etc.)
11/// - `Value`: pre-parsed JSON value (from batch requests, middleware inspection, etc.)
12pub enum DynInput {
13    /// Wraps a serde deserializer for lazy deserialization.
14    Deserializer(Box<dyn erased_serde::Deserializer<'static> + Send>),
15    /// Already-parsed JSON value.
16    Value(serde_json::Value),
17}
18
19impl DynInput {
20    /// Create from a JSON value.
21    pub fn from_value(value: serde_json::Value) -> Self {
22        DynInput::Value(value)
23    }
24
25    /// Deserialize into a concrete type. Consumes self (can only be called once).
26    pub fn deserialize<T: DeserializeOwned>(self) -> Result<T, ProcedureError> {
27        match self {
28            DynInput::Deserializer(mut de) => erased_serde::deserialize(&mut *de)
29                .map_err(|e| ProcedureError::Deserialize(DeserializeError::from(e))),
30            DynInput::Value(v) => serde_json::from_value(v)
31                .map_err(|e| ProcedureError::Deserialize(DeserializeError::from(e))),
32        }
33    }
34
35    /// Peek at the value (only works if already materialized).
36    pub fn as_value(&self) -> Option<&serde_json::Value> {
37        match self {
38            DynInput::Value(v) => Some(v),
39            DynInput::Deserializer(_) => None,
40        }
41    }
42
43    /// Materialize a `Deserializer` variant into a `Value` variant.
44    ///
45    /// Allows middleware to inspect input without permanently consuming it.
46    /// If already a `Value`, returns self unchanged.
47    pub fn materialize(self) -> Result<DynInput, ProcedureError> {
48        match self {
49            DynInput::Value(_) => Ok(self),
50            DynInput::Deserializer(mut de) => {
51                let v: serde_json::Value = erased_serde::deserialize(&mut *de)
52                    .map_err(|e| ProcedureError::Deserialize(DeserializeError::from(e)))?;
53                Ok(DynInput::Value(v))
54            }
55        }
56    }
57}
58
59impl std::fmt::Debug for DynInput {
60    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
61        match self {
62            DynInput::Deserializer(_) => f.debug_tuple("DynInput::Deserializer").finish(),
63            DynInput::Value(v) => f.debug_tuple("DynInput::Value").field(v).finish(),
64        }
65    }
66}
67
68#[cfg(test)]
69mod tests {
70    use super::*;
71    use serde::Deserialize;
72
73    #[derive(Debug, Deserialize, PartialEq)]
74    struct TestInput {
75        name: String,
76        age: u32,
77    }
78
79    #[test]
80    fn deserialize_from_value() {
81        let input = DynInput::from_value(serde_json::json!({"name": "Alice", "age": 30}));
82        let result: TestInput = input.deserialize().unwrap();
83        assert_eq!(
84            result,
85            TestInput {
86                name: "Alice".into(),
87                age: 30
88            }
89        );
90    }
91
92    #[test]
93    fn deserialize_from_value_type_mismatch() {
94        let input = DynInput::from_value(serde_json::json!({"wrong": "fields"}));
95        let result = input.deserialize::<TestInput>();
96        assert!(result.is_err());
97        assert!(matches!(
98            result.unwrap_err(),
99            ProcedureError::Deserialize(_)
100        ));
101    }
102
103    #[test]
104    fn as_value_on_value_variant() {
105        let input = DynInput::from_value(serde_json::json!(42));
106        assert_eq!(input.as_value(), Some(&serde_json::json!(42)));
107    }
108
109    #[test]
110    fn materialize_value_is_noop() {
111        let input = DynInput::from_value(serde_json::json!({"key": "value"}));
112        let materialized = input.materialize().unwrap();
113        assert_eq!(
114            materialized.as_value(),
115            Some(&serde_json::json!({"key": "value"}))
116        );
117    }
118
119    #[test]
120    fn debug_value_variant() {
121        let input = DynInput::from_value(serde_json::json!(42));
122        let debug = format!("{input:?}");
123        assert!(debug.contains("Value"));
124    }
125
126    #[test]
127    fn dyn_input_is_send() {
128        fn assert_send<T: Send>() {}
129        assert_send::<DynInput>();
130    }
131}