Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add & separator #3

Merged
merged 3 commits into from
Jan 19, 2025
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
feat: add & separator
guuzaa committed Jan 19, 2025
commit 08cdc22d6ad6b76ef68b9a5546eafcbd738b5262
115 changes: 88 additions & 27 deletions src/parser.rs
Original file line number Diff line number Diff line change
@@ -20,25 +20,23 @@ impl Parser {
}

pub fn parse(&self, input: &str, parallel: bool, verbose: bool) -> Executor {
let routines = if input.contains(';') {
self.parse_semicolon_separated(input)
// Check for & first as it's the most restrictive
if input.contains('&') {
let routines = self.parse_ampersand_separated(input);
Executor::new(parallel, verbose, routines, Strategy::Dependent)
} else if input.contains(';') {
let routines = self.parse_semicolon_separated(input);
Executor::new(parallel, verbose, routines, Strategy::Independent)
} else {
self.parse_space_separated(input)
};

Executor::new(parallel, verbose, routines, Strategy::Independent)
let routines = self.parse_space_separated(input);
Executor::new(parallel, verbose, routines, Strategy::Independent)
}
}

fn parse_space_separated(&self, input: &str) -> Vec<Routine> {
input
.split_whitespace()
.map(|cmd| {
let parts: Vec<&str> = cmd.split_whitespace().collect();
Routine {
name: parts[0].to_string(),
args: parts[1..].iter().map(|s| s.to_string()).collect(),
}
})
.map(|cmd| self.create_routine(cmd))
.collect()
}

@@ -47,22 +45,31 @@ impl Parser {
.split(';')
.map(str::trim)
.filter(|s| !s.is_empty())
.map(|cmd| {
let parts: Vec<&str> = cmd.split_whitespace().collect();
if parts.is_empty() {
return Routine {
name: String::new(),
args: Vec::new(),
};
}
Routine {
name: parts[0].to_string(),
args: parts[1..].iter().map(|s| s.to_string()).collect(),
}
})
.filter(|routine| !routine.name.is_empty())
.map(|cmd| self.create_routine(cmd))
.filter(|routine| !routine.is_empty())
.collect()
}

fn parse_ampersand_separated(&self, input: &str) -> Vec<Routine> {
input
.split('&')
.map(str::trim)
.filter(|s| !s.is_empty())
.map(|cmd| self.create_routine(cmd))
.filter(|routine| !routine.is_empty())
.collect()
}

fn create_routine(&self, cmd: &str) -> Routine {
let parts: Vec<&str> = cmd.split_whitespace().collect();
if parts.is_empty() {
return Routine::default();
}
Routine {
name: parts[0].to_string(),
args: parts[1..].iter().map(|s| s.to_string()).collect(),
}
}
}

#[cfg(test)]
@@ -101,6 +108,60 @@ mod tests {
assert!(executor.routines[1].args.is_empty());
}

#[test]
fn test_parse_ampersand_separated() {
let parser = Parser::new();
let input = "check & test & run";
let executor = parser.parse(input, false, false);

assert_eq!(executor.strategy, Strategy::Dependent);
assert_eq!(executor.routines.len(), 3);

assert_eq!(executor.routines[0].name, "check");
assert!(executor.routines[0].args.is_empty());

assert_eq!(executor.routines[1].name, "test");
assert!(executor.routines[1].args.is_empty());

assert_eq!(executor.routines[2].name, "run");
assert!(executor.routines[2].args.is_empty());
}

#[test]
fn test_parse_ampersand_with_args() {
let parser = Parser::new();
let input = "test --features feature1 & run --release";
let executor = parser.parse(input, false, false);

assert_eq!(executor.strategy, Strategy::Dependent);
assert_eq!(executor.routines.len(), 2);

assert_eq!(executor.routines[0].name, "test");
assert_eq!(executor.routines[0].args, vec!["--features", "feature1"]);

assert_eq!(executor.routines[1].name, "run");
assert_eq!(executor.routines[1].args, vec!["--release"]);
}

#[test]
fn test_parse_ampersand_without_spaces() {
let parser = Parser::new();
let input = "check&test&run";
let executor = parser.parse(input, false, false);

assert_eq!(executor.strategy, Strategy::Dependent);
assert_eq!(executor.routines.len(), 3);

assert_eq!(executor.routines[0].name, "check");
assert!(executor.routines[0].args.is_empty());

assert_eq!(executor.routines[1].name, "test");
assert!(executor.routines[1].args.is_empty());

assert_eq!(executor.routines[2].name, "run");
assert!(executor.routines[2].args.is_empty());
}

#[test]
fn test_parse_semicolon_with_empty() {
let parser = Parser::new();
6 changes: 5 additions & 1 deletion src/routine.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use std::io;
use std::process::{Command, Output, Stdio};

#[derive(Debug)]
#[derive(Debug, Default)]
pub(crate) struct Routine {
pub(crate) name: String,
pub(crate) args: Vec<String>,
@@ -28,4 +28,8 @@ impl Routine {
Ok((output.status.success(), output))
}
}

pub fn is_empty(&self) -> bool {
self.name.is_empty()
}
}