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