Skip to main content

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    #[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}