logger_rs/
logger.rs

1use std::default::Default;
2use std::fmt;
3use std::fs::OpenOptions;
4use std::io;
5use std::io::Write;
6use std::path::Path;
7
8use crate::style::{DefaultStyle, Style};
9
10/// Level of importance of the message
11#[derive(Clone, Copy, PartialEq)]
12pub enum Importance {
13    Warn,
14    Debug,
15    Success,
16    Fail,
17}
18
19impl fmt::Display for Importance {
20    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
21        let s = match self {
22            Importance::Warn => "Warn",
23            Importance::Debug => "Debug",
24            Importance::Success => "Success",
25            Importance::Fail => "Error",
26        };
27        write!(f, "[{}]", s)
28    }
29}
30
31/// The bridge receives a message and transfers it to a specific destination (e.g: console or a file).
32/// You can make your own bridge by implementing this trait.
33pub trait Bridge<T = ()> {
34    fn log(&self, msg: &str) -> T;
35}
36
37/// Main Logger. Each logger has its own bridge where the messages are transferred.
38pub struct Logger<T> {
39    bridge: Box<dyn Bridge<T>>,
40    style: Box<dyn Style>,
41}
42
43impl Default for Logger<()> {
44    fn default() -> Self {
45        Logger {
46            style: Box::new(DefaultStyle::default()),
47            bridge: Box::new(Console::default()),
48        }
49    }
50}
51
52impl<T> Logger<T> {
53    pub fn style(self, style: Box<dyn Style>) -> Self {
54        Logger { style, ..self }
55    }
56
57    pub fn bridge<U>(self, bridge: Box<dyn Bridge<U>>) -> Logger<U> {
58        Logger {
59            bridge,
60            style: self.style,
61        }
62    }
63
64    fn log(&self, imp: Importance, msg: &str) -> T {
65        let msg = self.style.format(imp, msg);
66        self.bridge.log(&msg)
67    }
68
69    pub fn fail<M: AsRef<str>>(&self, msg: M) -> T {
70        self.log(Importance::Fail, msg.as_ref())
71    }
72
73    pub fn warn<M: AsRef<str>>(&self, msg: M) -> T {
74        self.log(Importance::Warn, msg.as_ref())
75    }
76
77    pub fn debug<M: AsRef<str>>(&self, msg: M) -> T {
78        self.log(Importance::Debug, msg.as_ref())
79    }
80
81    pub fn success<M: AsRef<str>>(&self, msg: M) -> T {
82        self.log(Importance::Success, msg.as_ref())
83    }
84}
85
86/// Bridge used to log onto the console.
87pub struct Console;
88
89impl Default for Console {
90    fn default() -> Self {
91        Console
92    }
93}
94
95impl Bridge for Console {
96    fn log(&self, msg: &str) {
97        println!("{}", msg);
98    }
99}
100
101/// Bridge used to log inside a file.
102/// There are two destination files, one for normal logs and the other for the errors.
103/// The default destinations are both the same.
104pub struct File<'a> {
105    out: &'a Path,
106    append: bool,
107}
108
109impl<'a> File<'a> {
110    pub fn new<T: Into<&'a Path>>(path: T) -> Self {
111        File {
112            out: path.into(),
113            append: true,
114        }
115    }
116
117    /// Whether to overwrite the file completely or append the logs.
118    pub fn append(self, append: bool) -> Self {
119        File { append, ..self }
120    }
121
122    fn write(&self, content: &str) -> io::Result<()> {
123        let mut file = OpenOptions::new()
124            .append(self.append)
125            .create(true)
126            .write(true)
127            .open(self.out)?;
128        file.write_all(content.as_bytes())?;
129        Ok(())
130    }
131}
132
133impl Bridge<io::Result<()>> for File<'_> {
134    fn log(&self, msg: &str) -> io::Result<()> {
135        self.write(&msg)
136    }
137}