1use std::env;
7use std::process;
8use std::time::Instant;
9
10use chrono::Local;
11
12use crate::log::{self, Level, Sink};
13
14const BAR: &str = "======================================================================";
15
16pub struct Banner {
19 name: String,
20 start: Instant,
21}
22
23pub fn begin(name: &str, argv_line: &str) -> Banner {
26 let now = Local::now();
27 let cwd = env::current_dir()
28 .map(|p| p.display().to_string())
29 .unwrap_or_default();
30 log::log(Level::Run, Sink::ConsoleOnly, "");
31 log::log(Level::Run, Sink::ConsoleOnly, BAR);
32 log::log(
33 Level::Run,
34 Sink::ConsoleOnly,
35 &format!(" {name} | started {}", now.format("%Y-%m-%d %H:%M:%S")),
36 );
37 log::log(Level::Run, Sink::ConsoleOnly, BAR);
38 log::log(Level::Run, Sink::ConsoleOnly, "");
39 log::log(
40 Level::Run,
41 Sink::FileOnly,
42 &format!(
43 "BEGIN argv=\"{argv_line}\" pid={} ver={} cwd={cwd}",
44 process::id(),
45 env!("CARGO_PKG_VERSION")
46 ),
47 );
48 Banner {
49 name: name.to_string(),
50 start: Instant::now(),
51 }
52}
53
54impl Drop for Banner {
55 fn drop(&mut self) {
56 let end = Local::now();
57 let elapsed = self.start.elapsed().as_secs_f64();
58 log::log(
59 Level::Run,
60 Sink::FileOnly,
61 &format!("END elapsed={elapsed:.1}s"),
62 );
63 log::log(Level::Run, Sink::ConsoleOnly, "");
64 log::log(Level::Run, Sink::ConsoleOnly, BAR);
65 log::log(
66 Level::Run,
67 Sink::ConsoleOnly,
68 &format!(
69 " {} | finished {} ({elapsed:.1}s)",
70 self.name,
71 end.format("%Y-%m-%d %H:%M:%S")
72 ),
73 );
74 log::log(Level::Run, Sink::ConsoleOnly, BAR);
75 log::log(Level::Run, Sink::ConsoleOnly, "");
76 }
77}
78
79#[cfg(test)]
80mod tests {
81 use super::*;
82
83 #[test]
84 fn bar_is_seventy_equals() {
85 assert_eq!(BAR.len(), 70);
86 assert!(BAR.chars().all(|c| c == '='));
87 }
88}