Lines
73.21 %
Functions
83.33 %
Branches
100 %
/*
* Hurl (https://hurl.dev)
* Copyright (C) 2024 Orange
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
use hurl_core::reader::Reader;
/// Split a `str` into a vec of String params
pub fn split(s: &str) -> Result<Vec<String>, String> {
let mut params = vec![];
let mut parser = Parser::new(s);
while let Some(param) = parser.param()? {
params.push(param);
}
Ok(params)
struct Parser {
reader: Reader,
impl Parser {
fn new(s: &str) -> Parser {
let reader = Reader::new(s);
Parser { reader }
fn delimiter(&mut self) -> Option<(char, bool)> {
if self.reader.peek() == Some('\'') {
_ = self.reader.read();
Some(('\'', false))
} else if self.reader.peek() == Some('$') {
let save = self.reader.cursor();
Some(('\'', true))
} else {
self.reader.seek(save);
None
fn col(&self) -> usize {
self.reader.cursor().pos.column
fn param(&mut self) -> Result<Option<String>, String> {
_ = self.reader.read_while(|c| c == ' ');
if self.reader.is_eof() {
return Ok(None);
let mut value = String::new();
if let Some((delimiter, escaping)) = self.delimiter() {
while let Some(c1) = self.reader.read() {
if c1 == '\\' && escaping {
let c2 = match self.reader.read() {
Some('n') => '\n',
Some('t') => '\t',
Some('r') => '\r',
Some(c) => c,
_ => {
let col = self.col();
return Err(format!("Invalid escape at column {col}"));
};
value.push(c2);
} else if c1 == delimiter {
return Ok(Some(value));
value.push(c1);
Err(format!("Missing delimiter {delimiter} at column {col}"))
loop {
match self.reader.read() {
Some('\\') => {
if let Some(c) = self.reader.read() {
value.push(c);
Some(' ') => return Ok(Some(value)),
Some(c) => {
_ => return Ok(Some(value)),
#[cfg(test)]
mod test {
use crate::curl::args;
use crate::curl::args::Parser;
#[test]
fn test_split() {
let expected = vec!["AAA".to_string(), "BBB".to_string()];
assert_eq!(args::split(r#"AAA BBB"#).unwrap(), expected);
assert_eq!(args::split(r#" AAA BBB "#).unwrap(), expected);
assert_eq!(args::split(r#"AAA 'BBB'"#).unwrap(), expected);
assert_eq!(args::split(r#"AAA $'BBB'"#).unwrap(), expected);
let expected = vec!["'".to_string()];
assert_eq!(args::split(r"$'\''").unwrap(), expected);
fn test_split_error() {
assert_eq!(
args::split(r#"AAA 'BBB"#).err().unwrap(),
"Missing delimiter ' at column 9".to_string()
);
fn test_param_without_quote() {
let mut parser = Parser::new("value");
assert_eq!(parser.param().unwrap().unwrap(), "value".to_string());
assert_eq!(parser.col(), 6);
let mut parser = Parser::new(" value ");
assert_eq!(parser.col(), 8);
fn test_param_with_quote() {
let mut parser = Parser::new("'value'");
let mut parser = Parser::new(" 'value' ");
assert_eq!(parser.col(), 9);
let mut parser = Parser::new("'\\n'");
assert_eq!(parser.param().unwrap().unwrap(), "\\n".to_string());
assert_eq!(parser.col(), 5);
fn test_dollar_prefix() {
let mut parser = Parser::new("$'Test: \\''");
assert_eq!(parser.param().unwrap().unwrap(), "Test: '".to_string());
assert_eq!(parser.col(), 12);
let mut parser = Parser::new("$'\\n'");
assert_eq!(parser.param().unwrap().unwrap(), "\n".to_string());
fn test_param_missing_closing_quote() {
let mut parser = Parser::new("'value");
parser.param().err().unwrap(),
"Missing delimiter ' at column 7".to_string()
assert_eq!(parser.col(), 7);
fn test_no_more_param() {
assert_eq!(Parser::new("").param().unwrap(), None);
assert_eq!(Parser::new(" ").param().unwrap(), None);
fn test_delimiter() {
assert_eq!(parser.delimiter(), None);
assert_eq!(parser.col(), 1);
assert_eq!(parser.delimiter().unwrap(), ('\'', false));
assert_eq!(parser.col(), 2);
let mut parser = Parser::new("$'value'");
assert_eq!(parser.delimiter().unwrap(), ('\'', true));
assert_eq!(parser.col(), 3);
let mut parser = Parser::new("$value");