//! Configurable markdown-to-HTML rendering with sanitization presets. //! //! Provides four rendering presets for different trust levels: //! - **Permissive** -- full GFM (tables, footnotes, images, raw HTML). For trusted content. //! - **Standard** -- GFM without images. For app text fields. //! - **Strict** -- no images, no raw HTML, dangerous scheme filtering, nofollow. For UGC. //! - **Sanitize-only** -- ammonia cleaning without markdown parsing. For external HTML. //! //! Optional features add document loading, TOML frontmatter, @mention resolution, //! and quote attribution post-processing. #[cfg(any(feature = "mentions", feature = "assumptions", test))] mod code_spans; mod escape; mod render; mod sanitize; mod text; mod toc; #[cfg(feature = "assumptions")] mod assumptions; #[cfg(feature = "assumptions")] mod filters; #[cfg(feature = "directives")] mod directives; #[cfg(feature = "doc-loader")] mod doc_loader; #[cfg(feature = "frontmatter")] mod frontmatter; #[cfg(feature = "mentions")] mod mentions; #[cfg(feature = "quotes")] mod quotes; #[cfg(feature = "media-urls")] mod media_urls; // Re-export core types pub use render::{RenderResult, Renderer}; pub use sanitize::SanitizePreset; pub use text::{extract_title, reading_time_minutes, strip_first_heading, word_count}; pub use toc::{TocEntry, extract_toc, render_toc_html}; // Re-export feature-gated types #[cfg(feature = "assumptions")] pub use assumptions::{Assumptions, AssumptionsError, LookupValue}; #[cfg(feature = "assumptions")] pub use filters::{Filter, FilterArg, FilterError}; #[cfg(feature = "directives")] pub use directives::post_process_directives; #[cfg(feature = "doc-loader")] pub use doc_loader::{DocIndexEntry, DocLoader, DocLoaderConfig, DocPage, DocSearchEntry}; #[cfg(feature = "frontmatter")] pub use frontmatter::{Frontmatter, parse_frontmatter}; #[cfg(feature = "mentions")] pub use mentions::{extract_mentions, resolve_mentions}; #[cfg(feature = "quotes")] pub use quotes::{QuoteAuthor, post_process_quotes}; #[cfg(feature = "media-urls")] pub use media_urls::{img_to_video, rewrite_media_paths}; /// Render markdown with the permissive preset (GFM features, default ammonia). pub fn render_permissive(markdown: &str) -> String { Renderer::permissive().render(markdown) } /// Render markdown with the standard preset (GFM features, no images). pub fn render_standard(markdown: &str) -> String { Renderer::standard().render(markdown) } /// Render markdown with the strict preset (no images, no raw HTML, nofollow). pub fn render_strict(markdown: &str) -> String { Renderer::strict().render(markdown) } /// Sanitize HTML without markdown parsing. pub fn sanitize_html(html: &str) -> String { Renderer::sanitize_only().sanitize_html(html) } #[cfg(test)] mod top_level_tests { use super::*; // Direct unit tests for the top-level convenience wrappers. Without these, // mutating any of them to return `String::new()` or a sentinel passes the // suite (the wrappers were untested at the function level). #[test] fn render_permissive_emits_paragraph_markup() { let out = render_permissive("Hello **world**."); assert!(out.contains("

"), "expected paragraph: {out:?}"); assert!(out.contains("world"), "expected bold: {out:?}"); } #[test] fn render_standard_strips_images() { let out = render_standard("![alt](pic.png)"); assert!(!out.contains("evil() ![x](y.png)"); assert!(!out.contains("safe

"); assert!(out.contains("safe"), "kept text: {out:?}"); assert!(!out.contains("