1use std::path::{Path, PathBuf};
4
5use fallow_config::{ExternalPluginDef, PackageJson};
6
7pub mod registry {
8 #[derive(Debug, Clone, PartialEq, Eq)]
10 pub struct PluginRegexValidationError {
11 pub(super) inner: fallow_core::plugins::registry::PluginRegexValidationError,
12 }
13
14 impl From<fallow_core::plugins::registry::PluginRegexValidationError>
15 for PluginRegexValidationError
16 {
17 fn from(inner: fallow_core::plugins::registry::PluginRegexValidationError) -> Self {
18 Self { inner }
19 }
20 }
21
22 #[must_use]
24 pub fn builtin_plugin_names() -> Vec<&'static str> {
25 fallow_core::plugins::registry::builtin_plugin_names()
26 }
27
28 #[must_use]
30 pub fn format_plugin_regex_errors(errors: &[PluginRegexValidationError]) -> String {
31 let core_errors = errors
32 .iter()
33 .map(|error| error.inner.clone())
34 .collect::<Vec<_>>();
35 fallow_core::plugins::registry::format_plugin_regex_errors(&core_errors)
36 }
37}
38
39#[derive(Debug, Clone, Default)]
41pub struct AggregatedPluginResult {
42 inner: fallow_core::plugins::AggregatedPluginResult,
43}
44
45impl AggregatedPluginResult {
46 pub(crate) const fn as_core(&self) -> &fallow_core::plugins::AggregatedPluginResult {
47 &self.inner
48 }
49
50 #[must_use]
52 pub fn active_plugins(&self) -> &[String] {
53 &self.inner.active_plugins
54 }
55
56 pub fn merge_active_plugins_from(&mut self, other: &Self) {
58 for plugin_name in &other.inner.active_plugins {
59 if !self.inner.active_plugins.contains(plugin_name) {
60 self.inner.active_plugins.push(plugin_name.clone());
61 }
62 }
63 }
64}
65
66impl From<fallow_core::plugins::AggregatedPluginResult> for AggregatedPluginResult {
67 fn from(inner: fallow_core::plugins::AggregatedPluginResult) -> Self {
68 Self { inner }
69 }
70}
71
72pub struct PluginRegistry {
74 inner: fallow_core::plugins::PluginRegistry,
75}
76
77impl PluginRegistry {
78 #[must_use]
80 pub fn new(external: Vec<ExternalPluginDef>) -> Self {
81 Self {
82 inner: fallow_core::plugins::PluginRegistry::new(external),
83 }
84 }
85
86 #[must_use]
88 pub fn discovery_hidden_dirs(&self, pkg: &PackageJson, root: &Path) -> Vec<String> {
89 self.inner.discovery_hidden_dirs(pkg, root)
90 }
91
92 pub fn try_run(
94 &self,
95 pkg: &PackageJson,
96 root: &Path,
97 discovered_files: &[PathBuf],
98 ) -> Result<AggregatedPluginResult, Vec<registry::PluginRegexValidationError>> {
99 self.inner
100 .try_run(pkg, root, discovered_files)
101 .map(Into::into)
102 .map_err(|errors| errors.into_iter().map(Into::into).collect())
103 }
104}
105
106impl Default for PluginRegistry {
107 fn default() -> Self {
108 Self::new(vec![])
109 }
110}
111
112#[cfg(test)]
113mod tests {
114 use std::path::PathBuf;
115
116 use super::{AggregatedPluginResult, PluginRegistry};
117
118 #[test]
119 fn plugin_registry_try_run_returns_engine_result() {
120 let registry = PluginRegistry::default();
121 let result = registry
122 .try_run(
123 &fallow_config::PackageJson::default(),
124 &PathBuf::from("/repo"),
125 &[],
126 )
127 .expect("empty package should not produce regex errors");
128
129 assert!(result.active_plugins().is_empty());
130 }
131
132 #[test]
133 fn aggregated_plugin_result_merges_active_plugins() {
134 let mut base = AggregatedPluginResult::default();
135 base.inner.active_plugins.push("nextjs".into());
136 let mut incoming = AggregatedPluginResult::default();
137 incoming.inner.active_plugins.push("nextjs".into());
138 incoming.inner.active_plugins.push("vitest".into());
139
140 base.merge_active_plugins_from(&incoming);
141
142 assert_eq!(base.active_plugins(), ["nextjs", "vitest"]);
143 }
144}