tailwind_rs_postcss/
plugin_loader.rs1use crate::ast::CSSNode;
6use crate::error::Result;
7use serde::{Deserialize, Serialize};
8
9#[derive(Debug, Clone, Serialize, Deserialize)]
11pub struct PluginConfig {
12 pub name: String,
13 pub version: Option<String>,
14 pub options: serde_json::Value,
15}
16
17impl PluginConfig {
18 pub fn requires_js(&self) -> bool {
20 match self.name.as_str() {
22 "autoprefixer" | "cssnano" | "postcss-preset-env" | "postcss-import" => true,
23 _ => {
24 self.name.contains('/') || self.name.starts_with('@')
26 }
27 }
28 }
29}
30
31#[derive(Debug)]
33pub struct PluginLoader;
34
35#[derive(Debug)]
37pub enum PluginResult {
38 Native(NativePlugin),
39 JavaScript(JSPlugin),
40}
41
42#[derive(Debug)]
44pub struct NativePlugin;
45
46impl NativePlugin {
47 pub fn transform(&self, ast: CSSNode) -> Result<CSSNode> {
48 Ok(ast)
49 }
50}
51
52#[derive(Debug)]
54pub struct JSPlugin {
55 pub name: String,
56}
57
58impl PluginLoader {
59 pub fn new() -> Self {
61 Self
62 }
63
64 pub async fn load_plugin(&self, config: &PluginConfig) -> Result<PluginResult> {
66 if config.requires_js() {
67 Ok(PluginResult::JavaScript(JSPlugin {
69 name: config.name.clone(),
70 }))
71 } else {
72 Ok(PluginResult::Native(NativePlugin))
74 }
75 }
76
77 pub async fn load_plugins(&self, configs: &[PluginConfig]) -> Result<Vec<PluginResult>> {
79 let mut results = Vec::new();
80
81 for config in configs {
82 let result = self.load_plugin(config).await?;
83 results.push(result);
84 }
85
86 Ok(results)
87 }
88
89 pub fn validate_config(&self, config: &PluginConfig) -> Result<()> {
91 if config.name.is_empty() {
92 return Err(crate::error::PostCSSError::config(
93 "Plugin name cannot be empty",
94 ));
95 }
96
97 if !self.is_valid_plugin_name(&config.name) {
99 return Err(crate::error::PostCSSError::config(&format!(
100 "Invalid plugin name: {}",
101 config.name
102 )));
103 }
104
105 Ok(())
106 }
107
108 fn is_valid_plugin_name(&self, name: &str) -> bool {
110 !name.is_empty()
112 && name.len() <= 100
113 && name
114 .chars()
115 .all(|c| c.is_alphanumeric() || c == '-' || c == '_' || c == '/' || c == '@')
116 }
117}
118
119#[cfg(test)]
120mod tests {
121 use super::*;
122
123 #[test]
124 fn test_plugin_loader_creation() {
125 let _loader = PluginLoader::new();
126 assert!(true); }
128}