1use crate::*;
2use std::sync::{Arc, Mutex};
3
4
5pub(crate) enum Ctx {
6 Raw(String),
7 Context(Context),
8}
9
10
11type Ctxs = Arc<Mutex<Vec<Ctx>>>;
12
13
14pub(crate) enum SessionSrc<'a> {
15 Logger (&'a Logger),
16 Session(&'a Session),
17}
18
19
20pub struct Session {
30 name : String,
31 source : Source,
32 writer : Writer,
33 for_write : fn(&Context) -> String,
34 for_print : fn(&Context) -> String,
35 write_level: Level,
36 print_level: Level,
37 ctxs : Ctxs,
38 parent : Option<Ctxs>,
39 silent : Mutex<bool>,
40}
41
42
43impl Session {
44 #[track_caller]
45 pub(crate) fn new(name: impl Into<String>, source: SessionSrc<'_>, silent: bool) -> Self {
46 let name = name.into();
47
48 let (
49 source,
50 writer,
51 for_write,
52 for_print,
53 write_level,
54 print_level,
55 parent,
56 ) = match source {
57 SessionSrc::Logger(l) => (
58 Source::new(&l.name).session(&name),
59 l.writer.clone(),
60 l.for_write,
61 l.for_print,
62 l.write_level,
63 l.print_level,
64 None,
65 ),
66
67 SessionSrc::Session(s) => (
68 s.source.session(&name),
69 s.writer.clone(),
70 s.for_write,
71 s.for_print,
72 s.write_level,
73 s.print_level,
74 Some(s.ctxs.clone()),
75 ),
76 };
77
78 let header = Context::new_header(source.clone());
79 if !silent {
80 println!("{}", for_print(&header));
81 }
82
83 Self {
84 ctxs: Arc::new(Mutex::new(
85 vec![Ctx::Context(header)])),
86 silent: Mutex::new(silent),
87 name,
88 parent,
89 source,
90 writer,
91 for_write,
92 for_print,
93 write_level,
94 print_level,
95 }
96 }
97
98 #[track_caller]
103 pub fn session(&self, name: impl Into<String>, silent: bool) -> Self {
104 Self::new(name, SessionSrc::Session(self), silent)
105 }
106
107 #[track_caller]
112 pub fn session_then<F, T>(&self, name: impl Into<String>, silent: bool, callable: F) -> T
113 where F: FnOnce(Self) -> T
114 {
115 callable(self.session(name, silent))
116 }
117}
118
119
120impl Drop for Session {
121 fn drop(&mut self) {
122 if *self.silent.lock().unwrap() { return; }
123
124 let mut ctxs = self.ctxs.lock().unwrap();
125
126 let Ctx::Context(header) = ctxs.first().unwrap()
127 else { unreachable!() };
128
129 let footer = Context::new_footer(
130 self.source.clone(),
131 &header.start().unwrap(),
132 *header.location());
133
134 println!("{}", (self.for_print)(&footer));
135
136 let mut lines = Vec::new();
137
138 lines.push("┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━".to_string());
139
140 if self.parent.is_none() {
141 lines.push(format!("┃ Logger : {}", self.source.logger()));
142 }
143
144 lines.push(format!("┃ Session: {}", self.name));
145 lines.push(format!("┃ Elapsed: {:?}", footer.elapsed().unwrap()));
146 lines.push("┃".to_string());
147
148 ctxs.push(Ctx::Context(footer));
149
150 for ctx in ctxs.drain(..) {
151 match ctx {
152 Ctx::Raw(string) => {
153 let tmp = string.trim_start_matches("┃");
154
155 if tmp.starts_with("┏━")
156 || tmp.starts_with("┗━")
157 {
158 lines.push(format!("┃{}", &string[..string.len()-3]));
159 continue;
160 }
161
162 lines.push(format!("┃{string}"));
163 }
164
165 Ctx::Context(ctx) => {
166 for line in (self.for_write)(&ctx).split('\n') {
167 lines.push(format!("┃ {line}"));
168 }
169 }
170 }
171 }
172
173 lines.push("┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━".to_string());
174
175 match &self.parent {
176 Some(parent) => {
177 let mut parent = parent.lock().unwrap();
178
179 for line in lines {
180 parent.push(Ctx::Raw(line));
181 }
182 }
183
184 None => {
185 self.writer.lock().unwrap()
186 .write(lines.join("\n"));
187 }
188 }
189 }
190}
191
192
193impl LoggableInner for Session {
194 fn log(&self, level: Level, message: &str) {
195 let mut ctxs = self.ctxs.lock().unwrap();
196
197 let ctx = Context::new_message(
198 self.source.clone(), level, message);
199
200 if level >= self.print_level {
201 let mut silent = self.silent.lock().unwrap();
202
203 if *silent {
204 let Ctx::Context(header) = ctxs.first().unwrap()
205 else { unreachable!() };
206
207 println!("{}", (self.for_print)(&header));
208 *silent = false;
209 }
210
211 println!("{}", (self.for_print)(&ctx));
212 }
213
214 if level >= self.write_level {
215 ctxs.push(Ctx::Context(ctx));
216 }
217 }
218}
219
220
221impl Loggable for Session {
222 fn root_name(&self) -> &str {
223 self.source.logger().as_ref()
224 }
225
226 fn name(&self) -> &str {
227 self.name.as_str()
228 }
229
230 fn path(&self) -> String {
231 self.writer.lock().unwrap().path.clone()
232 }
233
234 fn write_level(&self) -> Level {
235 self.write_level
236 }
237
238 fn print_level(&self) -> Level {
239 self.print_level
240 }
241}