rust_loguru/formatters/
text.rs1use colored::Colorize;
2use std::fmt;
3
4use crate::formatters::FormatFn;
5use crate::formatters::FormatterTrait;
6use crate::level::LogLevel;
7use crate::record::Record;
8
9#[derive(Clone)]
11pub struct TextFormatter {
12 use_colors: bool,
14 include_timestamp: bool,
16 include_level: bool,
18 include_module: bool,
20 include_location: bool,
22 format_fn: Option<FormatFn>,
24}
25
26impl fmt::Debug for TextFormatter {
27 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
28 f.debug_struct("TextFormatter")
29 .field("use_colors", &self.use_colors)
30 .field("include_timestamp", &self.include_timestamp)
31 .field("include_level", &self.include_level)
32 .field("include_module", &self.include_module)
33 .field("include_location", &self.include_location)
34 .field("format_fn", &"<format_fn>")
35 .finish()
36 }
37}
38
39impl Default for TextFormatter {
40 fn default() -> Self {
41 Self {
42 use_colors: true,
43 include_timestamp: true,
44 include_level: true,
45 include_module: true,
46 include_location: true,
47 format_fn: None,
48 }
49 }
50}
51
52impl FormatterTrait for TextFormatter {
53 fn fmt(&self, record: &Record) -> String {
54 if let Some(format_fn) = &self.format_fn {
56 return format_fn(record);
57 }
58
59 let mut result = String::new();
60
61 if self.include_timestamp {
62 result.push_str(&record.timestamp().to_rfc3339());
63 result.push(' ');
64 }
65
66 if self.include_level {
67 let level_str = record.level().to_string();
68 if self.use_colors {
69 result.push_str(&match record.level() {
70 LogLevel::Trace => level_str.white().to_string(),
71 LogLevel::Debug => level_str.blue().to_string(),
72 LogLevel::Info => level_str.green().to_string(),
73 LogLevel::Warning => level_str.yellow().to_string(),
74 LogLevel::Error => level_str.red().to_string(),
75 LogLevel::Critical => level_str.red().bold().to_string(),
76 LogLevel::Success => level_str.green().bold().to_string(),
77 });
78 } else {
79 result.push_str(&level_str);
80 }
81 result.push_str(" - ");
82 }
83
84 if self.include_module {
85 let module = record.module();
86 if module != "unknown" {
87 result.push_str(module);
88 result.push(' ');
89 }
90 }
91
92 if self.include_location {
93 let file = record.file();
95 let line = record.line();
96 if file != "unknown" {
97 result.push_str(&format!("{}:{}", file, line));
98 result.push(' ');
99 }
100 }
101
102 result.push_str(record.message());
103
104 if !result.ends_with('\n') {
105 result.push('\n');
106 }
107
108 result
109 }
110
111 fn with_colors(&mut self, use_colors: bool) {
112 self.use_colors = use_colors;
113 }
114
115 fn with_timestamp(&mut self, include_timestamp: bool) {
116 self.include_timestamp = include_timestamp;
117 }
118
119 fn with_level(&mut self, include_level: bool) {
120 self.include_level = include_level;
121 }
122
123 fn with_module(&mut self, include_module: bool) {
124 self.include_module = include_module;
125 }
126
127 fn with_location(&mut self, include_location: bool) {
128 self.include_location = include_location;
129 }
130
131 fn with_pattern(&mut self, _pattern: String) {
132 }
134
135 fn with_format(&mut self, format_fn: FormatFn) {
136 self.format_fn = Some(format_fn);
137 }
138
139 fn box_clone(&self) -> Box<dyn FormatterTrait + Send + Sync> {
140 Box::new(self.clone())
141 }
142}
143
144#[cfg(test)]
145mod tests {
146 use super::*;
147 use crate::level::LogLevel;
148
149 #[test]
150 fn test_text_formatter_default() {
151 let formatter = TextFormatter::default();
152 let record = Record::new(
153 LogLevel::Info,
154 "Test message",
155 Some("test".to_string()),
156 Some("test.rs".to_string()),
157 Some(42),
158 );
159
160 let formatted = FormatterTrait::fmt(&formatter, &record);
161 assert!(formatted.contains("Test message"));
162 assert!(formatted.contains("INFO"));
163 assert!(formatted.contains("test"));
164 assert!(formatted.contains("test.rs:42"));
165 }
166
167 #[test]
168 fn test_text_formatter_no_colors() {
169 let mut formatter = TextFormatter::default();
170 formatter.with_colors(false);
171 let record = Record::new(
172 LogLevel::Info,
173 "Test message",
174 Some("test".to_string()),
175 Some("test.rs".to_string()),
176 Some(42),
177 );
178
179 let formatted = FormatterTrait::fmt(&formatter, &record);
180 assert!(formatted.contains("Test message"));
181 assert!(formatted.contains("INFO"));
182 assert!(formatted.contains("test"));
183 assert!(formatted.contains("test.rs:42"));
184 assert!(!formatted.contains("\x1b["));
185 }
186
187 #[test]
188 fn test_text_formatter_no_timestamp() {
189 let mut formatter = TextFormatter::default();
190 formatter.with_timestamp(false);
191 let record = Record::new(
192 LogLevel::Info,
193 "Test message",
194 Some("test".to_string()),
195 Some("test.rs".to_string()),
196 Some(42),
197 );
198
199 let formatted = FormatterTrait::fmt(&formatter, &record);
200 assert!(formatted.contains("Test message"));
201 assert!(formatted.contains("INFO"));
202 assert!(formatted.contains("test"));
203 assert!(formatted.contains("test.rs:42"));
204 assert!(!formatted.contains("2023")); }
206
207 #[test]
208 fn test_text_formatter_no_level() {
209 let mut formatter = TextFormatter::default();
210 formatter.with_level(false);
211 let record = Record::new(
212 LogLevel::Info,
213 "Test message",
214 Some("test".to_string()),
215 Some("test.rs".to_string()),
216 Some(42),
217 );
218
219 let formatted = FormatterTrait::fmt(&formatter, &record);
220 assert!(formatted.contains("Test message"));
221 assert!(!formatted.contains("INFO"));
222 assert!(formatted.contains("test"));
223 assert!(formatted.contains("test.rs:42"));
224 }
225
226 #[test]
227 fn test_text_formatter_no_module() {
228 let mut formatter = TextFormatter::default();
229 formatter.with_module(false);
230 let record = Record::new(
231 LogLevel::Info,
232 "Test message",
233 Some("test_module".to_string()),
234 Some("test.rs".to_string()),
235 Some(42),
236 );
237
238 let formatted = FormatterTrait::fmt(&formatter, &record);
239 assert!(formatted.contains("Test message"));
240 assert!(formatted.contains("INFO"));
241 assert!(!formatted.contains("test_module"));
242 assert!(formatted.contains("test.rs:42"));
243 }
244
245 #[test]
246
247 fn test_text_formatter_custom_format() {
248 use std::sync::Arc;
249 let mut formatter = TextFormatter::default();
250 formatter.with_format(Arc::new(|record: &Record| {
251 "CUSTOM: ".to_string() + record.message()
252 }));
253 let record = Record::new(
254 LogLevel::Info,
255 "Test message",
256 Some("test".to_string()),
257 Some("test.rs".to_string()),
258 Some(42),
259 );
260
261 let formatted = FormatterTrait::fmt(&formatter, &record);
262 assert_eq!(formatted, "CUSTOM: Test message");
263 }
264}