use std::fmt::{self, Display, Formatter};
use serde_json::{Map, Value};
use super::ErrorOrRejection;
use crate::error::{Error, ReservedErrorCode};
#[derive(Clone, Eq, PartialEq, Debug)]
pub enum Params {
Array(Vec<Value>),
Object(Map<String, Value>),
}
impl Params {
pub(super) fn try_from(request_id: &Value, params: Value) -> Result<Self, ErrorOrRejection> {
let err_invalid_request = |additional_info: &str| {
let error = Error::new(ReservedErrorCode::InvalidRequest, additional_info);
Err(ErrorOrRejection::Error {
id: request_id.clone(),
error,
})
};
match params {
Value::Null => Ok(Params::Array(vec![])),
Value::Bool(false) => err_invalid_request(
"If present, 'params' must be an Array or Object, but was 'false'",
),
Value::Bool(true) => err_invalid_request(
"If present, 'params' must be an Array or Object, but was 'true'",
),
Value::Number(_) => err_invalid_request(
"If present, 'params' must be an Array or Object, but was a Number",
),
Value::String(_) => err_invalid_request(
"If present, 'params' must be an Array or Object, but was a String",
),
Value::Array(array) => Ok(Params::Array(array)),
Value::Object(map) => Ok(Params::Object(map)),
}
}
pub fn is_array(&self) -> bool {
self.as_array().is_some()
}
pub fn as_array(&self) -> Option<&Vec<Value>> {
match self {
Params::Array(array) => Some(array),
_ => None,
}
}
pub fn as_array_mut(&mut self) -> Option<&mut Vec<Value>> {
match self {
Params::Array(array) => Some(array),
_ => None,
}
}
pub fn is_object(&self) -> bool {
self.as_object().is_some()
}
pub fn as_object(&self) -> Option<&Map<String, Value>> {
match self {
Params::Object(map) => Some(map),
_ => None,
}
}
pub fn as_object_mut(&mut self) -> Option<&mut Map<String, Value>> {
match self {
Params::Object(map) => Some(map),
_ => None,
}
}
pub fn is_empty(&self) -> bool {
match self {
Params::Array(array) => array.is_empty(),
Params::Object(map) => map.is_empty(),
}
}
}
impl Display for Params {
fn fmt(&self, formatter: &mut Formatter) -> fmt::Result {
Display::fmt(&Value::from(self.clone()), formatter)
}
}
impl Default for Params {
fn default() -> Self {
Params::Array(vec![])
}
}
impl From<Params> for Value {
fn from(params: Params) -> Self {
match params {
Params::Array(array) => Value::Array(array),
Params::Object(map) => Value::Object(map),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
fn should_fail_to_convert_invalid_params(bad_params: Value, expected_invalid_type_msg: &str) {
let original_id = Value::from(1_i8);
match Params::try_from(&original_id, bad_params).unwrap_err() {
ErrorOrRejection::Error { id, error } => {
assert_eq!(id, original_id);
let expected_error = format!(
r#"{{"code":-32600,"message":"Invalid Request","data":"If present, 'params' must be an Array or Object, but was {}"}}"#,
expected_invalid_type_msg
);
assert_eq!(serde_json::to_string(&error).unwrap(), expected_error);
}
other => panic!("unexpected: {:?}", other),
}
}
#[test]
fn should_convert_params_from_null() {
let original_id = Value::from(1_i8);
let params = Params::try_from(&original_id, Value::Null).unwrap();
assert!(matches!(params, Params::Array(v) if v.is_empty()));
}
#[test]
fn should_fail_to_convert_params_from_false() {
should_fail_to_convert_invalid_params(Value::Bool(false), "'false'")
}
#[test]
fn should_fail_to_convert_params_from_true() {
should_fail_to_convert_invalid_params(Value::Bool(true), "'true'")
}
#[test]
fn should_fail_to_convert_params_from_a_number() {
should_fail_to_convert_invalid_params(Value::from(9_u8), "a Number")
}
#[test]
fn should_fail_to_convert_params_from_a_string() {
should_fail_to_convert_invalid_params(Value::from("s"), "a String")
}
#[test]
fn should_convert_params_from_an_array() {
let original_id = Value::from(1_i8);
let params = Params::try_from(&original_id, Value::Array(vec![])).unwrap();
assert!(matches!(params, Params::Array(v) if v.is_empty()));
let array = vec![Value::from(9_i16), Value::Bool(false)];
let params = Params::try_from(&original_id, Value::Array(array.clone())).unwrap();
assert!(matches!(params, Params::Array(v) if v == array));
}
#[test]
fn should_convert_params_from_an_object() {
let original_id = Value::from(1_i8);
let params = Params::try_from(&original_id, Value::Object(Map::new())).unwrap();
assert!(matches!(params, Params::Object(v) if v.is_empty()));
let mut map = Map::new();
map.insert("a".to_string(), Value::from(9_i16));
map.insert("b".to_string(), Value::Bool(false));
let params = Params::try_from(&original_id, Value::Object(map.clone())).unwrap();
assert!(matches!(params, Params::Object(v) if v == map));
}
}