1mod calendar;
2mod doing;
3mod json;
4mod timing;
5
6use std::path::Path;
7
8use doing_error::Result;
9use doing_taskpaper::Entry;
10
11use crate::{Plugin, Registry};
12
13pub trait ImportPlugin: Plugin {
18 fn import(&self, path: &Path) -> Result<Vec<Entry>>;
20}
21
22pub fn default_registry() -> Registry<dyn ImportPlugin> {
24 let mut registry: Registry<dyn ImportPlugin> = Registry::new();
25 registry.register(Box::new(calendar::CalendarImport));
26 registry.register(Box::new(doing::DoingImport));
27 registry.register(Box::new(json::JsonImport));
28 registry.register(Box::new(timing::TimingImport));
29 registry
30}
31
32#[cfg(test)]
33mod test {
34 use std::path::Path;
35
36 use super::*;
37 use crate::PluginSettings;
38
39 struct MockPlugin {
40 name: String,
41 trigger: String,
42 }
43
44 impl MockPlugin {
45 fn new(name: &str, trigger: &str) -> Self {
46 Self {
47 name: name.into(),
48 trigger: trigger.into(),
49 }
50 }
51 }
52
53 impl Plugin for MockPlugin {
54 fn name(&self) -> &str {
55 &self.name
56 }
57
58 fn settings(&self) -> PluginSettings {
59 PluginSettings {
60 trigger: self.trigger.clone(),
61 }
62 }
63 }
64
65 impl ImportPlugin for MockPlugin {
66 fn import(&self, _path: &Path) -> Result<Vec<Entry>> {
67 Ok(Vec::new())
68 }
69 }
70
71 mod default_registry {
72 use pretty_assertions::assert_eq;
73
74 use super::*;
75
76 #[test]
77 fn it_registers_all_built_in_plugins() {
78 let registry = default_registry();
79
80 assert_eq!(
81 registry.available_formats(),
82 vec!["calendar", "doing", "json", "timing"]
83 );
84 }
85 }
86
87 mod registry_available_formats {
88 use pretty_assertions::assert_eq;
89
90 use super::*;
91
92 #[test]
93 fn it_returns_empty_for_new_registry() {
94 let registry = Registry::<dyn ImportPlugin>::new();
95
96 assert!(registry.available_formats().is_empty());
97 }
98
99 #[test]
100 fn it_returns_sorted_format_names() {
101 let mut registry = Registry::<dyn ImportPlugin>::new();
102 registry.register(Box::new(MockPlugin::new("timing", "timing")));
103 registry.register(Box::new(MockPlugin::new("doing", "doing")));
104
105 let formats = registry.available_formats();
106
107 assert_eq!(formats, vec!["doing", "timing"]);
108 }
109 }
110
111 mod registry_register {
112 use pretty_assertions::assert_eq;
113
114 use super::*;
115
116 #[test]
117 fn it_adds_plugin_to_registry() {
118 let mut registry = Registry::<dyn ImportPlugin>::new();
119
120 registry.register(Box::new(MockPlugin::new("doing", "doing")));
121
122 assert_eq!(registry.available_formats(), vec!["doing"]);
123 }
124
125 #[test]
126 #[should_panic(expected = "invalid trigger pattern")]
127 fn it_panics_on_invalid_trigger_pattern() {
128 let mut registry = Registry::<dyn ImportPlugin>::new();
129
130 registry.register(Box::new(MockPlugin::new("bad", "(?invalid")));
131 }
132 }
133
134 mod registry_resolve {
135 use pretty_assertions::assert_eq;
136
137 use super::*;
138
139 #[test]
140 fn it_matches_exact_format_name() {
141 let mut registry = Registry::<dyn ImportPlugin>::new();
142 registry.register(Box::new(MockPlugin::new("doing", "doing")));
143
144 let plugin = registry.resolve("doing").unwrap();
145
146 assert_eq!(plugin.name(), "doing");
147 }
148
149 #[test]
150 fn it_matches_case_insensitively() {
151 let mut registry = Registry::<dyn ImportPlugin>::new();
152 registry.register(Box::new(MockPlugin::new("doing", "doing")));
153
154 assert!(registry.resolve("DOING").is_some());
155 assert!(registry.resolve("Doing").is_some());
156 }
157
158 #[test]
159 fn it_returns_none_for_unknown_format() {
160 let mut registry = Registry::<dyn ImportPlugin>::new();
161 registry.register(Box::new(MockPlugin::new("doing", "doing")));
162
163 assert!(registry.resolve("csv").is_none());
164 }
165
166 #[test]
167 fn it_does_not_match_partial_strings() {
168 let mut registry = Registry::<dyn ImportPlugin>::new();
169 registry.register(Box::new(MockPlugin::new("doing", "doing")));
170
171 assert!(registry.resolve("doingx").is_none());
172 assert!(registry.resolve("xdoing").is_none());
173 }
174 }
175}