Skip to main content

clap_config_fallback/
arg.rs

1use std::ffi::OsString;
2
3use clap::ArgMatches;
4
5use crate::format::{CustomFormat, DefaultFormat, FormatArg};
6
7/// Converts an intermediate options struct into synthetic CLI args.
8///
9/// These args are fed back into clap for the final parse/validation pass.
10pub trait IntoArgs: Sized {
11    fn into_args(self) -> impl Iterator<Item = OsString> {
12        let mut args = Vec::new();
13
14        self.extend_args(&mut args);
15
16        args.into_iter()
17    }
18
19    fn extend_args(self, args: &mut Vec<OsString>);
20}
21
22/// Builds an intermediate options struct from clap `ArgMatches`.
23///
24/// This pass captures values explicitly provided on the CLI before config fallback is applied.
25pub trait FromArgs: Sized {
26    fn from_args(args: &ArgMatches) -> Option<Self>;
27}
28
29pub enum ArgValue<T> {
30    Scalar(T),
31    Repeated(Vec<T>),
32    Flag(bool),
33}
34
35pub struct Arg<T, F = DefaultFormat> {
36    value: ArgValue<T>,
37    name: Option<OsString>,
38    formatter: F,
39}
40
41impl<T> Arg<T> {
42    pub fn scalar(value: T) -> Self {
43        Self {
44            value: ArgValue::Scalar(value),
45            name: None,
46            formatter: DefaultFormat,
47        }
48    }
49
50    pub fn repeated(value: Vec<T>) -> Self {
51        Self {
52            value: ArgValue::Repeated(value),
53            name: None,
54            formatter: DefaultFormat,
55        }
56    }
57}
58
59impl Arg<bool, DefaultFormat> {
60    pub fn flag(value: bool) -> Self {
61        Self {
62            value: ArgValue::Flag(value),
63            name: None,
64            formatter: DefaultFormat,
65        }
66    }
67}
68
69impl<T, F> Arg<T, F> {
70    pub fn name(mut self, name: impl Into<OsString>) -> Self {
71        self.name = Some(name.into());
72
73        self
74    }
75
76    pub fn value_format<U>(self, formatter: U) -> Arg<T, CustomFormat<U>> {
77        Arg {
78            value: self.value,
79            name: self.name,
80            formatter: CustomFormat(formatter),
81        }
82    }
83}
84
85impl<T, F> IntoArgs for Arg<T, F>
86where
87    F: FormatArg<T>,
88{
89    fn extend_args(self, args: &mut Vec<OsString>) {
90        match self.value {
91            ArgValue::Scalar(value) => {
92                if let Some(name) = self.name {
93                    args.push(name);
94                }
95
96                args.push(self.formatter.format(value));
97            }
98            ArgValue::Repeated(values) => {
99                for value in values {
100                    if let Some(name) = &self.name {
101                        args.push(name.clone());
102                    }
103
104                    args.push(self.formatter.format(value));
105                }
106            }
107            ArgValue::Flag(value) => {
108                if value && let Some(name) = self.name {
109                    args.push(name);
110                }
111            }
112        }
113    }
114}
115
116impl<T> IntoArgs for Option<T>
117where
118    T: IntoArgs,
119{
120    fn extend_args(self, args: &mut Vec<OsString>) {
121        if let Some(value) = self {
122            value.extend_args(args);
123        }
124    }
125}