use crate::errors::{ClarDiagnostic, ClarDiagnosticResult};
use crate::evaluator::{
Evaluator, ev_and_then, ev_argument, ev_consumed, ev_long_option, ev_one, ev_option_terminator, ev_sequence,
ev_short_option, ev_stop_on_one, ev_subcommand, ev_zero_or_more_options,
};
use crate::lexer::{Lexer, Token};
use crate::matches::ClarMatches;
use crate::messages::get_error;
use crate::model::{ClarArgument, ClarCommand, ClarItem, ClarOption, ClarTerminator, update_paths};
use antex::ColorMode;
use std::collections::VecDeque;
#[derive(Debug, Clone)]
pub struct Clar {
app: String,
description: Option<String>,
items: Vec<ClarItem>,
cm: ColorMode,
}
impl Clar {
pub fn new(app: impl AsRef<str>) -> Self {
Self {
app: app.as_ref().to_string(),
description: None,
items: vec![],
cm: ColorMode::default(),
}
}
pub fn description(mut self, description: impl AsRef<str>) -> Self {
self.description = Some(description.as_ref().to_string());
self
}
pub fn terminator(mut self, t: ClarTerminator) -> Self {
self.items.clear();
self.items.push(ClarItem::Terminator(t));
self
}
pub fn options<O>(mut self, o: O) -> Self
where
O: IntoIterator<Item = ClarOption>,
{
self.items.clear();
self.items.push(ClarItem::Options(o.into_iter().collect()));
self
}
pub fn options_t<O>(&mut self, o: O, t: ClarTerminator)
where
O: IntoIterator<Item = ClarOption>,
{
self.items.clear();
self.items.push(ClarItem::Options(o.into_iter().collect()));
self.items.push(ClarItem::Terminator(t));
}
pub fn arguments<A>(mut self, a: A) -> Self
where
A: IntoIterator<Item = ClarArgument>,
{
self.items.clear();
self.items.push(ClarItem::Arguments(a.into_iter().collect()));
self
}
pub fn arguments_t<A>(mut self, a: A, t: ClarTerminator) -> Self
where
A: IntoIterator<Item = ClarArgument>,
{
self.items.clear();
self.items.push(ClarItem::Arguments(a.into_iter().collect()));
self.items.push(ClarItem::Terminator(t));
self
}
pub fn options_arguments<O, A>(mut self, o: O, a: A) -> Self
where
O: IntoIterator<Item = ClarOption>,
A: IntoIterator<Item = ClarArgument>,
{
self.items.clear();
self.items.push(ClarItem::Options(o.into_iter().collect()));
self.items.push(ClarItem::Arguments(a.into_iter().collect()));
self
}
pub fn options_arguments_t<O, A>(mut self, o: O, a: A, t: ClarTerminator) -> Self
where
O: IntoIterator<Item = ClarOption>,
A: IntoIterator<Item = ClarArgument>,
{
self.items.clear();
self.items.push(ClarItem::Options(o.into_iter().collect()));
self.items.push(ClarItem::Arguments(a.into_iter().collect()));
self.items.push(ClarItem::Terminator(t));
self
}
pub fn subcommands<S>(mut self, s: S) -> Self
where
S: IntoIterator<Item = ClarCommand>,
{
self.items.clear();
self.items.push(ClarItem::Commands(s.into_iter().collect()));
self
}
pub fn options_subcommands<O, S>(mut self, options: O, subcommands: S) -> Self
where
O: IntoIterator<Item = ClarOption>,
S: IntoIterator<Item = ClarCommand>,
{
self.items.clear();
self.items.push(ClarItem::Options(options.into_iter().collect()));
self.items.push(ClarItem::Commands(subcommands.into_iter().collect()));
self
}
pub fn resolve<I, S>(mut self, input: I) -> ClarDiagnosticResult<ClarMatches>
where
I: IntoIterator<Item = S>,
S: AsRef<str>,
{
update_paths(&mut self.items, &mut vec![]);
let mut tokens: VecDeque<Token> = Lexer::default().parse(input).map_err(|reason| {
let text = get_error(&reason, &self.app, &self.items, self.cm);
ClarDiagnostic::new(reason, text)
})?;
let evaluator = ev_consumed(self.build_evaluator(&self.items));
let mut values = vec![];
match evaluator(&mut tokens, &mut values) {
Ok(_) => Ok(ClarMatches::new(
self.app,
self.description,
values,
self.items,
self.cm,
)),
Err(reason) => {
let text = get_error(&reason, &self.app, &self.items, self.cm);
Err(ClarDiagnostic::new(reason, text))
}
}
}
fn build_evaluator(&self, items: &[ClarItem]) -> Evaluator {
let mut evaluators = vec![];
for item in items {
match item {
ClarItem::Options(options) => {
let mut options_evaluators = vec![];
let mut standalone_options_evaluators = vec![];
for option in options {
if option.is_standalone() {
if let Some(short_label) = option.get_short_label() {
standalone_options_evaluators.push(ev_short_option(*short_label, option.into()))
}
if let Some(long_label) = option.get_long_label() {
standalone_options_evaluators.push(ev_long_option(long_label, option.into()))
}
} else {
if let Some(short_label) = option.get_short_label() {
options_evaluators.push(ev_short_option(*short_label, option.into()))
}
if let Some(long_label) = option.get_long_label() {
options_evaluators.push(ev_long_option(long_label, option.into()))
}
}
}
evaluators.push(ev_stop_on_one(standalone_options_evaluators));
evaluators.push(ev_zero_or_more_options(options_evaluators));
}
ClarItem::Commands(subcommands) => {
let mut subcommand_evaluators = vec![];
for subcommand in subcommands {
subcommand_evaluators.push(ev_and_then(
ev_subcommand(subcommand.into()),
self.build_evaluator(subcommand.get_items()),
));
}
evaluators.push(ev_one(subcommand_evaluators));
}
ClarItem::Arguments(arguments) => {
for argument in arguments {
evaluators.push(ev_argument(argument.into()));
}
}
ClarItem::Terminator(option_terminator) => evaluators.push(ev_option_terminator(option_terminator.into())),
}
}
ev_sequence(evaluators)
}
}