diff --git a/Cargo.lock b/Cargo.lock index 90db9f34..5c4626ad 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1272,6 +1272,7 @@ dependencies = [ "log", "mdbook-core", "mdbook-preprocessor", + "mdbook-renderer", "mdbook-summary", "memchr", "notify", @@ -1334,7 +1335,10 @@ dependencies = [ name = "mdbook-renderer" version = "0.5.0-alpha.1" dependencies = [ + "anyhow", "mdbook-core", + "serde", + "serde_json", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index afdd72b4..2c3cf492 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,6 +25,7 @@ anyhow = "1.0.98" log = "0.4.27" mdbook-core = { path = "crates/mdbook-core" } mdbook-preprocessor = { path = "crates/mdbook-preprocessor" } +mdbook-renderer = { path = "crates/mdbook-renderer" } mdbook-summary = { path = "crates/mdbook-summary" } memchr = "2.7.5" pulldown-cmark = { version = "0.10.3", default-features = false, features = ["html"] } # Do not update, part of the public api. @@ -63,6 +64,7 @@ hex = "0.4.3" log.workspace = true mdbook-core.workspace = true mdbook-preprocessor.workspace = true +mdbook-renderer.workspace = true mdbook-summary.workspace = true memchr.workspace = true opener = "0.8.1" diff --git a/crates/mdbook-renderer/Cargo.toml b/crates/mdbook-renderer/Cargo.toml index 2ec83e47..38869282 100644 --- a/crates/mdbook-renderer/Cargo.toml +++ b/crates/mdbook-renderer/Cargo.toml @@ -8,7 +8,10 @@ repository.workspace = true rust-version.workspace = true [dependencies] +anyhow.workspace = true mdbook-core.workspace = true +serde.workspace = true +serde_json.workspace = true [lints] workspace = true diff --git a/crates/mdbook-renderer/src/lib.rs b/crates/mdbook-renderer/src/lib.rs index 8b09630d..56365420 100644 --- a/crates/mdbook-renderer/src/lib.rs +++ b/crates/mdbook-renderer/src/lib.rs @@ -1,3 +1,81 @@ //! Library to assist implementing an mdbook renderer. +use anyhow::Context; +use mdbook_core::book::Book; +use mdbook_core::config::Config; +use mdbook_core::errors::Result; +use serde::{Deserialize, Serialize}; +use std::collections::HashMap; +use std::io::Read; +use std::path::PathBuf; + pub use mdbook_core::MDBOOK_VERSION; +pub use mdbook_core::book; +pub use mdbook_core::config; +pub use mdbook_core::errors; + +/// An mdbook backend. +pub trait Renderer { + /// The `Renderer`'s name. + fn name(&self) -> &str; + + /// Invoke the `Renderer`, passing in all the necessary information for + /// describing a book. + fn render(&self, ctx: &RenderContext) -> Result<()>; +} + +/// The context provided to all renderers. +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct RenderContext { + /// Which version of `mdbook` did this come from (as written in `mdbook`'s + /// `Cargo.toml`). Useful if you know the renderer is only compatible with + /// certain versions of `mdbook`. + pub version: String, + /// The book's root directory. + pub root: PathBuf, + /// A loaded representation of the book itself. + pub book: Book, + /// The loaded configuration file. + pub config: Config, + /// Where the renderer *must* put any build artefacts generated. To allow + /// renderers to cache intermediate results, this directory is not + /// guaranteed to be empty or even exist. + pub destination: PathBuf, + /// Internal mapping of chapter titles. + /// + /// This is used internally by mdbook to compute custom chapter titles. + /// This should not be used outside of mdbook's internals. + #[serde(skip)] + pub chapter_titles: HashMap, + #[serde(skip)] + __non_exhaustive: (), +} + +impl RenderContext { + /// Create a new `RenderContext`. + pub fn new(root: P, book: Book, config: Config, destination: Q) -> RenderContext + where + P: Into, + Q: Into, + { + RenderContext { + book, + config, + version: crate::MDBOOK_VERSION.to_string(), + root: root.into(), + destination: destination.into(), + chapter_titles: HashMap::new(), + __non_exhaustive: (), + } + } + + /// Get the source directory's (absolute) path on disk. + pub fn source_dir(&self) -> PathBuf { + self.root.join(&self.config.book.src) + } + + /// Load a `RenderContext` from its JSON representation. + pub fn from_json(reader: R) -> Result { + serde_json::from_reader(reader).with_context(|| "Unable to deserialize the `RenderContext`") + } +} diff --git a/src/book/mod.rs b/src/book/mod.rs index 53109153..3b8879da 100644 --- a/src/book/mod.rs +++ b/src/book/mod.rs @@ -16,6 +16,7 @@ pub use mdbook_core::book::{Book, BookItem, BookItems, Chapter, SectionNumber}; use mdbook_core::config::{Config, RustEdition}; use mdbook_core::utils; use mdbook_preprocessor::{Preprocessor, PreprocessorContext}; +use mdbook_renderer::{RenderContext, Renderer}; pub use mdbook_summary::{Link, Summary, SummaryItem, parse_summary}; use std::ffi::OsString; use std::io::{IsTerminal, Write}; @@ -26,7 +27,7 @@ use toml::Value; use topological_sort::TopologicalSort; use crate::preprocess::{CmdPreprocessor, IndexPreprocessor, LinkPreprocessor}; -use crate::renderer::{CmdRenderer, HtmlHandlebars, MarkdownRenderer, RenderContext, Renderer}; +use crate::renderer::{CmdRenderer, HtmlHandlebars, MarkdownRenderer}; /// The object used to manage and build a book. pub struct MDBook { diff --git a/src/lib.rs b/src/lib.rs index 76c744f0..7a8c6e00 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -76,7 +76,7 @@ //! access to the various methods for working with the [`Config`]. //! //! [user guide]: https://rust-lang.github.io/mdBook/ -//! [`RenderContext`]: renderer::RenderContext +//! [`RenderContext`]: mdbook_renderer::RenderContext //! [relevant chapter]: https://rust-lang.github.io/mdBook/for_developers/backends.html //! [`Config`]: mdbook_core::config::Config @@ -88,6 +88,5 @@ pub mod theme; pub use crate::book::BookItem; pub use crate::book::MDBook; -pub use crate::renderer::Renderer; pub use mdbook_core::MDBOOK_VERSION; pub use mdbook_core::config::Config; diff --git a/src/renderer/mod.rs b/src/renderer/mod.rs index ce4bc025..d639b005 100644 --- a/src/renderer/mod.rs +++ b/src/renderer/mod.rs @@ -11,15 +11,12 @@ //! [For Developers]: https://rust-lang.github.io/mdBook/for_developers/index.html //! [RenderContext]: struct.RenderContext.html -use crate::book::Book; use anyhow::{Context, Result, bail}; use log::{error, info, trace, warn}; -use mdbook_core::config::Config; -use serde::{Deserialize, Serialize}; +use mdbook_renderer::{RenderContext, Renderer}; use shlex::Shlex; -use std::collections::HashMap; use std::fs; -use std::io::{self, ErrorKind, Read}; +use std::io::{self, ErrorKind}; use std::path::{Path, PathBuf}; use std::process::{Command, Stdio}; use toml::Value; @@ -30,76 +27,6 @@ pub use self::markdown_renderer::MarkdownRenderer; mod html_handlebars; mod markdown_renderer; -/// An arbitrary `mdbook` backend. -/// -/// Although it's quite possible for you to import `mdbook` as a library and -/// provide your own renderer, there are two main renderer implementations that -/// 99% of users will ever use: -/// -/// - [`HtmlHandlebars`] - the built-in HTML renderer -/// - [`CmdRenderer`] - a generic renderer which shells out to a program to do the -/// actual rendering -pub trait Renderer { - /// The `Renderer`'s name. - fn name(&self) -> &str; - - /// Invoke the `Renderer`, passing in all the necessary information for - /// describing a book. - fn render(&self, ctx: &RenderContext) -> Result<()>; -} - -/// The context provided to all renderers. -#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] -pub struct RenderContext { - /// Which version of `mdbook` did this come from (as written in `mdbook`'s - /// `Cargo.toml`). Useful if you know the renderer is only compatible with - /// certain versions of `mdbook`. - pub version: String, - /// The book's root directory. - pub root: PathBuf, - /// A loaded representation of the book itself. - pub book: Book, - /// The loaded configuration file. - pub config: Config, - /// Where the renderer *must* put any build artefacts generated. To allow - /// renderers to cache intermediate results, this directory is not - /// guaranteed to be empty or even exist. - pub destination: PathBuf, - #[serde(skip)] - pub(crate) chapter_titles: HashMap, - #[serde(skip)] - __non_exhaustive: (), -} - -impl RenderContext { - /// Create a new `RenderContext`. - pub fn new(root: P, book: Book, config: Config, destination: Q) -> RenderContext - where - P: Into, - Q: Into, - { - RenderContext { - book, - config, - version: crate::MDBOOK_VERSION.to_string(), - root: root.into(), - destination: destination.into(), - chapter_titles: HashMap::new(), - __non_exhaustive: (), - } - } - - /// Get the source directory's (absolute) path on disk. - pub fn source_dir(&self) -> PathBuf { - self.root.join(&self.config.book.src) - } - - /// Load a `RenderContext` from its JSON representation. - pub fn from_json(reader: R) -> Result { - serde_json::from_reader(reader).with_context(|| "Unable to deserialize the `RenderContext`") - } -} - /// A generic renderer which will shell out to an arbitrary executable. /// /// # Rendering Protocol diff --git a/tests/testsuite/renderer.rs b/tests/testsuite/renderer.rs index 923bd5dd..abf45502 100644 --- a/tests/testsuite/renderer.rs +++ b/tests/testsuite/renderer.rs @@ -2,7 +2,7 @@ use crate::prelude::*; use anyhow::Result; -use mdbook::renderer::{RenderContext, Renderer}; +use mdbook_renderer::{RenderContext, Renderer}; use snapbox::IntoData; use std::fs::File; use std::sync::{Arc, Mutex};