use crate::styled::{Color, Styled};
use std::fmt::{Display, Formatter};
pub(crate) use wasm_bindgen::prelude::wasm_bindgen;
#[wasm_bindgen]
extern "C" {
#[wasm_bindgen(js_namespace = console)]
pub fn log(s: &str);
}
#[derive(Debug, Default)]
pub enum Level {
Trace,
Debug,
#[default]
Info,
Warn,
Error,
Fatal,
Off,
}
impl Level {
pub(crate) fn ignore(&self) -> bool {
matches!(self, Level::Off)
}
pub(crate) fn wrap(&self, content: impl AsRef<str>) -> String {
let styled = self.to_string();
if let Some(pos) = styled.rfind("]") {
let (first_half, remaining) = styled.split_at(pos);
format!("{}@{}{}", first_half, content.as_ref(), remaining)
} else {
styled
}
}
}
impl Display for Level {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
const EMPTY: &'static str = "";
match self {
Self::Trace => write!(
f,
"{}",
Styled::with_content("[TRACE]")
.colored(Color::Green)
.into_log()
),
Self::Debug => write!(
f,
"{}",
Styled::with_content("[DEBUG]")
.colored(Color::Purple)
.into_log()
),
Self::Info => write!(
f,
"{}",
Styled::with_content("[INFO]")
.colored(Color::Cyan)
.into_log()
),
Self::Warn => write!(
f,
"{}",
Styled::with_content("[WARN]")
.colored(Color::Yellow)
.into_log()
),
Self::Error => write!(
f,
"{}",
Styled::with_content("[ERROR]")
.colored(Color::BrightRed)
.into_log()
),
Self::Fatal => write!(
f,
"{}",
Styled::with_content("[FATAL]")
.colored(Color::Red)
.into_log()
),
_ => write!(f, "{}", EMPTY),
}
}
}
#[wasm_bindgen]
#[derive(Clone, Debug)]
pub struct Logger(String);
impl Logger {
pub fn new_builder() -> Builder {
Builder::new()
}
pub fn print(&self) {
log(self.as_ref())
}
pub fn print_fmt(&self, fmt: impl Display) {
let formatted = format!("{}\t{}", self.as_ref(), fmt.to_string());
log(&formatted)
}
}
impl AsRef<str> for Logger {
fn as_ref(&self) -> &str {
&self.0
}
}
pub struct Builder {
level: Level,
name: Option<String>,
content: Option<String>,
}
impl Builder {
pub(crate) fn new() -> Self {
Self {
name: None,
level: Level::Off,
content: None,
}
}
pub fn with_level(mut self, level: Level) -> Self {
self.level = level;
self
}
pub fn named(mut self, name: impl AsRef<str>) -> Self {
self.name = Some(name.as_ref().into());
self
}
pub fn build(self) -> Logger {
let Self { name, level, content } = self;
let mut fmt = String::new();
if !level.ignore() {
if let Some(name) = name {
fmt.push_str(&level.wrap(name));
} else {
fmt.push_str(&format!("{}", level));
}
}
if let Some(content) = content {
fmt.push_str(&format!("{}", content));
}
Logger(fmt)
}
}