spdlog_internal/pattern_parser/
registry.rs

1use 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    pub fn with_builtin() -> Self {
18        let mut registry = Self {
19            formatters: HashMap::new(),
20        };
21
22        BuiltInFormatter::iter().for_each(|formatter| registry.register_builtin(formatter));
23        registry
24    }
25
26    pub fn register_custom(
27        &mut self,
28        placeholder: impl Into<Cow<'static, str>>,
29        factory: F,
30    ) -> Result<()> {
31        let placeholder = placeholder.into();
32
33        let incoming = PatternKind::Custom {
34            placeholder: placeholder.clone(),
35            factory,
36        };
37
38        match self.formatters.entry(placeholder) {
39            Entry::Occupied(entry) => Err(Error::ConflictName {
40                existing: entry.get().to_factory_erased(),
41                incoming: incoming.to_factory_erased(),
42            }),
43            Entry::Vacant(entry) => {
44                entry.insert(incoming);
45                Ok(())
46            }
47        }
48    }
49
50    pub fn find(&self, find_custom: bool, placeholder: impl AsRef<str>) -> Result<&PatternKind<F>> {
51        let placeholder = placeholder.as_ref();
52
53        match self.formatters.get(placeholder) {
54            Some(found) => match (found, find_custom) {
55                (PatternKind::BuiltIn(_), false) => Ok(found),
56                (PatternKind::Custom { .. }, true) => Ok(found),
57                (PatternKind::BuiltIn(_), true) => {
58                    Err(Error::Template(TemplateError::WrongPatternKindReference {
59                        is_builtin_as_custom: true,
60                        placeholder: placeholder.into(),
61                    }))
62                }
63                (PatternKind::Custom { .. }, false) => {
64                    Err(Error::Template(TemplateError::WrongPatternKindReference {
65                        is_builtin_as_custom: false,
66                        placeholder: placeholder.into(),
67                    }))
68                }
69            },
70            None => Err(Error::Template(TemplateError::UnknownPatternReference {
71                is_custom: find_custom,
72                placeholder: placeholder.into(),
73            })),
74        }
75    }
76}
77
78impl<F> PatternRegistry<F> {
79    pub(crate) fn register_builtin(&mut self, formatter: BuiltInFormatter) {
80        match self
81            .formatters
82            .entry(Cow::Borrowed(formatter.placeholder()))
83        {
84            Entry::Occupied(_) => {
85                impossible!("formatter={:?}", formatter)
86            }
87            Entry::Vacant(entry) => {
88                entry.insert(PatternKind::BuiltIn(formatter));
89            }
90        }
91    }
92}
93
94pub fn check_custom_pattern_names<N, I>(names: I) -> Result<()>
95where
96    N: AsRef<str> + Eq + PartialEq + Hash,
97    I: IntoIterator<Item = N>,
98{
99    let mut seen_names: HashMap<N, usize> = HashMap::new();
100    let mut result = Ok(());
101
102    for name in names {
103        if let Some(existing) = BuiltInFormatter::iter().find(|f| f.placeholder() == name.as_ref())
104        {
105            result = Error::push_err(
106                result,
107                Error::ConflictName {
108                    existing: PatternKind::BuiltIn(existing),
109                    incoming: PatternKind::Custom {
110                        placeholder: Cow::Owned(name.as_ref().into()),
111                        factory: (),
112                    },
113                },
114            );
115        }
116
117        if let Some(seen_count) = seen_names.get_mut(&name) {
118            *seen_count += 1;
119            if *seen_count == 2 {
120                let conflict_pattern = PatternKind::Custom {
121                    placeholder: Cow::Owned(name.as_ref().into()),
122                    factory: (),
123                };
124                result = Error::push_err(
125                    result,
126                    Error::ConflictName {
127                        existing: conflict_pattern.clone(),
128                        incoming: conflict_pattern,
129                    },
130                );
131            }
132        } else {
133            seen_names.insert(name, 1);
134        }
135    }
136
137    debug_assert!(seen_names.iter().all(|(_, seen_count)| *seen_count == 1) || result.is_err());
138
139    result
140}
141
142#[cfg(test)]
143mod tests {
144    use super::*;
145    use crate::pattern_parser::BuiltInFormatterInner;
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(BuiltInFormatterInner::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(BuiltInFormatterInner::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(BuiltInFormatterInner::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(BuiltInFormatterInner::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}