#[derive(Debug, Default, Clone)]
pub struct ClarOption {
name: String,
short_label: Option<char>,
long_label: Option<String>,
standalone: bool,
default_value: Option<String>,
default_missing_value: Option<String>,
possible_values: Vec<String>,
takes_value: Option<String>,
help: Option<String>,
help_long: Option<String>,
path: String,
}
impl ClarOption {
pub fn new(name: impl AsRef<str>, short: char, long: impl AsRef<str>) -> Self {
Self {
name: name.as_ref().to_string(),
short_label: Some(short),
long_label: Some(long.as_ref().to_string()),
..Default::default()
}
}
pub fn new_short(name: impl AsRef<str>, label: char) -> Self {
Self {
name: name.as_ref().to_string(),
short_label: Some(label),
..Default::default()
}
}
pub fn new_long(name: impl AsRef<str>, label: impl AsRef<str>) -> Self {
Self {
name: name.as_ref().to_string(),
long_label: Some(label.as_ref().to_string()),
..Default::default()
}
}
pub fn short(mut self, label: char) -> Self {
self.short_label = Some(label);
self
}
pub fn long(mut self, label: impl AsRef<str>) -> Self {
self.long_label = Some(label.as_ref().to_string());
self
}
pub fn standalone(mut self) -> Self {
self.standalone = true;
self
}
pub fn default_value(mut self, value: impl AsRef<str>) -> Self {
self.default_value = Some(value.as_ref().to_string());
self
}
pub fn default_missing_value(mut self, value: impl AsRef<str>) -> Self {
self.default_missing_value = Some(value.as_ref().to_string());
self
}
pub fn possible_values<I, S>(mut self, values: I) -> Self
where
I: IntoIterator<Item = S>,
S: AsRef<str>,
{
self.possible_values = values.into_iter().map(|value| value.as_ref().to_owned()).collect();
self
}
pub fn takes_value(mut self, caption: impl AsRef<str>) -> Self {
self.takes_value = Some(caption.as_ref().to_string());
self
}
pub fn help(mut self, content: impl AsRef<str>) -> Self {
self.help = Some(content.as_ref().to_string());
self.help_long = Some(content.as_ref().to_string());
self
}
pub fn help_long(mut self, content: impl AsRef<str>) -> Self {
self.help_long = Some(content.as_ref().to_string());
self
}
pub fn get_name(&self) -> &str {
&self.name
}
pub fn get_short_label(&self) -> &Option<char> {
&self.short_label
}
pub fn get_long_label(&self) -> &Option<String> {
&self.long_label
}
pub fn is_standalone(&self) -> bool {
self.standalone
}
pub fn get_default_value(&self) -> &Option<String> {
&self.default_value
}
pub fn get_default_missing_value(&self) -> &Option<String> {
&self.default_missing_value
}
pub fn get_takes_value(&self) -> &Option<String> {
&self.takes_value
}
pub fn get_possible_values(&self) -> &Vec<String> {
&self.possible_values
}
pub fn get_help(&self) -> &Option<String> {
&self.help
}
pub fn get_help_long(&self) -> &Option<String> {
&self.help_long
}
fn set_path(&mut self, parent_path: impl AsRef<str>) {
self.path = make_path(parent_path.as_ref(), &self.name);
}
pub(crate) fn get_path(&self) -> &str {
&self.path
}
}
#[derive(Debug, Default, Clone)]
pub struct ClarArgument {
name: String,
caption: String,
default_value: Option<String>,
required: bool,
help: Option<String>,
help_long: Option<String>,
path: String,
}
impl ClarArgument {
pub fn new(name: impl AsRef<str>) -> Self {
Self {
name: name.as_ref().to_string(),
caption: name.as_ref().to_uppercase(),
..Default::default()
}
}
pub fn caption(mut self, caption: impl AsRef<str>) -> Self {
self.caption = caption.as_ref().to_string();
self
}
pub fn default_value(mut self, default_value: impl AsRef<str>) -> Self {
self.default_value = Some(default_value.as_ref().to_string());
self
}
pub fn required(mut self) -> Self {
self.required = true;
self
}
pub fn help(mut self, help: impl AsRef<str>) -> Self {
self.help = Some(help.as_ref().to_string());
self
}
pub fn help_long(mut self, help_long: impl AsRef<str>) -> Self {
self.help_long = Some(help_long.as_ref().to_string());
self
}
pub fn get_name(&self) -> &str {
&self.name
}
pub fn get_caption(&self) -> &str {
&self.caption
}
pub fn get_default_value(&self) -> &Option<String> {
&self.default_value
}
pub fn is_required(&self) -> bool {
self.required
}
pub fn get_help(&self) -> &Option<String> {
&self.help
}
pub fn get_help_long(&self) -> &Option<String> {
if self.help_long.is_some() {
&self.help_long
} else {
self.get_help()
}
}
fn set_path(&mut self, parent_path: impl AsRef<str>) {
self.path = make_path(parent_path.as_ref(), &self.name);
}
pub(crate) fn get_path(&self) -> &str {
&self.path
}
}
#[derive(Debug, Default, Clone)]
pub struct ClarCommand {
name: String,
items: Vec<ClarItem>,
help: Option<String>,
help_long: Option<String>,
path: String,
}
impl ClarCommand {
pub fn new(name: impl AsRef<str>) -> Self {
Self {
name: name.as_ref().to_string(),
..Default::default()
}
}
pub fn help(mut self, help: impl AsRef<str>) -> Self {
self.help = Some(help.as_ref().to_string());
self
}
pub fn help_long(mut self, help_long: impl AsRef<str>) -> Self {
self.help_long = Some(help_long.as_ref().to_string());
self
}
pub fn terminator(&mut self, terminator: ClarTerminator) {
self.items.clear();
self.items.push(ClarItem::Terminator(terminator));
}
pub fn options<O>(mut self, options: O) -> Self
where
O: IntoIterator<Item = ClarOption>,
{
self.items.clear();
self.items.push(ClarItem::Options(options.into_iter().collect()));
self
}
pub fn options_t<O>(&mut self, options: O, t: ClarTerminator)
where
O: IntoIterator<Item = ClarOption>,
{
self.items.clear();
self.items.push(ClarItem::Options(options.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 commands<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, o: O, s: S) -> Self
where
O: IntoIterator<Item = ClarOption>,
S: IntoIterator<Item = ClarCommand>,
{
self.items.clear();
self.items.push(ClarItem::Options(o.into_iter().collect()));
self.items.push(ClarItem::Commands(s.into_iter().collect()));
self
}
pub fn get_name(&self) -> &str {
&self.name
}
pub(crate) fn get_items(&self) -> &Vec<ClarItem> {
&self.items
}
pub fn get_help(&self) -> &Option<String> {
&self.help
}
pub fn get_help_long(&self) -> &Option<String> {
if self.help_long.is_some() {
&self.help_long
} else {
self.get_help()
}
}
fn set_path(&mut self, parent_path: impl AsRef<str>) {
self.path = make_path(parent_path.as_ref(), &self.name);
}
pub(crate) fn get_path(&self) -> &str {
&self.path
}
}
#[derive(Debug, Default, Clone)]
pub struct ClarTerminator {
pub name: String,
required: bool,
path: String,
}
impl ClarTerminator {
pub fn new(name: impl AsRef<str>) -> Self {
Self {
name: name.as_ref().to_string(),
..Default::default()
}
}
pub fn required(mut self) -> Self {
self.required = true;
self
}
pub fn get_name(&self) -> &str {
&self.name
}
pub fn is_required(&self) -> bool {
self.required
}
fn set_path(&mut self, parent_path: impl AsRef<str>) {
self.path = make_path(parent_path.as_ref(), &self.name);
}
pub(crate) fn get_path(&self) -> &str {
&self.path
}
}
#[derive(Debug, Clone)]
pub enum ClarItem {
Options(Vec<ClarOption>),
Commands(Vec<ClarCommand>),
Arguments(Vec<ClarArgument>),
Terminator(ClarTerminator),
}
pub fn update_paths(items: &mut Vec<ClarItem>, segments: &mut Vec<String>) {
let parent_path = segments.join("/");
for item in items {
match item {
ClarItem::Commands(subcommands) => {
for subcommand in subcommands {
subcommand.set_path(&parent_path);
segments.push(subcommand.get_name().to_string());
update_paths(&mut subcommand.items, segments);
segments.pop();
}
}
ClarItem::Options(options) => options.iter_mut().for_each(|o| o.set_path(&parent_path)),
ClarItem::Arguments(arguments) => arguments.iter_mut().for_each(|a| a.set_path(&parent_path)),
ClarItem::Terminator(option_terminator) => option_terminator.set_path(&parent_path),
}
}
}
pub fn display_tree(items: &[ClarItem]) {
for item in items {
match item {
ClarItem::Commands(subcommands) => {
for subcommand in subcommands {
println!("{}", subcommand.get_path());
display_tree(&subcommand.items);
}
}
ClarItem::Options(options) => options.iter().for_each(|o| println!("{}", o.get_path())),
ClarItem::Arguments(arguments) => arguments.iter().for_each(|a| println!("{}", a.get_path())),
ClarItem::Terminator(option_terminator) => println!("{}", option_terminator.get_path()),
}
}
}
pub fn find_command<'a>(command_names: &'a [&str], items: &'a [ClarItem]) -> Option<&'a ClarCommand> {
let searched_name = command_names.first()?;
let command = items
.iter()
.filter_map(|item| {
if let ClarItem::Commands(commands) = item {
Some(commands)
} else {
None
}
})
.flatten()
.find(|command| command.get_name() == *searched_name)?;
let remaining_names = &command_names[1..];
if remaining_names.is_empty() {
Some(command)
} else {
find_command(remaining_names, command.get_items())
}
}
fn make_path(parent_path: &str, name: &str) -> String {
if parent_path.is_empty() {
name.to_string()
} else {
format!("{}/{}", parent_path, name)
}
}