1use log::LevelFilter;
4use std::collections::HashSet;
5use thiserror::Error;
6
7use crate::{append::Append, filter::Filter};
8
9#[derive(Debug)]
11pub struct Config {
12 appenders: Vec<Appender>,
13 root: Root,
14 loggers: Vec<Logger>,
15}
16
17impl Config {
18 pub fn builder() -> ConfigBuilder {
20 ConfigBuilder {
21 appenders: vec![],
22 loggers: vec![],
23 }
24 }
25
26 pub fn appenders(&self) -> &[Appender] {
28 &self.appenders
29 }
30
31 pub fn root(&self) -> &Root {
33 &self.root
34 }
35
36 pub fn root_mut(&mut self) -> &mut Root {
38 &mut self.root
39 }
40
41 pub fn loggers(&self) -> &[Logger] {
43 &self.loggers
44 }
45
46 pub(crate) fn unpack(self) -> (Vec<Appender>, Root, Vec<Logger>) {
47 let Config {
48 appenders,
49 root,
50 loggers,
51 } = self;
52 (appenders, root, loggers)
53 }
54}
55
56#[derive(Debug, Default)]
58pub struct ConfigBuilder {
59 appenders: Vec<Appender>,
60 loggers: Vec<Logger>,
61}
62
63impl ConfigBuilder {
64 pub fn appender(mut self, appender: Appender) -> ConfigBuilder {
66 self.appenders.push(appender);
67 self
68 }
69
70 pub fn appenders<I>(mut self, appenders: I) -> ConfigBuilder
72 where
73 I: IntoIterator<Item = Appender>,
74 {
75 self.appenders.extend(appenders);
76 self
77 }
78
79 pub fn logger(mut self, logger: Logger) -> ConfigBuilder {
81 self.loggers.push(logger);
82 self
83 }
84
85 pub fn loggers<I>(mut self, loggers: I) -> ConfigBuilder
87 where
88 I: IntoIterator<Item = Logger>,
89 {
90 self.loggers.extend(loggers);
91 self
92 }
93
94 pub fn build_lossy(self, mut root: Root) -> (Config, ConfigErrors) {
99 let mut errors: Vec<ConfigError> = vec![];
100
101 let ConfigBuilder { appenders, loggers } = self;
102
103 let mut ok_appenders = vec![];
104 let mut appender_names = HashSet::new();
105 for appender in appenders {
106 if appender_names.insert(appender.name.clone()) {
107 ok_appenders.push(appender);
108 } else {
109 errors.push(ConfigError::DuplicateAppenderName(appender.name));
110 }
111 }
112
113 let mut ok_root_appenders = vec![];
114 for appender in root.appenders {
115 if appender_names.contains(&appender) {
116 ok_root_appenders.push(appender);
117 } else {
118 errors.push(ConfigError::NonexistentAppender(appender));
119 }
120 }
121 root.appenders = ok_root_appenders;
122
123 let mut ok_loggers = vec![];
124 let mut logger_names = HashSet::new();
125 for mut logger in loggers {
126 if !logger_names.insert(logger.name.clone()) {
127 errors.push(ConfigError::DuplicateLoggerName(logger.name));
128 continue;
129 }
130
131 if let Err(err) = check_logger_name(&logger.name) {
132 errors.push(err);
133 continue;
134 }
135
136 let mut ok_logger_appenders = vec![];
137 for appender in logger.appenders {
138 if appender_names.contains(&appender) {
139 ok_logger_appenders.push(appender);
140 } else {
141 errors.push(ConfigError::NonexistentAppender(appender));
142 }
143 }
144 logger.appenders = ok_logger_appenders;
145
146 ok_loggers.push(logger);
147 }
148
149 let config = Config {
150 appenders: ok_appenders,
151 root,
152 loggers: ok_loggers,
153 };
154
155 (config, ConfigErrors(errors))
156 }
157
158 pub fn build(self, root: Root) -> Result<Config, ConfigErrors> {
160 let (config, errors) = self.build_lossy(root);
161 if errors.is_empty() {
162 Ok(config)
163 } else {
164 Err(errors)
165 }
166 }
167}
168
169#[derive(Debug)]
171pub struct Root {
172 level: LevelFilter,
173 appenders: Vec<String>,
174}
175
176impl Root {
177 pub fn builder() -> RootBuilder {
179 RootBuilder { appenders: vec![] }
180 }
181
182 pub fn level(&self) -> LevelFilter {
184 self.level
185 }
186
187 pub fn appenders(&self) -> &[String] {
189 &self.appenders
190 }
191
192 pub fn set_level(&mut self, level: LevelFilter) {
194 self.level = level;
195 }
196}
197
198#[derive(Clone, Eq, PartialEq, Hash, Debug)]
200pub struct RootBuilder {
201 appenders: Vec<String>,
202}
203
204impl RootBuilder {
205 pub fn appender<T>(mut self, appender: T) -> RootBuilder
207 where
208 T: Into<String>,
209 {
210 self.appenders.push(appender.into());
211 self
212 }
213
214 pub fn appenders<I>(mut self, appenders: I) -> RootBuilder
216 where
217 I: IntoIterator,
218 I::Item: Into<String>,
219 {
220 self.appenders.extend(appenders.into_iter().map(Into::into));
221 self
222 }
223
224 pub fn build(self, level: LevelFilter) -> Root {
226 Root {
227 level,
228 appenders: self.appenders,
229 }
230 }
231}
232
233#[derive(Debug)]
235pub struct Appender {
236 name: String,
237 appender: Box<dyn Append>,
238 filters: Vec<Box<dyn Filter>>,
239}
240
241impl Appender {
242 pub fn builder() -> AppenderBuilder {
244 AppenderBuilder { filters: vec![] }
245 }
246
247 pub fn name(&self) -> &str {
249 &self.name
250 }
251
252 pub fn appender(&self) -> &dyn Append {
254 &*self.appender
255 }
256
257 pub fn filters(&self) -> &[Box<dyn Filter>] {
259 &self.filters
260 }
261
262 pub(crate) fn unpack(self) -> (String, Box<dyn Append>, Vec<Box<dyn Filter>>) {
263 let Appender {
264 name,
265 appender,
266 filters,
267 } = self;
268 (name, appender, filters)
269 }
270}
271
272#[derive(Debug)]
274pub struct AppenderBuilder {
275 filters: Vec<Box<dyn Filter>>,
276}
277
278impl AppenderBuilder {
279 pub fn filter(mut self, filter: Box<dyn Filter>) -> AppenderBuilder {
281 self.filters.push(filter);
282 self
283 }
284
285 pub fn filters<I>(mut self, filters: I) -> AppenderBuilder
287 where
288 I: IntoIterator<Item = Box<dyn Filter>>,
289 {
290 self.filters.extend(filters);
291 self
292 }
293
294 pub fn build<T>(self, name: T, appender: Box<dyn Append>) -> Appender
296 where
297 T: Into<String>,
298 {
299 Appender {
300 name: name.into(),
301 appender,
302 filters: self.filters,
303 }
304 }
305}
306
307#[derive(Clone, Eq, PartialEq, Hash, Debug)]
309pub struct Logger {
310 name: String,
311 level: LevelFilter,
312 appenders: Vec<String>,
313 additive: bool,
314}
315
316impl Logger {
317 pub fn builder() -> LoggerBuilder {
321 LoggerBuilder {
322 appenders: vec![],
323 additive: true,
324 }
325 }
326
327 pub fn name(&self) -> &str {
329 &self.name
330 }
331
332 pub fn level(&self) -> LevelFilter {
334 self.level
335 }
336
337 pub fn appenders(&self) -> &[String] {
339 &self.appenders
340 }
341
342 pub fn additive(&self) -> bool {
344 self.additive
345 }
346}
347
348#[derive(Clone, Eq, PartialEq, Hash, Debug, Default)]
350pub struct LoggerBuilder {
351 appenders: Vec<String>,
352 additive: bool,
353}
354
355impl LoggerBuilder {
356 pub fn appender<T>(mut self, appender: T) -> LoggerBuilder
358 where
359 T: Into<String>,
360 {
361 self.appenders.push(appender.into());
362 self
363 }
364
365 pub fn appenders<I>(mut self, appenders: I) -> LoggerBuilder
367 where
368 I: IntoIterator,
369 I::Item: Into<String>,
370 {
371 self.appenders.extend(appenders.into_iter().map(Into::into));
372 self
373 }
374
375 pub fn additive(mut self, additive: bool) -> LoggerBuilder {
377 self.additive = additive;
378 self
379 }
380
381 pub fn build<T>(self, name: T, level: LevelFilter) -> Logger
383 where
384 T: Into<String>,
385 {
386 Logger {
387 name: name.into(),
388 level,
389 appenders: self.appenders,
390 additive: self.additive,
391 }
392 }
393}
394
395fn check_logger_name(name: &str) -> Result<(), ConfigError> {
396 if name.is_empty() {
397 return Err(ConfigError::InvalidLoggerName(name.to_owned()));
398 }
399
400 let mut streak = 0;
401 for ch in name.chars() {
402 if ch == ':' {
403 streak += 1;
404 if streak > 2 {
405 return Err(ConfigError::InvalidLoggerName(name.to_owned()));
406 }
407 } else {
408 if streak > 0 && streak != 2 {
409 return Err(ConfigError::InvalidLoggerName(name.to_owned()));
410 }
411 streak = 0;
412 }
413 }
414
415 if streak > 0 {
416 Err(ConfigError::InvalidLoggerName(name.to_owned()))
417 } else {
418 Ok(())
419 }
420}
421
422#[derive(Debug, Error)]
424#[error("Configuration errors: {0:#?}")]
425pub struct ConfigErrors(Vec<ConfigError>);
426
427impl ConfigErrors {
428 pub fn is_empty(&self) -> bool {
430 self.0.is_empty()
431 }
432 pub fn errors(&self) -> &[ConfigError] {
434 &self.0
435 }
436 pub fn handle(&mut self) {
438 for e in self.0.drain(..) {
439 crate::handle_error(&e.into());
440 }
441 }
442}
443
444#[derive(Debug, Error)]
446pub enum ConfigError {
447 #[error("Duplicate appender name `{0}`")]
449 DuplicateAppenderName(String),
450
451 #[error("Reference to nonexistent appender: `{0}`")]
453 NonexistentAppender(String),
454
455 #[error("Duplicate logger name `{0}`")]
457 DuplicateLoggerName(String),
458
459 #[error("Invalid logger name `{0}`")]
461 InvalidLoggerName(String),
462
463 #[doc(hidden)]
464 #[error("Reserved for future use")]
465 __Extensible,
466}
467
468#[cfg(test)]
469mod test {
470 #[test]
471 fn check_logger_name() {
472 let tests = [
473 ("", false),
474 ("asdf", true),
475 ("asdf::jkl", true),
476 ("::", false),
477 ("asdf::jkl::", false),
478 ("asdf:jkl", false),
479 ("asdf:::jkl", false),
480 ("asdf::jkl::", false),
481 ];
482
483 for &(ref name, expected) in &tests {
484 assert!(
485 expected == super::check_logger_name(name).is_ok(),
486 "{}",
487 name
488 );
489 }
490 }
491}