use std::fs::create_dir_all;
use std::{process::Command, str::FromStr};
use log::{debug, error};
use regex::Regex;
use serde::Deserialize;
use crate::filepath::FilePath;
use crate::tempfile::tempdir;
pub fn substitute(string: &str, path: &FilePath) -> String {
string
.replace("{full}", &path.full())
.replace("{dir}", &path.dir())
.replace("{name}", &path.name())
.replace("{ext}", &path.ext())
.replace("{\\{", "{{")
}
#[derive(Debug, Clone, Deserialize)]
pub struct Filter {
command: String,
outfile: Option<String>,
}
impl Filter {
pub fn new(command: String, outfile: Option<String>) -> Self {
Self { command, outfile }
}
pub fn tempdir(&self, path: &FilePath) -> Result<FilePath, String> {
match FilePath::from_str(&substitute(self.outfile.as_ref().unwrap(), path)) {
Ok(new) => Ok(tempdir(&self.command, &new)),
Err(e) => Err(format!(
"Filter outfile {} invalid: {e}",
self.outfile.as_ref().unwrap()
)),
}
}
pub fn has_outfile(&self) -> bool {
self.outfile.is_some()
}
pub fn exec(&self, path: &FilePath) -> bool {
let out = if self.has_outfile() {
let out = match self.tempdir(path) {
Ok(new) => new,
Err(e) => {
error!("{}", e);
return false;
}
};
if let Err(e) = create_dir_all(out.dir()) {
error!(
"Failed to create tempfile directory structure for filter: {}",
e
);
return false;
}
Some(out)
} else {
None
};
let re = Regex::new("([^\"]+\"|[^\\s]+)").unwrap();
let quotes = Regex::new("\"(.+)\"").unwrap();
let subbed_command = substitute(&self.command, path).replace(
"{outfile}",
&out.map(|f| f.full())
.unwrap_or_else(|| String::from("null")),
);
let mut args = re.captures_iter(&subbed_command);
let mut command = match args.next() {
Some(c) => c,
None => {
error!("`{}` is not a valid command", self.command);
return false;
}
}[0]
.to_string();
command = quotes.replace_all(&command, "$1").to_string();
debug!("Running filter `{}`", subbed_command);
match Command::new(command)
.args(
args.map(|s| quotes.replace_all(&s[0], "$1").to_string())
.collect::<Vec<_>>(),
)
.spawn()
{
Ok(mut child) => match child.wait() {
Ok(code) => {
if !code.success() {
error!(
"Filter `{}` exited with non-zero code: {}",
subbed_command,
code.code().unwrap_or(1)
);
return false;
}
}
Err(e) => {
error!("Filter `{}` failed with error: {}", subbed_command, e);
return false;
}
},
Err(e) => {
error!("Filter `{}` failed with error: {}", subbed_command, e);
return false;
}
}
debug!("Filter `{}` exited successfully", subbed_command);
true
}
}