use super::Command;
use crate::common::syntax::parse_arguments;
use crate::common::syntax::Mode;
use crate::common::syntax::OptionSpec;
use thiserror::Error;
use yash_env::semantics::Field;
use yash_env::Env;
use yash_syntax::source::pretty::Annotation;
use yash_syntax::source::pretty::AnnotationType;
use yash_syntax::source::pretty::Message;
use yash_syntax::source::Location;
pub const OPTION_SPECS: &[OptionSpec] = &[OptionSpec::new().short('a')];
#[derive(Clone, Debug, Eq, Error, PartialEq)]
pub enum Error {
#[error(transparent)]
CommonError(#[from] crate::common::syntax::ParseError<'static>),
#[error("cannot specify `-a` with other operands")]
ConflictingOptionAndOperand(Location),
#[error("no option or operand specified")]
MissingArgument,
}
impl Error {
pub fn to_message(&self) -> Message {
let (title, annotations) = match self {
Error::CommonError(e) => return e.into(),
Error::ConflictingOptionAndOperand(location) => (
"cannot specify `-a` with other operands".into(),
vec![Annotation::new(
AnnotationType::Error,
"cannot specify `-a` with other operands".into(),
location,
)],
),
Error::MissingArgument => ("no option or operand specified".into(), vec![]),
};
Message {
r#type: AnnotationType::Error,
title,
annotations,
footers: vec![],
}
}
}
impl<'a> From<&'a Error> for Message<'a> {
#[inline]
fn from(e: &'a Error) -> Self {
e.to_message()
}
}
pub fn parse(env: &Env, args: Vec<Field>) -> Result<Command, Error> {
let mode = Mode::with_env(env);
let (mut options, operands) = parse_arguments(OPTION_SPECS, mode, args)?;
for option in &options {
debug_assert_eq!(option.spec.get_short(), Some('a'));
}
match (options.pop(), operands.is_empty()) {
(None, true) => Err(Error::MissingArgument),
(None, false) => Ok(Command::Remove(operands)),
(Some(_), true) => Ok(Command::RemoveAll),
(Some(option), false) => Err(Error::ConflictingOptionAndOperand(option.location)),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn all_option() {
let env = Env::new_virtual();
let result = parse(&env, Field::dummies(["-a"]));
assert_eq!(result, Ok(Command::RemoveAll));
}
#[test]
fn operands() {
let env = Env::new_virtual();
let operands = Field::dummies(["foo", "bar"]);
let result = parse(&env, operands.clone());
assert_eq!(result, Ok(Command::Remove(operands)));
}
#[test]
fn missing_arguments() {
let env = Env::new_virtual();
let result = parse(&env, vec![]);
assert_eq!(result, Err(Error::MissingArgument));
}
#[test]
fn option_and_operand() {
let env = Env::new_virtual();
let args = Field::dummies(["-a", "foo"]);
let result = parse(&env, args);
assert_eq!(
result,
Err(Error::ConflictingOptionAndOperand(Location::dummy("-a"))),
);
}
}