tirbofish/dropbear
main / crates / eucalyptus-editor / src / plugin.rs · 4597 bytes
crates/eucalyptus-editor/src/plugin.rs
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
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
use crate::editor::EditorTabViewer;
use app_dirs2::AppDataType;
use egui::Ui;
use eucalyptus_core::APP_INFO;
use eucalyptus_core::states::PluginInfo;
use indexmap::IndexMap;
use libloading as lib;
use std::fs::ReadDir;
use std::path::PathBuf;
use std::time::Instant;
// this is for another time its just a blabber of code
pub trait EditorPlugin: Send + Sync {
fn id(&self) -> &str;
fn display_name(&self) -> &str;
fn ui(&mut self, ui: &mut Ui, viewer: &mut EditorTabViewer<'_>);
fn tab_title(&self) -> &str;
}
pub type PluginConstructor = fn() -> Box<dyn EditorPlugin>;
pub struct PluginRegistry {
pub plugins: IndexMap<String, Box<dyn EditorPlugin>>,
loaded_libraries: Vec<lib::Library>,
pub(crate) plugins_loaded: bool,
}
impl Default for PluginRegistry {
fn default() -> Self {
Self::new()
}
}
impl PluginRegistry {
pub fn new() -> Self {
Self {
plugins: IndexMap::new(),
loaded_libraries: Vec::new(),
plugins_loaded: false,
}
}
pub fn register(&mut self, plugin: Box<dyn EditorPlugin>) {
let id = plugin.id().to_string();
self.plugins.insert(id, plugin);
}
#[allow(clippy::borrowed_box)]
pub fn get(&self, id: &str) -> Option<&Box<dyn EditorPlugin>> {
self.plugins.get(id)
}
pub fn get_mut(&mut self, id: &str) -> Option<&mut Box<dyn EditorPlugin>> {
self.plugins.get_mut(id)
}
pub fn list_plugins(&self) -> Vec<PluginInfo> {
self.plugins
.values()
.map(|p| PluginInfo {
display_name: p.display_name().to_string(),
})
.collect()
}
pub fn load_plugins(&mut self) -> anyhow::Result<()> {
let appdir: PathBuf = app_dirs2::app_root(AppDataType::UserData, &APP_INFO)?;
let plugins_folder = appdir.join("plugins");
std::fs::create_dir_all(&plugins_folder)?;
let contents: ReadDir = std::fs::read_dir(&plugins_folder)?;
log::info!("Loading plugins from {}", plugins_folder.display());
let mut index: i32 = -1;
for (i, entry) in contents.enumerate() {
match entry {
Ok(e) => {
index = i as i32;
let now = Instant::now();
let path = e.path();
if let Some(ext) = path.extension() {
let ext_str = ext.to_str().unwrap_or("");
if !self.is_valid_extension_for_platform(ext_str) {
log::warn!(
"Skipping plugin {} - incompatible extension for this platform",
path.display()
);
continue;
}
match self.load_plugin_from_file(&path) {
Ok(plugin) => {
log::info!("Successfully loaded plugin: {}", path.display());
log::debug!(
"Plugin {} loaded in {:?}",
path.display(),
now.elapsed()
);
self.register(plugin);
}
Err(e) => {
log::error!("Failed to load plugin {}: {}", path.display(), e);
continue;
}
}
}
}
Err(err) => {
log::warn!("Failed to read directory entry: {}", err);
continue;
}
}
}
if index == -1 {
log::info!("No plugins found");
}
self.plugins_loaded = true;
Ok(())
}
fn is_valid_extension_for_platform(&self, ext: &str) -> bool {
match ext {
"dll" => cfg!(windows),
"so" => cfg!(unix) && !cfg!(target_os = "macos"),
"dylib" => cfg!(target_os = "macos"),
_ => false,
}
}
fn load_plugin_from_file(&mut self, path: &PathBuf) -> anyhow::Result<Box<dyn EditorPlugin>> {
let library = unsafe { lib::Library::new(path)? };
let constructor: lib::Symbol<PluginConstructor> = unsafe { library.get(b"create_plugin")? };
let plugin = constructor();
self.loaded_libraries.push(library);
Ok(plugin)
}
}