#![doc(html_root_url = "https://docs.rs/windows-args/0.2.0")]
#[cfg(windows)]
use std::ffi::{OsStr, OsString};
use std::fmt;
use crate::args::ArgsWtf8;
use wtf8::{Wtf8, Wtf8Buf};
mod wtf8like;
mod args;
pub struct Args { inner: ArgsWtf8<Wtf8Buf> }
#[cfg(windows)]
pub struct ArgsOs { inner: ArgsWtf8<OsString> }
#[cfg(windows)]
impl ArgsOs {
pub fn parse_cmd(input: &OsStr) -> Self {
ArgsOs { inner: ArgsWtf8::parse_cmd(input) }
}
pub fn parse_args(input: &OsStr) -> Self {
parse_args_via_parse_cmd(
input,
ArgsOs::parse_cmd,
OsString::with_capacity,
|buf, s| buf.push(s),
OsStr::len,
)
}
}
impl Args {
pub fn parse_cmd(input: &str) -> Self {
Args { inner: ArgsWtf8::parse_cmd(Wtf8::from_str(input)) }
}
pub fn parse_args(input: &str) -> Self {
parse_args_via_parse_cmd(
input,
Args::parse_cmd,
String::with_capacity,
String::push_str,
str::len,
)
}
}
fn expect_still_utf8(arg: Wtf8Buf) -> String {
arg.into_string().unwrap_or_else(|arg| {
panic!("\
valid UTF-8 became invalid after arg splitting?!
BadArg: {:?}\
", arg);
})
}
impl Iterator for Args {
type Item = String;
fn next(&mut self) -> Option<String> { self.inner.next().map(expect_still_utf8) }
fn size_hint(&self) -> (usize, Option<usize>) { self.inner.size_hint() }
}
impl ExactSizeIterator for Args {
fn len(&self) -> usize { self.inner.len() }
}
impl DoubleEndedIterator for Args {
fn next_back(&mut self) -> Option<String> { self.inner.next_back().map(expect_still_utf8) }
}
impl fmt::Debug for Args {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Args")
.field("inner", &self.inner.inner_debug())
.finish()
}
}
#[cfg(windows)]
impl Iterator for ArgsOs {
type Item = OsString;
fn next(&mut self) -> Option<OsString> { self.inner.next() }
fn size_hint(&self) -> (usize, Option<usize>) { self.inner.size_hint() }
}
#[cfg(windows)]
impl ExactSizeIterator for ArgsOs {
fn len(&self) -> usize { self.inner.len() }
}
#[cfg(windows)]
impl DoubleEndedIterator for ArgsOs {
fn next_back(&mut self) -> Option<OsString> { self.inner.next_back() }
}
#[cfg(windows)]
impl fmt::Debug for ArgsOs {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("ArgsOs")
.field("inner", &self.inner.inner_debug())
.finish()
}
}
fn parse_args_via_parse_cmd<A, OwnS, RefS: ?Sized>(
input: &RefS,
parse_cmd: impl FnOnce(&RefS) -> A,
with_capacity: impl FnOnce(usize) -> OwnS,
push_str: impl Fn(&mut OwnS, &RefS),
len: impl Fn(&RefS) -> usize,
) -> A
where
A: Iterator,
OwnS: std::ops::Deref<Target=RefS>,
str: AsRef<RefS>,
{
let mut modified_input = with_capacity(len(input) + 2);
push_str(&mut modified_input, "a ".as_ref());
push_str(&mut modified_input, input);
let mut out = parse_cmd(&modified_input);
out.next();
out
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn special_traits() {
assert_eq!(Args::parse_cmd("a b").next_back(), Some("b".into()));
assert_eq!(Args::parse_cmd("a b").len(), 2);
}
#[cfg(windows)]
#[test]
fn special_traits_windows() {
assert_eq!(ArgsOs::parse_cmd("a b".as_ref()).next_back(), Some("b".into()));
assert_eq!(ArgsOs::parse_cmd("a b".as_ref()).len(), 2);
}
#[test]
fn args_cmd_differences() {
assert_eq!(Args::parse_cmd("").collect::<Vec<_>>(), vec![String::new()]);
assert_eq!(Args::parse_args("").collect::<Vec<_>>(), Vec::<String>::new());
assert_eq!(
Args::parse_cmd(r#""abc\"def""#).collect::<Vec<_>>(),
vec!["abc\\".to_string(), "def".to_string(),
]);
assert_eq!(
Args::parse_args(r#""abc\"def""#).collect::<Vec<_>>(),
vec!["abc\"def".to_string()],
);
assert_eq!(
Args::parse_cmd(r#"a "abc\"def""#).collect::<Vec<_>>(),
vec!["a".to_string(), "abc\"def".to_string()],
);
assert_eq!(
Args::parse_cmd(r#"a "abc\"def""#).collect::<Vec<_>>(),
vec!["a".to_string(), "abc\"def".to_string()],
);
}
}