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#[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
31pub trait Bridge<T = ()> {
34 fn log(&self, msg: &str) -> T;
35}
36
37pub 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
86pub 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
101pub 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 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}