spdlog_internal/pattern_parser/
registry.rs1use std::{
2 borrow::Cow,
3 collections::{hash_map::Entry, HashMap},
4 fmt::Debug,
5 hash::Hash,
6};
7
8use super::{error::TemplateError, BuiltInFormatter, Error, PatternKind, Result};
9use crate::impossible;
10
11#[derive(Clone, Debug)]
12pub struct PatternRegistry<F> {
13 formatters: HashMap<Cow<'static, str>, PatternKind<F>>,
14}
15
16impl<F> PatternRegistry<F> {
17 #[must_use]
18 pub fn with_builtin() -> Self {
19 let mut registry = Self {
20 formatters: HashMap::new(),
21 };
22
23 BuiltInFormatter::iter().for_each(|formatter| registry.register_builtin(formatter));
24 registry
25 }
26
27 pub fn register_custom(
28 &mut self,
29 placeholder: impl Into<Cow<'static, str>>,
30 factory: F,
31 ) -> Result<()> {
32 let placeholder = placeholder.into();
33
34 let incoming = PatternKind::Custom {
35 placeholder: placeholder.clone(),
36 factory,
37 };
38
39 match self.formatters.entry(placeholder) {
40 Entry::Occupied(entry) => Err(Error::ConflictName {
41 existing: entry.get().to_factory_erased(),
42 incoming: incoming.to_factory_erased(),
43 }),
44 Entry::Vacant(entry) => {
45 entry.insert(incoming);
46 Ok(())
47 }
48 }
49 }
50
51 pub fn find(&self, find_custom: bool, placeholder: impl AsRef<str>) -> Result<&PatternKind<F>> {
52 let placeholder = placeholder.as_ref();
53
54 match self.formatters.get(placeholder) {
55 Some(found) => match (found, find_custom) {
56 (PatternKind::BuiltIn(_), false) => Ok(found),
57 (PatternKind::Custom { .. }, true) => Ok(found),
58 (PatternKind::BuiltIn(_), true) => {
59 Err(Error::Template(TemplateError::WrongPatternKindReference {
60 is_builtin_as_custom: true,
61 placeholder: placeholder.into(),
62 }))
63 }
64 (PatternKind::Custom { .. }, false) => {
65 Err(Error::Template(TemplateError::WrongPatternKindReference {
66 is_builtin_as_custom: false,
67 placeholder: placeholder.into(),
68 }))
69 }
70 },
71 None => Err(Error::Template(TemplateError::UnknownPatternReference {
72 is_custom: find_custom,
73 placeholder: placeholder.into(),
74 })),
75 }
76 }
77}
78
79impl<F> PatternRegistry<F> {
80 pub(crate) fn register_builtin(&mut self, formatter: BuiltInFormatter) {
81 match self
82 .formatters
83 .entry(Cow::Borrowed(formatter.placeholder()))
84 {
85 Entry::Occupied(_) => {
86 impossible!("formatter={:?}", formatter)
87 }
88 Entry::Vacant(entry) => {
89 entry.insert(PatternKind::BuiltIn(formatter));
90 }
91 }
92 }
93}
94
95pub fn check_custom_pattern_names<N, I>(names: I) -> Result<()>
96where
97 N: AsRef<str> + Eq + PartialEq + Hash,
98 I: IntoIterator<Item = N>,
99{
100 let mut seen_names: HashMap<N, usize> = HashMap::new();
101 let mut result = Ok(());
102
103 for name in names {
104 if let Some(existing) = BuiltInFormatter::iter().find(|f| f.placeholder() == name.as_ref())
105 {
106 result = Error::push_err(
107 result,
108 Error::ConflictName {
109 existing: PatternKind::BuiltIn(existing),
110 incoming: PatternKind::Custom {
111 placeholder: Cow::Owned(name.as_ref().into()),
112 factory: (),
113 },
114 },
115 );
116 }
117
118 if let Some(seen_count) = seen_names.get_mut(&name) {
119 *seen_count += 1;
120 if *seen_count == 2 {
121 let conflict_pattern = PatternKind::Custom {
122 placeholder: Cow::Owned(name.as_ref().into()),
123 factory: (),
124 };
125 result = Error::push_err(
126 result,
127 Error::ConflictName {
128 existing: conflict_pattern.clone(),
129 incoming: conflict_pattern,
130 },
131 );
132 }
133 } else {
134 seen_names.insert(name, 1);
135 }
136 }
137
138 debug_assert!(seen_names.iter().all(|(_, seen_count)| *seen_count == 1) || result.is_err());
139
140 result
141}
142
143#[cfg(test)]
144mod tests {
145 use super::*;
146
147 #[test]
148 fn custom_pattern_names_checker() {
149 use check_custom_pattern_names as check;
150
151 assert!(check(["a", "b"]).is_ok());
152 assert_eq!(
153 check(["a", "a"]),
154 Err(Error::ConflictName {
155 existing: PatternKind::Custom {
156 placeholder: "a".into(),
157 factory: ()
158 },
159 incoming: PatternKind::Custom {
160 placeholder: "a".into(),
161 factory: ()
162 }
163 })
164 );
165 assert_eq!(
166 check(["a", "b", "a"]),
167 Err(Error::ConflictName {
168 existing: PatternKind::Custom {
169 placeholder: "a".into(),
170 factory: ()
171 },
172 incoming: PatternKind::Custom {
173 placeholder: "a".into(),
174 factory: ()
175 }
176 })
177 );
178 assert_eq!(
179 check(["date"]),
180 Err(Error::ConflictName {
181 existing: PatternKind::BuiltIn(BuiltInFormatter::Date),
182 incoming: PatternKind::Custom {
183 placeholder: "date".into(),
184 factory: ()
185 }
186 })
187 );
188 assert_eq!(
189 check(["date", "a", "a"]),
190 Err(Error::Multiple(vec![
191 Error::ConflictName {
192 existing: PatternKind::BuiltIn(BuiltInFormatter::Date),
193 incoming: PatternKind::Custom {
194 placeholder: "date".into(),
195 factory: ()
196 }
197 },
198 Error::ConflictName {
199 existing: PatternKind::Custom {
200 placeholder: "a".into(),
201 factory: ()
202 },
203 incoming: PatternKind::Custom {
204 placeholder: "a".into(),
205 factory: ()
206 }
207 }
208 ]))
209 );
210 assert_eq!(
211 check(["date", "a", "a", "a"]),
212 Err(Error::Multiple(vec![
213 Error::ConflictName {
214 existing: PatternKind::BuiltIn(BuiltInFormatter::Date),
215 incoming: PatternKind::Custom {
216 placeholder: "date".into(),
217 factory: ()
218 }
219 },
220 Error::ConflictName {
221 existing: PatternKind::Custom {
222 placeholder: "a".into(),
223 factory: ()
224 },
225 incoming: PatternKind::Custom {
226 placeholder: "a".into(),
227 factory: ()
228 }
229 }
230 ]))
231 );
232 assert_eq!(
233 check(["b", "date", "a", "b", "a", "a"]),
234 Err(Error::Multiple(vec![
235 Error::ConflictName {
236 existing: PatternKind::BuiltIn(BuiltInFormatter::Date),
237 incoming: PatternKind::Custom {
238 placeholder: "date".into(),
239 factory: ()
240 }
241 },
242 Error::ConflictName {
243 existing: PatternKind::Custom {
244 placeholder: "b".into(),
245 factory: ()
246 },
247 incoming: PatternKind::Custom {
248 placeholder: "b".into(),
249 factory: ()
250 }
251 },
252 Error::ConflictName {
253 existing: PatternKind::Custom {
254 placeholder: "a".into(),
255 factory: ()
256 },
257 incoming: PatternKind::Custom {
258 placeholder: "a".into(),
259 factory: ()
260 }
261 }
262 ]))
263 );
264 }
265}