cinderella

[unmaintained] simple CI engine
Log | Files | Refs | README | LICENSE

commit cdb7201767eacf348708e48d7142e18f2aa85924
parent 88369a11c73c5b654c83fe489159908aa3b406b5
Author: Stefan Koch <programming@stefan-koch.name>
Date:   Tue, 24 Sep 2019 22:42:28 +0200

create a command parser that can handle quoted arguments

Diffstat:
Msrc/execution.rs | 13+++----------
Msrc/lib.rs | 1+
Asrc/parser.rs | 86+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 90 insertions(+), 10 deletions(-)

diff --git a/src/execution.rs b/src/execution.rs @@ -4,6 +4,7 @@ use std::io::Write; use evalexpr; +use crate::parser; use crate::pipeline; pub enum ExecutionResult { @@ -63,8 +64,8 @@ fn execute_pipeline<W: Write>( let cmd = replace_variables(&cmd, &variables); // TODO: Raise error if some variables remain unsubstituted? - let parts = split_command(&cmd); - let output = Command::new(parts[0]) + let parts = parser::parse_command(&cmd); + let output = Command::new(&parts[0]) .args(&parts[1..]) .output(); let output = match output { @@ -87,14 +88,6 @@ fn execute_pipeline<W: Write>( ExecutionResult::Success } -fn split_command<'a>(command: &'a str) -> Vec<&'a str> { - // TODO: Successful argument parsing needs a lot more details, - // e.g. for quoted arguments like myprogram "argument 1" - // but for a first shot this works - let parts: Vec<&str> = command.split(" ").collect(); - parts -} - fn execute_test(test: &str, variables: &HashMap<String, String>) -> bool { // not possible to use evalexpr Context, because evalexpr only handles // standard variable names without special characters (percentage diff --git a/src/lib.rs b/src/lib.rs @@ -8,6 +8,7 @@ use rand::distributions::Alphanumeric; mod config; mod vcs; +mod parser; mod pipeline; mod execution; mod mail; diff --git a/src/parser.rs b/src/parser.rs @@ -0,0 +1,86 @@ +pub fn parse_command(command: &str) -> Vec<String> { + // TODO: improve this to a good state machine + let mut parts = Vec::new(); + let mut in_quotes = false; + let mut in_word = false; + let mut start_idx = 0; + let mut next_char_escaped = false; + let mut buffer = String::new(); + + for (i, c) in command.chars().enumerate() { + if in_quotes { + if c == '"' { + parts.push(String::from(&command[start_idx..i])); + in_quotes = false; + } + } else { + if c == '\\' && !next_char_escaped { + buffer.push_str(&command[start_idx..i]); + buffer.push_str(&command[(i+1)..(i+2)]); + next_char_escaped = true; + start_idx = i + 2; + } else if c == '"' && !next_char_escaped { + in_quotes = true; + start_idx = i + 1; + next_char_escaped = false; + } else if !next_char_escaped && c != ' ' && !in_word { + start_idx = i; + in_word = true; + next_char_escaped = false; + } else if !next_char_escaped && c == ' ' && in_word { + parts.push(format!("{}{}", buffer, String::from(&command[start_idx..i]))); + buffer = String::new(); + in_word = false; + next_char_escaped = false; + } + } + } + + let end = command.len(); + if start_idx != end { + parts.push(format!("{}{}", buffer, String::from(&command[start_idx..end]))); + } + + parts +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_parse_simple_command() { + let result = parse_command("program execute something"); + + assert_eq!(result[0], "program"); + assert_eq!(result[1], "execute"); + assert_eq!(result[2], "something"); + } + + #[test] + fn test_parse_command_with_quoted_args() { + let result = parse_command("program \"execute something\" and \"something else\""); + + assert_eq!(result[0], "program"); + assert_eq!(result[1], "execute something"); + assert_eq!(result[2], "and"); + assert_eq!(result[3], "something else"); + } + + #[test] + fn test_parse_command_with_spaced_arg() { + let result = parse_command("program execute\\ something"); + + assert_eq!(result[0], "program"); + assert_eq!(result[1], "execute something"); + } + + #[test] + fn test_parse_virtualenv_tox_command() { + let result = parse_command("bash -c \"virtualenv env && source env/bin/activate && tox\""); + + assert_eq!(result[0], "bash"); + assert_eq!(result[1], "-c"); + assert_eq!(result[2], "virtualenv env && source env/bin/activate && tox"); + } +}