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

test(commands): command input parsing #305

Draft
wants to merge 4 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
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
1 change: 1 addition & 0 deletions crates/core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ itertools = "0.13.0"
quick_cache = "0.6.9"
shell-words = "1.1.0"
strum = { version = "0.26.3", features = ["derive"] }
winsplit = "0.1"
zstd = "0.13.2"

[target.'cfg(not(windows))'.dependencies]
Expand Down
46 changes: 41 additions & 5 deletions crates/core/src/repository/command_input.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,15 @@
str::FromStr,
};

use itertools::Itertools;

Check warning on line 7 in crates/core/src/repository/command_input.rs

View workflow job for this annotation

GitHub Actions / Cross checking x86_64-pc-windows-msvc

unused import: `itertools::Itertools`

Check warning on line 7 in crates/core/src/repository/command_input.rs

View workflow job for this annotation

GitHub Actions / Cross checking x86_64-pc-windows-gnu

unused import: `itertools::Itertools`

Check warning on line 7 in crates/core/src/repository/command_input.rs

View workflow job for this annotation

GitHub Actions / Cross checking x86_64-apple-darwin

unused import: `itertools::Itertools`

Check warning on line 7 in crates/core/src/repository/command_input.rs

View workflow job for this annotation

GitHub Actions / Cross checking aarch64-apple-darwin

unused import: `itertools::Itertools`

Check warning on line 7 in crates/core/src/repository/command_input.rs

View workflow job for this annotation

GitHub Actions / Cross checking x86_64-unknown-linux-gnu

unused import: `itertools::Itertools`

Check warning on line 7 in crates/core/src/repository/command_input.rs

View workflow job for this annotation

GitHub Actions / Cross checking x86_64-unknown-linux-musl

unused import: `itertools::Itertools`

Check warning on line 7 in crates/core/src/repository/command_input.rs

View workflow job for this annotation

GitHub Actions / Cross checking x86_64-pc-windows-msvc

unused import: `itertools::Itertools`

Check warning on line 7 in crates/core/src/repository/command_input.rs

View workflow job for this annotation

GitHub Actions / Cross checking x86_64-pc-windows-gnu

unused import: `itertools::Itertools`

Check warning on line 7 in crates/core/src/repository/command_input.rs

View workflow job for this annotation

GitHub Actions / Cross checking x86_64-apple-darwin

unused import: `itertools::Itertools`

Check warning on line 7 in crates/core/src/repository/command_input.rs

View workflow job for this annotation

GitHub Actions / Cross checking aarch64-apple-darwin

unused import: `itertools::Itertools`

Check warning on line 7 in crates/core/src/repository/command_input.rs

View workflow job for this annotation

GitHub Actions / Cross checking x86_64-unknown-linux-gnu

unused import: `itertools::Itertools`

Check warning on line 7 in crates/core/src/repository/command_input.rs

View workflow job for this annotation

GitHub Actions / Cross checking x86_64-unknown-linux-musl

unused import: `itertools::Itertools`
use log::{debug, error, trace, warn};
use serde::{Deserialize, Serialize, Serializer};
use serde_with::{serde_as, DisplayFromStr, PickFirst};

use crate::{
error::{RepositoryErrorKind, RusticErrorKind},
RusticError, RusticResult,
};
use crate::{error::RepositoryErrorKind, RusticError, RusticResult};

#[cfg(not(windows))]
use crate::error::RusticErrorKind;

/// A command to be called which can be given as CLI option as well as in config files
/// `CommandInput` implements Serialize/Deserialize as well as FromStr.
Expand Down Expand Up @@ -220,7 +221,42 @@
}

/// helper to split arguments
// TODO: Maybe use special parser (winsplit?) for windows?
#[cfg(not(windows))]
fn split(s: &str) -> RusticResult<Vec<String>> {
Ok(shell_words::split(s).map_err(|err| RusticErrorKind::Command(err.into()))?)
}

/// helper to split arguments
///
// We keep the return type as `RusticResult<Vec<String>>` to keep the internal api
// consistent with the other platforms.
#[allow(clippy::unnecessary_wraps)]
#[cfg(windows)]
fn split(s: &str) -> RusticResult<Vec<String>> {
Copy link
Member

@aawsome aawsome Oct 4, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

with this, this is a change and not only a PR about tests;-)

I experimented shortly on winsplit. One drawback I found was that it seems to be impossible to have the reverse operation of split - this makes a suitable Display impl quite hard.

Also I just wanted to note that a OS-dependent parsing may cause irritation for users using both windows and non-windows. I don't use windows, but are there cases where shellwords::split don't work on windows commands?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I'm playing around with it to get a feeling for the parsing. It's true, that we need to be attentive here.

Ok(winsplit::split(s))
}

#[cfg(test)]
mod tests {
use super::*;
use rstest::rstest;

#[rstest]
#[case("echo hello", "echo", &["hello"])]
#[case("sh -c 'echo bla >> out'", "sh", &["-c", "echo", "bla", ">>", "out"])]
#[case("echo hello >> /tmp/hello", "echo", &["hello", ">>", "/tmp/hello"])]
#[case("test -f /tmp/hello --key 'some value' arg1 arg2", "test", &["-f", "/tmp/hello", "--key", "some value", "arg1", "arg2"])]
#[cfg(windows)]
#[case("C:\\ProgramFiles\\Example\\example.exe --key 'some value' arg1 arg2", "C:\\ProgramFiles\\Example\\example.exe", &["--key", "some value", "arg1", "arg2"])]
fn test_command_input_parsing_passes(
#[case] input: &str,
#[case] command: &str,
#[case] args: &[&str],
) {
let cmd = CommandInput::from_str(input).unwrap();
assert_eq!(command, cmd.command());
assert_eq!(args, cmd.args());
assert_eq!(OnFailure::default(), cmd.on_failure());
assert_eq!(input, cmd.to_string());
}
}
Loading