Skip to content

Commit 71b4fd2

Browse files
LancernSpriteOvO
authored andcommitted
Add runtime_pattern macro
1 parent 93377a8 commit 71b4fd2

File tree

9 files changed

+442
-271
lines changed

9 files changed

+442
-271
lines changed

spdlog-internal/src/pattern_parser/error.rs

+60-2
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,37 @@ use thiserror::Error;
66
use super::PatternKind;
77
use crate::impossible;
88

9-
#[derive(Error, Debug)]
9+
#[derive(Error, Debug, PartialEq)]
1010
pub enum Error {
1111
ConflictName {
1212
existing: PatternKind<()>,
1313
incoming: PatternKind<()>,
1414
},
1515
Template(TemplateError),
1616
Parse(NomError<String>),
17+
Multiple(Vec<Error>),
18+
#[cfg(test)]
19+
__ForInternalTestsUseOnly(usize),
20+
}
21+
22+
impl Error {
23+
pub fn push_err<T>(result: Result<T>, new: Self) -> Result<T> {
24+
match result {
25+
Ok(_) => Err(new),
26+
Err(Self::Multiple(mut errors)) => {
27+
errors.push(new);
28+
Err(Self::Multiple(errors))
29+
}
30+
Err(prev) => Err(Error::Multiple(vec![prev, new])),
31+
}
32+
}
33+
34+
pub fn push_result<T, N>(result: Result<T>, new: Result<N>) -> Result<T> {
35+
match new {
36+
Ok(_) => result,
37+
Err(err) => Self::push_err(result, err),
38+
}
39+
}
1740
}
1841

1942
impl Display for Error {
@@ -44,11 +67,22 @@ impl Display for Error {
4467
Error::Parse(err) => {
4568
write!(f, "failed to parse template string: {}", err)
4669
}
70+
Error::Multiple(errs) => {
71+
writeln!(f, "{} errors detected:", errs.len())?;
72+
for err in errs {
73+
writeln!(f, " - {}", err)?;
74+
}
75+
Ok(())
76+
}
77+
#[cfg(test)]
78+
Error::__ForInternalTestsUseOnly(value) => {
79+
write!(f, "{}", value)
80+
}
4781
}
4882
}
4983
}
5084

51-
#[derive(Error, Debug)]
85+
#[derive(Error, Debug, Eq, PartialEq)]
5286
pub enum TemplateError {
5387
WrongPatternKindReference {
5488
is_built_as_custom: bool,
@@ -104,3 +138,27 @@ impl Display for TemplateError {
104138
}
105139

106140
pub type Result<T> = std::result::Result<T, Error>;
141+
142+
#[cfg(test)]
143+
mod tests {
144+
use super::*;
145+
146+
#[test]
147+
fn push_err() {
148+
macro_rules! make_err {
149+
( $($inputs:tt)+ ) => {
150+
Error::__ForInternalTestsUseOnly($($inputs)*)
151+
};
152+
}
153+
154+
assert!(matches!(
155+
Error::push_err(Ok(()), make_err!(1)),
156+
Err(make_err!(1))
157+
));
158+
159+
assert!(matches!(
160+
Error::push_err::<()>(Err(make_err!(1)), make_err!(2)),
161+
Err(Error::Multiple(v)) if matches!(v[..], [make_err!(1), make_err!(2)])
162+
));
163+
}
164+
}

spdlog-internal/src/pattern_parser/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ pub mod parse;
99
mod registry;
1010

1111
pub use error::{Error, Result};
12-
pub use registry::PatternRegistry;
12+
pub use registry::{check_custom_pattern_names, PatternRegistry};
1313

1414
#[derive(
1515
Clone, Copy, Debug, Eq, PartialEq, IntoStaticStr, EnumDiscriminants, EnumIter, EnumString,

spdlog-internal/src/pattern_parser/registry.rs

+174
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use std::{
22
borrow::Cow,
33
collections::{hash_map::Entry, HashMap},
44
fmt::Debug,
5+
hash::Hash,
56
};
67

78
use super::{error::TemplateError, BuiltInFormatter, Error, PatternKind, Result};
@@ -89,3 +90,176 @@ impl<F> PatternRegistry<F> {
8990
}
9091
}
9192
}
93+
94+
pub fn check_custom_pattern_names<N, I>(names: I) -> Result<()>
95+
where
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)]
143+
mod 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+
}

spdlog-macros/src/lib.rs

+15-1
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,26 @@
88
mod pattern;
99

1010
use proc_macro::TokenStream;
11+
use proc_macro2::TokenStream as TokenStream2;
12+
use spdlog_internal::pattern_parser::Result;
1113

1214
#[proc_macro]
1315
pub fn pattern(input: TokenStream) -> TokenStream {
1416
let pattern = syn::parse_macro_input!(input);
17+
into_or_error(pattern::pattern_impl(pattern))
18+
}
19+
20+
#[proc_macro]
21+
pub fn runtime_pattern(input: TokenStream) -> TokenStream {
22+
// We must make this macro a procedural macro because macro by example
23+
// cannot match the "$" token which is used in the custom patterns.
24+
25+
let runtime_pattern = syn::parse_macro_input!(input);
26+
into_or_error(pattern::runtime_pattern_impl(runtime_pattern))
27+
}
1528

16-
match pattern::pattern_impl(pattern) {
29+
fn into_or_error(result: Result<TokenStream2>) -> TokenStream {
30+
match result {
1731
Ok(stream) => stream.into(),
1832
Err(err) => panic!("{}", err),
1933
}

0 commit comments

Comments
 (0)