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