just_engine/runner/plugin/
config.rs1use std::path::Path;
4use std::fs;
5
6use super::registry::PluginError;
7
8#[derive(Debug, Clone)]
10pub struct NativePluginConfig {
11 pub path: String,
13 pub enabled: bool,
15}
16
17#[derive(Debug, Clone)]
19pub struct JsPluginConfig {
20 pub path: String,
22 pub enabled: bool,
24}
25
26#[derive(Debug, Clone)]
28pub struct OverrideConfig {
29 pub plugin: String,
31 pub method: String,
33}
34
35#[derive(Debug, Clone)]
37pub struct PluginConfig {
38 pub native_plugins: Vec<NativePluginConfig>,
40 pub js_plugins: Vec<JsPluginConfig>,
42 pub overrides: std::collections::HashMap<String, OverrideConfig>,
44}
45
46impl PluginConfig {
47 pub fn new() -> Self {
49 PluginConfig {
50 native_plugins: Vec::new(),
51 js_plugins: Vec::new(),
52 overrides: std::collections::HashMap::new(),
53 }
54 }
55
56 pub fn load(path: &Path) -> Result<Self, PluginError> {
72 let content = fs::read_to_string(path)
73 .map_err(|e| PluginError::ConfigError(format!("Failed to read config file: {}", e)))?;
74
75 Self::parse(&content)
76 }
77
78 pub fn parse(content: &str) -> Result<Self, PluginError> {
81 let mut config = PluginConfig::new();
82
83 let mut current_section = String::new();
86 let mut in_native_array = false;
87 let mut in_js_array = false;
88
89 for line in content.lines() {
90 let line = line.trim();
91
92 if line.is_empty() || line.starts_with('#') {
94 continue;
95 }
96
97 if line.starts_with('[') && line.ends_with(']') {
99 current_section = line[1..line.len() - 1].to_string();
100 in_native_array = false;
101 in_js_array = false;
102 continue;
103 }
104
105 if current_section == "plugins" {
107 if line.starts_with("native") {
108 in_native_array = line.contains('[');
109 in_js_array = false;
110 } else if line.starts_with("javascript") {
111 in_js_array = line.contains('[');
112 in_native_array = false;
113 } else if line == "]" {
114 in_native_array = false;
115 in_js_array = false;
116 } else if in_native_array || in_js_array {
117 if let Some(entry) = Self::parse_plugin_entry(line) {
119 if in_native_array {
120 config.native_plugins.push(NativePluginConfig {
121 path: entry.0,
122 enabled: entry.1,
123 });
124 } else {
125 config.js_plugins.push(JsPluginConfig {
126 path: entry.0,
127 enabled: entry.1,
128 });
129 }
130 }
131 }
132 }
133
134 if current_section == "overrides" {
136 if let Some((key, override_config)) = Self::parse_override_entry(line) {
137 config.overrides.insert(key, override_config);
138 }
139 }
140 }
141
142 Ok(config)
143 }
144
145 fn parse_plugin_entry(line: &str) -> Option<(String, bool)> {
147 let line = line.trim().trim_start_matches('{').trim_end_matches('}').trim_end_matches(',');
148
149 let mut path = None;
150 let mut enabled = true;
151
152 for part in line.split(',') {
153 let part = part.trim();
154 if part.starts_with("path") {
155 if let Some(value) = part.split('=').nth(1) {
156 path = Some(
157 value
158 .trim()
159 .trim_matches('"')
160 .to_string(),
161 );
162 }
163 } else if part.starts_with("enabled") {
164 if let Some(value) = part.split('=').nth(1) {
165 enabled = value.trim() == "true";
166 }
167 }
168 }
169
170 path.map(|p| (p, enabled))
171 }
172
173 fn parse_override_entry(line: &str) -> Option<(String, OverrideConfig)> {
175 let parts: Vec<&str> = line.splitn(2, '=').collect();
176 if parts.len() != 2 {
177 return None;
178 }
179
180 let key = parts[0].trim().trim_matches('"').to_string();
181 let value = parts[1].trim().trim_start_matches('{').trim_end_matches('}');
182
183 let mut plugin = None;
184 let mut method = None;
185
186 for part in value.split(',') {
187 let part = part.trim();
188 if part.starts_with("plugin") {
189 if let Some(v) = part.split('=').nth(1) {
190 plugin = Some(v.trim().trim_matches('"').to_string());
191 }
192 } else if part.starts_with("method") {
193 if let Some(v) = part.split('=').nth(1) {
194 method = Some(v.trim().trim_matches('"').to_string());
195 }
196 }
197 }
198
199 if let (Some(plugin), Some(method)) = (plugin, method) {
200 Some((key, OverrideConfig { plugin, method }))
201 } else {
202 None
203 }
204 }
205}
206
207impl Default for PluginConfig {
208 fn default() -> Self {
209 Self::new()
210 }
211}
212
213#[cfg(test)]
214mod tests {
215 use super::*;
216
217 #[test]
218 fn test_parse_empty_config() {
219 let config = PluginConfig::parse("").unwrap();
220 assert!(config.native_plugins.is_empty());
221 assert!(config.js_plugins.is_empty());
222 assert!(config.overrides.is_empty());
223 }
224
225 #[test]
226 fn test_parse_plugin_entry() {
227 let entry = PluginConfig::parse_plugin_entry(
228 r#"{ path = "./plugins/lib.so", enabled = true }"#
229 );
230 assert!(entry.is_some());
231 let (path, enabled) = entry.unwrap();
232 assert_eq!(path, "./plugins/lib.so");
233 assert!(enabled);
234 }
235}