devrc_plugins/
execution.rs1use std::ffi::OsStr;
2
3use devrc_core::{logging::LogLevel, workshop::Designer};
4use libloading::{Library, Symbol};
5
6use crate::{
7 config::{Config, ExecutionConfig},
8 errors::{DevrcPluginError, DevrcPluginResult},
9 plugin::Plugin,
10};
11
12pub trait ExecutionPlugin: Plugin {
13 fn execute(
14 &self,
15 execution_config: ExecutionConfig,
16 code: &str,
17 environment: &indexmap::IndexMap<String, String>,
18 ) -> DevrcPluginResult<i32>;
19}
20
21#[macro_export]
29macro_rules! declare_execution_plugin {
30 ($plugin_type:ty, $constructor:path) => {
31 #[no_mangle]
32 pub extern "C" fn _plugin_create() -> *mut $crate::ExecutionPlugin {
33 let constructor: fn() -> $plugin_type = $constructor;
35
36 let object = constructor();
37 let boxed: Box<$crate::ExecutionPlugin> = Box::new(object);
38 Box::into_raw(boxed)
39 }
40 };
41}
42
43#[derive(Default)]
44pub struct ExecutionPluginManager {
45 plugins: Vec<(String, Box<dyn ExecutionPlugin>)>,
46 loaded_libraries: Vec<Library>,
47 designer: Designer,
48 logger: LogLevel,
49}
50
51impl std::fmt::Debug for ExecutionPluginManager {
52 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
53 f.debug_struct("PluginManager").finish()
54 }
55}
56
57impl ExecutionPluginManager {
58 pub fn new() -> ExecutionPluginManager {
59 ExecutionPluginManager {
60 plugins: Vec::new(),
61 loaded_libraries: Vec::new(),
62 designer: Designer::default(),
63 logger: LogLevel::default(),
64 }
65 }
66
67 pub fn setup_logger(&mut self, logger: LogLevel) {
68 self.logger = logger;
69 }
70
71 pub unsafe fn load_plugin<P: AsRef<OsStr>>(
75 &mut self,
76 name: &str,
77 filename: P,
78 logger: LogLevel,
79 ) -> DevrcPluginResult<()> {
80 type PluginCreate = unsafe fn() -> *mut dyn ExecutionPlugin;
81
82 let lib = Library::new(filename.as_ref())?;
83
84 self.loaded_libraries.push(lib);
88
89 let lib = self.loaded_libraries.last().unwrap();
90
91 let constructor: Symbol<PluginCreate> = lib.get(b"_plugin_create")?;
92 let boxed_raw = constructor();
93
94 let mut plugin = Box::from_raw(boxed_raw);
95
96 logger.debug(
97 &format!(
98 "\n==> Loading PLUGIN: `{}` as `{}` from `{:?}` ...",
99 plugin.name(),
100 name,
101 filename.as_ref()
102 ),
103 &self.designer.banner(),
104 );
105
106 plugin.on_plugin_load(Config {
107 logger,
108 designer: self.designer,
109 });
110 self.plugins.push((name.to_string(), plugin));
111 Ok(())
112 }
113
114 pub fn unload(&mut self) {
117 self.logger
118 .debug("\n==> Unloading PLUGINS ...", &self.designer.banner());
119
120 for (name, plugin) in self.plugins.drain(..) {
121 self.logger.debug(
122 &format!("\n==> Upload PLUGIN: `{}` named `{}` ", plugin.name(), name),
123 &self.designer.banner(),
124 );
125 plugin.on_plugin_unload();
126 }
127
128 for lib in self.loaded_libraries.drain(..) {
129 drop(lib);
130 }
131 }
132
133 pub fn get_plugin(
134 &mut self,
135 plugin_name: &str,
136 ) -> DevrcPluginResult<&Box<dyn ExecutionPlugin>> {
137 for (name, plugin) in &self.plugins {
138 if name == plugin_name {
139 return Ok(plugin);
140 }
141 }
142
143 Err(DevrcPluginError::NotFound(plugin_name.to_string()))
144 }
145}
146
147impl Drop for ExecutionPluginManager {
148 fn drop(&mut self) {
149 if !self.plugins.is_empty() || !self.loaded_libraries.is_empty() {
150 self.unload();
151 }
152 }
153}