1use crate::json_rpc::ErrorData;
10use serde::{Deserialize, Serialize};
11use serde_json::{Map, Value};
12
13#[derive(Clone, Debug, Serialize, Deserialize)]
15#[serde(tag = "type")]
16#[serde(rename_all = "snake_case")]
17pub enum ParamValidationError {
18 Custom(Custom),
19 Unrecognized(Unrecognized),
20 InvalidParam(InvalidParam),
21}
22
23impl ParamValidationError {
24 pub fn custom(message: String) -> Self {
25 Self::Custom(Custom { message })
26 }
27
28 pub fn unrecognized(unrecognized: Vec<String>) -> Self {
29 Self::Unrecognized(Unrecognized { unrecognized })
30 }
31
32 pub fn invalid_params(property: String, message: String) -> Self {
33 Self::InvalidParam(InvalidParam { property, message })
34 }
35}
36
37impl From<ParamValidationError> for ErrorData<serde_json::Value> {
38 fn from(err: ParamValidationError) -> Self {
39 let result_ser = serde_json::to_value(err);
40 match result_ser {
41 Ok(data) => ErrorData::invalid_params(data),
42 Err(err) => ErrorData::internal_error(Value::String(format!(
43 "Deserialization failed: {:?}",
44 err
45 ))),
46 }
47 }
48}
49
50#[derive(Clone, Debug, Serialize, Deserialize)]
51pub struct Custom {
52 message: String,
53}
54
55#[derive(Clone, Debug, Serialize, Deserialize)]
56pub struct Unrecognized {
57 unrecognized: Vec<String>,
58}
59
60#[derive(Clone, Debug, Serialize, Deserialize)]
61pub struct InvalidParam {
62 property: String,
63 message: String,
64}
65
66pub trait ExpectedFields {
67 fn expected_fields() -> Vec<String>;
68}
69
70pub fn from_value<'de, T: Deserialize<'de> + ExpectedFields>(
72 value: serde_json::Value,
73) -> Result<T, ParamValidationError> {
74 let value = match value {
75 Value::Object(_) => value,
76 Value::Null => Value::Null,
77 _ => {
78 return Err(ParamValidationError::Custom(Custom {
79 message: "Arguments should be passed by name".to_string(),
80 }))
81 }
82 };
83
84 let expected_fields = T::expected_fields();
86 let expected_fields: Vec<&str> = expected_fields.iter().map(|x| x.as_ref()).collect();
87 let unrecognized = list_unrecogninzed_fields(expected_fields.as_ref(), &value);
88 if !unrecognized.is_empty() {
89 return Err(ParamValidationError::Unrecognized(Unrecognized {
90 unrecognized,
91 }));
92 }
93
94 serde_path_to_error::deserialize::<'de, _, T>(value).map_err(|e| {
95 ParamValidationError::InvalidParam(InvalidParam {
96 property: e.path().to_string(),
97 message: e.to_string(),
98 })
99 })
100}
101
102fn list_unrecogninzed_fields(
104 expected_arguments: &[&str],
105 json_value: &serde_json::Value,
106) -> Vec<String> {
107 match json_value {
108 Value::Object(map) => {
109 let mut current_prefix = Vec::new();
110 let mut current_result = Vec::new();
111 list_unrecognized_fields_impl(
112 expected_arguments,
113 &map,
114 &mut current_prefix,
115 &mut current_result,
116 )
117 }
118 _ => Vec::new(),
119 }
120}
121
122fn list_unrecognized_fields_impl(
123 expected_arguments: &[&str],
124 map: &Map<String, Value>,
125 current_prefix: &mut Vec<Box<str>>,
126 current_result: &mut Vec<String>,
127) -> Vec<String> {
128 for (name, value) in map {
129 match value {
130 Value::Object(map) => {
131 current_prefix.push(name.clone().into_boxed_str());
132 list_unrecognized_fields_impl(
133 expected_arguments,
134 map,
135 current_prefix,
136 current_result,
137 );
138 current_prefix.pop();
139 }
140 _ => {
141 current_prefix.push(name.clone().into_boxed_str());
142 let argument = current_prefix.join(".");
143 if !expected_arguments.contains(&argument.as_ref()) {
144 current_result.push(argument.to_string());
145 }
146 current_prefix.pop();
147 }
148 };
149 }
150
151 current_result.clone()
152}
153
154#[cfg(test)]
155mod test {
156 use super::*;
157 use crate::lsps0::parameter_validation::ParamValidationError;
158 use serde_json::json;
159
160 #[test]
161 fn test_find_unrecognized_arguments() {
162 struct Case<'a> {
163 value: serde_json::Value,
164 expected_arguments: &'a [&'a str],
165 expected_result: Vec<String>,
166 }
167
168 let cases = vec![
169 Case {
170 value: serde_json::json!({}),
171 expected_arguments: &[],
172 expected_result: vec![],
173 },
174 Case {
175 value: serde_json::json!({"param_a": "a"}),
176 expected_arguments: &[],
177 expected_result: vec!["param_a".to_string()],
178 },
179 Case {
180 value: json!({"param_a" : "a"}),
181 expected_arguments: &["param_a"],
182 expected_result: vec![],
183 },
184 Case {
185 value: serde_json::json!({"param_a" : {"field_a" : "a"}}),
186 expected_arguments: &[],
187 expected_result: vec!["param_a.field_a".to_string()],
188 },
189 Case {
190 value: serde_json::json!({"param_a" : {"field_a" : "a"}}),
191 expected_arguments: &["param_a.field_a"],
192 expected_result: vec![],
193 },
194 Case {
195 value: serde_json::json!({"param_a" : "a", "param_b" : "b"}),
196 expected_arguments: &["f1", "f2"],
197 expected_result: vec!["param_a".to_string(), "param_b".to_string()],
198 },
199 ];
200
201 for case in cases {
202 let result = list_unrecogninzed_fields(case.expected_arguments, &case.value);
203 assert_eq!(result, case.expected_result);
204 }
205 }
206
207 #[test]
208 fn serialize_error_data() {
209 assert_eq!(
210 serde_json::to_value(ParamValidationError::Custom(Custom {
211 message: "arg by name".to_string()
212 }))
213 .unwrap(),
214 json!({"type" : "custom", "message" : "arg by name"})
215 );
216
217 assert_eq!(
218 serde_json::to_value(ParamValidationError::InvalidParam(InvalidParam {
219 property: "param_a".to_string(),
220 message: "Not an integer".to_string()
221 }))
222 .unwrap(),
223 json!({"type" : "invalid_param", "property" : "param_a", "message" : "Not an integer"})
224 );
225
226 assert_eq!(
227 serde_json::to_value(ParamValidationError::Unrecognized(Unrecognized {
228 unrecognized: vec!["param_a".to_string()]
229 }),)
230 .unwrap(),
231 json!({"type" : "unrecognized", "unrecognized" : ["param_a"] })
232 )
233 }
234
235 #[test]
236 fn convert_invalid_params_to_error_data() {
237 let error = ParamValidationError::unrecognized(vec!["param_a".to_string()]);
238 let error_data: ErrorData<serde_json::Value> = error.into();
239
240 assert_eq!(error_data.code, -32602);
241 assert_eq!(
242 error_data
243 .data
244 .expect("is not None")
245 .get("unrecognized")
246 .expect("unrecognized field exists")[0],
247 "param_a"
248 );
249 }
250}