casper_json_rpc/request/
params.rs

1use std::fmt::{self, Display, Formatter};
2
3use serde_json::{Map, Value};
4
5use super::ErrorOrRejection;
6use crate::error::{Error, ReservedErrorCode};
7
8/// The "params" field of a JSON-RPC request.
9///
10/// As per [the JSON-RPC specification](https://www.jsonrpc.org/specification#parameter_structures),
11/// if present these must be a JSON Array or Object.
12///
13/// **NOTE:** Currently we treat '"params": null' as '"params": []', but this deviation from the
14/// standard will be removed in an upcoming release, and `null` will become an invalid value.
15///
16/// `Params` is effectively a restricted [`serde_json::Value`], and can be converted to a `Value`
17/// using `Value::from()` if required.
18#[derive(Clone, Eq, PartialEq, Debug)]
19pub enum Params {
20    /// Represents a JSON Array.
21    Array(Vec<Value>),
22    /// Represents a JSON Object.
23    Object(Map<String, Value>),
24}
25
26impl Params {
27    pub(super) fn try_from(request_id: &Value, params: Value) -> Result<Self, ErrorOrRejection> {
28        let err_invalid_request = |additional_info: &str| {
29            let error = Error::new(ReservedErrorCode::InvalidRequest, additional_info);
30            Err(ErrorOrRejection::Error {
31                id: request_id.clone(),
32                error,
33            })
34        };
35
36        match params {
37            Value::Null => Ok(Params::Array(vec![])),
38            Value::Bool(false) => err_invalid_request(
39                "If present, 'params' must be an Array or Object, but was 'false'",
40            ),
41            Value::Bool(true) => err_invalid_request(
42                "If present, 'params' must be an Array or Object, but was 'true'",
43            ),
44            Value::Number(_) => err_invalid_request(
45                "If present, 'params' must be an Array or Object, but was a Number",
46            ),
47            Value::String(_) => err_invalid_request(
48                "If present, 'params' must be an Array or Object, but was a String",
49            ),
50            Value::Array(array) => Ok(Params::Array(array)),
51            Value::Object(map) => Ok(Params::Object(map)),
52        }
53    }
54
55    /// Returns `true` if `self` is an Array, otherwise returns `false`.
56    pub fn is_array(&self) -> bool {
57        self.as_array().is_some()
58    }
59
60    /// Returns a reference to the inner `Vec` if `self` is an Array, otherwise returns `None`.
61    pub fn as_array(&self) -> Option<&Vec<Value>> {
62        match self {
63            Params::Array(array) => Some(array),
64            _ => None,
65        }
66    }
67
68    /// Returns a mutable reference to the inner `Vec` if `self` is an Array, otherwise returns
69    /// `None`.
70    pub fn as_array_mut(&mut self) -> Option<&mut Vec<Value>> {
71        match self {
72            Params::Array(array) => Some(array),
73            _ => None,
74        }
75    }
76
77    /// Returns `true` if `self` is an Object, otherwise returns `false`.
78    pub fn is_object(&self) -> bool {
79        self.as_object().is_some()
80    }
81
82    /// Returns a reference to the inner `Map` if `self` is an Object, otherwise returns `None`.
83    pub fn as_object(&self) -> Option<&Map<String, Value>> {
84        match self {
85            Params::Object(map) => Some(map),
86            _ => None,
87        }
88    }
89
90    /// Returns a mutable reference to the inner `Map` if `self` is an Object, otherwise returns
91    /// `None`.
92    pub fn as_object_mut(&mut self) -> Option<&mut Map<String, Value>> {
93        match self {
94            Params::Object(map) => Some(map),
95            _ => None,
96        }
97    }
98
99    /// Returns `true` if `self` is an empty Array or an empty Object, otherwise returns `false`.
100    pub fn is_empty(&self) -> bool {
101        match self {
102            Params::Array(array) => array.is_empty(),
103            Params::Object(map) => map.is_empty(),
104        }
105    }
106}
107
108impl Display for Params {
109    fn fmt(&self, formatter: &mut Formatter) -> fmt::Result {
110        Display::fmt(&Value::from(self.clone()), formatter)
111    }
112}
113
114/// The default value for `Params` is an empty Array.
115impl Default for Params {
116    fn default() -> Self {
117        Params::Array(vec![])
118    }
119}
120
121impl From<Params> for Value {
122    fn from(params: Params) -> Self {
123        match params {
124            Params::Array(array) => Value::Array(array),
125            Params::Object(map) => Value::Object(map),
126        }
127    }
128}
129
130#[cfg(test)]
131mod tests {
132    use super::*;
133
134    fn should_fail_to_convert_invalid_params(bad_params: Value, expected_invalid_type_msg: &str) {
135        let original_id = Value::from(1_i8);
136        match Params::try_from(&original_id, bad_params).unwrap_err() {
137            ErrorOrRejection::Error { id, error } => {
138                assert_eq!(id, original_id);
139                let expected_error = format!(
140                    r#"{{"code":-32600,"message":"Invalid Request","data":"If present, 'params' must be an Array or Object, but was {}"}}"#,
141                    expected_invalid_type_msg
142                );
143                assert_eq!(serde_json::to_string(&error).unwrap(), expected_error);
144            }
145            other => panic!("unexpected: {:?}", other),
146        }
147    }
148
149    #[test]
150    fn should_convert_params_from_null() {
151        let original_id = Value::from(1_i8);
152
153        let params = Params::try_from(&original_id, Value::Null).unwrap();
154        assert!(matches!(params, Params::Array(v) if v.is_empty()));
155    }
156
157    #[test]
158    fn should_fail_to_convert_params_from_false() {
159        should_fail_to_convert_invalid_params(Value::Bool(false), "'false'")
160    }
161
162    #[test]
163    fn should_fail_to_convert_params_from_true() {
164        should_fail_to_convert_invalid_params(Value::Bool(true), "'true'")
165    }
166
167    #[test]
168    fn should_fail_to_convert_params_from_a_number() {
169        should_fail_to_convert_invalid_params(Value::from(9_u8), "a Number")
170    }
171
172    #[test]
173    fn should_fail_to_convert_params_from_a_string() {
174        should_fail_to_convert_invalid_params(Value::from("s"), "a String")
175    }
176
177    #[test]
178    fn should_convert_params_from_an_array() {
179        let original_id = Value::from(1_i8);
180
181        let params = Params::try_from(&original_id, Value::Array(vec![])).unwrap();
182        assert!(matches!(params, Params::Array(v) if v.is_empty()));
183
184        let array = vec![Value::from(9_i16), Value::Bool(false)];
185        let params = Params::try_from(&original_id, Value::Array(array.clone())).unwrap();
186        assert!(matches!(params, Params::Array(v) if v == array));
187    }
188
189    #[test]
190    fn should_convert_params_from_an_object() {
191        let original_id = Value::from(1_i8);
192
193        let params = Params::try_from(&original_id, Value::Object(Map::new())).unwrap();
194        assert!(matches!(params, Params::Object(v) if v.is_empty()));
195
196        let mut map = Map::new();
197        map.insert("a".to_string(), Value::from(9_i16));
198        map.insert("b".to_string(), Value::Bool(false));
199        let params = Params::try_from(&original_id, Value::Object(map.clone())).unwrap();
200        assert!(matches!(params, Params::Object(v) if v == map));
201    }
202}