datex_core/utils/
logger.rs

1use crate::datex_values::Value;
2
3use super::color::{Color, AnsiCodes};
4
5pub struct Logger<'a> {
6	name: String,
7	is_production: bool,
8	formatting: LogFormatting,
9	context: &'a LoggerContext
10}
11
12#[derive(Clone, Copy)]
13pub enum LogLevel {
14    VERBOSE,
15    DEFAULT,
16    WARNING,
17    ERROR,
18}
19
20#[derive(PartialEq)]
21pub enum LogFormatting {
22    PlainText,
23    Color4Bit,
24    ColorRGB
25}
26
27pub struct LoggerContext {
28	pub log_redirect: Option<fn(&str)->()>
29}
30
31static DEVELOPMENT_LOG_LEVEL:u8 = LogLevel::VERBOSE as u8;
32static PRODUCTION_LOG_LEVEL:u8 = LogLevel::VERBOSE  as u8;
33
34
35impl Logger<'_> {
36
37
38	pub fn new<'a>(context: &'a LoggerContext, name:&'a str, is_production:bool, formatting:LogFormatting) -> Logger<'a> {
39		return Logger {name:(*name).to_string(), is_production, formatting, context}
40	}
41	pub fn new_for_production<'a>(context: &'a LoggerContext, name:&'a str) -> Logger<'a> {
42		return Logger {name:(*name).to_string(), is_production:true, formatting:LogFormatting::ColorRGB, context}
43	}
44	pub fn new_for_development<'a>(context: &'a LoggerContext, name:&'a str) -> Logger<'a> {
45		return Logger {name:(*name).to_string(), is_production:false, formatting:LogFormatting::ColorRGB, context}
46	}
47
48
49
50	fn log(&self, text:&str, data: &Vec<Box<dyn Value>>, color:Color, log_level:LogLevel, only_log_own_stream:bool, add_tag:bool) {
51		if !self.log_level_allowed(log_level.clone()) {return}
52
53		let formatted = self.generate_log_string(text, data, color, add_tag);
54		self.log_raw(&formatted, log_level, only_log_own_stream);
55	}
56
57	fn generate_log_string(&self, text:&str, _data: &Vec<Box<dyn Value>>, color:Color, add_tag:bool) -> String {
58		let message = text;
59		let end = if self.formatting == LogFormatting::PlainText {""} else {AnsiCodes::RESET};
60
61		if add_tag {format!("{}{}{}", self.get_tag(color), message, end)}
62		else {format!("{}{}", message, end)}
63	}
64
65	fn log_raw(&self, text:&str, log_level:LogLevel, _only_log_own_stream:bool) {
66
67        if !self.log_level_allowed(log_level) {return}
68
69
70		let handler = self.context.log_redirect;
71
72		// log handler
73		if handler.is_some() {(handler.as_ref().unwrap())(text)}
74		// default std out
75		else {println!("{}", text)}
76	}
77
78	// check if the current production/development log level includes a log level
79	fn log_level_allowed(&self, log_level:LogLevel) -> bool {
80		let log_level_u8 = log_level as u8;
81		
82		if self.is_production && (log_level_u8 < PRODUCTION_LOG_LEVEL) {false} // don't log for production
83        else if !self.is_production && (log_level_u8 < DEVELOPMENT_LOG_LEVEL) {false} // don't log for development
84		else {true}
85	}
86
87	fn get_tag(&self, color:Color) -> String {
88		let color_esc = self.get_formatting_color(color);
89		let mut tag = "".to_string();
90
91        // handle tag:
92        let esc_tag = self.formatting != LogFormatting::PlainText;
93
94        // start tag
95        if esc_tag {
96			let end = if self.formatting == LogFormatting::ColorRGB {AnsiCodes::BOLD.to_string()} else {"".to_string()};
97			tag += &format!("{}{}{}{}{}", AnsiCodes::INVERSE, AnsiCodes::UNDERLINE, self.get_formatting_color_bg(Color::BLACK), color_esc, end);
98        }
99
100		if self.formatting == LogFormatting::PlainText {tag += &format!("[{}]", self.name)}
101        else {tag +=  &format!(" {} ", self.name)}
102
103        // tag content
104        // if (this.origin) {
105        //     if (this.formatting == LogFormatting.PLAINTEXT) tag += `[${this.origin}]`;
106        //     else tag +=  " " + this.origin  + " ";
107        // }
108        // if (this.pointer) {
109        //     if (this.formatting == LogFormatting.PLAINTEXT) tag += `[${this.pointer}]`;
110        //     else tag += ESCAPE_SEQUENCES.INVERSE+ESCAPE_SEQUENCES.UNDERLINE + this.getFormattingColor(COLOR.POINTER) + " " + this.pointer + " ";
111        // }
112        // end tag
113        if esc_tag {tag += &format!("{} {}", AnsiCodes::RESET, color_esc)}
114
115        return tag;
116	}
117
118	fn get_formatting_color(&self, color:Color) -> String {
119        if self.formatting == LogFormatting::Color4Bit {return color.as_ansi_4_bit().to_string()}
120        else if self.formatting == LogFormatting::ColorRGB {return color.as_ansi_rgb()}
121        else if self.formatting == LogFormatting::PlainText {return "".to_string()}
122        else {return AnsiCodes::COLOR_DEFAULT.to_string()}
123    }
124
125	fn get_formatting_color_bg(&self, color:Color) -> String {
126        if self.formatting == LogFormatting::Color4Bit {return color.as_ansi_4_bit_bg().to_string()}
127        else if self.formatting == LogFormatting::ColorRGB {return color.as_ansi_rgb_bg()}
128        else if self.formatting == LogFormatting::PlainText {return "".to_string()}
129        else {return AnsiCodes::COLOR_DEFAULT.to_string()}
130    }
131
132	// public log methods
133
134	pub fn success(&self, message:&str) {
135		self.log(message, &Vec::new(), Color::GREEN, LogLevel::DEFAULT, false, true);
136	}
137
138	pub fn error(&self, message:&str) {
139		self.log(message, &Vec::new(), Color::RED, LogLevel::ERROR, false, true);
140	}
141
142	pub fn warn(&self, message:&str) {
143		self.log(message, &Vec::new(), Color::YELLOW, LogLevel::WARNING, false, true);
144	}
145
146	pub fn info(&self, message:&str) {
147		self.log(message, &Vec::new(), Color::DEFAULT, LogLevel::DEFAULT, false, true);
148	}
149
150	pub fn debug(&self, message:&str) {
151		self.log(message, &Vec::new(), Color::CYAN, LogLevel::VERBOSE, false, true);
152	}
153
154	pub fn plain(&self, message:&str) {
155		self.log(message, &Vec::new(), Color::WHITE, LogLevel::DEFAULT, false, false);
156	}
157}