use std::{
env,
fmt::Debug,
marker::PhantomData,
};
pub trait LogAgent {
type Item;
fn new() -> Self;
fn create_log( item: Self::Item ) -> Self;
fn append_log( &mut self, item: Self::Item );
}
impl<T> LogAgent for Vec<T> {
type Item = T;
fn new() -> Self { Vec::new() }
fn create_log( item: T ) -> Self { vec![ item ] }
fn append_log( &mut self, item: T ) { self.push( item ); }
}
impl<T> LogAgent for PhantomData<T> {
type Item = T;
fn new() -> Self { PhantomData }
fn create_log( _item: T ) -> Self { PhantomData }
fn append_log( &mut self, _item: T ) {}
}
impl LogAgent for String {
type Item = String;
fn new() -> Self { String::new() }
fn create_log( item: String ) -> Self { item }
fn append_log( &mut self, item: String ) { self.push_str( &format!( "\n{}", item )); }
}
#[derive( PartialEq,Eq )]
pub struct Log<Inner, Agent: LogAgent = Vec<Frame>> {
pub error : Inner,
pub agent : Agent,
}
#[cfg( not( feature = "pretty_log" ))]
impl<Inner,Agent> Debug for Log<Inner,Agent>
where Inner: Debug
, Agent: Debug + LogAgent
{
fn fmt( &self, f: &mut std::fmt::Formatter ) -> std::fmt::Result {
f.debug_struct("Log")
.field( "error", &self.error )
.field( "agent", &self.agent )
.finish()
}
}
#[cfg( feature = "pretty_log" )]
impl<Inner,Agent> Debug for Log<Inner,Agent>
where Inner: Debug
, Agent: Debug + LogAgent
{
fn fmt( &self, f: &mut std::fmt::Formatter ) -> std::fmt::Result {
if f.alternate() {
f.debug_struct("Log")
.field( "error", &self.error )
.field( "agent", &self.agent )
.finish()
} else {
write!( f, "{:#?}", self )
}
}
}
pub type NoLog<Inner,Item=Frame> = Log<Inner,PhantomData<Item>>;
pub trait ToLog<Agent> : Sized
where Agent : LogAgent
{
fn new_log( self ) -> Log<Self,Agent>;
fn to_log( self, item: Agent::Item ) -> Log<Self,Agent>;
}
impl<Inner,Agent> ToLog<Agent> for Inner
where Agent : LogAgent
{
fn new_log( self ) -> Log<Self,Agent> {
Log{ error: self, agent: Agent::new() }
}
fn to_log( self, item: Agent::Item ) -> Log<Inner,Agent> {
Log{ error: self, agent: Agent::create_log( item )}
}
}
pub trait Logger<Agent> : Sized
where Agent : LogAgent
{
fn log( self, item: Agent::Item ) -> Self;
}
impl<Agent,E> Logger<Agent> for Log<E,Agent>
where Agent : LogAgent
{
fn log( mut self, item: Agent::Item ) -> Self {
self.agent.append_log( item );
self
}
}
macro_rules! impl_logger_for_predefined_enumx {
($($enumx:ident => $($_index:ident $gen:ident)*;)+) => {
use enumx::prelude::*;
$(
impl<Agent$(,$gen)*> Logger<Agent> for $enumx<$($gen),*>
where Agent : LogAgent
$(, $gen : Logger<Agent> )*
{
fn log( self, _item: Agent::Item ) -> Self {
match self {
$( $enumx::$_index( $_index ) => $enumx::$_index( Logger::<Agent>::log( $_index, _item )), )*
}
}
}
)+
};
}
impl_logger_for_predefined_enumx! {
Enum0 => ;
Enum1 => _0 T0;
Enum2 => _0 T0 _1 T1;
Enum3 => _0 T0 _1 T1 _2 T2;
Enum4 => _0 T0 _1 T1 _2 T2 _3 T3;
Enum5 => _0 T0 _1 T1 _2 T2 _3 T3 _4 T4;
Enum6 => _0 T0 _1 T1 _2 T2 _3 T3 _4 T4 _5 T5;
Enum7 => _0 T0 _1 T1 _2 T2 _3 T3 _4 T4 _5 T5 _6 T6;
Enum8 => _0 T0 _1 T1 _2 T2 _3 T3 _4 T4 _5 T5 _6 T6 _7 T7;
Enum9 => _0 T0 _1 T1 _2 T2 _3 T3 _4 T4 _5 T5 _6 T6 _7 T7 _8 T8;
Enum10 => _0 T0 _1 T1 _2 T2 _3 T3 _4 T4 _5 T5 _6 T6 _7 T7 _8 T8 _9 T9;
Enum11 => _0 T0 _1 T1 _2 T2 _3 T3 _4 T4 _5 T5 _6 T6 _7 T7 _8 T8 _9 T9 _10 T10;
Enum12 => _0 T0 _1 T1 _2 T2 _3 T3 _4 T4 _5 T5 _6 T6 _7 T7 _8 T8 _9 T9 _10 T10 _11 T11;
Enum13 => _0 T0 _1 T1 _2 T2 _3 T3 _4 T4 _5 T5 _6 T6 _7 T7 _8 T8 _9 T9 _10 T10 _11 T11 _12 T12;
Enum14 => _0 T0 _1 T1 _2 T2 _3 T3 _4 T4 _5 T5 _6 T6 _7 T7 _8 T8 _9 T9 _10 T10 _11 T11 _12 T12 _13 T13;
Enum15 => _0 T0 _1 T1 _2 T2 _3 T3 _4 T4 _5 T5 _6 T6 _7 T7 _8 T8 _9 T9 _10 T10 _11 T11 _12 T12 _13 T13 _14 T14;
Enum16 => _0 T0 _1 T1 _2 T2 _3 T3 _4 T4 _5 T5 _6 T6 _7 T7 _8 T8 _9 T9 _10 T10 _11 T11 _12 T12 _13 T13 _14 T14 _15 T15;
}
#[derive( Debug, PartialEq, Eq )]
pub struct Env<Agent: LogAgent>( Agent );
impl<Agent> LogAgent for Env<Agent>
where Agent : LogAgent
{
type Item = <Agent as LogAgent>::Item;
fn new() -> Self {
Env( Agent::new() )
}
fn append_log( &mut self, item: Self::Item ) {
if env_log_enabled() {
self.0.append_log( item );
}
}
fn create_log( item: Self::Item ) -> Self {
if env_log_enabled() {
Env( Agent::create_log( item ))
} else {
Env( Agent::new() )
}
}
}
fn env_log_enabled() -> bool {
env::var( "RUST_BACKTRACE" )
.map( |value| value == "1" || value == "full" )
.unwrap_or( false )
}
pub trait MapToLog<Agent> : Sized
where Agent : LogAgent
{
type Output;
fn map_to_log( self, item: Agent::Item ) -> Self::Output;
}
impl<Agent,T,E> MapToLog<Agent> for Result<T,E>
where E : ToLog<Agent>
, Agent : LogAgent
{
type Output = Result<Log<T,Agent>, E>;
fn map_to_log( self, item: Agent::Item ) -> Self::Output {
self.map( |v| v.to_log( item ))
}
}
pub trait MapErrToLog<Agent> : Sized
where Agent : LogAgent
{
type Output;
fn map_err_to_log( self, item: Agent::Item ) -> Self::Output;
}
impl<Agent,T,E> MapErrToLog<Agent> for Result<T,E>
where E : ToLog<Agent>
, Agent : LogAgent
{
type Output = Result<T, Log<E,Agent>>;
fn map_err_to_log( self, item: Agent::Item ) -> Self::Output {
self.map_err( |e| e.to_log( item ))
}
}
pub trait MapLog<Agent> : Sized
where Agent : LogAgent
{
type Output;
fn map_log( self, item: Agent::Item ) -> Self::Output;
}
impl<Agent,T,E> MapLog<Agent> for Result<T,E>
where T : Logger<Agent>
, Agent : LogAgent
{
type Output = Self;
fn map_log( self, item: Agent::Item ) -> Self::Output {
self.map( |e| e.log( item ))
}
}
pub trait MapErrLog<Agent> : Sized
where Agent : LogAgent
{
type Output;
fn map_err_log( self, item: Agent::Item ) -> Self::Output;
}
impl<Agent,T,E> MapErrLog<Agent> for Result<T,E>
where E : Logger<Agent>
, Agent : LogAgent
{
type Output = Self;
fn map_err_log( self, item: Agent::Item ) -> Self::Output {
self.map_err( |e| e.log( item ))
}
}
#[derive( Debug,Default,PartialEq,Eq,PartialOrd,Ord )]
pub struct Frame {
pub module : &'static str,
pub file : &'static str,
pub line : u32,
pub column : u32,
pub info : Option<String>,
}
impl Frame {
pub fn new( module: &'static str, file: &'static str, line: u32, column: u32, info: Option<String> ) -> Self {
Frame{ module, file, line, column, info }
}
}
#[macro_export]
macro_rules! frame {
( $expr:expr ) => {
Frame::new( module_path!(), file!(), line!(), column!(), Some( String::from( $expr )))
};
() => {
Frame::new( module_path!(), file!(), line!(), column!(), None )
};
}