1#![warn(missing_docs)]
43#![warn(clippy::pedantic)]
44#![allow(clippy::module_name_repetitions)]
45#![allow(clippy::missing_errors_doc)]
46#![allow(clippy::missing_panics_doc)]
47
48pub mod cache;
49pub mod cli;
50pub mod config;
51pub mod error;
52pub mod metrics;
53pub mod server;
54pub mod tools;
55pub mod utils;
56
57pub use crate::config::{AppConfig, ServerConfig};
58pub use crate::error::{Error, Result};
60pub use crate::server::CratesDocsServer;
62
63pub const VERSION: &str = env!("CARGO_PKG_VERSION");
67
68pub const NAME: &str = "crates-docs";
70
71#[deprecated(note = "Please use init_logging_with_config instead")]
76pub fn init_logging(debug: bool) -> Result<()> {
77 use tracing_subscriber::{fmt, prelude::*, EnvFilter};
78
79 let filter = if debug {
80 EnvFilter::new("debug")
81 } else {
82 EnvFilter::new("info")
83 };
84
85 let fmt_layer = fmt::layer()
86 .with_target(true)
87 .with_thread_ids(true)
88 .with_thread_names(true)
89 .compact();
90
91 tracing_subscriber::registry()
92 .with(filter)
93 .with(fmt_layer)
94 .try_init()
95 .map_err(|e| error::Error::initialization("logging", e.to_string()))?;
96
97 Ok(())
98}
99
100pub fn init_logging_with_config(config: &crate::config::LoggingConfig) -> Result<()> {
105 use tracing_subscriber::{fmt, prelude::*, EnvFilter};
106
107 macro_rules! fmt_layer {
109 () => {
110 fmt::layer()
111 .with_target(true)
112 .with_thread_ids(true)
113 .with_thread_names(true)
114 .compact()
115 };
116 ($writer:expr) => {
117 fmt::layer()
118 .with_writer($writer)
119 .with_target(true)
120 .with_thread_ids(true)
121 .with_thread_names(true)
122 .compact()
123 };
124 }
125
126 macro_rules! try_init {
128 ($subscriber:expr) => {
129 $subscriber
130 .try_init()
131 .map_err(|e| error::Error::initialization("logging", e.to_string()))?
132 };
133 }
134
135 let level = match config.level.to_lowercase().as_str() {
137 "trace" => "trace",
138 "debug" => "debug",
139 "warn" => "warn",
140 "error" => "error",
141 _ => "info",
142 };
143
144 let filter = EnvFilter::new(level);
145
146 match (config.enable_console, config.enable_file, &config.file_path) {
148 (true, true, Some(file_path)) => {
150 let (log_dir, log_file_name) = parse_log_path(file_path);
151 ensure_log_directory(&log_dir)?;
152 let file_appender = tracing_appender::rolling::daily(&log_dir, log_file_name);
153
154 try_init!(tracing_subscriber::registry()
155 .with(filter)
156 .with(fmt_layer!())
157 .with(fmt_layer!(file_appender)));
158 }
159
160 (true, _, _) | (false, false, _) => {
162 try_init!(tracing_subscriber::registry()
163 .with(filter)
164 .with(fmt_layer!()));
165 }
166
167 (false, true, Some(file_path)) => {
169 let (log_dir, log_file_name) = parse_log_path(file_path);
170 ensure_log_directory(&log_dir)?;
171 let file_appender = tracing_appender::rolling::daily(&log_dir, log_file_name);
172
173 try_init!(tracing_subscriber::registry()
174 .with(filter)
175 .with(fmt_layer!(file_appender)));
176 }
177
178 _ => {
180 try_init!(tracing_subscriber::registry()
181 .with(filter)
182 .with(fmt_layer!()));
183 }
184 }
185
186 Ok(())
187}
188
189fn parse_log_path(file_path: &str) -> (std::path::PathBuf, std::ffi::OsString) {
191 let path = std::path::Path::new(file_path);
192 let log_dir = path
193 .parent()
194 .filter(|p| !p.as_os_str().is_empty())
195 .map_or_else(|| std::path::PathBuf::from("."), std::path::PathBuf::from);
196 let log_file_name = path.file_name().map_or_else(
197 || std::ffi::OsString::from("crates-docs.log"),
198 std::ffi::OsString::from,
199 );
200 (log_dir, log_file_name)
201}
202
203fn ensure_log_directory(log_dir: &std::path::Path) -> Result<()> {
205 std::fs::create_dir_all(log_dir).map_err(|e| {
206 error::Error::initialization("log_directory", format!("Failed to create: {e}"))
207 })
208}