Skip to main content

gravityfile_plugin/
config.rs

1//! Plugin configuration and metadata.
2
3use std::collections::HashSet;
4use std::path::PathBuf;
5
6use serde::{Deserialize, Serialize};
7
8use crate::types::PluginKind;
9
10/// Global plugin system configuration.
11#[derive(Debug, Clone)]
12pub struct PluginConfig {
13    /// Directory where plugins are stored.
14    pub plugin_dir: PathBuf,
15
16    /// Directory for plugin configuration files.
17    pub config_dir: PathBuf,
18
19    /// Whether to enable the plugin system.
20    pub enabled: bool,
21
22    /// Default timeout for plugin operations in milliseconds.
23    pub default_timeout_ms: u64,
24
25    /// Maximum memory per plugin in MB (0 = unlimited).
26    pub max_memory_mb: usize,
27
28    /// Whether to allow plugins to access the network.
29    pub allow_network: bool,
30
31    /// List of disabled plugin names.
32    pub disabled_plugins: HashSet<String>,
33}
34
35impl Default for PluginConfig {
36    fn default() -> Self {
37        let config_dir = dirs::config_dir()
38            .unwrap_or_else(|| PathBuf::from("."))
39            .join("gravityfile");
40
41        Self {
42            plugin_dir: config_dir.join("plugins"),
43            config_dir,
44            enabled: true,
45            default_timeout_ms: 5000,
46            max_memory_mb: 256,
47            allow_network: false,
48            disabled_plugins: HashSet::new(),
49        }
50    }
51}
52
53impl PluginConfig {
54    /// Create a new config with a custom plugin directory.
55    pub fn with_plugin_dir(mut self, dir: PathBuf) -> Self {
56        self.plugin_dir = dir;
57        self
58    }
59
60    /// Set the default timeout.
61    pub fn with_timeout(mut self, timeout_ms: u64) -> Self {
62        self.default_timeout_ms = timeout_ms;
63        self
64    }
65
66    /// Disable a specific plugin.
67    pub fn disable_plugin(mut self, name: impl Into<String>) -> Self {
68        self.disabled_plugins.insert(name.into());
69        self
70    }
71
72    /// Check if a plugin is disabled.
73    pub fn is_disabled(&self, name: &str) -> bool {
74        self.disabled_plugins.contains(name)
75    }
76}
77
78/// Metadata about a plugin from its plugin.toml file.
79#[derive(Debug, Clone, Serialize, Deserialize)]
80pub struct PluginMetadata {
81    /// Plugin name (unique identifier).
82    pub name: String,
83
84    /// Plugin version.
85    #[serde(default = "default_version")]
86    pub version: String,
87
88    /// Human-readable description.
89    #[serde(default)]
90    pub description: String,
91
92    /// Plugin author.
93    #[serde(default)]
94    pub author: String,
95
96    /// Plugin homepage/repository URL.
97    #[serde(default)]
98    pub url: String,
99
100    /// Runtime to use (lua, rhai, wasm).
101    #[serde(default = "default_runtime")]
102    pub runtime: String,
103
104    /// Plugin kind/category.
105    #[serde(default)]
106    pub kind: PluginKind,
107
108    /// Entry point file (relative to plugin directory).
109    #[serde(default = "default_entry")]
110    pub entry: String,
111
112    /// Minimum gravityfile version required.
113    #[serde(default)]
114    pub min_version: Option<String>,
115
116    /// Hooks this plugin wants to receive.
117    #[serde(default)]
118    pub hooks: PluginHooks,
119
120    /// Permissions requested by this plugin.
121    #[serde(default)]
122    pub permissions: PluginPermissions,
123
124    /// Plugin dependencies (other plugin names).
125    #[serde(default)]
126    pub dependencies: Vec<String>,
127}
128
129fn default_version() -> String {
130    "0.1.0".to_string()
131}
132
133fn default_runtime() -> String {
134    "lua".to_string()
135}
136
137fn default_entry() -> String {
138    "main.lua".to_string()
139}
140
141impl Default for PluginMetadata {
142    fn default() -> Self {
143        Self {
144            name: String::new(),
145            version: default_version(),
146            description: String::new(),
147            author: String::new(),
148            url: String::new(),
149            runtime: default_runtime(),
150            kind: PluginKind::Hook,
151            entry: default_entry(),
152            min_version: None,
153            hooks: PluginHooks::default(),
154            permissions: PluginPermissions::default(),
155            dependencies: vec![],
156        }
157    }
158}
159
160/// Hooks that a plugin wants to receive.
161#[derive(Debug, Clone, Default, Serialize, Deserialize)]
162pub struct PluginHooks {
163    /// Navigation events.
164    #[serde(default)]
165    pub on_navigate: bool,
166    #[serde(default)]
167    pub on_drill_down: bool,
168    #[serde(default)]
169    pub on_back: bool,
170
171    /// Scan events.
172    #[serde(default)]
173    pub on_scan_start: bool,
174    #[serde(default)]
175    pub on_scan_progress: bool,
176    #[serde(default)]
177    pub on_scan_complete: bool,
178
179    /// File operation events.
180    #[serde(default)]
181    pub on_delete_start: bool,
182    #[serde(default)]
183    pub on_delete_complete: bool,
184    #[serde(default)]
185    pub on_copy_start: bool,
186    #[serde(default)]
187    pub on_copy_complete: bool,
188    #[serde(default)]
189    pub on_move_start: bool,
190    #[serde(default)]
191    pub on_move_complete: bool,
192
193    /// UI events.
194    #[serde(default)]
195    pub on_render: bool,
196    #[serde(default)]
197    pub on_action: bool,
198    #[serde(default)]
199    pub on_mode_change: bool,
200
201    /// Lifecycle events.
202    #[serde(default)]
203    pub on_startup: bool,
204    #[serde(default)]
205    pub on_shutdown: bool,
206}
207
208/// Permissions requested by a plugin.
209#[derive(Debug, Clone, Default, Serialize, Deserialize)]
210pub struct PluginPermissions {
211    /// Filesystem access level.
212    #[serde(default)]
213    pub filesystem: FilesystemPermission,
214
215    /// Network access.
216    #[serde(default)]
217    pub network: bool,
218
219    /// Allowed external commands.
220    #[serde(default)]
221    pub commands: Vec<String>,
222
223    /// Environment variable access.
224    #[serde(default)]
225    pub env: bool,
226}
227
228/// Filesystem permission levels.
229#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
230#[serde(rename_all = "lowercase")]
231pub enum FilesystemPermission {
232    /// No filesystem access.
233    #[default]
234    None,
235
236    /// Read-only access to scan root.
237    Read,
238
239    /// Read/write access to scan root.
240    Write,
241
242    /// Full filesystem access (dangerous).
243    Full,
244}
245
246impl PluginPermissions {
247    /// Check if plugin can read files.
248    pub fn can_read(&self) -> bool {
249        !matches!(self.filesystem, FilesystemPermission::None)
250    }
251
252    /// Check if plugin can write files.
253    pub fn can_write(&self) -> bool {
254        matches!(
255            self.filesystem,
256            FilesystemPermission::Write | FilesystemPermission::Full
257        )
258    }
259
260    /// Check if plugin can run a command.
261    pub fn can_run_command(&self, cmd: &str) -> bool {
262        self.commands.iter().any(|c| c == cmd || c == "*")
263    }
264}