1use std::cmp;
18use std::fmt;
19use std::str::FromStr;
20use std::time::SystemTime;
21
22use crate::Error;
23use crate::kv;
24use crate::kv::KeyValues;
25use crate::str::Str;
26
27#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
28enum MaybeStaticStr<'a> {
29 Str(&'a str),
30 Static(&'static str),
31}
32
33impl<'a> MaybeStaticStr<'a> {
34 fn get(&self) -> &'a str {
35 match *self {
36 MaybeStaticStr::Str(s) => s,
37 MaybeStaticStr::Static(s) => s,
38 }
39 }
40
41 fn into_str(self) -> Str<'static> {
42 match self {
43 MaybeStaticStr::Str(s) => Str::new_owned(s.to_owned()),
44 MaybeStaticStr::Static(s) => Str::new(s),
45 }
46 }
47}
48
49#[derive(Clone, Debug)]
51pub struct Record<'a> {
52 now: SystemTime,
54
55 metadata: Metadata<'a>,
57 module_path: Option<MaybeStaticStr<'a>>,
58 file: Option<MaybeStaticStr<'a>>,
59 line: Option<u32>,
60
61 args: fmt::Arguments<'a>,
63
64 kvs: KeyValues<'a>,
66}
67
68impl<'a> Record<'a> {
69 pub fn time(&self) -> SystemTime {
71 self.now
72 }
73
74 pub fn metadata(&self) -> &Metadata<'a> {
76 &self.metadata
77 }
78
79 pub fn level(&self) -> Level {
81 self.metadata.level()
82 }
83
84 pub fn target(&self) -> &'a str {
86 self.metadata.target()
87 }
88
89 pub fn module_path(&self) -> Option<&'a str> {
91 self.module_path.map(|s| s.get())
92 }
93
94 pub fn file(&self) -> Option<&'a str> {
96 self.file.map(|s| s.get())
97 }
98
99 pub fn filename(&self) -> std::borrow::Cow<'a, str> {
103 self.file()
104 .map(std::path::Path::new)
105 .and_then(std::path::Path::file_name)
106 .map(std::ffi::OsStr::to_string_lossy)
107 .unwrap_or_default()
108 }
109
110 pub fn line(&self) -> Option<u32> {
112 self.line
113 }
114
115 pub fn args(&self) -> &fmt::Arguments<'a> {
117 &self.args
118 }
119
120 pub fn key_values(&self) -> &KeyValues<'a> {
122 &self.kvs
123 }
124
125 pub fn to_owned(&self) -> RecordOwned {
127 RecordOwned {
128 now: self.now,
129 metadata: MetadataOwned {
130 level: self.metadata.level,
131 target: Str::new_owned(self.metadata.target),
132 },
133 module_path: self.module_path.map(MaybeStaticStr::into_str),
134 file: self.file.map(MaybeStaticStr::into_str),
135 line: self.line,
136 args: match self.args.as_str() {
137 Some(s) => Str::new(s),
138 None => Str::new_owned(self.args.to_string()),
139 },
140 kvs: self
141 .kvs
142 .iter()
143 .map(|(k, v)| (k.to_owned(), v.to_owned()))
144 .collect(),
145 }
146 }
147}
148
149#[derive(Debug)]
151pub struct RecordBuilder<'a> {
152 record: Record<'a>,
153}
154
155impl Default for RecordBuilder<'_> {
156 fn default() -> Self {
157 RecordBuilder {
158 record: Record {
159 now: SystemTime::now(),
160 metadata: MetadataBuilder::default().build(),
161 module_path: None,
162 file: None,
163 line: None,
164 args: format_args!(""),
165 kvs: Default::default(),
166 },
167 }
168 }
169}
170
171impl<'a> RecordBuilder<'a> {
172 pub fn args(mut self, args: fmt::Arguments<'a>) -> Self {
174 self.record.args = args;
175 self
176 }
177
178 pub fn metadata(mut self, metadata: Metadata<'a>) -> Self {
182 self.record.metadata = metadata;
183 self
184 }
185
186 pub fn level(mut self, level: Level) -> Self {
188 self.record.metadata.level = level;
189 self
190 }
191
192 pub fn target(mut self, target: &'a str) -> Self {
194 self.record.metadata.target = target;
195 self
196 }
197
198 pub fn module_path(mut self, path: Option<&'a str>) -> Self {
200 self.record.module_path = path.map(MaybeStaticStr::Str);
201 self
202 }
203
204 pub fn module_path_static(mut self, path: &'static str) -> Self {
206 self.record.module_path = Some(MaybeStaticStr::Static(path));
207 self
208 }
209
210 pub fn file(mut self, file: Option<&'a str>) -> Self {
212 self.record.file = file.map(MaybeStaticStr::Str);
213 self
214 }
215
216 pub fn file_static(mut self, file: &'static str) -> Self {
218 self.record.file = Some(MaybeStaticStr::Static(file));
219 self
220 }
221
222 pub fn line(mut self, line: Option<u32>) -> Self {
224 self.record.line = line;
225 self
226 }
227
228 pub fn key_values(mut self, kvs: impl Into<KeyValues<'a>>) -> Self {
230 self.record.kvs = kvs.into();
231 self
232 }
233
234 pub fn build(self) -> Record<'a> {
236 self.record
237 }
238}
239
240#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
242pub struct Metadata<'a> {
243 level: Level,
244 target: &'a str,
245}
246
247impl<'a> Metadata<'a> {
248 pub fn level(&self) -> Level {
250 self.level
251 }
252
253 pub fn target(&self) -> &'a str {
255 self.target
256 }
257}
258
259#[derive(Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
261pub struct MetadataBuilder<'a> {
262 metadata: Metadata<'a>,
263}
264
265impl Default for MetadataBuilder<'_> {
266 fn default() -> Self {
267 MetadataBuilder {
268 metadata: Metadata {
269 level: Level::Info,
270 target: Default::default(),
271 },
272 }
273 }
274}
275
276impl<'a> MetadataBuilder<'a> {
277 pub fn level(mut self, arg: Level) -> Self {
279 self.metadata.level = arg;
280 self
281 }
282
283 pub fn target(mut self, target: &'a str) -> Self {
285 self.metadata.target = target;
286 self
287 }
288
289 pub fn build(self) -> Metadata<'a> {
291 self.metadata
292 }
293}
294
295#[derive(Clone, Debug)]
297pub struct RecordOwned {
298 now: SystemTime,
300
301 metadata: MetadataOwned,
303 module_path: Option<Str<'static>>,
304 file: Option<Str<'static>>,
305 line: Option<u32>,
306
307 args: Str<'static>,
309
310 kvs: Vec<(kv::KeyOwned, kv::ValueOwned)>,
312}
313
314#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
316struct MetadataOwned {
317 level: Level,
318 target: Str<'static>,
319}
320
321impl RecordOwned {
322 pub fn execute(&self, f: impl FnOnce(&Record) -> Result<(), Error>) -> Result<(), Error> {
326 f(&Record {
327 now: self.now,
328 metadata: Metadata {
329 level: self.metadata.level,
330 target: &self.metadata.target,
331 },
332 module_path: self.module_path.as_deref().map(MaybeStaticStr::Str),
333 file: self.file.as_deref().map(MaybeStaticStr::Str),
334 line: self.line,
335 args: format_args!("{}", self.args),
336 kvs: KeyValues::from(self.kvs.as_slice()),
337 })
338 }
339}
340
341#[repr(usize)]
343#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
344pub enum Level {
345 Error = 100,
349 Warn = 200,
353 Info = 300,
357 Debug = 400,
361 Trace = 500,
365}
366
367impl Level {
368 pub fn as_str(&self) -> &'static str {
372 match self {
373 Level::Error => "ERROR",
374 Level::Warn => "WARN",
375 Level::Info => "INFO",
376 Level::Debug => "DEBUG",
377 Level::Trace => "TRACE",
378 }
379 }
380}
381
382impl fmt::Display for Level {
383 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
384 f.pad(self.as_str())
385 }
386}
387
388#[repr(usize)]
390#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
391pub enum LevelFilter {
392 Off = 0,
394 Error = 100,
396 Warn = 200,
398 Info = 300,
400 Debug = 400,
402 Trace = 500,
404}
405
406impl LevelFilter {
407 pub fn as_str(&self) -> &'static str {
411 match self {
412 LevelFilter::Off => "OFF",
413 LevelFilter::Error => "ERROR",
414 LevelFilter::Warn => "WARN",
415 LevelFilter::Info => "INFO",
416 LevelFilter::Debug => "DEBUG",
417 LevelFilter::Trace => "TRACE",
418 }
419 }
420}
421
422impl fmt::Display for LevelFilter {
423 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
424 f.pad(self.as_str())
425 }
426}
427
428impl PartialEq<LevelFilter> for Level {
429 fn eq(&self, other: &LevelFilter) -> bool {
430 PartialEq::eq(&(*self as usize), &(*other as usize))
431 }
432}
433
434impl PartialOrd<LevelFilter> for Level {
435 fn partial_cmp(&self, other: &LevelFilter) -> Option<cmp::Ordering> {
436 Some(Ord::cmp(&(*self as usize), &(*other as usize)))
437 }
438}
439
440impl PartialEq<Level> for LevelFilter {
441 fn eq(&self, other: &Level) -> bool {
442 other.eq(self)
443 }
444}
445
446impl PartialOrd<Level> for LevelFilter {
447 fn partial_cmp(&self, other: &Level) -> Option<cmp::Ordering> {
448 Some(Ord::cmp(&(*self as usize), &(*other as usize)))
449 }
450}
451
452#[derive(Debug, PartialEq, Eq)]
454#[non_exhaustive]
455pub struct ParseLevelError {}
456
457impl fmt::Display for ParseLevelError {
458 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
459 fmt.write_str("malformed log level")
460 }
461}
462
463impl std::error::Error for ParseLevelError {}
464
465impl FromStr for Level {
466 type Err = ParseLevelError;
467 fn from_str(s: &str) -> Result<Level, Self::Err> {
468 for (name, level) in [
469 ("error", Level::Error),
470 ("warn", Level::Warn),
471 ("info", Level::Info),
472 ("debug", Level::Debug),
473 ("trace", Level::Trace),
474 ] {
475 if s.eq_ignore_ascii_case(name) {
476 return Ok(level);
477 }
478 }
479
480 Err(ParseLevelError {})
481 }
482}
483
484impl FromStr for LevelFilter {
485 type Err = ParseLevelError;
486 fn from_str(s: &str) -> Result<LevelFilter, Self::Err> {
487 for (name, level) in [
488 ("off", LevelFilter::Off),
489 ("error", LevelFilter::Error),
490 ("warn", LevelFilter::Warn),
491 ("info", LevelFilter::Info),
492 ("debug", LevelFilter::Debug),
493 ("trace", LevelFilter::Trace),
494 ] {
495 if s.eq_ignore_ascii_case(name) {
496 return Ok(level);
497 }
498 }
499
500 Err(ParseLevelError {})
501 }
502}