meli/meli/src/command/argcheck.rs

181 lines
5.3 KiB
Rust

/*
* meli
*
* Copyright 2017 Manos Pitsidianakis
*
* This file is part of meli.
*
* meli is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* meli is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with meli. If not, see <http://www.gnu.org/licenses/>.
*/
//! Helper type for showing the exact reason why a command was invalid.
use super::*;
pub enum ArgCheck<const MIN: u8, const MAX: u8> {
Start { __func__: &'static str },
BeforeArgument { so_far: u8, __func__: &'static str },
Eof { so_far: u8, __func__: &'static str },
}
impl<const MIN: u8, const MAX: u8> ArgCheck<MIN, MAX> {
#[inline]
pub fn new(__func__: &'static str) -> Self {
Self::Start { __func__ }
}
#[inline]
pub fn start(&mut self, input: &[u8]) -> Result<(), CommandError> {
let Self::Start { __func__ } = *self else {
unreachable!(
"ArgCheck::start called with invalid variant: {}",
if matches!(self, Self::BeforeArgument { .. }) {
"BeforeArgument"
} else {
"Eof"
}
);
};
let is_empty = input.trim().is_empty();
if is_empty && MIN > 0 {
return Err(CommandError::WrongNumberOfArguments {
too_many: false,
takes: (MIN, MAX.into()),
given: 0,
__func__,
inner: format!(
"needs {}{} arguments.",
if MIN == MAX { "at least " } else { "" },
MIN
)
.into(),
});
}
*self = Self::BeforeArgument {
so_far: 0,
__func__,
};
Ok(())
}
#[inline]
pub fn inc(&mut self, input: &[u8]) -> Result<(), CommandError> {
let Self::BeforeArgument { __func__, so_far } = *self else {
unreachable!(
"ArgCheck::inc called with invalid variant: {}",
if matches!(self, Self::Start { .. }) {
"Start"
} else {
"Eof"
}
);
};
let is_empty = input.trim().is_empty();
let new_value = so_far + 1;
if is_empty && new_value > MAX {
return Err(CommandError::WrongNumberOfArguments {
too_many: true,
takes: (MIN, MAX.into()),
given: new_value,
__func__,
inner: format!(
"needs {}{} arguments.",
if MIN == MAX { "at least " } else { "" },
MIN
)
.into(),
});
}
*self = Self::BeforeArgument {
so_far: new_value,
__func__,
};
Ok(())
}
#[inline]
pub fn finish(&mut self, input: &[u8]) -> Result<(), CommandError> {
let Self::BeforeArgument { __func__, so_far } = *self else {
unreachable!(
"ArgCheck::finish called with invalid variant: {}",
if matches!(self, Self::Start { .. }) {
"Start"
} else {
"Eof"
}
);
};
let is_empty = input.trim().is_empty();
if !is_empty {
assert!(so_far <= MAX);
assert!(so_far >= MIN);
return Err(CommandError::WrongNumberOfArguments {
too_many: true,
takes: (MIN, MAX.into()),
given: so_far + 1,
__func__,
inner: format!(
"needs {}{} arguments.",
if MIN == MAX { "at least " } else { "" },
MIN
)
.into(),
});
}
*self = Self::Eof { so_far, __func__ };
Ok(())
}
}
macro_rules! arg_init {
(min_arg: $n:expr, max_arg: $x:expr, $func:tt) => {{
ArgCheck::<$n, $x>::new(stringify!($func))
}};
}
//macro_rules! arg_value_check {
// ($tag:literal, $input:expr) => {{
// if tag::<&'_ str, &'_ [u8],
// melib::nom::error::Error<&[u8]>>($tag)($input).is_err() { return
// Ok(( $input,
// Err(CommandError::BadValue {
// inner: $tag.to_string().into(),
// }),
// ));
// }
// tag($tag)($input)
// }};
//}
macro_rules! arg_chk {
(start $check:ident, $input:expr) => {{
if let Err(err) = $check.start($input) {
return Ok(($input, Err(err)));
};
}};
(inc $check:ident, $input:expr) => {{
if let Err(err) = $check.inc($input) {
return Ok(($input, Err(err)));
};
}};
(finish $check:ident, $input:expr) => {{
if let Err(err) = $check.finish($input) {
return Ok(($input, Err(err)));
};
}};
}