Skip to main content

rumdl_lib/rules/md057_existing_relative_links/
md057_config.rs

1use crate::rule_config_serde::RuleConfig;
2use serde::{Deserialize, Serialize};
3
4/// How to handle absolute links (paths starting with /)
5#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
6#[serde(rename_all = "snake_case")]
7pub enum AbsoluteLinksOption {
8    /// Ignore absolute links (don't validate them) - this is the default
9    #[default]
10    Ignore,
11    /// Warn about absolute links (they can't be validated as local paths)
12    Warn,
13    /// Resolve absolute links relative to MkDocs docs_dir and validate
14    RelativeToDocs,
15    /// Resolve absolute links relative to one or more explicit root directories.
16    /// First match wins; reports broken only when all roots miss.
17    RelativeToRoots,
18}
19
20/// Configuration for MD057 (relative link validation)
21///
22/// This rule validates that relative links point to existing files.
23#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
24#[serde(default, rename_all = "kebab-case")]
25pub struct MD057Config {
26    /// How to handle absolute links (paths starting with /)
27    /// - "ignore" (default): Skip validation for absolute links
28    /// - "warn": Report a warning for absolute links
29    /// - "relative_to_docs": Resolve relative to MkDocs docs_dir and validate
30    /// - "relative_to_roots": Resolve relative to one or more configured root directories
31    #[serde(alias = "absolute_links")]
32    pub absolute_links: AbsoluteLinksOption,
33
34    /// Warn when relative links contain unnecessary path traversal.
35    /// When enabled, `../sub_dir/file.md` from within `sub_dir/` warns
36    /// and suggests the shorter equivalent `file.md`.
37    #[serde(alias = "compact_paths")]
38    pub compact_paths: bool,
39
40    /// Warn when a relative link points at the file it is written in.
41    ///
42    /// Following such a link reloads the page the reader is already on. When
43    /// the link carries a fragment, `file.md#section` from within `file.md`
44    /// warns and suggests `#section`, which reaches the same heading without
45    /// the reload. A link to the whole file has no equivalent shorter form, so
46    /// it is reported without a fix.
47    #[serde(alias = "self_referential_links")]
48    pub self_referential_links: bool,
49
50    /// Additional directories to search when a relative link is not found
51    /// relative to the file's directory.
52    ///
53    /// Paths are resolved relative to the project root (where `.rumdl.toml` or
54    /// `pyproject.toml` is found), or relative to the current working directory.
55    ///
56    /// For Obsidian users: the attachment folder is auto-detected from
57    /// `.obsidian/app.json` when `flavor = "obsidian"` is set, so this option
58    /// is typically not needed. Use it for custom setups or non-Obsidian tools.
59    ///
60    /// Example:
61    /// ```toml
62    /// [MD057]
63    /// search-paths = ["assets", "images", "attachments"]
64    /// ```
65    #[serde(alias = "search_paths")]
66    pub search_paths: Vec<String>,
67
68    /// Root directories used when `absolute-links = "relative_to_roots"`.
69    ///
70    /// Absolute links are resolved against each configured root in order, then
71    /// against the project root as an implicit fallback. The first root under
72    /// which the target file exists passes the check. A warning is emitted only
73    /// when no resolution finds the file.
74    ///
75    /// The implicit project-root fallback supports both link styles in the same
76    /// project without extra configuration: `/foo.md` (relative to a configured
77    /// root) and `/content/en/foo.md` (literal path from the project root).
78    ///
79    /// Paths are resolved relative to the project root when not absolute.
80    /// Trailing slashes are normalized automatically.
81    ///
82    /// When `roots` is empty, absolute links are validated against the project
83    /// root only — useful for single-root projects where every absolute link
84    /// resolves directly from the project root.
85    ///
86    /// Example:
87    /// ```toml
88    /// [MD057]
89    /// absolute-links = "relative_to_roots"
90    /// roots = ["content/en", "content/zh-cn"]
91    /// ```
92    pub roots: Vec<String>,
93
94    /// Also check path-shaped values in the document's frontmatter.
95    ///
96    /// Off by default, because frontmatter has no syntax marking a value as a
97    /// link: a path-shaped value is only a guess at one. Static-site generators
98    /// also resolve frontmatter paths from the site root rather than the
99    /// document's own directory, so checking them like body links reports
100    /// working paths as broken.
101    ///
102    /// Enable it for projects whose frontmatter paths really are relative to
103    /// the document, and use `ignore-frontmatter-fields` for the keys that are
104    /// not.
105    ///
106    /// Example:
107    /// ```toml
108    /// [MD057]
109    /// check-frontmatter = true
110    /// ignore-frontmatter-fields = ["image", "cover"]
111    /// ```
112    pub check_frontmatter: bool,
113
114    /// Top-level frontmatter keys whose values are not checked. Matched
115    /// case-insensitively. A parent key excludes its whole subtree. Applies
116    /// only when `check-frontmatter` is enabled.
117    pub ignore_frontmatter_fields: Vec<String>,
118}
119
120impl RuleConfig for MD057Config {
121    const RULE_NAME: &'static str = "MD057";
122}