use super::{callsite, field};
use crate::stdlib::{fmt, str::FromStr};
pub struct Metadata<'a> {
name: &'static str,
target: &'a str,
level: Level,
module_path: Option<&'a str>,
file: Option<&'a str>,
line: Option<u32>,
fields: field::FieldSet,
kind: Kind,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Kind(KindInner);
#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
pub struct Level(LevelInner);
impl<'a> Metadata<'a> {
pub const fn new(
name: &'static str,
target: &'a str,
level: Level,
file: Option<&'a str>,
line: Option<u32>,
module_path: Option<&'a str>,
fields: field::FieldSet,
kind: Kind,
) -> Self {
Metadata {
name,
target,
level,
module_path,
file,
line,
fields,
kind,
}
}
pub fn fields(&self) -> &field::FieldSet {
&self.fields
}
pub fn level(&self) -> &Level {
&self.level
}
pub fn name(&self) -> &'static str {
self.name
}
pub fn target(&self) -> &'a str {
self.target
}
pub fn module_path(&self) -> Option<&'a str> {
self.module_path
}
pub fn file(&self) -> Option<&'a str> {
self.file
}
pub fn line(&self) -> Option<u32> {
self.line
}
#[inline]
pub fn callsite(&self) -> callsite::Identifier {
self.fields.callsite()
}
pub fn is_event(&self) -> bool {
self.kind.is_event()
}
pub fn is_span(&self) -> bool {
self.kind.is_span()
}
}
impl<'a> fmt::Debug for Metadata<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut meta = f.debug_struct("Metadata");
meta.field("name", &self.name)
.field("target", &self.target)
.field("level", &self.level);
if let Some(path) = self.module_path() {
meta.field("module_path", &path);
}
match (self.file(), self.line()) {
(Some(file), Some(line)) => {
meta.field("location", &format_args!("{}:{}", file, line));
}
(Some(file), None) => {
meta.field("file", &format_args!("{}", file));
}
(None, Some(line)) => {
meta.field("line", &line);
}
(None, None) => {}
};
meta.field("fields", &format_args!("{}", self.fields))
.field("callsite", &self.callsite())
.field("kind", &self.kind)
.finish()
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
enum KindInner {
Event,
Span,
}
impl Kind {
pub const EVENT: Kind = Kind(KindInner::Event);
pub const SPAN: Kind = Kind(KindInner::Span);
pub fn is_span(&self) -> bool {
match self {
Kind(KindInner::Span) => true,
_ => false,
}
}
pub fn is_event(&self) -> bool {
match self {
Kind(KindInner::Event) => true,
_ => false,
}
}
}
impl Level {
pub const ERROR: Level = Level(LevelInner::Error);
pub const WARN: Level = Level(LevelInner::Warn);
pub const INFO: Level = Level(LevelInner::Info);
pub const DEBUG: Level = Level(LevelInner::Debug);
pub const TRACE: Level = Level(LevelInner::Trace);
}
impl fmt::Display for Level {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
Level::TRACE => f.pad("TRACE"),
Level::DEBUG => f.pad("DEBUG"),
Level::INFO => f.pad("INFO"),
Level::WARN => f.pad("WARN"),
Level::ERROR => f.pad("ERROR"),
}
}
}
#[cfg(feature = "std")]
impl crate::stdlib::error::Error for ParseLevelError {}
impl FromStr for Level {
type Err = ParseLevelError;
fn from_str(s: &str) -> Result<Self, ParseLevelError> {
s.parse::<usize>()
.map_err(|_| ParseLevelError { _p: () })
.and_then(|num| match num {
1 => Ok(Level::ERROR),
2 => Ok(Level::WARN),
3 => Ok(Level::INFO),
4 => Ok(Level::DEBUG),
5 => Ok(Level::TRACE),
_ => Err(ParseLevelError { _p: () }),
})
.or_else(|_| match s {
s if s.eq_ignore_ascii_case("error") => Ok(Level::ERROR),
s if s.eq_ignore_ascii_case("warn") => Ok(Level::WARN),
s if s.eq_ignore_ascii_case("info") => Ok(Level::INFO),
s if s.eq_ignore_ascii_case("debug") => Ok(Level::DEBUG),
s if s.eq_ignore_ascii_case("trace") => Ok(Level::TRACE),
_ => Err(ParseLevelError { _p: () }),
})
}
}
#[repr(usize)]
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
enum LevelInner {
Error = 1,
Warn,
Info,
Debug,
Trace,
}
#[derive(Debug)]
pub struct ParseLevelError {
_p: (),
}
impl fmt::Display for ParseLevelError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.pad(
"error parsing level: expected one of \"error\", \"warn\", \
\"info\", \"debug\", \"trace\", or a number 1-5",
)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn level_from_str() {
assert_eq!("error".parse::<Level>().unwrap(), Level::ERROR);
assert_eq!("4".parse::<Level>().unwrap(), Level::DEBUG);
assert!("0".parse::<Level>().is_err())
}
}