pub fn parse_args(args: &[String]) -> (String, Vec<String>) {
if args.is_empty() {
return (String::new(), Vec::new());
}
let cmd = args[0].clone();
let rest = args[1..].to_vec();
(cmd, rest)
}
pub fn parse_line(input: &str) -> (String, Vec<String>) {
let tokens = tokenize(input);
parse_args_slice(&tokens)
}
#[inline(always)]
fn parse_args_slice(tokens: &[String]) -> (String, Vec<String>) {
if tokens.is_empty() {
return (String::new(), Vec::new());
}
(tokens[0].clone(), tokens[1..].to_vec())
}
fn tokenize(input: &str) -> Vec<String> {
let mut tokens = Vec::with_capacity(8);
let mut cur = String::new();
let mut chars = input.chars().peekable();
let mut in_single = false;
let mut in_double = false;
while let Some(ch) = chars.next() {
match ch {
'\\' => {
if in_single {
if let Some(&next) = chars.peek() {
cur.push(next);
chars.next();
} else {
cur.push('\\');
}
} else if in_double {
if let Some(&next) = chars.peek() {
if next == '"' {
cur.push('"');
chars.next();
} else {
if next.is_whitespace() {
cur.push(next);
chars.next();
} else if next == '\\' {
cur.push('\\');
chars.next();
} else {
cur.push('\\');
}
}
} else {
cur.push('\\');
}
} else {
if let Some(&next) = chars.peek() {
if next.is_whitespace() {
cur.push(next);
chars.next();
} else if next == '\\' {
cur.push('\\');
chars.next();
} else if next == '"' {
in_double = true;
chars.next();
} else if next == '\'' {
in_single = true;
chars.next();
} else {
cur.push('\\');
}
} else {
cur.push('\\');
}
}
}
'"' if !in_single => {
if in_double {
if let Some('"') = chars.peek().copied() {
cur.push('"');
chars.next();
} else {
in_double = false;
if cur.is_empty() {
match chars.peek().copied() {
Some(c) if c.is_whitespace() => tokens.push(String::new()),
None => tokens.push(String::new()),
_ => {}
}
}
}
} else {
if !cur.is_empty() {
cur.push('"');
} else {
in_double = true;
}
}
}
'\'' if !in_double => {
if in_single {
in_single = false;
if cur.is_empty() {
match chars.peek().copied() {
Some(c) if c.is_whitespace() => tokens.push(String::new()),
None => tokens.push(String::new()),
_ => {}
}
}
} else {
in_single = true;
}
}
c if c.is_whitespace() && !in_single && !in_double => {
if !cur.is_empty() {
tokens.push(std::mem::take(&mut cur));
}
}
c => cur.push(c),
}
}
if !cur.is_empty() {
tokens.push(cur);
}
tokens
}