solverforge_console/
init.rs1use std::sync::OnceLock;
2
3use tracing_subscriber::layer::SubscriberExt;
4use tracing_subscriber::util::SubscriberInitExt;
5use tracing_subscriber::EnvFilter;
6
7use crate::banner;
8use crate::SolverConsoleLayer;
9
10static INIT: OnceLock<()> = OnceLock::new();
11
12pub fn init() {
17 INIT.get_or_init(|| {
18 banner::print_banner();
19
20 #[cfg(feature = "verbose-logging")]
21 let solver_level = "solverforge_solver=debug";
22 #[cfg(not(feature = "verbose-logging"))]
23 let solver_level = "solverforge_solver=info";
24
25 let rust_log = std::env::var("RUST_LOG").ok();
26 let rust_log = rust_log.as_deref();
27
28 let mut filter = EnvFilter::builder()
29 .with_default_directive(solver_level.parse().unwrap())
30 .from_env_lossy();
31
32 if !rust_log_has_directive_for(rust_log, "solverforge_solver")
33 && !rust_log_has_global_trace(rust_log)
34 {
35 filter = filter.add_directive(solver_level.parse().unwrap());
36 }
37
38 if !rust_log_has_directive_for(rust_log, "solverforge_dynamic")
39 && !rust_log_has_global_trace(rust_log)
40 {
41 filter = filter.add_directive("solverforge_dynamic=info".parse().unwrap());
42 }
43
44 let _ = tracing_subscriber::registry()
45 .with(filter)
46 .with(SolverConsoleLayer)
47 .try_init();
48 });
49}
50
51fn rust_log_has_directive_for(rust_log: Option<&str>, target: &str) -> bool {
52 rust_log
53 .into_iter()
54 .flat_map(|value| value.split(','))
55 .map(str::trim)
56 .filter(|directive| !directive.is_empty())
57 .filter_map(|directive| directive.split_once('=').map(|(target, _)| target.trim()))
58 .any(|directive_target| {
59 directive_target == target
60 || directive_target
61 .strip_prefix(target)
62 .is_some_and(|rest| rest.starts_with("::"))
63 })
64}
65
66fn rust_log_has_global_trace(rust_log: Option<&str>) -> bool {
67 rust_log
68 .into_iter()
69 .flat_map(|value| value.split(','))
70 .map(|directive| directive.trim().to_ascii_lowercase())
71 .any(|directive| directive == "trace")
72}
73
74#[cfg(test)]
75mod tests {
76 use super::{rust_log_has_directive_for, rust_log_has_global_trace};
77
78 #[test]
79 fn unrelated_rust_log_does_not_disable_solver_console_defaults() {
80 let rust_log = Some("warn,imap_codec=error,imap_client=error");
81
82 assert!(!rust_log_has_directive_for(rust_log, "solverforge_solver"));
83 assert!(!rust_log_has_directive_for(rust_log, "solverforge_dynamic"));
84 assert!(!rust_log_has_global_trace(rust_log));
85 }
86
87 #[test]
88 fn explicit_solver_target_disables_default_solver_directive() {
89 assert!(rust_log_has_directive_for(
90 Some("warn,solverforge_solver=trace"),
91 "solverforge_solver"
92 ));
93 assert!(rust_log_has_directive_for(
94 Some("solverforge_solver::phase=trace"),
95 "solverforge_solver"
96 ));
97 }
98
99 #[test]
100 fn global_trace_disables_default_solver_directives() {
101 assert!(rust_log_has_global_trace(Some(
102 "trace,imap_codec=error,imap_client=error"
103 )));
104 assert!(!rust_log_has_global_trace(Some(
105 "warn,solverforge_solver=trace"
106 )));
107 }
108}