2017-11-15 01:56:32 +01:00
|
|
|
mod command;
|
2018-04-13 22:46:17 +02:00
|
|
|
mod job;
|
2017-10-14 18:04:11 +02:00
|
|
|
|
2022-06-16 09:22:43 +02:00
|
|
|
use std::ffi::OsString;
|
2022-05-12 16:41:47 +02:00
|
|
|
use std::io;
|
|
|
|
use std::iter;
|
2022-06-16 09:22:43 +02:00
|
|
|
use std::path::{Path, PathBuf};
|
2022-05-12 16:41:47 +02:00
|
|
|
use std::process::Stdio;
|
2023-05-04 07:33:44 +02:00
|
|
|
use std::sync::Mutex;
|
2017-10-14 18:04:11 +02:00
|
|
|
|
2022-03-07 08:58:19 +01:00
|
|
|
use anyhow::{bail, Result};
|
2022-05-12 16:41:47 +02:00
|
|
|
use argmax::Command;
|
2017-11-03 01:39:03 +01:00
|
|
|
|
2022-10-13 23:33:54 +02:00
|
|
|
use crate::exit_codes::{merge_exitcodes, ExitCode};
|
2022-06-16 09:22:43 +02:00
|
|
|
use crate::fmt::{FormatTemplate, Token};
|
2019-09-13 22:26:27 +02:00
|
|
|
|
2022-02-28 08:39:52 +01:00
|
|
|
use self::command::{execute_commands, handle_cmd_error};
|
2018-11-11 18:00:01 +01:00
|
|
|
pub use self::job::{batch, job};
|
2021-10-17 05:52:04 +02:00
|
|
|
|
2018-11-11 18:00:01 +01:00
|
|
|
/// Execution mode of the command
|
2022-09-11 21:29:43 +02:00
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
2018-11-11 18:00:01 +01:00
|
|
|
pub enum ExecutionMode {
|
2018-11-12 18:43:40 +01:00
|
|
|
/// Command is executed for each search result
|
2018-11-11 18:00:01 +01:00
|
|
|
OneByOne,
|
|
|
|
/// Command is run for a batch of results at once
|
|
|
|
Batch,
|
|
|
|
}
|
|
|
|
|
2017-10-14 18:04:11 +02:00
|
|
|
#[derive(Debug, Clone, PartialEq)]
|
2022-02-18 09:21:00 +01:00
|
|
|
pub struct CommandSet {
|
2018-11-11 18:00:01 +01:00
|
|
|
mode: ExecutionMode,
|
2022-02-18 09:21:00 +01:00
|
|
|
commands: Vec<CommandTemplate>,
|
2017-10-14 18:04:11 +02:00
|
|
|
}
|
|
|
|
|
2022-02-18 09:21:00 +01:00
|
|
|
impl CommandSet {
|
2023-01-17 08:17:50 +01:00
|
|
|
pub fn new<I, T, S>(input: I) -> Result<CommandSet>
|
2018-11-11 18:00:01 +01:00
|
|
|
where
|
2023-01-17 08:17:50 +01:00
|
|
|
I: IntoIterator<Item = T>,
|
|
|
|
T: IntoIterator<Item = S>,
|
2018-11-11 18:00:01 +01:00
|
|
|
S: AsRef<str>,
|
|
|
|
{
|
2022-03-07 08:58:19 +01:00
|
|
|
Ok(CommandSet {
|
2022-02-18 09:21:00 +01:00
|
|
|
mode: ExecutionMode::OneByOne,
|
2022-03-07 08:58:19 +01:00
|
|
|
commands: input
|
|
|
|
.into_iter()
|
|
|
|
.map(CommandTemplate::new)
|
|
|
|
.collect::<Result<_>>()?,
|
|
|
|
})
|
2018-11-11 18:00:01 +01:00
|
|
|
}
|
|
|
|
|
2023-01-17 08:17:50 +01:00
|
|
|
pub fn new_batch<I, T, S>(input: I) -> Result<CommandSet>
|
2018-11-11 18:00:01 +01:00
|
|
|
where
|
2023-01-17 08:17:50 +01:00
|
|
|
I: IntoIterator<Item = T>,
|
|
|
|
T: IntoIterator<Item = S>,
|
2018-11-11 18:00:01 +01:00
|
|
|
S: AsRef<str>,
|
|
|
|
{
|
2022-02-18 09:21:00 +01:00
|
|
|
Ok(CommandSet {
|
|
|
|
mode: ExecutionMode::Batch,
|
|
|
|
commands: input
|
|
|
|
.into_iter()
|
|
|
|
.map(|args| {
|
2022-03-07 08:58:19 +01:00
|
|
|
let cmd = CommandTemplate::new(args)?;
|
2022-02-18 09:21:00 +01:00
|
|
|
if cmd.number_of_tokens() > 1 {
|
2022-03-07 08:58:19 +01:00
|
|
|
bail!("Only one placeholder allowed for batch commands");
|
2022-02-18 09:21:00 +01:00
|
|
|
}
|
|
|
|
if cmd.args[0].has_tokens() {
|
2022-03-07 08:58:19 +01:00
|
|
|
bail!("First argument of exec-batch is expected to be a fixed executable");
|
2022-02-18 09:21:00 +01:00
|
|
|
}
|
|
|
|
Ok(cmd)
|
|
|
|
})
|
|
|
|
.collect::<Result<Vec<_>>>()?,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn in_batch_mode(&self) -> bool {
|
|
|
|
self.mode == ExecutionMode::Batch
|
|
|
|
}
|
|
|
|
|
2022-06-17 10:08:24 +02:00
|
|
|
pub fn execute(
|
|
|
|
&self,
|
|
|
|
input: &Path,
|
|
|
|
path_separator: Option<&str>,
|
2023-01-19 07:24:05 +01:00
|
|
|
out_perm: &Mutex<()>,
|
2022-06-17 10:08:24 +02:00
|
|
|
buffer_output: bool,
|
|
|
|
) -> ExitCode {
|
2022-02-28 08:39:52 +01:00
|
|
|
let commands = self
|
|
|
|
.commands
|
|
|
|
.iter()
|
|
|
|
.map(|c| c.generate(input, path_separator));
|
2023-01-19 07:24:05 +01:00
|
|
|
execute_commands(commands, out_perm, buffer_output)
|
2022-02-18 09:21:00 +01:00
|
|
|
}
|
|
|
|
|
2022-06-17 10:08:24 +02:00
|
|
|
pub fn execute_batch<I>(&self, paths: I, limit: usize, path_separator: Option<&str>) -> ExitCode
|
2022-02-18 09:21:00 +01:00
|
|
|
where
|
|
|
|
I: Iterator<Item = PathBuf>,
|
|
|
|
{
|
2022-05-12 16:41:47 +02:00
|
|
|
let builders: io::Result<Vec<_>> = self
|
|
|
|
.commands
|
|
|
|
.iter()
|
|
|
|
.map(|c| CommandBuilder::new(c, limit))
|
|
|
|
.collect();
|
|
|
|
|
|
|
|
match builders {
|
|
|
|
Ok(mut builders) => {
|
|
|
|
for path in paths {
|
|
|
|
for builder in &mut builders {
|
|
|
|
if let Err(e) = builder.push(&path, path_separator) {
|
|
|
|
return handle_cmd_error(Some(&builder.cmd), e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for builder in &mut builders {
|
|
|
|
if let Err(e) = builder.finish() {
|
|
|
|
return handle_cmd_error(Some(&builder.cmd), e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-10-13 23:33:54 +02:00
|
|
|
merge_exitcodes(builders.iter().map(|b| b.exit_code()))
|
2022-02-18 09:21:00 +01:00
|
|
|
}
|
2022-05-12 16:41:47 +02:00
|
|
|
Err(e) => handle_cmd_error(None, e),
|
2018-11-12 18:43:40 +01:00
|
|
|
}
|
2022-05-12 16:41:47 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Represents a multi-exec command as it is built.
|
|
|
|
#[derive(Debug)]
|
|
|
|
struct CommandBuilder {
|
|
|
|
pre_args: Vec<OsString>,
|
2022-06-16 09:22:43 +02:00
|
|
|
path_arg: FormatTemplate,
|
2022-05-12 16:41:47 +02:00
|
|
|
post_args: Vec<OsString>,
|
|
|
|
cmd: Command,
|
|
|
|
count: usize,
|
|
|
|
limit: usize,
|
2022-10-13 23:33:54 +02:00
|
|
|
exit_code: ExitCode,
|
2022-05-12 16:41:47 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
impl CommandBuilder {
|
|
|
|
fn new(template: &CommandTemplate, limit: usize) -> io::Result<Self> {
|
|
|
|
let mut pre_args = vec![];
|
|
|
|
let mut path_arg = None;
|
|
|
|
let mut post_args = vec![];
|
|
|
|
|
|
|
|
for arg in &template.args {
|
|
|
|
if arg.has_tokens() {
|
|
|
|
path_arg = Some(arg.clone());
|
2022-11-08 09:09:06 +01:00
|
|
|
} else if path_arg.is_none() {
|
2022-05-12 16:41:47 +02:00
|
|
|
pre_args.push(arg.generate("", None));
|
|
|
|
} else {
|
|
|
|
post_args.push(arg.generate("", None));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
let cmd = Self::new_command(&pre_args)?;
|
|
|
|
|
|
|
|
Ok(Self {
|
|
|
|
pre_args,
|
|
|
|
path_arg: path_arg.unwrap(),
|
|
|
|
post_args,
|
|
|
|
cmd,
|
|
|
|
count: 0,
|
|
|
|
limit,
|
2022-10-13 23:33:54 +02:00
|
|
|
exit_code: ExitCode::Success,
|
2022-05-12 16:41:47 +02:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
fn new_command(pre_args: &[OsString]) -> io::Result<Command> {
|
|
|
|
let mut cmd = Command::new(&pre_args[0]);
|
|
|
|
cmd.stdin(Stdio::inherit());
|
|
|
|
cmd.stdout(Stdio::inherit());
|
|
|
|
cmd.stderr(Stdio::inherit());
|
|
|
|
cmd.try_args(&pre_args[1..])?;
|
|
|
|
Ok(cmd)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn push(&mut self, path: &Path, separator: Option<&str>) -> io::Result<()> {
|
|
|
|
if self.limit > 0 && self.count >= self.limit {
|
|
|
|
self.finish()?;
|
|
|
|
}
|
|
|
|
|
|
|
|
let arg = self.path_arg.generate(path, separator);
|
|
|
|
if !self
|
|
|
|
.cmd
|
|
|
|
.args_would_fit(iter::once(&arg).chain(&self.post_args))
|
|
|
|
{
|
|
|
|
self.finish()?;
|
|
|
|
}
|
|
|
|
|
|
|
|
self.cmd.try_arg(arg)?;
|
|
|
|
self.count += 1;
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn finish(&mut self) -> io::Result<()> {
|
|
|
|
if self.count > 0 {
|
|
|
|
self.cmd.try_args(&self.post_args)?;
|
2022-10-13 23:33:54 +02:00
|
|
|
if !self.cmd.status()?.success() {
|
|
|
|
self.exit_code = ExitCode::GeneralError;
|
|
|
|
}
|
2022-05-12 16:41:47 +02:00
|
|
|
|
|
|
|
self.cmd = Self::new_command(&self.pre_args)?;
|
|
|
|
self.count = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
2018-11-11 18:00:01 +01:00
|
|
|
}
|
2022-10-13 23:33:54 +02:00
|
|
|
|
|
|
|
fn exit_code(&self) -> ExitCode {
|
|
|
|
self.exit_code
|
|
|
|
}
|
2022-02-18 09:21:00 +01:00
|
|
|
}
|
2018-11-11 18:00:01 +01:00
|
|
|
|
2022-02-18 09:21:00 +01:00
|
|
|
/// Represents a template that is utilized to generate command strings.
|
|
|
|
///
|
|
|
|
/// The template is meant to be coupled with an input in order to generate a command. The
|
|
|
|
/// `generate_and_execute()` method will be used to generate a command and execute it.
|
|
|
|
#[derive(Debug, Clone, PartialEq)]
|
|
|
|
struct CommandTemplate {
|
2022-06-16 09:22:43 +02:00
|
|
|
args: Vec<FormatTemplate>,
|
2022-02-18 09:21:00 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
impl CommandTemplate {
|
2022-03-07 08:58:19 +01:00
|
|
|
fn new<I, S>(input: I) -> Result<CommandTemplate>
|
2017-11-03 01:39:03 +01:00
|
|
|
where
|
|
|
|
I: IntoIterator<Item = S>,
|
|
|
|
S: AsRef<str>,
|
|
|
|
{
|
|
|
|
let mut args = Vec::new();
|
|
|
|
let mut has_placeholder = false;
|
|
|
|
|
|
|
|
for arg in input {
|
|
|
|
let arg = arg.as_ref();
|
|
|
|
|
2022-06-16 09:22:43 +02:00
|
|
|
let tmpl = FormatTemplate::parse(arg);
|
2023-05-04 07:33:44 +02:00
|
|
|
has_placeholder |= tmpl.has_tokens();
|
|
|
|
args.push(tmpl);
|
2017-10-14 20:04:04 +02:00
|
|
|
}
|
2017-10-14 18:04:11 +02:00
|
|
|
|
2022-03-07 08:58:19 +01:00
|
|
|
// We need to check that we have at least one argument, because if not
|
|
|
|
// it will try to execute each file and directory it finds.
|
|
|
|
//
|
|
|
|
// Sadly, clap can't currently handle this for us, see
|
|
|
|
// https://github.com/clap-rs/clap/issues/3542
|
|
|
|
if args.is_empty() {
|
|
|
|
bail!("No executable provided for --exec or --exec-batch");
|
|
|
|
}
|
|
|
|
|
2017-10-14 18:04:11 +02:00
|
|
|
// If a placeholder token was not supplied, append one at the end of the command.
|
2017-11-03 01:39:03 +01:00
|
|
|
if !has_placeholder {
|
2022-06-16 09:22:43 +02:00
|
|
|
args.push(FormatTemplate::Tokens(vec![Token::Placeholder]));
|
2017-10-14 18:04:11 +02:00
|
|
|
}
|
|
|
|
|
2022-03-07 08:58:19 +01:00
|
|
|
Ok(CommandTemplate { args })
|
2018-11-11 18:00:01 +01:00
|
|
|
}
|
|
|
|
|
2018-11-12 18:43:40 +01:00
|
|
|
fn number_of_tokens(&self) -> usize {
|
2018-11-11 18:00:01 +01:00
|
|
|
self.args.iter().filter(|arg| arg.has_tokens()).count()
|
|
|
|
}
|
|
|
|
|
2017-11-15 01:56:32 +01:00
|
|
|
/// Generates and executes a command.
|
2017-10-14 18:04:11 +02:00
|
|
|
///
|
2017-11-15 01:56:32 +01:00
|
|
|
/// Using the internal `args` field, and a supplied `input` variable, a `Command` will be
|
2022-02-28 08:39:52 +01:00
|
|
|
/// build.
|
2022-05-12 16:41:47 +02:00
|
|
|
fn generate(&self, input: &Path, path_separator: Option<&str>) -> io::Result<Command> {
|
2022-11-08 09:09:06 +01:00
|
|
|
let mut cmd = Command::new(self.args[0].generate(input, path_separator));
|
2017-11-15 01:56:32 +01:00
|
|
|
for arg in &self.args[1..] {
|
2022-11-08 09:09:06 +01:00
|
|
|
cmd.try_arg(arg.generate(input, path_separator))?;
|
2018-11-11 18:00:01 +01:00
|
|
|
}
|
2022-05-12 16:41:47 +02:00
|
|
|
Ok(cmd)
|
2017-10-14 18:04:11 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
2018-10-03 10:36:15 +02:00
|
|
|
use super::*;
|
2017-10-14 18:04:11 +02:00
|
|
|
|
2023-05-04 07:33:44 +02:00
|
|
|
fn generate_str(template: &CommandTemplate, input: &str) -> Vec<String> {
|
|
|
|
template
|
|
|
|
.args
|
|
|
|
.iter()
|
|
|
|
.map(|arg| arg.generate(input, None).into_string().unwrap())
|
|
|
|
.collect()
|
|
|
|
}
|
|
|
|
|
2017-10-14 18:04:11 +02:00
|
|
|
#[test]
|
2018-10-03 10:36:15 +02:00
|
|
|
fn tokens_with_placeholder() {
|
|
|
|
assert_eq!(
|
2022-06-17 10:08:24 +02:00
|
|
|
CommandSet::new(vec![vec![&"echo", &"${SHELL}:"]]).unwrap(),
|
2022-02-18 09:21:00 +01:00
|
|
|
CommandSet {
|
|
|
|
commands: vec![CommandTemplate {
|
|
|
|
args: vec![
|
2022-06-16 09:22:43 +02:00
|
|
|
FormatTemplate::Text("echo".into()),
|
|
|
|
FormatTemplate::Text("${SHELL}:".into()),
|
|
|
|
FormatTemplate::Tokens(vec![Token::Placeholder]),
|
2022-02-18 09:21:00 +01:00
|
|
|
]
|
|
|
|
}],
|
2018-11-11 18:00:01 +01:00
|
|
|
mode: ExecutionMode::OneByOne,
|
2018-10-03 10:36:15 +02:00
|
|
|
}
|
|
|
|
);
|
|
|
|
}
|
2017-11-03 01:39:03 +01:00
|
|
|
|
2018-10-03 10:36:15 +02:00
|
|
|
#[test]
|
|
|
|
fn tokens_with_no_extension() {
|
2017-10-14 20:04:04 +02:00
|
|
|
assert_eq!(
|
2022-06-17 10:08:24 +02:00
|
|
|
CommandSet::new(vec![vec!["echo", "{.}"]]).unwrap(),
|
2022-02-18 09:21:00 +01:00
|
|
|
CommandSet {
|
|
|
|
commands: vec![CommandTemplate {
|
|
|
|
args: vec![
|
2022-06-16 09:22:43 +02:00
|
|
|
FormatTemplate::Text("echo".into()),
|
|
|
|
FormatTemplate::Tokens(vec![Token::NoExt]),
|
2022-02-18 09:21:00 +01:00
|
|
|
],
|
|
|
|
}],
|
2018-11-11 18:00:01 +01:00
|
|
|
mode: ExecutionMode::OneByOne,
|
2017-11-03 01:39:03 +01:00
|
|
|
}
|
2017-10-14 20:04:04 +02:00
|
|
|
);
|
2017-10-14 18:04:11 +02:00
|
|
|
}
|
2018-10-03 10:36:15 +02:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn tokens_with_basename() {
|
|
|
|
assert_eq!(
|
2022-06-17 10:08:24 +02:00
|
|
|
CommandSet::new(vec![vec!["echo", "{/}"]]).unwrap(),
|
2022-02-18 09:21:00 +01:00
|
|
|
CommandSet {
|
|
|
|
commands: vec![CommandTemplate {
|
|
|
|
args: vec![
|
2022-06-16 09:22:43 +02:00
|
|
|
FormatTemplate::Text("echo".into()),
|
|
|
|
FormatTemplate::Tokens(vec![Token::Basename]),
|
2022-02-18 09:21:00 +01:00
|
|
|
],
|
|
|
|
}],
|
2018-11-11 18:00:01 +01:00
|
|
|
mode: ExecutionMode::OneByOne,
|
2018-10-03 10:36:15 +02:00
|
|
|
}
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn tokens_with_parent() {
|
|
|
|
assert_eq!(
|
2022-06-17 10:08:24 +02:00
|
|
|
CommandSet::new(vec![vec!["echo", "{//}"]]).unwrap(),
|
2022-02-18 09:21:00 +01:00
|
|
|
CommandSet {
|
|
|
|
commands: vec![CommandTemplate {
|
|
|
|
args: vec![
|
2022-06-16 09:22:43 +02:00
|
|
|
FormatTemplate::Text("echo".into()),
|
|
|
|
FormatTemplate::Tokens(vec![Token::Parent]),
|
2022-02-18 09:21:00 +01:00
|
|
|
],
|
|
|
|
}],
|
2018-11-11 18:00:01 +01:00
|
|
|
mode: ExecutionMode::OneByOne,
|
2018-10-03 10:36:15 +02:00
|
|
|
}
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn tokens_with_basename_no_extension() {
|
|
|
|
assert_eq!(
|
2022-06-17 10:08:24 +02:00
|
|
|
CommandSet::new(vec![vec!["echo", "{/.}"]]).unwrap(),
|
2022-02-18 09:21:00 +01:00
|
|
|
CommandSet {
|
|
|
|
commands: vec![CommandTemplate {
|
|
|
|
args: vec![
|
2022-06-16 09:22:43 +02:00
|
|
|
FormatTemplate::Text("echo".into()),
|
|
|
|
FormatTemplate::Tokens(vec![Token::BasenameNoExt]),
|
2022-02-18 09:21:00 +01:00
|
|
|
],
|
|
|
|
}],
|
2018-11-11 18:00:01 +01:00
|
|
|
mode: ExecutionMode::OneByOne,
|
2018-10-03 10:36:15 +02:00
|
|
|
}
|
|
|
|
);
|
|
|
|
}
|
2018-11-09 21:35:01 +01:00
|
|
|
|
2023-05-04 07:33:44 +02:00
|
|
|
#[test]
|
|
|
|
fn tokens_with_literal_braces() {
|
|
|
|
let template = CommandTemplate::new(vec!["{{}}", "{{", "{.}}"]).unwrap();
|
|
|
|
assert_eq!(
|
|
|
|
generate_str(&template, "foo"),
|
|
|
|
vec!["{}", "{", "{.}", "foo"]
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn tokens_with_literal_braces_and_placeholder() {
|
|
|
|
let template = CommandTemplate::new(vec!["{{{},end}"]).unwrap();
|
|
|
|
assert_eq!(generate_str(&template, "foo"), vec!["{foo,end}"]);
|
|
|
|
}
|
|
|
|
|
2018-11-09 21:35:01 +01:00
|
|
|
#[test]
|
|
|
|
fn tokens_multiple() {
|
|
|
|
assert_eq!(
|
2022-06-17 10:08:24 +02:00
|
|
|
CommandSet::new(vec![vec!["cp", "{}", "{/.}.ext"]]).unwrap(),
|
2022-02-18 09:21:00 +01:00
|
|
|
CommandSet {
|
|
|
|
commands: vec![CommandTemplate {
|
|
|
|
args: vec![
|
2022-06-16 09:22:43 +02:00
|
|
|
FormatTemplate::Text("cp".into()),
|
|
|
|
FormatTemplate::Tokens(vec![Token::Placeholder]),
|
|
|
|
FormatTemplate::Tokens(vec![
|
2022-02-18 09:21:00 +01:00
|
|
|
Token::BasenameNoExt,
|
|
|
|
Token::Text(".ext".into())
|
|
|
|
]),
|
|
|
|
],
|
|
|
|
}],
|
2018-11-11 18:00:01 +01:00
|
|
|
mode: ExecutionMode::OneByOne,
|
2018-11-09 21:35:01 +01:00
|
|
|
}
|
|
|
|
);
|
|
|
|
}
|
2018-11-11 18:00:01 +01:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn tokens_single_batch() {
|
|
|
|
assert_eq!(
|
2022-06-17 10:08:24 +02:00
|
|
|
CommandSet::new_batch(vec![vec!["echo", "{.}"]]).unwrap(),
|
2022-02-18 09:21:00 +01:00
|
|
|
CommandSet {
|
|
|
|
commands: vec![CommandTemplate {
|
2022-02-18 09:27:40 +01:00
|
|
|
args: vec![
|
2022-06-16 09:22:43 +02:00
|
|
|
FormatTemplate::Text("echo".into()),
|
|
|
|
FormatTemplate::Tokens(vec![Token::NoExt]),
|
2022-02-18 09:27:40 +01:00
|
|
|
],
|
2022-02-18 09:21:00 +01:00
|
|
|
}],
|
2018-11-11 18:00:01 +01:00
|
|
|
mode: ExecutionMode::Batch,
|
|
|
|
}
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn tokens_multiple_batch() {
|
2022-06-17 10:08:24 +02:00
|
|
|
assert!(CommandSet::new_batch(vec![vec!["echo", "{.}", "{}"]]).is_err());
|
2021-01-03 20:17:52 +01:00
|
|
|
}
|
|
|
|
|
2022-03-07 08:58:19 +01:00
|
|
|
#[test]
|
|
|
|
fn template_no_args() {
|
|
|
|
assert!(CommandTemplate::new::<Vec<_>, &'static str>(vec![]).is_err());
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn command_set_no_args() {
|
2022-06-17 10:08:24 +02:00
|
|
|
assert!(CommandSet::new(vec![vec!["echo"], vec![]]).is_err());
|
2022-03-07 08:58:19 +01:00
|
|
|
}
|
|
|
|
|
2021-01-03 20:17:52 +01:00
|
|
|
#[test]
|
|
|
|
fn generate_custom_path_separator() {
|
2022-06-16 09:22:43 +02:00
|
|
|
let arg = FormatTemplate::Tokens(vec![Token::Placeholder]);
|
2021-01-03 20:17:52 +01:00
|
|
|
macro_rules! check {
|
|
|
|
($input:expr, $expected:expr) => {
|
|
|
|
assert_eq!(arg.generate($input, Some("#")), OsString::from($expected));
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
check!("foo", "foo");
|
|
|
|
check!("foo/bar", "foo#bar");
|
|
|
|
check!("/foo/bar/baz", "#foo#bar#baz");
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(windows)]
|
|
|
|
#[test]
|
|
|
|
fn generate_custom_path_separator_windows() {
|
2022-06-16 09:22:43 +02:00
|
|
|
let arg = FormatTemplate::Tokens(vec![Token::Placeholder]);
|
2021-01-03 20:17:52 +01:00
|
|
|
macro_rules! check {
|
|
|
|
($input:expr, $expected:expr) => {
|
|
|
|
assert_eq!(arg.generate($input, Some("#")), OsString::from($expected));
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
// path starting with a drive letter
|
|
|
|
check!(r"C:\foo\bar", "C:#foo#bar");
|
|
|
|
// UNC path
|
|
|
|
check!(r"\\server\share\path", "##server#share#path");
|
|
|
|
// Drive Relative path - no separator after the colon omits the RootDir path component.
|
|
|
|
// This is uncommon, but valid
|
|
|
|
check!(r"C:foo\bar", "C:foo#bar");
|
|
|
|
|
2021-08-14 11:46:53 +02:00
|
|
|
// forward slashes should get normalized and interpreted as separators
|
2021-01-03 20:17:52 +01:00
|
|
|
check!("C:/foo/bar", "C:#foo#bar");
|
|
|
|
check!("C:foo/bar", "C:foo#bar");
|
|
|
|
|
2021-08-14 11:46:53 +02:00
|
|
|
// Rust does not interpret "//server/share" as a UNC path, but rather as a normal
|
2021-01-03 20:17:52 +01:00
|
|
|
// absolute path that begins with RootDir, and the two slashes get combined together as
|
|
|
|
// a single path separator during normalization.
|
|
|
|
//check!("//server/share/path", "##server#share#path");
|
2018-11-11 18:00:01 +01:00
|
|
|
}
|
2017-10-14 20:04:04 +02:00
|
|
|
}
|