1use std::borrow::Cow;
2use std::ffi::OsStr;
3use std::ffi::OsString;
4use std::fmt::Display;
5use std::ops::Deref;
6
7use crate::str::CowOsStrUtils;
8use crate::ARef;
9use crate::Error;
10
11const EQUAL: char = '=';
12
13#[derive(Debug, Clone, Default)]
14pub struct ArgInfo<'a> {
15 pub name: Cow<'a, str>,
16
17 pub value: Option<Cow<'a, OsStr>>,
18}
19
20impl<'a> ArgInfo<'a> {
21 pub fn parse(val: &'a OsStr) -> Result<Self, Error> {
67 let arg_display = format!("{}", std::path::Path::new(val).display());
68
69 crate::trace!("parsing command line argument {val:?}");
70 if let Some((name, value)) = crate::str::split_once(val, EQUAL) {
71 let name = name
73 .to_str(|v| v.trim())
74 .ok_or_else(|| Error::arg(&arg_display, "failed convert OsStr to str"))?;
75
76 if name.is_empty() {
77 return Err(Error::arg(arg_display, "can not be empty"));
78 }
79 Ok(Self {
80 name,
81 value: Some(value),
82 })
83 } else {
84 let name = val
85 .to_str()
86 .ok_or_else(|| Error::arg(arg_display, "failed convert OsStr to str"))?;
87
88 Ok(Self {
89 name: Cow::Borrowed(name),
90 value: None,
91 })
92 }
93 }
94}
95
96#[derive(Debug, Clone, Default)]
97pub struct Args {
98 inner: ARef<Vec<OsString>>,
99}
100
101impl Args {
102 pub fn new<S: Into<OsString>>(inner: impl Iterator<Item = S>) -> Self {
103 Self {
104 inner: ARef::new(inner.map(|v| v.into()).collect()),
105 }
106 }
107
108 pub fn from_env() -> Self {
110 Self::new(std::env::args_os())
111 }
112
113 pub fn unwrap_or_clone(self) -> Vec<OsString> {
114 ARef::unwrap_or_clone(self.inner)
115 }
116}
117
118impl<T: Into<OsString>, I: IntoIterator<Item = T>> From<I> for Args {
119 fn from(value: I) -> Self {
120 Self::new(value.into_iter())
121 }
122}
123
124impl From<Args> for Vec<OsString> {
125 fn from(value: Args) -> Self {
126 value.unwrap_or_clone()
127 }
128}
129
130impl Deref for Args {
131 type Target = Vec<OsString>;
132
133 fn deref(&self) -> &Self::Target {
134 &self.inner
135 }
136}
137
138pub fn iter2<'a, 'b>(
139 args: &'a [&'b OsStr],
140) -> impl Iterator<Item = (&'a &'b OsStr, Option<&'a &'b OsStr>)> {
141 args.iter()
142 .scan(args.iter().skip(1), |i, e| Some((e, i.next())))
143}
144
145impl Display for Args {
146 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
147 write!(
148 f,
149 "Args {{[{}]}}",
150 self.inner
151 .iter()
152 .map(|v| format!("{:?}", v))
153 .collect::<Vec<String>>()
154 .join(", ")
155 )
156 }
157}
158
159#[cfg(test)]
160mod test {
161
162 use std::ffi::OsStr;
163
164 use super::Args;
165
166 #[test]
167 fn test_args() {
168 let args = Args::from(["--opt", "value", "--bool", "pos"]);
169 let mut iter = args
170 .iter()
171 .zip(args.iter().skip(1).map(Some).chain(None))
172 .enumerate();
173
174 if let Some((idx, (opt, arg))) = iter.next() {
175 assert_eq!(idx, 0);
176 assert_eq!(opt, OsStr::new("--opt"));
177 assert_eq!(arg.map(|v| v.as_ref()), Some(OsStr::new("value")));
178 }
179
180 if let Some((idx, (opt, arg))) = iter.next() {
181 assert_eq!(idx, 1);
182 assert_eq!(opt, OsStr::new("value"));
183 assert_eq!(arg.map(|v| v.as_ref()), Some(OsStr::new("--bool")));
184 }
185
186 if let Some((idx, (opt, arg))) = iter.next() {
187 assert_eq!(idx, 2);
188 assert_eq!(opt, OsStr::new("--bool"));
189 assert_eq!(arg.map(|v| v.as_ref()), Some(OsStr::new("pos")));
190 }
191
192 if let Some((idx, (opt, arg))) = iter.next() {
193 assert_eq!(idx, 3);
194 assert_eq!(opt, OsStr::new("pos"));
195 assert_eq!(arg, None);
196 }
197
198 assert_eq!(iter.next(), None);
199 }
200}