forest/rpc/reflect/
parser.rs

1// Copyright 2019-2025 ChainSafe Systems
2// SPDX-License-Identifier: Apache-2.0, MIT
3use std::collections::VecDeque;
4
5use openrpc_types::ParamStructure;
6use serde::Deserialize;
7use serde_json::{Value, json};
8
9use super::{jsonrpc_types::RequestParameters, util::Optional as _};
10use crate::rpc::error::ServerError;
11
12/// Parser for JSON-RPC parameters.
13/// Abstracts calling convention, checks for unexpected params etc, so that
14/// rust [`Fn`]s may be called.
15#[derive(Debug)]
16pub struct Parser<'a> {
17    params: Option<ParserInner>,
18    /// What arguments do we expect to parse?
19    argument_names: &'a [&'a str],
20    /// How many times has the user called us so far?
21    call_count: usize,
22    /// How many positional parameters are required?
23    n_required: usize,
24    /// Has any error occurred?
25    has_errored: bool,
26}
27
28#[derive(Debug)]
29enum ParserInner {
30    ByPosition(VecDeque<Value>), // for O(1) pop_front
31    ByName(serde_json::Map<String, Value>),
32}
33
34impl Drop for Parser<'_> {
35    fn drop(&mut self) {
36        if !std::thread::panicking() && !self.has_errored {
37            assert!(
38                self.call_count >= self.argument_names.len(),
39                "`Parser` has unhandled parameters - did you forget to call `parse`?"
40            );
41        }
42    }
43}
44
45impl<'a> Parser<'a> {
46    /// The user promises to call [`Parser::parse`] `names.len()` times.
47    ///
48    /// # Panics
49    /// - if the contract above is not upheld.
50    pub fn new(
51        params: Option<RequestParameters>,
52        names: &'a [&'a str], // in position order
53        calling_convention: ParamStructure,
54        n_required: usize,
55    ) -> Result<Self, ServerError> {
56        Self::_new(params, names, calling_convention, n_required).map_err(Into::into)
57    }
58    fn _new(
59        params: Option<RequestParameters>,
60        names: &'a [&'a str],
61        calling_convention: ParamStructure,
62        n_required: usize,
63    ) -> Result<Self, ParseError<'a>> {
64        let params = match (params, calling_convention) {
65            // ignore the calling convention if there are no arguments to parse
66            (None, _) => None,
67            (Some(params), _) if names.is_empty() && params.is_empty() => None,
68            // contradicts calling convention
69            (Some(RequestParameters::ByPosition(_)), ParamStructure::ByName) => {
70                return Err(ParseError::MustBeNamed);
71            }
72            (Some(RequestParameters::ByName(_)), ParamStructure::ByPosition) => {
73                return Err(ParseError::MustBePositional);
74            }
75            // In each call to `parse`, we check for unexpected args.
76            // But if the caller never calls `parse`, we wouldn't catch unexpected args.
77            // this is the case when the caller expects no arguments (when `names.is_empty()`).
78            // so do the checking here
79            (Some(RequestParameters::ByPosition(it)), _) if names.is_empty() && !it.is_empty() => {
80                return Err(ParseError::UnexpectedPositional(it.len()));
81            }
82            (Some(RequestParameters::ByName(it)), _) if names.is_empty() && !it.is_empty() => {
83                return Err(ParseError::UnexpectedNamed(
84                    it.into_iter().map(|(it, _)| it).collect(),
85                ));
86            }
87            // calling convention matches, continue
88            (Some(RequestParameters::ByPosition(it)), _) => {
89                Some(ParserInner::ByPosition(VecDeque::from(it)))
90            }
91            (Some(RequestParameters::ByName(it)), _) => Some(ParserInner::ByName(it)),
92        };
93
94        Ok(Self {
95            params,
96            argument_names: names,
97            call_count: 0,
98            n_required,
99            has_errored: false,
100        })
101    }
102    fn error<T>(&mut self, e: ParseError<'a>) -> Result<T, ParseError<'a>> {
103        self.has_errored = true;
104        Err(e)
105    }
106    pub fn parse<T>(&mut self) -> Result<T, ServerError>
107    where
108        T: for<'de> Deserialize<'de>,
109    {
110        self._parse().map_err(Into::into)
111    }
112    fn _parse<T>(&mut self) -> Result<T, ParseError<'a>>
113    where
114        T: for<'de> Deserialize<'de>,
115    {
116        let index = self.call_count;
117        self.call_count += 1;
118        let name = match self.argument_names.get(index) {
119            Some(it) => *it,
120            None => panic!(
121                "`Parser` was initialized with {} arguments, but `parse` was called {} times",
122                self.argument_names.len(),
123                self.call_count
124            ),
125        };
126        let ty = std::any::type_name::<T>();
127        let missing_parameter = ParseError::Missing {
128            index,
129            n_required: self.n_required,
130            name,
131            ty,
132        };
133        let deserialize_error = |error| ParseError::Deser {
134            index,
135            name,
136            ty,
137            error,
138        };
139        let t = match &mut self.params {
140            None => match T::optional() {
141                true => T::unwrap_none(),
142                false => self.error(missing_parameter)?,
143            },
144            Some(ParserInner::ByName(it)) => match it.remove(name) {
145                Some(it) => match serde_json::from_value::<T>(it) {
146                    Ok(it) => it,
147                    Err(e) => self.error(deserialize_error(e))?,
148                },
149                None => match T::optional() {
150                    true => T::unwrap_none(),
151                    false => self.error(missing_parameter)?,
152                },
153            },
154            Some(ParserInner::ByPosition(it)) => match it.pop_front() {
155                Some(it) => match serde_json::from_value::<T>(it) {
156                    Ok(it) => it,
157                    Err(e) => self.error(deserialize_error(e))?,
158                },
159                None => match T::optional() {
160                    true if self.call_count > self.n_required => T::unwrap_none(),
161                    _ => self.error(missing_parameter)?,
162                },
163            },
164        };
165        let final_arg = self.call_count >= self.argument_names.len();
166        if final_arg {
167            match self.params.take() {
168                Some(ParserInner::ByName(it)) => match it.is_empty() {
169                    true => {}
170                    false => self.error(ParseError::UnexpectedNamed(
171                        it.into_iter().map(|(k, _)| k).collect(),
172                    ))?,
173                },
174                Some(ParserInner::ByPosition(it)) => match it.len() {
175                    0 => {}
176                    n => self.error(ParseError::UnexpectedPositional(n))?,
177                },
178                None => {}
179            };
180        }
181        Ok(t)
182    }
183}
184
185/// Broken out error type for writing tests
186#[derive(Debug)]
187enum ParseError<'a> {
188    Missing {
189        index: usize,
190        n_required: usize,
191        name: &'a str,
192        ty: &'a str,
193    },
194    Deser {
195        index: usize,
196        name: &'a str,
197        ty: &'a str,
198        error: serde_json::Error,
199    },
200    UnexpectedPositional(usize),
201    UnexpectedNamed(Vec<String>),
202    MustBeNamed,
203    MustBePositional,
204}
205
206impl<'a> From<ParseError<'a>> for ServerError {
207    fn from(value: ParseError<'a>) -> Self {
208        match value {
209            ParseError::Missing {
210                index,
211                n_required,
212                name,
213                ty,
214            } => ServerError::invalid_params(
215                "missing required parameter",
216                json!({
217                    "index": index,
218                    "n_required": n_required,
219                    "name": name,
220                    "type": ty
221                }),
222            ),
223            ParseError::Deser {
224                index,
225                name,
226                ty,
227                error,
228            } => ServerError::invalid_params(
229                "error deserializing parameter",
230                json!({
231                    "index": index,
232                    "name": name,
233                    "type": ty,
234                    "error": error.to_string()
235                }),
236            ),
237            ParseError::UnexpectedPositional(n) => {
238                ServerError::invalid_params("unexpected trailing arguments", json!({"count": n}))
239            }
240            ParseError::UnexpectedNamed(names) => {
241                ServerError::invalid_params("unexpected named arguments", json!(names))
242            }
243            ParseError::MustBeNamed => {
244                ServerError::invalid_params("this method only accepts arguments by-name", None)
245            }
246            ParseError::MustBePositional => {
247                ServerError::invalid_params("this method only accepts arguments by-position", None)
248            }
249        }
250    }
251}
252
253#[cfg(test)]
254mod tests {
255    use super::*;
256
257    macro_rules! from_value {
258        ($tt:tt) => {
259            serde_json::from_value(serde_json::json!($tt)).unwrap()
260        };
261    }
262
263    #[test]
264    fn optional() {
265        // no params where optional
266        let mut parser = Parser::_new(None, &["p0"], ParamStructure::Either, 0).unwrap();
267        assert_eq!(None::<i32>, parser._parse::<Option<i32>>().unwrap());
268
269        // positional optional
270        let mut parser =
271            Parser::_new(from_value!([]), &["opt"], ParamStructure::Either, 0).unwrap();
272        assert_eq!(None::<i32>, parser._parse::<Option<i32>>().unwrap());
273
274        // named optional
275        let mut parser =
276            Parser::_new(from_value!({}), &["opt"], ParamStructure::Either, 0).unwrap();
277        assert_eq!(None::<i32>, parser._parse::<Option<i32>>().unwrap());
278
279        // positional optional with mandatory
280        let mut parser =
281            Parser::_new(from_value!([0]), &["p0", "opt"], ParamStructure::Either, 0).unwrap();
282        assert_eq!(Some(0), parser._parse::<Option<i32>>().unwrap());
283        assert_eq!(None::<i32>, parser._parse::<Option<i32>>().unwrap());
284
285        // named optional with mandatory
286        let mut parser = Parser::_new(
287            from_value!({"p0": 0}),
288            &["p0", "opt"],
289            ParamStructure::Either,
290            0,
291        )
292        .unwrap();
293        assert_eq!(Some(0), parser._parse::<Option<i32>>().unwrap());
294        assert_eq!(None::<i32>, parser._parse::<Option<i32>>().unwrap());
295    }
296
297    #[test]
298    fn missing() {
299        // missing only named
300        let mut parser = Parser::_new(from_value!({}), &["p0"], ParamStructure::Either, 0).unwrap();
301        assert!(matches!(
302            parser._parse::<i32>().unwrap_err(),
303            ParseError::Missing { name: "p0", .. },
304        ));
305
306        // missing only positional
307        let mut parser = Parser::_new(from_value!([]), &["p0"], ParamStructure::Either, 0).unwrap();
308        assert!(matches!(
309            parser._parse::<i32>().unwrap_err(),
310            ParseError::Missing { name: "p0", .. },
311        ));
312
313        // missing only positional
314        let mut parser = Parser::_new(from_value!([]), &["p0"], ParamStructure::Either, 1).unwrap();
315        assert!(matches!(
316            parser._parse::<Option<i32>>().unwrap_err(),
317            ParseError::Missing { name: "p0", .. },
318        ));
319
320        // missing a named
321        let mut parser = Parser::_new(
322            from_value!({"p0": 0}),
323            &["p0", "p1"],
324            ParamStructure::Either,
325            0,
326        )
327        .unwrap();
328        assert_eq!(0, parser._parse::<i32>().unwrap());
329        assert!(matches!(
330            parser._parse::<i32>().unwrap_err(),
331            ParseError::Missing { name: "p1", .. },
332        ));
333
334        // missing a positional
335        let mut parser =
336            Parser::_new(from_value!([0]), &["p0", "p1"], ParamStructure::Either, 0).unwrap();
337        assert_eq!(0, parser._parse::<i32>().unwrap());
338        assert!(matches!(
339            parser._parse::<i32>().unwrap_err(),
340            ParseError::Missing { name: "p1", .. },
341        ));
342    }
343
344    #[test]
345    fn unexpected() {
346        // named but expected none
347        assert!(matches!(
348            Parser::_new(from_value!({ "surprise": () }), &[], ParamStructure::Either,0).unwrap_err(),
349            ParseError::UnexpectedNamed(it) if it == ["surprise"],
350        ));
351
352        // positional but expected none
353        assert!(matches!(
354            Parser::_new(from_value!(["surprise"]), &[], ParamStructure::Either, 0).unwrap_err(),
355            ParseError::UnexpectedPositional(1),
356        ));
357
358        // named after one
359        let mut parser = Parser::_new(
360            from_value!({ "p0": 0, "surprise": () }),
361            &["p0"],
362            ParamStructure::Either,
363            0,
364        )
365        .unwrap();
366        assert!(matches!(
367            parser._parse::<i32>().unwrap_err(),
368            ParseError::UnexpectedNamed(it) if it == ["surprise"]
369        ));
370
371        // positional after one
372        let mut parser = Parser::_new(
373            from_value!([1, "surprise"]),
374            &["p0"],
375            ParamStructure::Either,
376            0,
377        )
378        .unwrap();
379        assert!(matches!(
380            parser._parse::<i32>().unwrap_err(),
381            ParseError::UnexpectedPositional(1),
382        ));
383    }
384
385    #[test]
386    #[should_panic = "`Parser` was initialized with 0 arguments, but `parse` was called 1 times"]
387    fn called_too_much() {
388        let mut parser = Parser::_new(None, &[], ParamStructure::Either, 0).unwrap();
389        let _ = parser._parse::<()>();
390        unreachable!()
391    }
392
393    #[test]
394    #[should_panic = "`Parser` has unhandled parameters - did you forget to call `parse`?"]
395    fn called_too_little() {
396        Parser::_new(None, &["p0"], ParamStructure::Either, 0).unwrap();
397    }
398}