rustledger_plugin/native/
mod.rs1mod plugins;
7
8pub use plugins::*;
9
10use crate::types::PluginInput;
11use crate::types::PluginOutput;
12
13pub trait NativePlugin: Send + Sync {
15 fn name(&self) -> &'static str;
17
18 fn description(&self) -> &'static str;
20
21 fn process(&self, input: PluginInput) -> PluginOutput;
23}
24
25pub struct NativePluginRegistry {
27 plugins: Vec<Box<dyn NativePlugin>>,
28}
29
30fn plugin_short_name(name: &str) -> &str {
37 name.rsplit('.').next().unwrap_or(name)
38}
39
40impl NativePluginRegistry {
41 pub fn new() -> Self {
43 Self {
44 plugins: vec![
45 Box::new(ImplicitPricesPlugin),
46 Box::new(CheckCommodityPlugin),
47 Box::new(AutoTagPlugin::new()),
48 Box::new(AutoAccountsPlugin),
49 Box::new(LeafOnlyPlugin),
50 Box::new(NoDuplicatesPlugin),
51 Box::new(OneCommodityPlugin),
52 Box::new(UniquePricesPlugin),
53 Box::new(CheckClosingPlugin),
54 Box::new(CloseTreePlugin),
55 Box::new(CoherentCostPlugin),
56 Box::new(ForecastPlugin),
57 Box::new(SellGainsPlugin),
58 Box::new(PedanticPlugin),
59 Box::new(RxTxnPlugin),
60 Box::new(SplitExpensesPlugin),
61 Box::new(UnrealizedPlugin::new()),
62 Box::new(NoUnusedPlugin),
63 Box::new(CheckDrainedPlugin),
64 Box::new(CommodityAttrPlugin::new()),
65 Box::new(CheckAverageCostPlugin::new()),
66 Box::new(CurrencyAccountsPlugin::new()),
67 Box::new(ZerosumPlugin),
68 Box::new(EffectiveDatePlugin),
69 Box::new(GenerateBaseCcyPricesPlugin),
70 Box::new(RenameAccountsPlugin),
71 Box::new(ValuationPlugin),
72 Box::new(CapitalGainsLongShortPlugin),
73 Box::new(CapitalGainsGainLossPlugin),
74 Box::new(BoxAccrualPlugin),
75 ],
76 }
77 }
78
79 pub fn find(&self, name: &str) -> Option<&dyn NativePlugin> {
84 let short_name = plugin_short_name(name);
85 self.plugins
86 .iter()
87 .find(|p| p.name() == short_name)
88 .map(std::convert::AsRef::as_ref)
89 }
90
91 pub fn list(&self) -> Vec<&dyn NativePlugin> {
93 self.plugins.iter().map(AsRef::as_ref).collect()
94 }
95
96 pub fn is_builtin(name: &str) -> bool {
100 let short_name = plugin_short_name(name);
101 let registry = Self::new();
103 registry.plugins.iter().any(|p| p.name() == short_name)
104 }
105}
106
107impl Default for NativePluginRegistry {
108 fn default() -> Self {
109 Self::new()
110 }
111}
112
113#[cfg(test)]
114mod tests {
115 use super::*;
116
117 #[test]
118 fn test_plugin_short_name_bare() {
119 assert_eq!(plugin_short_name("zerosum"), "zerosum");
120 assert_eq!(plugin_short_name("implicit_prices"), "implicit_prices");
121 }
122
123 #[test]
124 fn test_plugin_short_name_beancount_plugins() {
125 assert_eq!(
126 plugin_short_name("beancount.plugins.implicit_prices"),
127 "implicit_prices"
128 );
129 assert_eq!(
130 plugin_short_name("beancount.plugins.check_commodity"),
131 "check_commodity"
132 );
133 }
134
135 #[test]
136 fn test_plugin_short_name_beanahead() {
137 assert_eq!(
138 plugin_short_name("beanahead.plugins.rx_txn_plugin"),
139 "rx_txn_plugin"
140 );
141 }
142
143 #[test]
144 fn test_plugin_short_name_reds_plugins() {
145 assert_eq!(
146 plugin_short_name("beancount_reds_plugins.zerosum.zerosum"),
147 "zerosum"
148 );
149 assert_eq!(
150 plugin_short_name("beancount_reds_plugins.capital_gains_classifier.gain_loss"),
151 "gain_loss"
152 );
153 assert_eq!(
154 plugin_short_name("beancount_reds_plugins.effective_date.effective_date"),
155 "effective_date"
156 );
157 }
158
159 #[test]
160 fn test_plugin_short_name_tarioch() {
161 assert_eq!(
162 plugin_short_name("tariochbctools.plugins.generate_base_ccy_prices"),
163 "generate_base_ccy_prices"
164 );
165 }
166
167 #[test]
168 fn test_plugin_short_name_empty() {
169 assert_eq!(plugin_short_name(""), "");
170 }
171
172 #[test]
173 fn test_registry_find_short_name() {
174 let registry = NativePluginRegistry::new();
175 assert!(registry.find("implicit_prices").is_some());
176 assert!(registry.find("zerosum").is_some());
177 assert!(registry.find("nonexistent").is_none());
178 }
179
180 #[test]
181 fn test_registry_find_qualified_name() {
182 let registry = NativePluginRegistry::new();
183 assert!(registry.find("beancount.plugins.implicit_prices").is_some());
184 assert!(registry.find("beanahead.plugins.rx_txn_plugin").is_some());
185 assert!(
186 registry
187 .find("beancount_reds_plugins.zerosum.zerosum")
188 .is_some()
189 );
190 assert!(
191 registry
192 .find("beancount_reds_plugins.capital_gains_classifier.gain_loss")
193 .is_some()
194 );
195 }
196
197 #[test]
198 fn test_is_builtin_short_name() {
199 assert!(NativePluginRegistry::is_builtin("implicit_prices"));
200 assert!(NativePluginRegistry::is_builtin("zerosum"));
201 assert!(!NativePluginRegistry::is_builtin("nonexistent"));
202 }
203
204 #[test]
205 fn test_is_builtin_qualified_name() {
206 assert!(NativePluginRegistry::is_builtin(
207 "beancount.plugins.implicit_prices"
208 ));
209 assert!(NativePluginRegistry::is_builtin(
210 "beanahead.plugins.rx_txn_plugin"
211 ));
212 assert!(NativePluginRegistry::is_builtin(
213 "beancount_reds_plugins.zerosum.zerosum"
214 ));
215 assert!(!NativePluginRegistry::is_builtin("some.random.nonexistent"));
216 }
217}