1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
//! Plugin Interface
//!
//! A composite processor for all godwit-compatible plugins.
//! Must follow a unified standard to keep minimal deviation.
use crate::errors::{IOError, PluginError};
use crate::settings;
use getter_derive_rs::Getter;
use serde::{Deserialize, Serialize};
use std::{
	error::Error,
	process::{Command, Output, Stdio},
};

/// Plugin structure
#[derive(Debug, Deserialize, Serialize, Getter, Clone)]
#[serde(rename_all = "snake_case")]
pub struct Plugin {
	name: String,
	exec: String,
}

impl Default for Plugin {
	fn default() -> Self {
		Plugin {
			name: Default::default(),
			exec: Default::default(),
		}
	}
}

/// Call a detached external process for a plugin.
pub fn invoke(plugin_str: &str, args_options: Option<Vec<&str>>) -> Result<Output, Box<dyn Error>> {
	let command_str = get_plugin(plugin_str)?.get_exec();
	let mut args = args_options.unwrap_or_default();

	let command = str_to_command!(command_str, &mut args);

	let output = Command::new(command).args(args).output()?;

	Ok(output)
}

/// Bind stdio to spawned child process and pipe io to it.
pub fn bind(plugin_str: &str, args_options: Option<Vec<&str>>) -> Result<Vec<u8>, Box<dyn Error>> {
	let command_str = get_plugin(plugin_str)?.get_exec();
	let mut args = args_options.unwrap_or_default();

	let command = str_to_command!(command_str, &mut args);

	let child = Command::new(command)
		.args(args)
		.stdin(Stdio::piped())
		.stderr(Stdio::piped())
		.stdout(Stdio::piped())
		.spawn()?;

	let output = child.wait_with_output()?;

	if output.status.success() {
		Ok(output.stdout)
	} else {
		Err(IOError::StdErr {
			err: String::from_utf8(output.stderr)?,
		}
		.into())
	}
}

/// Retrieve plugin from queried name.
pub fn get_plugin(q_plugin: &str) -> Result<Plugin, Box<dyn Error>> {
	let plugins = settings::get_settings()?.get_plugins();
	plugins
		.into_iter()
		.find(|plugin| plugin.name == q_plugin)
		.map_or(
			Err(PluginError::PluginNotFound {
				plugin: q_plugin.into(),
			}
			.into()),
			|plugin| Ok(plugin),
		)
}

/// Create new plugin.
pub fn new(name: &str, exec: &str) -> Plugin {
	Plugin {
		name: name.to_string(),
		exec: exec.to_string(),
	}
}