1 //! `comma` parses command-line-style strings. See [`parse_command`] for details.
3 use std
::iter
::{Peekable}
;
6 fn parse_escape(chars
: &mut Peekable
<Chars
>) -> Option
<char> {
7 return Some(match chars
.next()?
{
15 fn parse_string(chars
: &mut Peekable
<Chars
>, delim
: char) -> Option
<String
> {
16 let mut output
= String
::new();
18 while let Some(ch
) = chars
.next() {
21 } else if ch
== '
\\'
{
22 output
.push(parse_escape(chars
)?
);
31 /// Parses a command into a list of individual tokens.
32 /// Each token is separated by one or more characters of whitespace.
33 /// Pairs of single- or double-quotes can be used to ignore whitespace. Within pairs of quotation
34 /// marks, a backslash (\) can be used to escape any character. The special escape sequences
35 /// '\n', '\r', and '\t' are also handled as Newlines, Carriage Returns, and Tabs, respectively.
36 /// Should a quotation mark be mismatched (no counterpart terminating mark exists), this function
37 /// will return None. Otherwise, it returns a list of tokens in the input string.
38 pub fn parse_command(input
: &str) -> Option
<Vec
<String
>> {
39 let mut next_push
= true;
40 let mut chars
= input
.chars().peekable();
41 let mut output
: Vec
<String
> = Vec
::new();
43 while let Some(ch
) = chars
.next() {
44 if ch
.is_whitespace() {
47 if next_push { output.push(String::new()); next_push = false; }
50 output
.last_mut()?
.push(parse_escape(&mut chars
)?
);
51 } else if ch
== '
"' || ch == '\'' {
52 output.last_mut()?.push_str(parse_string(&mut chars, ch)?.as_str());
54 output.last_mut()?.push(ch);
65 use crate::{parse_command};
69 let result = parse_command("hello world
\\'this is
\\' a
\"quoted
\\\"string
\\\"\"").unwrap();
71 vec![String::from("hello
"), String::from("world
"),
72 String::from("'this
"), String::from("is'
"), String::from("a
"),
73 String::from("quoted
\"string
\"")]);
78 assert_eq!(parse_command("Hello 'world
"), None);
83 // This contains a CJK IDEOGRAPH EXTENSION G character, which is invisible.
84 let result = parse_command("ß 𱁬
").unwrap();
87 vec![String::from("ß
"), String::from("𱁬
")]