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