1#![doc(html_root_url = "https://docs.rs/windows-args/0.2.0")]
2
3#[cfg(windows)]
34use std::ffi::{OsStr, OsString};
35use std::fmt;
36use crate::args::ArgsWtf8;
37use wtf8::{Wtf8, Wtf8Buf};
38
39mod wtf8like;
40mod args;
41
42pub struct Args { inner: ArgsWtf8<Wtf8Buf> }
47
48#[cfg(windows)]
53pub struct ArgsOs { inner: ArgsWtf8<OsString> }
54
55#[cfg(windows)]
56impl ArgsOs {
57 pub fn parse_cmd(input: &OsStr) -> Self {
72 ArgsOs { inner: ArgsWtf8::parse_cmd(input) }
73 }
74
75 pub fn parse_args(input: &OsStr) -> Self {
90 parse_args_via_parse_cmd(
91 input,
92 ArgsOs::parse_cmd,
93 OsString::with_capacity,
94 |buf, s| buf.push(s),
95 OsStr::len,
96 )
97 }
98}
99
100impl Args {
101 pub fn parse_cmd(input: &str) -> Self {
114 Args { inner: ArgsWtf8::parse_cmd(Wtf8::from_str(input)) }
115 }
116
117 pub fn parse_args(input: &str) -> Self {
130 parse_args_via_parse_cmd(
131 input,
132 Args::parse_cmd,
133 String::with_capacity,
134 String::push_str,
135 str::len,
136 )
137 }
138}
139
140fn expect_still_utf8(arg: Wtf8Buf) -> String {
141 arg.into_string().unwrap_or_else(|arg| {
142 panic!("\
143valid UTF-8 became invalid after arg splitting?!
144BadArg: {:?}\
145", arg);
146 })
147}
148
149impl Iterator for Args {
150 type Item = String;
151 fn next(&mut self) -> Option<String> { self.inner.next().map(expect_still_utf8) }
152 fn size_hint(&self) -> (usize, Option<usize>) { self.inner.size_hint() }
153}
154
155impl ExactSizeIterator for Args {
156 fn len(&self) -> usize { self.inner.len() }
157}
158
159impl DoubleEndedIterator for Args {
160 fn next_back(&mut self) -> Option<String> { self.inner.next_back().map(expect_still_utf8) }
161}
162
163impl fmt::Debug for Args {
164 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
165 f.debug_struct("Args")
166 .field("inner", &self.inner.inner_debug())
167 .finish()
168 }
169}
170
171#[cfg(windows)]
172impl Iterator for ArgsOs {
173 type Item = OsString;
174 fn next(&mut self) -> Option<OsString> { self.inner.next() }
175 fn size_hint(&self) -> (usize, Option<usize>) { self.inner.size_hint() }
176}
177
178#[cfg(windows)]
179impl ExactSizeIterator for ArgsOs {
180 fn len(&self) -> usize { self.inner.len() }
181}
182
183#[cfg(windows)]
184impl DoubleEndedIterator for ArgsOs {
185 fn next_back(&mut self) -> Option<OsString> { self.inner.next_back() }
186}
187
188#[cfg(windows)]
189impl fmt::Debug for ArgsOs {
190 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
191 f.debug_struct("ArgsOs")
192 .field("inner", &self.inner.inner_debug())
193 .finish()
194 }
195}
196
197fn parse_args_via_parse_cmd<A, OwnS, RefS: ?Sized>(
198 input: &RefS,
199 parse_cmd: impl FnOnce(&RefS) -> A,
200 with_capacity: impl FnOnce(usize) -> OwnS,
201 push_str: impl Fn(&mut OwnS, &RefS),
202 len: impl Fn(&RefS) -> usize,
203) -> A
204where
205 A: Iterator,
206 OwnS: std::ops::Deref<Target=RefS>,
207 str: AsRef<RefS>,
208{
209 let mut modified_input = with_capacity(len(input) + 2);
211 push_str(&mut modified_input, "a ".as_ref());
212 push_str(&mut modified_input, input);
213
214 let mut out = parse_cmd(&modified_input);
216 out.next();
217
218 out
219}
220
221#[cfg(test)]
222mod tests {
223 use super::*;
224
225 #[test]
226 fn special_traits() {
227 assert_eq!(Args::parse_cmd("a b").next_back(), Some("b".into()));
228 assert_eq!(Args::parse_cmd("a b").len(), 2);
229 }
230
231 #[cfg(windows)]
232 #[test]
233 fn special_traits_windows() {
234 assert_eq!(ArgsOs::parse_cmd("a b".as_ref()).next_back(), Some("b".into()));
235 assert_eq!(ArgsOs::parse_cmd("a b".as_ref()).len(), 2);
236 }
237
238 #[test]
239 fn args_cmd_differences() {
240 assert_eq!(Args::parse_cmd("").collect::<Vec<_>>(), vec![String::new()]);
241 assert_eq!(Args::parse_args("").collect::<Vec<_>>(), Vec::<String>::new());
242
243 assert_eq!(
244 Args::parse_cmd(r#""abc\"def""#).collect::<Vec<_>>(),
245 vec!["abc\\".to_string(), "def".to_string(),
246 ]);
247 assert_eq!(
248 Args::parse_args(r#""abc\"def""#).collect::<Vec<_>>(),
249 vec!["abc\"def".to_string()],
250 );
251
252 assert_eq!(
253 Args::parse_cmd(r#"a "abc\"def""#).collect::<Vec<_>>(),
254 vec!["a".to_string(), "abc\"def".to_string()],
255 );
256 assert_eq!(
257 Args::parse_cmd(r#"a "abc\"def""#).collect::<Vec<_>>(),
258 vec!["a".to_string(), "abc\"def".to_string()],
259 );
260 }
261}