use crate::{ColorMode, Target, Targetable};
use std::{borrow::Cow, fmt::Display, sync::Arc, time::Instant};
use trillium_client::{ClientHandler, Conn, Result};
pub mod formatters;
pub use formatters::dev_formatter;
pub use trillium_logger_macros::client_log_format;
pub trait ClientLogFormatter: Send + Sync + 'static {
type Output: Display + Send + Sync + 'static;
fn format(&self, conn: &Conn, color: bool) -> Self::Output;
}
#[derive(Copy, Clone, Debug)]
pub(crate) struct RequestStart(pub(crate) Instant);
pub struct ClientLogger<F> {
format: F,
color_mode: ColorMode,
target: Arc<dyn Targetable>,
}
impl<F> std::fmt::Debug for ClientLogger<F> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ClientLogger")
.field("color_mode", &self.color_mode)
.finish_non_exhaustive()
}
}
impl ClientLogger<()> {
pub fn new() -> ClientLogger<impl ClientLogFormatter> {
ClientLogger {
format: dev_formatter,
color_mode: ColorMode::Auto,
target: Arc::new(Target::Stdout),
}
}
}
impl<T> ClientLogger<T> {
pub fn with_formatter<Formatter: ClientLogFormatter>(
self,
formatter: Formatter,
) -> ClientLogger<Formatter> {
ClientLogger {
format: formatter,
color_mode: self.color_mode,
target: self.target,
}
}
}
impl<F: ClientLogFormatter> ClientLogger<F> {
pub fn with_color_mode(mut self, color_mode: ColorMode) -> Self {
self.color_mode = color_mode;
self
}
pub fn with_target(mut self, target: impl Targetable) -> Self {
self.target = Arc::new(target);
self
}
}
impl<F: ClientLogFormatter> ClientHandler for ClientLogger<F> {
async fn run(&self, conn: &mut Conn) -> Result<()> {
conn.insert_state(RequestStart(Instant::now()));
Ok(())
}
async fn after_response(&self, conn: &mut Conn) -> Result<()> {
let output = self.format.format(conn, self.color_mode.is_enabled());
self.target.write(output.to_string());
Ok(())
}
fn name(&self) -> Cow<'static, str> {
"trillium-logger ClientLogger".into()
}
}
pub fn client_logger() -> ClientLogger<impl ClientLogFormatter> {
ClientLogger::new()
}