toolkit for mdBook [mirror of my GitHub repo] docs.tonywu.dev/mdbookkit/
permalinks rust-analyzer mdbook
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

refactor: record issue severity through tracing

Tony Wu c62a77ca e1fe4c1e

+107 -62
+5 -8
crates/mdbook-permalinks/src/main.rs
··· 17 17 18 18 use mdbookkit::{ 19 19 book::{BookConfigHelper, BookHelper, book_from_stdin}, 20 - diagnostics::Issue, 21 20 emit_debug, emit_error, 22 - error::{ExitProcess, OnWarning}, 21 + error::{ExitProcess, OnWarning, has_severity}, 23 22 logging::Logging, 24 23 ticker, ticker_item, 25 24 url::{ExpectUrl, UrlFromPath}, ··· 130 129 131 130 self.resolve(&mut content); 132 131 133 - let status = self 134 - .reporter(&content, |_| true) 132 + self.reporter(&content, |_| true) 135 133 .name_display(|url| self.rel_path(url)) 136 134 .build() 137 - .to_stderr() 138 - .to_status(); 135 + .to_stderr(); 139 136 140 137 content.log_stats(); 141 138 142 139 // bail before emitting changes 143 - self.config.fail_on_warnings.check(status.level())?; 140 + self.config.fail_on_warnings.check()?; 144 141 145 142 let mut result = book 146 143 .iter_chapters() ··· 160 157 } 161 158 }); 162 159 163 - if status.level() <= Level::WARN { 160 + if has_severity(Level::WARN) { 164 161 warn!("Finished with problems"); 165 162 } else { 166 163 info!("Finished");
+25 -27
crates/mdbook-rustdoc-links/src/main.rs
··· 15 15 16 16 use mdbookkit::{ 17 17 book::{BookConfigHelper, BookHelper, book_from_stdin, string_from_stdin}, 18 - diagnostics::Issue, 19 18 emit_debug, emit_error, emit_trace, emit_warning, 20 - error::{ExitProcess, FutureWithError}, 19 + error::{ExitProcess, FutureWithError, has_severity}, 21 20 logging::Logging, 22 21 }; 23 22 ··· 25 24 cache::{Cache, FileCache}, 26 25 client::Client, 27 26 env::{Config, Environment, RustAnalyzer}, 28 - link::{LinkState, diagnostic::LinkStatus}, 29 - page::Pages, 27 + link::LinkState, 28 + page::{Pages, Statistics}, 30 29 resolver::Resolver, 31 30 }; 32 31 ··· 117 116 118 117 let env = client.stop().await; 119 118 120 - let status = content 119 + content 121 120 .reporter() 122 121 .name_display(|path| path.display().to_string()) 123 122 .build() 124 - .to_stderr() 125 - .to_status(); 126 - 127 - link_report(&content); 123 + .to_stderr(); 128 124 129 - match status { 130 - LinkStatus::Unresolved => { 131 - if env.config.cache_dir.is_some() { 132 - warn! { "The `cache-dir` option is enabled, but some items could not \ 133 - be resolved, which will cause rust-analyzer to always run \ 134 - despite the cache." } 135 - } 136 - } 137 - LinkStatus::Ok | LinkStatus::Debug => { 138 - info!("Finished"); 139 - } 125 + if link_report(&content).items_pending > 0 && env.config.cache_dir.is_some() { 126 + warn! { "The `cache-dir` option is enabled, but some items could not \ 127 + be resolved. This will cause rust-analyzer to always run \ 128 + despite the cache." } 140 129 } 141 130 142 131 // bail before emitting changes 143 - env.config.fail_on_warnings.check(status.level())?; 132 + env.config.fail_on_warnings.check()?; 144 133 145 134 if content.modified() { 146 135 FileCache::save(&env, &content) ··· 170 159 171 160 book.to_stdout(&ctx)?; 172 161 162 + if has_severity(Level::WARN) { 163 + warn!("Finished with problems"); 164 + } else { 165 + info!("Finished"); 166 + } 167 + 173 168 Ok(()) 174 169 } 175 170 ··· 198 193 199 194 let env = client.stop().await; 200 195 201 - let status = content 196 + content 202 197 .reporter() 203 198 .name_display(|_| "<stdin>".into()) 204 199 .build() 205 - .to_stderr() 206 - .to_status(); 200 + .to_stderr(); 207 201 208 202 link_report(&content); 209 203 ··· 214 208 (content.get(&env.emit_config()).map(|emit| emit.to_string())) 215 209 .and_then(|output| Ok(std::io::stdout().write_all(output.as_bytes())?))?; 216 210 217 - env.config.fail_on_warnings.check(status.level())?; 211 + env.config.fail_on_warnings.check()?; 218 212 219 213 Ok(()) 220 214 } 221 215 222 - fn link_report<K>(content: &Pages<'_, K>) { 216 + fn link_report<K>(content: &Pages<'_, K>) -> Statistics { 223 217 let mut iter = content.iter(); 224 218 225 219 let result = iter.deduped(|link| match link.state() { ··· 228 222 LinkState::Unparsed => None, 229 223 }); 230 224 231 - info!("Converted {}", iter.stats().fmt_resolved()); 225 + let stats = iter.stats(); 226 + 227 + info!("Converted {}", stats.fmt_resolved()); 232 228 233 229 if tracing::enabled!(target: "link-report", Level::DEBUG) { 234 230 for (item, link) in result ··· 246 242 } 247 243 } 248 244 } 245 + 246 + stats.clone() 249 247 } 250 248 251 249 fn config(ctx: &PreprocessorContext) -> Result2<Config> {
+1 -1
crates/mdbook-rustdoc-links/src/page.rs
··· 265 265 } 266 266 } 267 267 268 - #[derive(Debug, Default)] 268 + #[derive(Debug, Default, Clone)] 269 269 pub struct Statistics { 270 270 pub links_pending: usize, 271 271 pub items_pending: usize,
+8 -7
crates/mdbookkit/src/diagnostics.rs
··· 18 18 use crate::{ 19 19 emit_debug, 20 20 env::{is_colored, is_logging}, 21 - error::ExpectFmt, 21 + error::{ExpectFmt, put_severity}, 22 22 logging::stderr, 23 23 }; 24 24 ··· 316 316 where 317 317 P: IssueItem, 318 318 { 319 - pub fn to_status(&self) -> P::Kind { 320 - self.items 321 - .iter() 322 - .map(|p| p.status()) 323 - .min_by_key(|s| s.level()) 324 - .unwrap_or_default() 319 + pub fn to_level(&self) -> Option<Level> { 320 + self.items.iter().map(|p| p.status().level()).min() 325 321 } 326 322 327 323 pub fn to_stderr(&self) -> &Self { ··· 335 331 write!(stderr(), "\n{}", self.to_report()) 336 332 .tap_err(emit_debug!()) 337 333 .ok(); 334 + if let Some(level) = self.to_level() { 335 + // explicitly set severity because graphical reports 336 + // do not go through tracing 337 + put_severity(level); 338 + } 338 339 }; 339 340 340 341 self
+63 -18
crates/mdbookkit/src/error.rs
··· 1 - use std::{fmt::Display, process::exit, sync::LockResult}; 1 + use std::{ 2 + fmt::Display, 3 + process::exit, 4 + sync::{ 5 + LockResult, 6 + atomic::{AtomicU8, Ordering}, 7 + }, 8 + }; 2 9 3 10 use anyhow::{Context, Error, Result, anyhow}; 4 11 use serde::Deserialize; 5 12 use tap::Pipe; 6 - use tracing::Level; 13 + use tracing::{Event, Level, Subscriber}; 14 + use tracing_subscriber::{Layer, layer}; 7 15 8 16 use crate::env::is_ci; 9 17 18 + static MAX_SEVERITY: AtomicU8 = AtomicU8::new(0); 19 + 20 + pub fn has_severity(level: Level) -> bool { 21 + MAX_SEVERITY.load(Ordering::Relaxed) >= level_to_severity(level) 22 + } 23 + 24 + #[inline] 25 + pub fn put_severity(level: Level) { 26 + let severity = level_to_severity(level); 27 + MAX_SEVERITY.fetch_max(severity, Ordering::Relaxed); 28 + } 29 + 30 + pub struct EventLevelLayer; 31 + 32 + impl<S: Subscriber> Layer<S> for EventLevelLayer { 33 + fn on_event(&self, event: &Event<'_>, _ctx: layer::Context<'_, S>) { 34 + put_severity(*event.metadata().level()); 35 + } 36 + } 37 + 38 + fn level_to_severity(level: Level) -> u8 { 39 + if level <= Level::ERROR { 40 + 50 41 + } else if level <= Level::WARN { 42 + 40 43 + } else if level <= Level::INFO { 44 + 30 45 + } else if level <= Level::DEBUG { 46 + 20 47 + } else { 48 + 10 49 + } 50 + } 51 + 10 52 /// Flag indicating how the program should proceed when there are warnings. 11 53 /// 12 54 /// Used in preprocessor options. ··· 27 69 } 28 70 29 71 impl OnWarning { 30 - pub fn check(&self, level: Level) -> Result<()> { 31 - match level { 32 - Level::ERROR => Err(anyhow!("Preprocessor has errors")), 33 - Level::WARN => match self { 34 - Self::AlwaysFail => anyhow! {"Treating warnings as errors because the \ 35 - `fail-on-warnings` option is set to \"always\""} 72 + pub fn check(&self) -> Result<()> { 73 + if has_severity(Level::ERROR) { 74 + Err(anyhow!("Preprocessor has errors")) 75 + } else if has_severity(Level::WARN) { 76 + match (self, is_ci()) { 77 + (Self::AlwaysFail, _) => anyhow! { "Treating warnings as errors because the \ 78 + `fail-on-warnings` option is set to \"always\"" } 36 79 .pipe(Err), 37 - Self::FailInCi => { 38 - let Some(ci) = is_ci() else { 39 - return Ok(()); 40 - }; 41 - anyhow! {"Treating warnings as errors because CI={ci} and the \ 42 - `fail-on-warnings` option is set to \"ci\""} 80 + (Self::FailInCi, Some(ci)) => { 81 + anyhow! { "Treating warnings as errors because CI={ci} and the \ 82 + `fail-on-warnings` option is set to \"ci\"" } 43 83 .pipe(Err) 44 84 } 45 - }, 46 - _ => Ok(()), 85 + (Self::FailInCi, None) => Ok(()), 86 + } 87 + } else { 88 + Ok(()) 47 89 } 48 90 } 49 91 50 92 pub fn adjusted<T, E>(&self, result: Result<Result<T, E>, E>) -> Result<Result<T, E>, E> { 51 93 match result { 52 94 Err(error) => Err(error), 53 - Ok(Err(error)) if is_ci().is_some() => Err(error), 54 - Ok(Err(error)) => Ok(Err(error)), 95 + Ok(Err(error)) => match (self, is_ci()) { 96 + (Self::AlwaysFail, _) => Err(error), 97 + (Self::FailInCi, Some(_)) => Err(error), 98 + (Self::FailInCi, None) => Ok(Err(error)), 99 + }, 55 100 Ok(Ok(result)) => Ok(Ok(result)), 56 101 } 57 102 }
+5 -1
crates/mdbookkit/src/logging.rs
··· 86 86 util::SubscriberInitExt, 87 87 }; 88 88 89 - use crate::env::{MDBOOK_LOG, is_colored, is_logging, set_colored, set_logging}; 89 + use crate::{ 90 + env::{MDBOOK_LOG, is_colored, is_logging, set_colored, set_logging}, 91 + error::EventLevelLayer, 92 + }; 90 93 91 94 use self::writer::{MultiProgressTicker, MultiProgressWriter}; 92 95 ··· 169 172 }); 170 173 171 174 tracing_subscriber::registry() 175 + .with(EventLevelLayer) 172 176 .with(filter) 173 177 .with(logger) 174 178 .with(ticker)