use std::collections::VecDeque;
use openrpc_types::ParamStructure;
use serde::Deserialize;
use serde_json::{json, Value};
use super::{jsonrpc_types::RequestParameters, util::Optional as _};
use crate::rpc::error::ServerError;
#[derive(Debug)]
pub struct Parser<'a> {
params: Option<ParserInner>,
argument_names: &'a [&'a str],
call_count: usize,
n_required: usize,
has_errored: bool,
}
#[derive(Debug)]
enum ParserInner {
ByPosition(VecDeque<Value>), ByName(serde_json::Map<String, Value>),
}
impl Drop for Parser<'_> {
fn drop(&mut self) {
if !std::thread::panicking() && !self.has_errored {
assert!(
self.call_count >= self.argument_names.len(),
"`Parser` has unhandled parameters - did you forget to call `parse`?"
);
}
}
}
impl<'a> Parser<'a> {
pub fn new(
params: Option<RequestParameters>,
names: &'a [&'a str], calling_convention: ParamStructure,
n_required: usize,
) -> Result<Self, ServerError> {
Self::_new(params, names, calling_convention, n_required).map_err(Into::into)
}
fn _new(
params: Option<RequestParameters>,
names: &'a [&'a str],
calling_convention: ParamStructure,
n_required: usize,
) -> Result<Self, ParseError> {
let params = match (params, calling_convention) {
(None, _) => None,
(Some(params), _) if names.is_empty() && params.is_empty() => None,
(Some(RequestParameters::ByPosition(_)), ParamStructure::ByName) => {
return Err(ParseError::MustBeNamed)
}
(Some(RequestParameters::ByName(_)), ParamStructure::ByPosition) => {
return Err(ParseError::MustBePositional)
}
(Some(RequestParameters::ByPosition(it)), _) if names.is_empty() && !it.is_empty() => {
return Err(ParseError::UnexpectedPositional(it.len()))
}
(Some(RequestParameters::ByName(it)), _) if names.is_empty() && !it.is_empty() => {
return Err(ParseError::UnexpectedNamed(
it.into_iter().map(|(it, _)| it).collect(),
))
}
(Some(RequestParameters::ByPosition(it)), _) => {
Some(ParserInner::ByPosition(VecDeque::from(it)))
}
(Some(RequestParameters::ByName(it)), _) => Some(ParserInner::ByName(it)),
};
Ok(Self {
params,
argument_names: names,
call_count: 0,
n_required,
has_errored: false,
})
}
fn error<T>(&mut self, e: ParseError<'a>) -> Result<T, ParseError<'a>> {
self.has_errored = true;
Err(e)
}
pub fn parse<T>(&mut self) -> Result<T, ServerError>
where
T: for<'de> Deserialize<'de>,
{
self._parse().map_err(Into::into)
}
fn _parse<T>(&mut self) -> Result<T, ParseError<'a>>
where
T: for<'de> Deserialize<'de>,
{
let index = self.call_count;
self.call_count += 1;
let name = match self.argument_names.get(index) {
Some(it) => *it,
None => panic!(
"`Parser` was initialized with {} arguments, but `parse` was called {} times",
self.argument_names.len(),
self.call_count
),
};
let ty = std::any::type_name::<T>();
let missing_parameter = ParseError::Missing {
index,
n_required: self.n_required,
name,
ty,
};
let deserialize_error = |error| ParseError::Deser {
index,
name,
ty,
error,
};
let t = match &mut self.params {
None => match T::optional() {
true => T::unwrap_none(),
false => self.error(missing_parameter)?,
},
Some(ParserInner::ByName(it)) => match it.remove(name) {
Some(it) => match serde_json::from_value::<T>(it) {
Ok(it) => it,
Err(e) => self.error(deserialize_error(e))?,
},
None => match T::optional() {
true => T::unwrap_none(),
false => self.error(missing_parameter)?,
},
},
Some(ParserInner::ByPosition(it)) => match it.pop_front() {
Some(it) => match serde_json::from_value::<T>(it) {
Ok(it) => it,
Err(e) => self.error(deserialize_error(e))?,
},
None => match T::optional() {
true if self.call_count > self.n_required => T::unwrap_none(),
_ => self.error(missing_parameter)?,
},
},
};
let final_arg = self.call_count >= self.argument_names.len();
if final_arg {
match self.params.take() {
Some(ParserInner::ByName(it)) => match it.is_empty() {
true => {}
false => self.error(ParseError::UnexpectedNamed(
it.into_iter().map(|(k, _)| k).collect(),
))?,
},
Some(ParserInner::ByPosition(it)) => match it.len() {
0 => {}
n => self.error(ParseError::UnexpectedPositional(n))?,
},
None => {}
};
}
Ok(t)
}
}
#[derive(Debug)]
enum ParseError<'a> {
Missing {
index: usize,
n_required: usize,
name: &'a str,
ty: &'a str,
},
Deser {
index: usize,
name: &'a str,
ty: &'a str,
error: serde_json::Error,
},
UnexpectedPositional(usize),
UnexpectedNamed(Vec<String>),
MustBeNamed,
MustBePositional,
}
impl<'a> From<ParseError<'a>> for ServerError {
fn from(value: ParseError<'a>) -> Self {
match value {
ParseError::Missing {
index,
n_required,
name,
ty,
} => ServerError::invalid_params(
"missing required parameter",
json!({
"index": index,
"n_required": n_required,
"name": name,
"type": ty
}),
),
ParseError::Deser {
index,
name,
ty,
error,
} => ServerError::invalid_params(
"error deserializing parameter",
json!({
"index": index,
"name": name,
"type": ty,
"error": error.to_string()
}),
),
ParseError::UnexpectedPositional(n) => {
ServerError::invalid_params("unexpected trailing arguments", json!({"count": n}))
}
ParseError::UnexpectedNamed(names) => {
ServerError::invalid_params("unexpected named arguments", json!(names))
}
ParseError::MustBeNamed => {
ServerError::invalid_params("this method only accepts arguments by-name", None)
}
ParseError::MustBePositional => {
ServerError::invalid_params("this method only accepts arguments by-position", None)
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
macro_rules! from_value {
($tt:tt) => {
serde_json::from_value(serde_json::json!($tt)).unwrap()
};
}
#[test]
fn optional() {
let mut parser = Parser::_new(None, &["p0"], ParamStructure::Either, 0).unwrap();
assert_eq!(None::<i32>, parser._parse().unwrap());
let mut parser =
Parser::_new(from_value!([]), &["opt"], ParamStructure::Either, 0).unwrap();
assert_eq!(None::<i32>, parser._parse().unwrap());
let mut parser =
Parser::_new(from_value!({}), &["opt"], ParamStructure::Either, 0).unwrap();
assert_eq!(None::<i32>, parser._parse().unwrap());
let mut parser =
Parser::_new(from_value!([0]), &["p0", "opt"], ParamStructure::Either, 0).unwrap();
assert_eq!(Some(0), parser._parse().unwrap());
assert_eq!(None::<i32>, parser._parse().unwrap());
let mut parser = Parser::_new(
from_value!({"p0": 0}),
&["p0", "opt"],
ParamStructure::Either,
0,
)
.unwrap();
assert_eq!(Some(0), parser._parse().unwrap());
assert_eq!(None::<i32>, parser._parse().unwrap());
}
#[test]
fn missing() {
let mut parser = Parser::_new(from_value!({}), &["p0"], ParamStructure::Either, 0).unwrap();
assert!(matches!(
parser._parse::<i32>().unwrap_err(),
ParseError::Missing { name: "p0", .. },
));
let mut parser = Parser::_new(from_value!([]), &["p0"], ParamStructure::Either, 0).unwrap();
assert!(matches!(
parser._parse::<i32>().unwrap_err(),
ParseError::Missing { name: "p0", .. },
));
let mut parser = Parser::_new(from_value!([]), &["p0"], ParamStructure::Either, 1).unwrap();
assert!(matches!(
parser._parse::<Option<i32>>().unwrap_err(),
ParseError::Missing { name: "p0", .. },
));
let mut parser = Parser::_new(
from_value!({"p0": 0}),
&["p0", "p1"],
ParamStructure::Either,
0,
)
.unwrap();
assert_eq!(0, parser._parse::<i32>().unwrap());
assert!(matches!(
parser._parse::<i32>().unwrap_err(),
ParseError::Missing { name: "p1", .. },
));
let mut parser =
Parser::_new(from_value!([0]), &["p0", "p1"], ParamStructure::Either, 0).unwrap();
assert_eq!(0, parser._parse::<i32>().unwrap());
assert!(matches!(
parser._parse::<i32>().unwrap_err(),
ParseError::Missing { name: "p1", .. },
));
}
#[test]
fn unexpected() {
assert!(matches!(
Parser::_new(from_value!({ "surprise": () }), &[], ParamStructure::Either,0).unwrap_err(),
ParseError::UnexpectedNamed(it) if it == ["surprise"],
));
assert!(matches!(
Parser::_new(from_value!(["surprise"]), &[], ParamStructure::Either, 0).unwrap_err(),
ParseError::UnexpectedPositional(1),
));
let mut parser = Parser::_new(
from_value!({ "p0": 0, "surprise": () }),
&["p0"],
ParamStructure::Either,
0,
)
.unwrap();
assert!(matches!(
parser._parse::<i32>().unwrap_err(),
ParseError::UnexpectedNamed(it) if it == ["surprise"]
));
let mut parser = Parser::_new(
from_value!([1, "surprise"]),
&["p0"],
ParamStructure::Either,
0,
)
.unwrap();
assert!(matches!(
parser._parse::<i32>().unwrap_err(),
ParseError::UnexpectedPositional(1),
));
}
#[test]
#[should_panic = "`Parser` was initialized with 0 arguments, but `parse` was called 1 times"]
fn called_too_much() {
let mut parser = Parser::_new(None, &[], ParamStructure::Either, 0).unwrap();
let _ = parser._parse::<()>();
unreachable!()
}
#[test]
#[should_panic = "`Parser` has unhandled parameters - did you forget to call `parse`?"]
fn called_too_little() {
Parser::_new(None, &["p0"], ParamStructure::Either, 0).unwrap();
}
}