1#![warn(missing_docs)]
2
3use nargo_types::{NargoContext, NargoValue, Result};
4use serde::{Deserialize, Serialize};
5use std::{collections::HashMap, sync::Arc};
6
7#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
9pub enum PluginLifecycle {
10 Init,
12 PreParse,
14 Parse,
16 PostParse,
18 PreTransform,
20 Transform,
22 PostTransform,
24 PreBundle,
26 Bundle,
28 PostBundle,
30 Cleanup,
32}
33
34#[derive(Debug, Clone, Serialize, Deserialize)]
36pub struct PluginConfig {
37 pub name: String,
39 pub version: String,
41 pub description: String,
43 pub author: Option<String>,
45 pub homepage: Option<String>,
47 pub priority: i32,
49 pub config: HashMap<String, NargoValue>,
51 pub enabled: bool,
53}
54
55impl Default for PluginConfig {
56 fn default() -> Self {
57 Self { name: String::new(), version: "0.1.0".to_string(), description: String::new(), author: None, homepage: None, priority: 0, config: HashMap::new(), enabled: true }
58 }
59}
60
61pub trait Plugin: Send + Sync {
63 fn name(&self) -> &str;
65
66 fn version(&self) -> &str {
68 "0.1.0"
69 }
70
71 fn description(&self) -> &str {
73 ""
74 }
75
76 fn author(&self) -> Option<&str> {
78 None
79 }
80
81 fn homepage(&self) -> Option<&str> {
83 None
84 }
85
86 fn priority(&self) -> i32 {
88 0
89 }
90
91 fn on_init(&self, _ctx: Arc<NargoContext>, _config: &PluginConfig) -> Result<()> {
93 Ok(())
94 }
95
96 fn on_pre_parse(&self, _source: &str) -> Result<Option<String>> {
98 Ok(None)
99 }
100
101 fn on_parse(&self, _source: &str) -> Result<Option<String>> {
103 Ok(None)
104 }
105
106 fn on_post_parse(&self, _source: &str) -> Result<Option<String>> {
108 Ok(None)
109 }
110
111 fn on_pre_transform(&self, _code: &str) -> Result<Option<String>> {
113 Ok(None)
114 }
115
116 fn on_transform(&self, _code: &str) -> Result<Option<String>> {
118 Ok(None)
119 }
120
121 fn on_post_transform(&self, _code: &str) -> Result<Option<String>> {
123 Ok(None)
124 }
125
126 fn on_pre_bundle(&self, _bundle: &str) -> Result<Option<String>> {
128 Ok(None)
129 }
130
131 fn on_bundle(&self, _bundle: &str) -> Result<Option<String>> {
133 Ok(None)
134 }
135
136 fn on_post_bundle(&self, _bundle: &str) -> Result<Option<String>> {
138 Ok(None)
139 }
140
141 fn on_cleanup(&self) -> Result<()> {
143 Ok(())
144 }
145}
146
147pub struct JsPlugin {
149 name: String,
150 version: String,
151 description: String,
152}
153
154impl JsPlugin {
155 pub fn new(name: String, version: String, description: String, _source: &str) -> Result<Self> {
157 Ok(Self { name, version, description })
158 }
159}
160
161impl Plugin for JsPlugin {
162 fn name(&self) -> &str {
163 &self.name
164 }
165
166 fn version(&self) -> &str {
167 &self.version
168 }
169
170 fn description(&self) -> &str {
171 &self.description
172 }
173}
174
175pub struct PluginManager {
177 ctx: Arc<NargoContext>,
178 plugins: Vec<Box<dyn Plugin>>,
179 configs: HashMap<String, PluginConfig>,
180}
181
182impl PluginManager {
183 pub fn new(ctx: Arc<NargoContext>) -> Self {
185 Self { ctx, plugins: Vec::new(), configs: HashMap::new() }
186 }
187
188 pub fn register(&mut self, plugin: Box<dyn Plugin>) {
190 let config = PluginConfig { name: plugin.name().to_string(), version: plugin.version().to_string(), description: plugin.description().to_string(), author: plugin.author().map(|s| s.to_string()), homepage: plugin.homepage().map(|s| s.to_string()), priority: plugin.priority(), enabled: true, ..Default::default() };
191 self.register_with_config(plugin, config);
192 }
193
194 pub fn register_with_config(&mut self, plugin: Box<dyn Plugin>, config: PluginConfig) {
196 tracing::info!("Registering plugin: {} v{}", plugin.name(), plugin.version());
197 self.configs.insert(plugin.name().to_string(), config);
198 self.plugins.push(plugin);
199 self.sort_plugins();
200 }
201
202 fn sort_plugins(&mut self) {
204 self.plugins.sort_by(|a, b| {
205 let a_priority = a.priority();
206 let b_priority = b.priority();
207 a_priority.cmp(&b_priority)
208 });
209 }
210
211 pub fn init_all(&self) -> Result<()> {
213 for plugin in &self.plugins {
214 if let Some(config) = self.configs.get(plugin.name()) {
215 if config.enabled {
216 plugin.on_init(self.ctx.clone(), config)?;
217 }
218 }
219 }
220 Ok(())
221 }
222
223 pub async fn parse(&self, mut source: String) -> Result<String> {
225 for plugin in &self.plugins {
226 if let Some(config) = self.configs.get(plugin.name()) {
227 if !config.enabled {
228 continue;
229 }
230 }
231 if let Some(new_source) = plugin.on_pre_parse(&source)? {
232 source = new_source;
233 }
234 if let Some(new_source) = plugin.on_parse(&source)? {
235 source = new_source;
236 }
237 if let Some(new_source) = plugin.on_post_parse(&source)? {
238 source = new_source;
239 }
240 }
241 Ok(source)
242 }
243
244 pub async fn transform(&self, mut code: String) -> Result<String> {
246 for plugin in &self.plugins {
247 if let Some(config) = self.configs.get(plugin.name()) {
248 if !config.enabled {
249 continue;
250 }
251 }
252 if let Some(new_code) = plugin.on_pre_transform(&code)? {
253 code = new_code;
254 }
255 if let Some(new_code) = plugin.on_transform(&code)? {
256 code = new_code;
257 }
258 if let Some(new_code) = plugin.on_post_transform(&code)? {
259 code = new_code;
260 }
261 }
262 Ok(code)
263 }
264
265 pub async fn bundle(&self, mut bundle: String) -> Result<String> {
267 for plugin in &self.plugins {
268 if let Some(config) = self.configs.get(plugin.name()) {
269 if !config.enabled {
270 continue;
271 }
272 }
273 if let Some(new_bundle) = plugin.on_pre_bundle(&bundle)? {
274 bundle = new_bundle;
275 }
276 if let Some(new_bundle) = plugin.on_bundle(&bundle)? {
277 bundle = new_bundle;
278 }
279 if let Some(new_bundle) = plugin.on_post_bundle(&bundle)? {
280 bundle = new_bundle;
281 }
282 }
283 Ok(bundle)
284 }
285
286 pub fn cleanup_all(&self) -> Result<()> {
288 for plugin in &self.plugins {
289 plugin.on_cleanup()?;
290 }
291 Ok(())
292 }
293
294 pub fn plugins(&self) -> &[Box<dyn Plugin>] {
296 &self.plugins
297 }
298
299 pub fn get_plugin(&self, name: &str) -> Option<&Box<dyn Plugin>> {
301 self.plugins.iter().find(|p| p.name() == name)
302 }
303
304 pub fn get_config(&self, name: &str) -> Option<&PluginConfig> {
306 self.configs.get(name)
307 }
308
309 pub fn set_plugin_enabled(&mut self, name: &str, enabled: bool) -> Result<()> {
311 if let Some(config) = self.configs.get_mut(name) {
312 config.enabled = enabled;
313 Ok(())
314 }
315 else {
316 Err(nargo_types::Error::external_error("PluginManager".to_string(), format!("Plugin {} not found", name), nargo_types::Span::unknown()))
317 }
318 }
319
320 pub fn get_plugins_info(&self) -> Vec<(&str, &PluginConfig)> {
322 self.plugins.iter().filter_map(|p| self.configs.get(p.name()).map(|c| (p.name(), c))).collect()
323 }
324}