#![no_std]
#![cfg(windows)]
#![cfg_attr(docsrs, feature(doc_cfg))]
use core::{
char::{decode_utf16, REPLACEMENT_CHARACTER},
fmt,
num::NonZeroU16,
slice,
};
const SPACE: u16 = b' ' as _;
const TAB: u16 = b'\t' as _;
const QUOTE: u16 = b'"' as _;
const SLASH: u16 = b'\\' as _;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Token {
Unit(NonZeroU16),
NextArg,
}
impl Token {
#[inline]
pub fn as_u16(self) -> u16 {
match self {
Token::Unit(u) => u.get(),
Token::NextArg => 0,
}
}
#[inline]
pub fn is_next_arg(self) -> bool {
self == Token::NextArg
}
}
#[derive(Debug, Clone)]
pub struct Parser {
iter: ParseArgs,
}
impl Parser {
pub fn from_env() -> Self {
Parser()
}
}
impl Iterator for Parser {
type Item = Token;
fn next(&mut self) -> Option<Self::Item> {
self.iter
.next()
.map(|w| unsafe { Token::Unit(NonZeroU16::new_unchecked(w)) })
.or_else(|| {
self.iter.move_to_next_arg();
if self.iter.cursor.peek().is_none() {
None
} else {
Some(Token::NextArg)
}
})
}
fn size_hint(&self) -> (usize, Option<usize>) {
(0, Some(self.iter.cursor.max_len() as _))
}
}
#[allow(nonstandard_style)]
#[doc(hidden)]
pub fn Parser() -> Parser {
Parser {
iter: ParseArgs::from_env(),
}
}
pub fn null_separated_list() -> impl Iterator<Item = char> + fmt::Debug + Clone {
scalars(null_separated_list_wide())
}
pub fn null_separated_list_wide() -> impl Iterator<Item = u16> + fmt::Debug + Clone {
Parser().map(|t| t.as_u16())
}
#[derive(Clone)]
pub struct Argument {
arg: WideIter,
is_arg0: bool,
}
impl Argument {
pub fn scalars(&self) -> impl Iterator<Item = char> + fmt::Debug + Clone {
scalars(self.utf16_units())
}
pub fn code_points(&self) -> impl Iterator<Item = u32> + fmt::Debug + Clone {
code_points(self.utf16_units())
}
pub fn utf16_units(&self) -> impl Iterator<Item = u16> + fmt::Debug + Clone {
ParseArgs::new(self.arg, self.is_arg0)
}
pub fn raw_arg(&self) -> &'static [u16] {
unsafe { self.arg.as_slice() }
}
fn eq<I: Iterator<Item = u16>>(&self, other: I) -> bool {
self.utf16_units().eq(other)
}
}
impl fmt::Debug for Argument {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Argument")
.field("arg", &self.arg.ptr)
.field("is_arg0", &self.is_arg0)
.finish()
}
}
impl Eq for Argument {}
impl PartialEq<Argument> for Argument {
fn eq(&self, other: &Argument) -> bool {
self.eq(other.utf16_units())
}
}
impl PartialEq<Argument> for &str {
fn eq(&self, other: &Argument) -> bool {
other.eq(self.encode_utf16())
}
}
impl PartialEq<&str> for Argument {
fn eq(&self, other: &&str) -> bool {
self.eq(other.encode_utf16())
}
}
impl PartialEq<&[u16]> for Argument {
fn eq(&self, other: &&[u16]) -> bool {
self.eq(other.iter().copied())
}
}
impl PartialEq<Argument> for &[u16] {
fn eq(&self, other: &Argument) -> bool {
other.eq(self.iter().copied())
}
}
pub struct ArgsNative {
next: ParseArgs,
}
impl ArgsNative {
pub fn from_env() -> Self {
let arg = ParseArgs::from_env();
Self { next: arg }
}
}
impl fmt::Debug for ArgsNative {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("ArgsNative")
.field("next_arg", &self.next.cursor.ptr)
.field("is_arg0", &self.next.is_arg0)
.finish()
}
}
impl Iterator for ArgsNative {
type Item = Argument;
fn next(&mut self) -> Option<Self::Item> {
let current = Argument {
arg: self.next.cursor,
is_arg0: self.next.is_arg0,
};
if current.arg.peek() == None {
None
} else {
self.next.move_to_next_arg();
Some(current)
}
}
}
pub fn args_native() -> ArgsNative {
ArgsNative::from_env()
}
#[derive(Copy, Clone, Debug)]
struct WideIter {
ptr: *const u16,
}
impl WideIter {
unsafe fn new(ptr: *const u16) -> Self {
Self { ptr }
}
fn next(&mut self) -> Option<u16> {
unsafe {
let next = self.peek()?;
self.ptr = self.ptr.add(1);
Some(next)
}
}
fn peek(&self) -> Option<u16> {
match unsafe { *self.ptr } {
0 => None,
next => Some(next),
}
}
unsafe fn as_slice<'a>(self) -> &'a [u16] {
let mut len = 0;
while *self.ptr.add(len) != 0 {
len += 1;
}
slice::from_raw_parts(self.ptr, len)
}
fn max_len(mut self) -> u16 {
let mut len: u16 = 0;
while self.next().is_some() {
len += 1;
}
len
}
fn skip_whitespace(&mut self) {
while self.peek() == Some(SPACE) || self.peek() == Some(TAB) {
self.next();
}
}
}
impl Iterator for WideIter {
type Item = u16;
fn next(&mut self) -> Option<Self::Item> {
self.next()
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
enum EscapeMode {
Unescaped,
LiteralQuote,
}
#[derive(Clone, Debug)]
struct EscapeIter {
counter: u16,
mode: EscapeMode,
}
impl EscapeIter {
fn new(iter: &mut WideIter) -> Self {
let mut counter: u16 = 1;
let mut mode = EscapeMode::Unescaped;
loop {
match iter.peek() {
Some(SLASH) => counter += 1,
Some(QUOTE) => {
if counter.is_odd() {
iter.next();
mode = EscapeMode::LiteralQuote;
}
counter /= 2;
break;
}
_ => break,
}
iter.next();
}
Self { counter, mode }
}
fn next(&mut self) -> Option<u16> {
match self.counter.checked_sub(1) {
Some(n) => {
self.counter = n;
Some(SLASH)
}
None if self.mode == EscapeMode::LiteralQuote => {
self.mode = EscapeMode::Unescaped;
Some(QUOTE)
}
None => None,
}
}
}
impl Iterator for EscapeIter {
type Item = u16;
fn next(&mut self) -> Option<Self::Item> {
self.next()
}
}
#[derive(Clone, Debug)]
struct ParseArgs {
cursor: WideIter,
quote_mode: bool,
escape_iter: Option<EscapeIter>,
is_arg0: bool,
}
impl ParseArgs {
fn from_env() -> Self {
Self::new(command_line(), true)
}
fn new(arg: WideIter, is_arg0: bool) -> Self {
Self {
cursor: arg,
quote_mode: false,
escape_iter: None,
is_arg0,
}
}
fn move_to_next_arg(&mut self) {
while self.next().is_some() {}
self.cursor.skip_whitespace();
self.is_arg0 = false;
}
}
impl Iterator for ParseArgs {
type Item = u16;
fn next(&mut self) -> Option<Self::Item> {
loop {
if let Some(iter) = self.escape_iter.as_mut() {
match iter.next() {
Some(w) => return Some(w),
None => self.escape_iter = None,
}
}
match self.cursor.peek()? {
SPACE | TAB if not(self.quote_mode) => {
return None;
}
SLASH if not(self.is_arg0) => {
self.cursor.next();
let slashes = EscapeIter::new(&mut self.cursor);
self.escape_iter = Some(slashes);
}
QUOTE => {
self.cursor.next();
if not(self.is_arg0) && self.quote_mode && self.cursor.peek() == Some(QUOTE) {
return self.cursor.next();
} else {
self.quote_mode.toggle();
}
}
_ => return self.cursor.next(),
}
}
}
}
fn scalars<I: Iterator<Item = u16> + fmt::Debug + Clone>(
iter: I,
) -> impl Iterator<Item = char> + fmt::Debug + Clone {
decode_utf16(iter).map(|u| u.unwrap_or(REPLACEMENT_CHARACTER))
}
fn code_points<I: Iterator<Item = u16> + fmt::Debug + Clone>(
iter: I,
) -> impl Iterator<Item = u32> + fmt::Debug + Clone {
decode_utf16(iter).map(|u| match u {
Ok(c) => c as u32,
Err(e) => e.unpaired_surrogate() as u32,
})
}
trait Toggle {
fn toggle(&mut self);
}
impl Toggle for bool {
#[inline(always)]
fn toggle(&mut self) {
*self = not(*self)
}
}
trait OddEven {
fn is_odd(self) -> bool;
}
impl OddEven for u16 {
#[inline(always)]
fn is_odd(self) -> bool {
self & 1 == 1
}
}
#[inline(always)]
fn not(v: bool) -> bool {
!v
}
fn command_line() -> WideIter {
unsafe { WideIter::new(GetCommandLineW()) }
}
extern "system" {
fn GetCommandLineW() -> *const u16;
}