arg_fn/lib.rs
1//! Argument parsing crate that allows the user to specify what to do for each argument.
2//!
3//! # Example
4//!
5//! ```
6//! #[derive(PartialEq, Debug, Default)]
7//! struct Config {
8//! foo: bool,
9//! bar: bool,
10//! }
11//!
12//! let cfg = arg_fn::Parser::new(Config::default(), |_cfg, _arg| {})
13//! .arg("-foo", |cfg| cfg.foo = true)
14//! .arg("-nofoo", |cfg| cfg.foo = false)
15//! .arg("-bar", |cfg| cfg.bar = true)
16//! .arg("-nobar", |cfg| cfg.bar = false)
17//! .parse(["-bar", "-nofoo", "-foo", "-nobar", "-foo"]);
18//!
19//! assert_eq!(
20//! cfg,
21//! Config {
22//! foo: true,
23//! bar: false,
24//! }
25//! )
26//! ```
27
28use std::{borrow::Cow, collections::HashMap};
29
30/// Parser struct containing the config, a map of arguments to functions, and a function that is
31/// called when an argument is not in the map.
32///
33/// # Example
34///
35/// ```
36/// #[derive(PartialEq, Debug, Default)]
37/// struct Config {
38/// foo: bool,
39/// bar: bool,
40/// }
41///
42/// let cfg = arg_fn::Parser::new(Config::default(), |_cfg, _arg| {})
43/// .arg("-foo", |cfg| cfg.foo = true)
44/// .arg("-nofoo", |cfg| cfg.foo = false)
45/// .arg("-bar", |cfg| cfg.bar = true)
46/// .arg("-nobar", |cfg| cfg.bar = false)
47/// .parse(["-bar", "-nofoo", "-foo", "-nobar", "-foo"]);
48///
49/// assert_eq!(
50/// cfg,
51/// Config {
52/// foo: true,
53/// bar: false,
54/// }
55/// )
56/// ```
57#[allow(clippy::type_complexity)]
58#[must_use]
59pub struct Parser<'a, Config: 'a> {
60 config: Config,
61 arguments: HashMap<Cow<'a, str>, Box<dyn Fn(&mut Config) + 'a>>,
62 unknown: Box<dyn Fn(&mut Config, &'a str) + 'a>,
63}
64
65impl<'a, Config: 'a> Parser<'a, Config> {
66 pub fn new(config: Config, unknown: impl Fn(&mut Config, &'a str) + 'a) -> Self {
67 Self {
68 config,
69 arguments: HashMap::new(),
70 unknown: Box::new(unknown),
71 }
72 }
73
74 #[allow(clippy::type_complexity)]
75 pub fn with_arguments(
76 config: Config,
77 arguments: HashMap<Cow<'a, str>, Box<dyn Fn(&mut Config) + 'a>>,
78 unknown: impl Fn(&mut Config, &'a str) + 'a,
79 ) -> Self {
80 Self {
81 config,
82 arguments,
83 unknown: Box::new(unknown),
84 }
85 }
86
87 pub fn arg(
88 mut self,
89 argument: impl Into<Cow<'a, str>>,
90 callback: impl Fn(&mut Config) + 'a,
91 ) -> Self {
92 self.arguments.insert(argument.into(), Box::new(callback));
93 self
94 }
95
96 pub fn parse(mut self, input: impl IntoIterator<Item = &'a str>) -> Config {
97 for arg in input {
98 if let Some(callback) = self.arguments.get(arg) {
99 callback(&mut self.config);
100 } else {
101 (self.unknown)(&mut self.config, arg);
102 };
103 }
104
105 self.config
106 }
107}
108
109impl<'a, Config: Default> Default for Parser<'a, Config> {
110 fn default() -> Self {
111 Self::new(Config::default(), |_, _| {})
112 }
113}