···22use serde::Deserialize;
33use tap::Pipe;
4455+/// Flag indicating how the program should proceed when there are warnings.
66+///
77+/// Used in preprocessor options.
88+///
99+/// Doc comments for variants in this enum will show up in autogenerated docs.
510#[cfg_attr(feature = "common-cli", derive(clap::ValueEnum))]
611#[derive(Deserialize, Debug, Default, Clone, Copy)]
712#[serde(rename_all = "lowercase")]
+3
crates/mdbookkit/src/lib.rs
···33//! This is the lib documentation. If you are looking for the mdBook [preprocessors]
44//! that this crate provides, visit <https://tonywu6.github.io/mdbookkit/> instead.
55//!
66+//! At the moment, the sole purpose of this crate is to facilitate easier testing. Most of the APIs
77+//! are not designed for library use and are explicitly NOT stable.
88+//!
69//! [preprocessors]: https://rust-lang.github.io/mdBook/format/configuration/preprocessors.html
710811#[cfg(feature = "common-logger")]
+2
crates/mdbookkit/src/logging.rs
···11+//! Progress reporting and logging for preprocessors.
22+13use std::{fmt, sync::mpsc};
2435pub fn spinner() -> SpinnerHandle {
+23-12
crates/mdbookkit/src/logging/terminal.rs
···15151616use super::{styled, Message};
17171818-/// Either a [console::Term] or an [env_logger::Logger].
1818+/// Either a [`console::Term`] or an [`env_logger::Logger`].
1919+///
2020+/// This is automatically detected upon installing as the global logger. The logic is:
2121+///
2222+/// - If the `RUST_LOG` env var is set, this will use [`env_logger`].
2323+/// - If stderr is not "user-attended", as determined by [`console::user_attended_stderr()`],
2424+/// like if stderr is piped to a file, this will use [`env_logger`].
2525+/// - Otherwise, this will use [`console`].
2626+///
2727+/// When this is a [`console::Term`], logs are handled by the global [`indicatif`] spinner.
2828+///
2929+/// When this is an [`env_logger::Logger`], there will not be a spinner, and progress
3030+/// reports are printed as logs instead.
1931pub enum ConsoleLogger {
2032 Console(Term),
2133 Logger(Logger),
2234}
23352424-impl Default for ConsoleLogger {
2525- fn default() -> Self {
3636+impl ConsoleLogger {
3737+ /// Install a [`ConsoleLogger`] as the global [`log`] logger.
3838+ pub fn install(name: &str) {
3939+ maybe_spinner(name);
4040+ let logger = Box::new(Self::new());
4141+ log::set_boxed_logger(logger).expect("logger should not have been set");
4242+ log::set_max_level(LevelFilter::max());
4343+ }
4444+4545+ fn new() -> Self {
2646 if let Some(spinner) = SPINNER.get() {
2747 Self::Console(spinner.term.clone())
2848 } else {
···3252 .build()
3353 .pipe(Self::Logger)
3454 }
3535- }
3636-}
3737-3838-impl ConsoleLogger {
3939- pub fn install(name: &str) {
4040- maybe_spinner(name);
4141- let logger = Box::new(Self::default());
4242- log::set_boxed_logger(logger).expect("logger should not have been set");
4343- log::set_max_level(LevelFilter::max());
4455 }
4556}
4657
+35-11
crates/mdbookkit/src/markdown.rs
···11+//! Markdown-related utilities.
22+13use std::{borrow::Cow, fmt::Write, ops::Range};
2435use pulldown_cmark::{Event, Options};
46use pulldown_cmark_to_cmark::{cmark, Error};
57use tap::Pipe;
6899+/// _Patch_ a Markdown string, instead of regenerating it entirely.
1010+///
1111+/// Currently, whitespace is NOT preserved when using [`pulldown_cmark_to_cmark`] to
1212+/// generate Markdown from a [`pulldown_cmark::Event`] stream.
1313+///
1414+/// This is problematic for mdBook preprocessors, because preprocessors downstream
1515+/// may need to work on syntax that is whitespace-sensitive. Normalizing all whitespace
1616+/// could cause such usage to no longer be recognized. An example is [`mdbook-alerts`][alerts]
1717+/// which works on GitHub's ["alerts"][gh-alerts] syntax.
1818+///
1919+/// [alerts]: https://crates.io/crates/mdbook-alerts
2020+/// [gh-alerts]: https://docs.github.com/en/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax#alerts
721pub struct PatchStream<'a, S> {
822 source: &'a str,
923 stream: S,
···4256 }
4357}
44585959+impl<'a, S> PatchStream<'a, S> {
6060+ /// Create a new patch stream.
6161+ ///
6262+ /// `stream` should be an [`Iterator`] yielding (`E`, [`Range<usize>`]), where `E` is an
6363+ /// [`Iterator`] yielding [`Event`]s.
6464+ ///
6565+ /// [`Range<usize>`] is the byte span in `source` that should be patched.
6666+ ///
6767+ /// `E` is the replacement event stream to be rendered into `source`
6868+ /// using [`pulldown_cmark_to_cmark`].
6969+ pub fn new(source: &'a str, stream: S) -> Self {
7070+ Self {
7171+ source,
7272+ stream,
7373+ start: Some(0),
7474+ patch: None,
7575+ }
7676+ }
7777+}
7878+4579impl<'a, S> PatchStream<'a, S>
4680where
4781 Self: Iterator<Item = Result<Cow<'a, str>, Error>>,
4882{
8383+ /// Render the patched Markdown source.
4984 pub fn into_string(self) -> Result<String, Error> {
5085 let mut out = String::new();
5186 for chunk in self {
5287 write!(out, "{}", chunk?).unwrap();
5388 }
5489 Ok(out)
5555- }
5656-}
5757-5858-impl<'a, S> PatchStream<'a, S> {
5959- pub fn new(source: &'a str, stream: S) -> Self {
6060- Self {
6161- source,
6262- stream,
6363- start: Some(0),
6464- patch: None,
6565- }
6690 }
6791}
6892