commands: add and fix variants for RequestParserError
This commit is contained in:
130
src/commands.rs
130
src/commands.rs
@@ -56,6 +56,13 @@ where
|
||||
/// The command name used within the protocol
|
||||
const COMMAND: &'static str;
|
||||
|
||||
// TODO: add these for ease of throwing parsing errors
|
||||
// /// The minimum number of arguments this command takes
|
||||
// const MIN_ARGS: u32;
|
||||
|
||||
// /// The maximum number of arguments this command takes
|
||||
// const MAX_ARGS: Option<u32>;
|
||||
|
||||
/// Converts this specific request type to it's corresponding variant in the generic Request enum.
|
||||
fn into_request_enum(self) -> crate::Request;
|
||||
|
||||
@@ -256,7 +263,17 @@ macro_rules! empty_command_request {
|
||||
fn parse(
|
||||
mut parts: crate::commands::RequestTokenizer<'_>,
|
||||
) -> Result<Self, crate::commands::RequestParserError> {
|
||||
debug_assert!(parts.next().is_none());
|
||||
if parts.next().is_some() {
|
||||
return Err(crate::commands::RequestParserError::TooManyArguments {
|
||||
expected_min: 0,
|
||||
expected_max: 0,
|
||||
found: parts
|
||||
.count()
|
||||
.try_into()
|
||||
.unwrap_or(u32::MAX)
|
||||
.saturating_add(1),
|
||||
});
|
||||
}
|
||||
|
||||
Ok(paste::paste! { [<$name Request>] })
|
||||
}
|
||||
@@ -323,14 +340,34 @@ macro_rules! single_item_command_request {
|
||||
fn parse(
|
||||
mut parts: crate::commands::RequestTokenizer<'_>,
|
||||
) -> Result<Self, crate::commands::RequestParserError> {
|
||||
let item_token = parts
|
||||
.next()
|
||||
.ok_or(crate::commands::RequestParserError::UnexpectedEOF)?;
|
||||
let item_token =
|
||||
parts
|
||||
.next()
|
||||
.ok_or(crate::commands::RequestParserError::MissingArguments {
|
||||
expected_min: 1,
|
||||
expected_max: 1,
|
||||
found: 0,
|
||||
})?;
|
||||
|
||||
let item = item_token.parse::<$item_type>().map_err(|_| {
|
||||
crate::commands::RequestParserError::SyntaxError(0, item_token.to_owned())
|
||||
crate::commands::RequestParserError::SubtypeParserError {
|
||||
argument_index: 1,
|
||||
expected_type: stringify!($item_type).to_string(),
|
||||
raw_input: item_token.to_owned(),
|
||||
}
|
||||
})?;
|
||||
|
||||
debug_assert!(parts.next().is_none());
|
||||
if parts.next().is_some() {
|
||||
return Err(crate::commands::RequestParserError::TooManyArguments {
|
||||
expected_min: 1,
|
||||
expected_max: 1,
|
||||
found: parts
|
||||
.count()
|
||||
.try_into()
|
||||
.unwrap_or(u32::MAX)
|
||||
.saturating_add(2),
|
||||
});
|
||||
}
|
||||
|
||||
Ok(paste::paste! { [<$name Request>] ( item ) })
|
||||
}
|
||||
@@ -378,12 +415,27 @@ macro_rules! single_optional_item_command_request {
|
||||
.next()
|
||||
.map(|s| {
|
||||
s.parse().map_err(|_| {
|
||||
crate::commands::RequestParserError::SyntaxError(0, s.to_owned())
|
||||
crate::commands::RequestParserError::SubtypeParserError {
|
||||
argument_index: 1,
|
||||
expected_type: stringify!($item_type).to_string(),
|
||||
raw_input: s.to_owned(),
|
||||
}
|
||||
})
|
||||
})
|
||||
.transpose()?;
|
||||
|
||||
debug_assert!(parts.next().is_none());
|
||||
if parts.next().is_some() {
|
||||
let item_count = if item.is_some() { 1 } else { 0 };
|
||||
return Err(crate::commands::RequestParserError::TooManyArguments {
|
||||
expected_min: 0,
|
||||
expected_max: 1,
|
||||
found: parts
|
||||
.count()
|
||||
.try_into()
|
||||
.unwrap_or(u32::MAX)
|
||||
.saturating_add(item_count),
|
||||
});
|
||||
}
|
||||
|
||||
Ok(paste::paste! { [<$name Request>] ( item ) })
|
||||
}
|
||||
@@ -491,18 +543,68 @@ pub(crate) use single_optional_item_command_request;
|
||||
|
||||
#[derive(Error, Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||
pub enum RequestParserError {
|
||||
#[error("Found empty line while parsing request")]
|
||||
EmptyLine,
|
||||
|
||||
// TODO: remove this, replaced by various other errors
|
||||
#[error("Could not parse the request due to a syntax error at position {0}: {1}")]
|
||||
SyntaxError(u64, String),
|
||||
|
||||
#[error("A command list was expected to be closed, but the end was not found at line {0}")]
|
||||
MissingCommandListEnd(u64),
|
||||
#[error(
|
||||
"Could not parse argument {argument_index} of the request (expected type: {expected_type}, raw input: '{raw_input}')"
|
||||
)]
|
||||
SubtypeParserError {
|
||||
/// The index of the argument that failed to parse
|
||||
argument_index: u32,
|
||||
|
||||
#[error("A command list was found inside another command list at line {0}")]
|
||||
NestedCommandList(u64),
|
||||
/// The expected type of the argument
|
||||
expected_type: String,
|
||||
|
||||
#[error("A command list was closed unexpectedly at line {0}")]
|
||||
UnexpectedCommandListEnd(u64),
|
||||
/// The raw input that failed to parse
|
||||
raw_input: String,
|
||||
},
|
||||
|
||||
#[error(
|
||||
"Too many arguments were provided in the request (expected between {expected_min} and {expected_max}, found {found})"
|
||||
)]
|
||||
TooManyArguments {
|
||||
/// The minimum number of arguments that were expected
|
||||
expected_min: u32,
|
||||
|
||||
/// The maximum number of arguments that were expected
|
||||
expected_max: u32,
|
||||
|
||||
/// The number of arguments that were found
|
||||
found: u32,
|
||||
},
|
||||
|
||||
#[error(
|
||||
"Not enough arguments were provided in the request (expected between {expected_min} and {expected_max}, found {found})"
|
||||
)]
|
||||
MissingArguments {
|
||||
/// The minimum number of arguments that were expected
|
||||
expected_min: u32,
|
||||
|
||||
/// The maximum number of arguments that were expected
|
||||
expected_max: u32,
|
||||
|
||||
/// The number of arguments that were found
|
||||
found: u32,
|
||||
},
|
||||
|
||||
#[error("A command list was expected to be closed, but the end was not found")]
|
||||
MissingCommandListEnd,
|
||||
|
||||
#[error("A command list was found inside another command list at line {line}")]
|
||||
NestedCommandList {
|
||||
/// The line where the nested command list was found
|
||||
line: u32,
|
||||
},
|
||||
|
||||
#[error("An unexpected command list end was found")]
|
||||
UnexpectedCommandListEnd,
|
||||
|
||||
// TODO: remove this, replaced by EmptyLine + MissingArguments
|
||||
#[error("Request ended early, while more arguments were expected")]
|
||||
UnexpectedEOF,
|
||||
// #[error("Request is missing terminating newline")]
|
||||
|
||||
@@ -399,11 +399,7 @@ impl Request {
|
||||
.ok_or(RequestParserError::UnexpectedEOF)?;
|
||||
let mut parts = RequestTokenizer::new(line);
|
||||
|
||||
match parts
|
||||
.next()
|
||||
.ok_or(RequestParserError::SyntaxError(0, line.to_string()))?
|
||||
.trim()
|
||||
{
|
||||
match parts.next().ok_or(RequestParserError::EmptyLine)?.trim() {
|
||||
"command_list_begin" => {
|
||||
let mut commands = Vec::new();
|
||||
let mut i = 1;
|
||||
@@ -411,10 +407,10 @@ impl Request {
|
||||
i += 1;
|
||||
let (line, _rest) = rest
|
||||
.split_once('\n')
|
||||
.ok_or(RequestParserError::MissingCommandListEnd(i))?;
|
||||
.ok_or(RequestParserError::MissingCommandListEnd)?;
|
||||
match line.trim() {
|
||||
"command_list_begin" => {
|
||||
return Err(RequestParserError::NestedCommandList(i));
|
||||
return Err(RequestParserError::NestedCommandList { line: i });
|
||||
}
|
||||
"command_list_end" => {
|
||||
return Ok(Request::CommandList(commands));
|
||||
@@ -426,7 +422,7 @@ impl Request {
|
||||
}
|
||||
}
|
||||
}
|
||||
"command_list_end" => Err(RequestParserError::UnexpectedCommandListEnd(0)),
|
||||
"command_list_end" => Err(RequestParserError::UnexpectedCommandListEnd),
|
||||
|
||||
/* querying mpd status */
|
||||
ClearErrorRequest::COMMAND => parse_req!(ClearErrorRequest, parts),
|
||||
|
||||
Reference in New Issue
Block a user