1use crate::ktlint::console::{Console, LINE_SEPARATOR};
5
6pub const KTLINT_COMMAND_LINE: &str = "io.github.ktlint.core.cli.internal.KtlintCommandLine";
7pub const FILE_UTILS: &str = "io.github.ktlint.core.cli.internal.FileUtils";
8pub const REPORTER_AGGREGATOR: &str = "io.github.ktlint.core.cli.internal.ReporterAggregator";
9pub const KTLINT_SERVICE_LOADER: &str = "io.github.ktlint.core.cli.internal.KtlintServiceLoader";
10pub const LOAD_RULE_PROVIDERS: &str = "io.github.ktlint.core.cli.internal.LoadRuleProviders";
11pub const BASELINE: &str = "io.github.ktlint.core.cli.reporter.baseline.Baseline";
12pub const EDITOR_CONFIG_DEFAULTS_LOADER: &str = "io.github.ktlint.core.rule.engine.internal.EditorConfigDefaultsLoader";
13pub const GENERATE_EDITOR_CONFIG_SUB_COMMAND: &str = "io.github.ktlint.core.cli.internal.GenerateEditorConfigSubCommand";
14
15#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
16pub enum Level {
17 Trace,
18 Debug,
19 Info,
20 Warn,
21 Error,
22 Off,
24}
25
26impl Level {
27 pub fn parse(value: &str) -> Result<Level, String> {
29 match value.to_uppercase().as_str() {
30 "TRACE" => Ok(Level::Trace),
31 "DEBUG" => Ok(Level::Debug),
32 "INFO" => Ok(Level::Info),
33 "WARN" => Ok(Level::Warn),
34 "ERROR" => Ok(Level::Error),
35 "NONE" => Ok(Level::Off),
36 _ => Err(format!("Invalid log level '{value}'")),
37 }
38 }
39
40 fn name(self) -> &'static str {
41 match self {
42 Level::Trace => "TRACE",
43 Level::Debug => "DEBUG",
44 Level::Info => "INFO",
45 Level::Warn => "WARN",
46 Level::Error => "ERROR",
47 Level::Off => "OFF",
48 }
49 }
50}
51
52#[derive(Clone)]
53pub struct Logger {
54 console: Console,
55 min_level: Level,
56}
57
58impl Logger {
59 pub fn new(console: Console, min_level: Level) -> Logger {
60 Logger { console, min_level }
61 }
62
63 pub fn is_enabled(&self, level: Level) -> bool {
64 level != Level::Off && level >= self.min_level
65 }
66
67 pub fn log(&self, level: Level, logger: &str, message: impl FnOnce() -> String) {
69 if self.is_enabled(level) {
70 self.console
71 .out(&format!("{} [main] {} {logger} -- {}{LINE_SEPARATOR}", local_time(), level.name(), message()));
72 }
73 }
74
75 pub fn trace(&self, logger: &str, message: impl FnOnce() -> String) {
76 self.log(Level::Trace, logger, message);
77 }
78
79 pub fn debug(&self, logger: &str, message: impl FnOnce() -> String) {
80 self.log(Level::Debug, logger, message);
81 }
82
83 pub fn info(&self, logger: &str, message: impl FnOnce() -> String) {
84 self.log(Level::Info, logger, message);
85 }
86
87 pub fn warn(&self, logger: &str, message: impl FnOnce() -> String) {
88 self.log(Level::Warn, logger, message);
89 }
90
91 pub fn error(&self, logger: &str, message: impl FnOnce() -> String) {
92 self.log(Level::Error, logger, message);
93 }
94}
95
96fn local_time() -> String {
98 let (hour, minute, second, millis) = local_clock();
99 format!("{hour:02}:{minute:02}:{second:02}.{millis:03}")
100}
101
102#[cfg(windows)]
103fn local_clock() -> (u16, u16, u16, u16) {
104 #[link(name = "kernel32")]
105 unsafe extern "system" {
106 fn GetLocalTime(system_time: *mut [u16; 8]);
107 }
108 let mut st = [0u16; 8];
109 unsafe { GetLocalTime(&mut st) };
111 (st[4], st[5], st[6], st[7])
112}
113
114#[cfg(all(unix, target_pointer_width = "64"))]
115fn local_clock() -> (u16, u16, u16, u16) {
116 unsafe extern "C" {
117 fn localtime_r(time: *const i64, tm: *mut [i32; 16]) -> *mut [i32; 16];
118 }
119 let now = std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH).unwrap_or_default();
120 let secs = now.as_secs() as i64;
121 let mut tm = [0i32; 16];
123 let ok = unsafe { !localtime_r(&secs, &mut tm).is_null() };
125 if ok {
126 (tm[2] as u16, tm[1] as u16, tm[0] as u16, now.subsec_millis() as u16)
127 } else {
128 utc_clock(now)
129 }
130}
131
132#[cfg(not(any(windows, all(unix, target_pointer_width = "64"))))]
133fn local_clock() -> (u16, u16, u16, u16) {
134 utc_clock(std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH).unwrap_or_default())
135}
136
137#[cfg_attr(windows, allow(dead_code))]
138fn utc_clock(now: std::time::Duration) -> (u16, u16, u16, u16) {
139 let day_secs = now.as_secs() % 86_400;
140 ((day_secs / 3600) as u16, (day_secs / 60 % 60) as u16, (day_secs % 60) as u16, now.subsec_millis() as u16)
141}