1use std::borrow::Cow;
18use std::cmp;
19use std::fmt;
20use std::str::FromStr;
21use std::time::SystemTime;
22
23use crate::kv;
24use crate::kv::KeyValues;
25use crate::str::Str;
26
27#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
31enum MaybeStaticStr<'a> {
32 Str(&'a str),
33 Static(&'static str),
34}
35
36impl<'a> MaybeStaticStr<'a> {
37 fn get(&self) -> &'a str {
38 match *self {
39 MaybeStaticStr::Str(s) => s,
40 MaybeStaticStr::Static(s) => s,
41 }
42 }
43
44 fn get_static(&self) -> Option<&'static str> {
45 match *self {
46 MaybeStaticStr::Str(_) => None,
47 MaybeStaticStr::Static(s) => Some(s),
48 }
49 }
50
51 fn into_str(self) -> Str<'static> {
52 match self {
53 MaybeStaticStr::Str(s) => Str::new_shared(s),
54 MaybeStaticStr::Static(s) => Str::new(s),
55 }
56 }
57}
58
59#[derive(Clone, Debug)]
61pub struct Record<'a> {
62 now: SystemTime,
64
65 metadata: Metadata<'a>,
67 module_path: Option<MaybeStaticStr<'a>>,
68 file: Option<MaybeStaticStr<'a>>,
69 line: Option<u32>,
70
71 payload: Str<'static>,
73
74 kvs: KeyValues<'a>,
76}
77
78impl<'a> Record<'a> {
79 pub fn time(&self) -> SystemTime {
81 self.now
82 }
83
84 pub fn metadata(&self) -> &Metadata<'a> {
86 &self.metadata
87 }
88
89 pub fn level(&self) -> Level {
91 self.metadata.level()
92 }
93
94 pub fn target(&self) -> &'a str {
96 self.metadata.target()
97 }
98
99 pub fn module_path(&self) -> Option<&'a str> {
101 self.module_path.map(|s| s.get())
102 }
103
104 pub fn module_path_static(&self) -> Option<&'static str> {
106 self.module_path.and_then(|s| s.get_static())
107 }
108
109 pub fn file(&self) -> Option<&'a str> {
111 self.file.map(|s| s.get())
112 }
113
114 pub fn file_static(&self) -> Option<&'static str> {
116 self.file.and_then(|s| s.get_static())
117 }
118
119 pub fn filename(&self) -> Cow<'a, str> {
123 self.file()
124 .map(std::path::Path::new)
125 .and_then(std::path::Path::file_name)
126 .map(std::ffi::OsStr::to_string_lossy)
127 .unwrap_or_default()
128 }
129
130 pub fn line(&self) -> Option<u32> {
132 self.line
133 }
134
135 pub fn payload(&self) -> &str {
137 self.payload.get()
138 }
139
140 pub fn payload_static(&self) -> Option<&'static str> {
142 self.payload.get_static()
143 }
144
145 pub fn key_values(&self) -> &KeyValues<'a> {
147 &self.kvs
148 }
149
150 pub fn to_owned(&self) -> RecordOwned {
152 RecordOwned {
153 now: self.now,
154 metadata: MetadataOwned {
155 level: self.metadata.level,
156 target: Str::new_shared(self.metadata.target),
157 },
158 module_path: self.module_path.map(MaybeStaticStr::into_str),
159 file: self.file.map(MaybeStaticStr::into_str),
160 line: self.line,
161 payload: self.payload.clone(),
162 kvs: self
163 .kvs
164 .iter()
165 .map(|(k, v)| (k.to_owned(), v.to_owned()))
166 .collect(),
167 }
168 }
169
170 pub fn to_builder(&self) -> RecordBuilder<'a> {
172 RecordBuilder {
173 record: Record {
174 now: self.now,
175 metadata: Metadata {
176 level: self.metadata.level,
177 target: self.metadata.target,
178 },
179 module_path: self.module_path,
180 file: self.file,
181 line: self.line,
182 payload: self.payload.clone(),
183 kvs: self.kvs.clone(),
184 },
185 }
186 }
187
188 pub fn builder() -> RecordBuilder<'a> {
190 RecordBuilder::default()
191 }
192}
193
194#[derive(Debug)]
196pub struct RecordBuilder<'a> {
197 record: Record<'a>,
198}
199
200impl Default for RecordBuilder<'_> {
201 fn default() -> Self {
202 RecordBuilder {
203 record: Record {
204 now: SystemTime::now(),
205 metadata: MetadataBuilder::default().build(),
206 module_path: None,
207 file: None,
208 line: None,
209 payload: Default::default(),
210 kvs: Default::default(),
211 },
212 }
213 }
214}
215
216impl<'a> RecordBuilder<'a> {
217 pub fn payload(mut self, payload: impl Into<Cow<'static, str>>) -> Self {
219 self.record.payload = match payload.into() {
220 Cow::Borrowed(s) => Str::new(s),
221 Cow::Owned(s) => Str::new_shared(s),
222 };
223 self
224 }
225
226 pub fn metadata(mut self, metadata: Metadata<'a>) -> Self {
230 self.record.metadata = metadata;
231 self
232 }
233
234 pub fn level(mut self, level: Level) -> Self {
236 self.record.metadata.level = level;
237 self
238 }
239
240 pub fn target(mut self, target: &'a str) -> Self {
242 self.record.metadata.target = target;
243 self
244 }
245
246 pub fn module_path(mut self, path: Option<&'a str>) -> Self {
248 self.record.module_path = path.map(MaybeStaticStr::Str);
249 self
250 }
251
252 pub fn module_path_static(mut self, path: &'static str) -> Self {
254 self.record.module_path = Some(MaybeStaticStr::Static(path));
255 self
256 }
257
258 pub fn file(mut self, file: Option<&'a str>) -> Self {
260 self.record.file = file.map(MaybeStaticStr::Str);
261 self
262 }
263
264 pub fn file_static(mut self, file: &'static str) -> Self {
266 self.record.file = Some(MaybeStaticStr::Static(file));
267 self
268 }
269
270 pub fn line(mut self, line: Option<u32>) -> Self {
272 self.record.line = line;
273 self
274 }
275
276 pub fn key_values(mut self, kvs: impl Into<KeyValues<'a>>) -> Self {
278 self.record.kvs = kvs.into();
279 self
280 }
281
282 pub fn build(self) -> Record<'a> {
284 self.record
285 }
286}
287
288#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
290pub struct Metadata<'a> {
291 level: Level,
292 target: &'a str,
293}
294
295impl<'a> Metadata<'a> {
296 pub fn level(&self) -> Level {
298 self.level
299 }
300
301 pub fn target(&self) -> &'a str {
303 self.target
304 }
305
306 pub fn to_builder(&self) -> MetadataBuilder<'a> {
308 MetadataBuilder {
309 metadata: Metadata {
310 level: self.level,
311 target: self.target,
312 },
313 }
314 }
315
316 pub fn builder() -> MetadataBuilder<'a> {
318 MetadataBuilder::default()
319 }
320}
321
322#[derive(Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
324pub struct MetadataBuilder<'a> {
325 metadata: Metadata<'a>,
326}
327
328impl Default for MetadataBuilder<'_> {
329 fn default() -> Self {
330 MetadataBuilder {
331 metadata: Metadata {
332 level: Level::Info,
333 target: Default::default(),
334 },
335 }
336 }
337}
338
339impl<'a> MetadataBuilder<'a> {
340 pub fn level(mut self, arg: Level) -> Self {
342 self.metadata.level = arg;
343 self
344 }
345
346 pub fn target(mut self, target: &'a str) -> Self {
348 self.metadata.target = target;
349 self
350 }
351
352 pub fn build(self) -> Metadata<'a> {
354 self.metadata
355 }
356}
357
358#[derive(Clone, Debug)]
360pub struct RecordOwned {
361 now: SystemTime,
363
364 metadata: MetadataOwned,
366 module_path: Option<Str<'static>>,
367 file: Option<Str<'static>>,
368 line: Option<u32>,
369
370 payload: Str<'static>,
372
373 kvs: Vec<(kv::KeyOwned, kv::ValueOwned)>,
375}
376
377#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
379struct MetadataOwned {
380 level: Level,
381 target: Str<'static>,
382}
383
384impl RecordOwned {
385 pub fn as_record(&self) -> Record<'_> {
387 Record {
388 now: self.now,
389 metadata: Metadata {
390 level: self.metadata.level,
391 target: &self.metadata.target,
392 },
393 module_path: self.module_path.as_deref().map(MaybeStaticStr::Str),
394 file: self.file.as_deref().map(MaybeStaticStr::Str),
395 line: self.line,
396 payload: self.payload.clone(),
397 kvs: KeyValues::from(self.kvs.as_slice()),
398 }
399 }
400}
401
402#[repr(usize)]
404#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
405pub enum Level {
406 Error = 100,
410 Warn = 200,
414 Info = 300,
418 Debug = 400,
422 Trace = 500,
426}
427
428impl Level {
429 pub fn as_str(&self) -> &'static str {
433 match self {
434 Level::Error => "ERROR",
435 Level::Warn => "WARN",
436 Level::Info => "INFO",
437 Level::Debug => "DEBUG",
438 Level::Trace => "TRACE",
439 }
440 }
441
442 pub fn to_level_filter(&self) -> LevelFilter {
444 match self {
445 Level::Error => LevelFilter::Error,
446 Level::Warn => LevelFilter::Warn,
447 Level::Info => LevelFilter::Info,
448 Level::Debug => LevelFilter::Debug,
449 Level::Trace => LevelFilter::Trace,
450 }
451 }
452}
453
454impl fmt::Display for Level {
455 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
456 f.pad(self.as_str())
457 }
458}
459
460#[repr(usize)]
462#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
463pub enum LevelFilter {
464 Off = 0,
466 Error = 100,
468 Warn = 200,
470 Info = 300,
472 Debug = 400,
474 Trace = 500,
476}
477
478impl LevelFilter {
479 pub fn as_str(&self) -> &'static str {
483 match self {
484 LevelFilter::Off => "OFF",
485 LevelFilter::Error => "ERROR",
486 LevelFilter::Warn => "WARN",
487 LevelFilter::Info => "INFO",
488 LevelFilter::Debug => "DEBUG",
489 LevelFilter::Trace => "TRACE",
490 }
491 }
492}
493
494impl fmt::Display for LevelFilter {
495 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
496 f.pad(self.as_str())
497 }
498}
499
500impl PartialEq<LevelFilter> for Level {
501 fn eq(&self, other: &LevelFilter) -> bool {
502 PartialEq::eq(&(*self as usize), &(*other as usize))
503 }
504}
505
506impl PartialOrd<LevelFilter> for Level {
507 fn partial_cmp(&self, other: &LevelFilter) -> Option<cmp::Ordering> {
508 Some(Ord::cmp(&(*self as usize), &(*other as usize)))
509 }
510}
511
512impl PartialEq<Level> for LevelFilter {
513 fn eq(&self, other: &Level) -> bool {
514 other.eq(self)
515 }
516}
517
518impl PartialOrd<Level> for LevelFilter {
519 fn partial_cmp(&self, other: &Level) -> Option<cmp::Ordering> {
520 Some(Ord::cmp(&(*self as usize), &(*other as usize)))
521 }
522}
523
524#[derive(Debug, PartialEq, Eq)]
526#[non_exhaustive]
527pub struct ParseLevelError {}
528
529impl fmt::Display for ParseLevelError {
530 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
531 fmt.write_str("malformed log level")
532 }
533}
534
535impl std::error::Error for ParseLevelError {}
536
537impl FromStr for Level {
538 type Err = ParseLevelError;
539 fn from_str(s: &str) -> Result<Level, Self::Err> {
540 for (name, level) in [
541 ("error", Level::Error),
542 ("warn", Level::Warn),
543 ("info", Level::Info),
544 ("debug", Level::Debug),
545 ("trace", Level::Trace),
546 ] {
547 if s.eq_ignore_ascii_case(name) {
548 return Ok(level);
549 }
550 }
551
552 Err(ParseLevelError {})
553 }
554}
555
556impl FromStr for LevelFilter {
557 type Err = ParseLevelError;
558 fn from_str(s: &str) -> Result<LevelFilter, Self::Err> {
559 for (name, level) in [
560 ("off", LevelFilter::Off),
561 ("error", LevelFilter::Error),
562 ("warn", LevelFilter::Warn),
563 ("info", LevelFilter::Info),
564 ("debug", LevelFilter::Debug),
565 ("trace", LevelFilter::Trace),
566 ] {
567 if s.eq_ignore_ascii_case(name) {
568 return Ok(level);
569 }
570 }
571
572 Err(ParseLevelError {})
573 }
574}