//! Loading the published documentation tree (`site-docs/public`). //! //! Lives in the library rather than in `main.rs` so the test harness builds the //! loader the same way production does. It used to be a private binary //! function, and the harness could only construct a `DocLoader` with //! `sections: vec![]`, so under test the site had no doc pages at all: nothing //! could cover `/docs`, and the two integration tests that would have proven //! the docs half of the sitemap and the search index had to be dropped. //! //! One definition means the harness cannot drift from the server the way an //! imitation of this config would. use docengine::{DocLoader, DocLoaderConfig}; use mnw_assumptions::Assumptions; use std::sync::Arc; /// Where the published docs live. `DOCS_PATH` overrides for a deployment whose /// working directory is not the crate root. pub fn docs_path() -> String { std::env::var("DOCS_PATH").unwrap_or_else(|_| "site-docs/public".to_string()) } /// Where the business assumptions live, substituted into doc bodies. pub fn assumptions_path() -> String { std::env::var("ASSUMPTIONS_PATH") .unwrap_or_else(|_| "docs/business/assumptions.toml".to_string()) } /// Load and validate business assumptions. `Err` carries a human-readable /// message; normal startup turns it into a panic, the check modes into a /// sentinel line. Shared so the two paths validate identically. pub fn load_assumptions() -> Result, String> { let path = assumptions_path(); let a = Assumptions::load(&path) .map_err(|e| format!("failed to load assumptions from {path}: {e}"))?; a.validate() .map_err(|e| format!("assumptions validation failed:\n{e}"))?; tracing::info!(path = %path, "assumptions loaded and validated"); Ok(Arc::new(a)) } /// Build the documentation loader from disk with the production config. /// /// Shared by normal startup, the `MNW_CHECK_DOCS` integrity check, and the test /// harness, so all three see the exact same sections, link prefix, examples /// path, and assumption substitution. The broken-link report cannot drift from /// what is served, and a test asserting over `/docs` is asserting over the real /// tree. pub fn build_doc_loader(assumptions: Arc) -> DocLoader { let docs_path = docs_path(); DocLoader::load( std::path::Path::new(&docs_path), &DocLoaderConfig { sections: vec![ ("about".to_string(), "About".to_string()), ("guide".to_string(), "Guide".to_string()), ("developer".to_string(), "Developer".to_string()), ("legal".to_string(), "Legal".to_string()), ("support".to_string(), "Support".to_string()), ("tech".to_string(), "Tech".to_string()), ], link_prefix: "/docs".to_string(), unpublished_pattern: Some("unpublished/".to_string()), examples_path: Some(std::path::Path::new(&docs_path).join("../examples")), pre_process: Some(Box::new(move |md: &str| { assumptions.substitute(md).map_err(|e| e.to_string()) })), }, ) }