fd/src/exec/mod.rs

178 lines
5.5 KiB
Rust
Raw Normal View History

2017-10-21 10:16:03 +02:00
// Copyright (c) 2017 fd developers
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0>
// or the MIT license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.
2017-10-14 18:04:11 +02:00
// TODO: Possible optimization could avoid pushing characters on a buffer.
mod command;
2017-10-14 22:42:47 +02:00
mod token;
2017-10-14 18:04:11 +02:00
mod job;
2017-10-20 21:54:43 +02:00
mod input;
2017-10-14 18:04:11 +02:00
2017-11-03 01:39:03 +01:00
use std::borrow::Cow;
2017-10-14 18:04:11 +02:00
use std::path::Path;
use std::process::Command;
2017-10-14 23:59:36 +02:00
use std::sync::{Arc, Mutex};
2017-10-14 18:04:11 +02:00
2017-11-03 01:39:03 +01:00
use regex::Regex;
use self::input::{basename, dirname, remove_extension};
use self::command::execute_command;
2017-10-14 22:42:47 +02:00
use self::token::Token;
2017-10-14 18:04:11 +02:00
pub use self::job::job;
/// Represents a template that is utilized to generate command strings.
2017-10-14 18:04:11 +02:00
///
/// 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.
2017-10-14 18:04:11 +02:00
#[derive(Debug, Clone, PartialEq)]
pub struct CommandTemplate {
args: Vec<ArgumentTemplate>,
2017-10-14 18:04:11 +02:00
}
impl CommandTemplate {
pub fn new<I, S>(input: I) -> CommandTemplate
2017-11-03 01:39:03 +01:00
where
I: IntoIterator<Item = S>,
S: AsRef<str>,
{
lazy_static! {
static ref PLACEHOLDER_PATTERN: Regex = Regex::new(r"\{(/?\.?|//)\}").unwrap();
2017-11-03 01:39:03 +01:00
}
let mut args = Vec::new();
let mut has_placeholder = false;
for arg in input {
let arg = arg.as_ref();
let mut tokens = Vec::new();
let mut start = 0;
for placeholder in PLACEHOLDER_PATTERN.find_iter(arg) {
2017-11-03 01:39:03 +01:00
// Leading text before the placeholder.
if placeholder.start() > start {
tokens.push(Token::Text(arg[start..placeholder.start()].to_owned()));
2017-10-14 20:04:04 +02:00
}
2017-11-03 01:39:03 +01:00
start = placeholder.end();
match placeholder.as_str() {
"{}" => tokens.push(Token::Placeholder),
"{.}" => tokens.push(Token::NoExt),
"{/}" => tokens.push(Token::Basename),
"{//}" => tokens.push(Token::Parent),
"{/.}" => tokens.push(Token::BasenameNoExt),
_ => panic!("Unhandled placeholder"),
2017-10-14 20:04:04 +02:00
}
2017-11-03 01:39:03 +01:00
has_placeholder = true;
}
// Without a placeholder, the argument is just fixed text.
if tokens.is_empty() {
args.push(ArgumentTemplate::Text(arg.to_owned()));
2017-11-03 01:39:03 +01:00
continue;
}
if start < arg.len() {
// Trailing text after last placeholder.
tokens.push(Token::Text(arg[start..].to_owned()));
2017-10-14 18:04:11 +02:00
}
args.push(ArgumentTemplate::Tokens(tokens));
2017-10-14 20:04:04 +02:00
}
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 {
args.push(ArgumentTemplate::Tokens(vec![Token::Placeholder]));
2017-10-14 18:04:11 +02:00
}
CommandTemplate { args: args }
2017-10-14 18:04:11 +02:00
}
/// Generates and executes a command.
2017-10-14 18:04:11 +02:00
///
/// Using the internal `args` field, and a supplied `input` variable, a `Command` will be
/// build. Once all arguments have been processed, the command is executed.
pub fn generate_and_execute(&self, input: &Path, out_perm: Arc<Mutex<()>>) {
2017-11-03 01:39:03 +01:00
let input = input
.strip_prefix(".")
.unwrap_or(input)
.to_string_lossy()
.into_owned();
let mut cmd = Command::new(self.args[0].generate(&input).as_ref());
for arg in &self.args[1..] {
cmd.arg(arg.generate(&input).as_ref());
2017-10-14 18:04:11 +02:00
}
execute_command(cmd, out_perm)
2017-10-14 18:04:11 +02:00
}
}
/// Represents a template for a single command argument.
///
/// The argument is either a collection of `Token`s including at least one placeholder variant, or
/// a fixed text.
#[derive(Clone, Debug, PartialEq)]
enum ArgumentTemplate {
Tokens(Vec<Token>),
Text(String),
}
impl ArgumentTemplate {
pub fn generate<'a>(&'a self, path: &str) -> Cow<'a, str> {
use self::Token::*;
match *self {
ArgumentTemplate::Tokens(ref tokens) => {
let mut s = String::new();
for token in tokens {
match *token {
Basename => s += basename(path),
BasenameNoExt => s += remove_extension(basename(path)),
NoExt => s += remove_extension(path),
Parent => s += dirname(path),
Placeholder => s += path,
Text(ref string) => s += string,
}
}
Cow::Owned(s)
}
ArgumentTemplate::Text(ref text) => Cow::Borrowed(text),
}
}
}
2017-10-14 18:04:11 +02:00
#[cfg(test)]
mod tests {
2018-01-01 12:16:43 +01:00
use super::{ArgumentTemplate, CommandTemplate, Token};
2017-10-14 18:04:11 +02:00
#[test]
fn tokens() {
let expected = CommandTemplate {
2017-11-03 01:39:03 +01:00
args: vec![
ArgumentTemplate::Text("echo".into()),
ArgumentTemplate::Text("${SHELL}:".into()),
ArgumentTemplate::Tokens(vec![Token::Placeholder]),
2017-11-03 01:39:03 +01:00
],
2017-10-14 18:04:11 +02:00
};
assert_eq!(CommandTemplate::new(&[&"echo", &"${SHELL}:"]), expected);
2017-11-03 01:39:03 +01:00
2017-10-14 20:04:04 +02:00
assert_eq!(
CommandTemplate::new(&["echo", "{.}"]),
CommandTemplate {
2017-11-03 01:39:03 +01:00
args: vec![
ArgumentTemplate::Text("echo".into()),
ArgumentTemplate::Tokens(vec![Token::NoExt]),
2017-11-03 01:39:03 +01:00
],
}
2017-10-14 20:04:04 +02:00
);
2017-10-14 18:04:11 +02:00
}
2017-10-14 20:04:04 +02:00
}