use std::fmt;
#[must_use]
pub struct CommandLineWParser<'argsline> {
line: std::slice::Iter<'argsline, u16>,
}
impl<'argsline> CommandLineWParser<'argsline> {
#[inline]
#[must_use]
pub fn new(command_line_args_ucs2: &'argsline [u16]) -> Self {
Self {
line: command_line_args_ucs2.iter(),
}
}
}
impl<'a> fmt::Debug for CommandLineWParser<'a> {
#[cold]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
String::from_utf16_lossy(self.line.as_slice()).fmt(f)
}
}
#[derive(Debug)]
enum State {
BetweenArgs,
InArg(bool),
OnQuote,
Backslashes(usize, bool),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CharCode {
Quoted(u16),
Unquoted(u16),
}
const SPACE: u16 = b' ' as u16;
const TAB: u16 = b'\t' as u16;
const QUOTE: u16 = b'"' as u16;
const BACKSLASH: u16 = b'\\' as u16;
impl<'argsline> CommandLineWParser<'argsline> {
pub fn accumulate_next<CharacterAccumulator>(&mut self, mut push: CharacterAccumulator) -> bool
where CharacterAccumulator: FnMut(CharCode)
{
use self::State::*;
let mut state = BetweenArgs;
for &cu in &mut self.line {
state = match state {
BetweenArgs => match cu {
SPACE | TAB => BetweenArgs,
QUOTE => InArg(true),
BACKSLASH => Backslashes(1, false),
c => {
push(CharCode::Unquoted(c));
InArg(false)
},
},
InArg(quoted) => match cu {
BACKSLASH => Backslashes(1, quoted),
QUOTE if quoted => OnQuote,
QUOTE if !quoted => InArg(true),
SPACE | TAB if !quoted => {
return true;
},
c => {
push(if quoted { CharCode::Quoted(c) } else { CharCode::Unquoted(c) });
InArg(quoted)
},
},
OnQuote => match cu {
QUOTE => {
push(CharCode::Quoted(QUOTE));
InArg(false)
},
SPACE | TAB => {
return true;
},
c => {
push(CharCode::Unquoted(c));
InArg(false)
},
},
Backslashes(count, quoted) => match cu {
BACKSLASH => Backslashes(count + 1, quoted),
QUOTE => {
let b = if quoted { CharCode::Quoted(BACKSLASH) } else { CharCode::Unquoted(BACKSLASH) };
for _ in 0..count/2 {
push(b);
}
if count & 1 != 0 {
push(if quoted { CharCode::Quoted(QUOTE) } else { CharCode::Unquoted(QUOTE) });
InArg(quoted)
} else if quoted {
return true;
} else {
InArg(quoted)
}
},
c => {
let b = if quoted { CharCode::Quoted(BACKSLASH) } else { CharCode::Unquoted(BACKSLASH) };
for _ in 0..count {
push(b);
}
match c {
SPACE | TAB if !quoted => return true,
c => {
push(if quoted { CharCode::Quoted(c) } else { CharCode::Unquoted(c) });
InArg(quoted)
},
}
},
},
};
}
match state {
BetweenArgs => false,
OnQuote | InArg(..) => true,
Backslashes(count, quoted) => {
let b = if quoted { CharCode::Quoted(BACKSLASH) } else { CharCode::Unquoted(BACKSLASH) };
for _ in 0..count {
push(b);
}
true
},
}
}
}