use crate::plugins::ProgramWriter; use anyhow::{Context, Result, anyhow}; use log::*; use std::env; use std::fs; use std::io; use std::io::Write; use std::os::unix::fs::PermissionsExt; use std::process::{Command, Stdio}; use crate::meta_plugin::MetaPlugin; #[derive(Clone, Debug)] pub struct MetaPluginProgram { pub program: String, pub args: Vec, pub supported: bool, pub meta_name: String, pub is_default: bool, } impl MetaPluginProgram { pub fn new(program: &str, args: Vec<&str>, meta_name: String, is_default: bool) -> MetaPluginProgram { let program_path = get_program_path(program); let supported = program_path.is_ok(); MetaPluginProgram { program: program_path.unwrap_or(program.to_string()), args: args.iter().map(|s| s.to_string()).collect(), supported, meta_name, is_default, } } } impl MetaPlugin for MetaPluginProgram { fn is_supported(&self) -> bool { self.supported } fn create(&self) -> Result> { debug!("META: Writing using {:?}", *self); let program = self.program.clone(); let args = self.args.clone(); debug!("META: Executing command: {:?} {:?}", program, args); let mut process = Command::new(program.clone()) .args(args.clone()) .stdin(Stdio::piped()) .stdout(Stdio::piped()) .spawn() .context(anyhow!( "Problem spawning child process: {:?} {:?}", program, args ))?; Ok(Box::new(ProgramWriter { stdin: process.stdin.take().unwrap(), })) } fn finalize(&mut self) -> io::Result { let program = self.program.clone(); let args = self.args.clone(); debug!("META: Executing command for finalize: {:?} {:?}", program, args); let output = Command::new(program) .args(args) .stdin(Stdio::null()) .stdout(Stdio::piped()) .stderr(Stdio::piped()) .output()?; if output.status.success() { let stdout = String::from_utf8_lossy(&output.stdout); Ok(stdout.trim().to_string()) } else { let stderr = String::from_utf8_lossy(&output.stderr); Err(io::Error::new( io::ErrorKind::Other, format!("Command failed: {}", stderr.trim()), )) } } fn update(&mut self, _data: &[u8]) { // This is handled by the ProgramWriter implementation } fn meta_name(&mut self) -> String { self.meta_name.clone() } fn is_default(&self) -> bool { self.is_default } } fn get_program_path(program: &str) -> Result { debug!("META: Looking for executable: {}", program); if let Ok(path) = env::var("PATH") { for p in path.split(':') { let p_str = format!("{}/{}", p, program); let stat = fs::metadata(p_str.clone()); if let Ok(stat) = stat { let md = stat; let permissions = md.permissions(); if md.is_file() && permissions.mode() & 0o111 != 0 { return Ok(p_str); } } } } Err(anyhow!("Unable to find binary {} in PATH", program)) }