Skip to main content

mdtype_rules_stdlib/
lib.rs

1//! Built-in body rules for `mdtype`.
2//!
3//! Each rule lives in its own module and is trivially copy-pasteable as a template for
4//! new rules in downstream crates. Add new rules here **or** in an external crate — never
5//! in `mdtype-core`.
6
7#![forbid(unsafe_code)]
8
9pub mod forbid_h1;
10pub mod forbidden_sections;
11pub mod required_sections;
12pub mod section_order;
13
14use mdtype_core::nodes::{AstNode, NodeValue};
15use mdtype_core::BodyRuleFactory;
16
17/// Concatenate the rendered text of a heading node's children.
18///
19/// Strong/emphasis spans are flattened, fenced code spans contribute their literal,
20/// all other inlines are ignored. Used by every section-matching rule so the rules
21/// agree on what `## *Summary*` "is".
22pub(crate) fn heading_text<'a>(heading: &'a AstNode<'a>) -> String {
23    let mut buf = String::new();
24    for desc in heading.descendants().skip(1) {
25        let data = desc.data.borrow();
26        match &data.value {
27            NodeValue::Text(t) => buf.push_str(t),
28            NodeValue::Code(c) => buf.push_str(&c.literal),
29            _ => {}
30        }
31    }
32    buf
33}
34
35/// Return the set of factories for every stdlib rule. Register these with your
36/// `SchemaSource` so YAML schemas may reference their rule ids.
37#[must_use]
38pub fn register_stdlib() -> Vec<Box<dyn BodyRuleFactory>> {
39    vec![
40        Box::new(forbid_h1::Factory),
41        Box::new(required_sections::Factory),
42        Box::new(section_order::Factory),
43        Box::new(forbidden_sections::Factory),
44    ]
45}