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 fn is_synth(&self) -> bool {
38 false
39 }
40}
41
42pub struct NativePluginRegistry {
44 plugins: Vec<Box<dyn NativePlugin>>,
45}
46
47#[inline]
54fn plugin_short_name(name: &str) -> &str {
55 name.rsplit('.').next().unwrap_or(name)
56}
57
58impl NativePluginRegistry {
59 pub fn new() -> Self {
61 Self {
62 plugins: vec![
63 Box::new(ImplicitPricesPlugin),
64 Box::new(CheckCommodityPlugin),
65 Box::new(AutoTagPlugin::new()),
66 Box::new(AutoAccountsPlugin),
67 Box::new(LeafOnlyPlugin),
68 Box::new(NoDuplicatesPlugin),
69 Box::new(OneCommodityPlugin),
70 Box::new(UniquePricesPlugin),
71 Box::new(CheckClosingPlugin),
72 Box::new(CloseTreePlugin),
73 Box::new(CoherentCostPlugin),
74 Box::new(ForecastPlugin),
75 Box::new(SellGainsPlugin),
76 Box::new(PedanticPlugin),
77 Box::new(RxTxnPlugin),
78 Box::new(SplitExpensesPlugin),
79 Box::new(UnrealizedPlugin::new()),
80 Box::new(NoUnusedPlugin),
81 Box::new(CheckDrainedPlugin),
82 Box::new(CommodityAttrPlugin::new()),
83 Box::new(CheckAverageCostPlugin::new()),
84 Box::new(CurrencyAccountsPlugin::new()),
85 Box::new(ZerosumPlugin),
86 Box::new(EffectiveDatePlugin),
87 Box::new(GenerateBaseCcyPricesPlugin),
88 Box::new(RenameAccountsPlugin),
89 Box::new(ValuationPlugin),
90 Box::new(CapitalGainsLongShortPlugin),
91 Box::new(CapitalGainsGainLossPlugin),
92 Box::new(BoxAccrualPlugin),
93 ],
94 }
95 }
96
97 pub fn find(&self, name: &str) -> Option<&dyn NativePlugin> {
102 let short_name = plugin_short_name(name);
103 self.plugins
104 .iter()
105 .find(|p| p.name() == short_name)
106 .map(std::convert::AsRef::as_ref)
107 }
108
109 pub fn list(&self) -> Vec<&dyn NativePlugin> {
111 self.plugins.iter().map(AsRef::as_ref).collect()
112 }
113
114 pub fn is_builtin(name: &str) -> bool {
118 let short_name = plugin_short_name(name);
119 let registry = Self::new();
121 registry.plugins.iter().any(|p| p.name() == short_name)
122 }
123}
124
125impl Default for NativePluginRegistry {
126 fn default() -> Self {
127 Self::new()
128 }
129}
130
131#[cfg(test)]
132mod tests {
133 use super::*;
134
135 #[test]
136 fn test_plugin_short_name_bare() {
137 assert_eq!(plugin_short_name("zerosum"), "zerosum");
138 assert_eq!(plugin_short_name("implicit_prices"), "implicit_prices");
139 }
140
141 #[test]
142 fn test_plugin_short_name_beancount_plugins() {
143 assert_eq!(
144 plugin_short_name("beancount.plugins.implicit_prices"),
145 "implicit_prices"
146 );
147 assert_eq!(
148 plugin_short_name("beancount.plugins.check_commodity"),
149 "check_commodity"
150 );
151 }
152
153 #[test]
154 fn test_plugin_short_name_beanahead() {
155 assert_eq!(
156 plugin_short_name("beanahead.plugins.rx_txn_plugin"),
157 "rx_txn_plugin"
158 );
159 }
160
161 #[test]
162 fn test_plugin_short_name_reds_plugins() {
163 assert_eq!(
164 plugin_short_name("beancount_reds_plugins.zerosum.zerosum"),
165 "zerosum"
166 );
167 assert_eq!(
168 plugin_short_name("beancount_reds_plugins.capital_gains_classifier.gain_loss"),
169 "gain_loss"
170 );
171 assert_eq!(
172 plugin_short_name("beancount_reds_plugins.effective_date.effective_date"),
173 "effective_date"
174 );
175 }
176
177 #[test]
178 fn test_plugin_short_name_tarioch() {
179 assert_eq!(
180 plugin_short_name("tariochbctools.plugins.generate_base_ccy_prices"),
181 "generate_base_ccy_prices"
182 );
183 }
184
185 #[test]
186 fn test_plugin_short_name_empty() {
187 assert_eq!(plugin_short_name(""), "");
188 }
189
190 #[test]
191 fn test_registry_find_short_name() {
192 let registry = NativePluginRegistry::new();
193 assert!(registry.find("implicit_prices").is_some());
194 assert!(registry.find("zerosum").is_some());
195 assert!(registry.find("nonexistent").is_none());
196 }
197
198 #[test]
199 fn test_registry_find_qualified_name() {
200 let registry = NativePluginRegistry::new();
201 assert!(registry.find("beancount.plugins.implicit_prices").is_some());
202 assert!(registry.find("beanahead.plugins.rx_txn_plugin").is_some());
203 assert!(
204 registry
205 .find("beancount_reds_plugins.zerosum.zerosum")
206 .is_some()
207 );
208 assert!(
209 registry
210 .find("beancount_reds_plugins.capital_gains_classifier.gain_loss")
211 .is_some()
212 );
213 }
214
215 #[test]
216 fn test_is_builtin_short_name() {
217 assert!(NativePluginRegistry::is_builtin("implicit_prices"));
218 assert!(NativePluginRegistry::is_builtin("zerosum"));
219 assert!(!NativePluginRegistry::is_builtin("nonexistent"));
220 }
221
222 #[test]
223 fn test_is_builtin_qualified_name() {
224 assert!(NativePluginRegistry::is_builtin(
225 "beancount.plugins.implicit_prices"
226 ));
227 assert!(NativePluginRegistry::is_builtin(
228 "beanahead.plugins.rx_txn_plugin"
229 ));
230 assert!(NativePluginRegistry::is_builtin(
231 "beancount_reds_plugins.zerosum.zerosum"
232 ));
233 assert!(!NativePluginRegistry::is_builtin("some.random.nonexistent"));
234 }
235}