1use serde::{Deserialize, Serialize};
2use std::fmt;
3use std::str::FromStr;
4
5#[derive(Debug, Clone, Copy, Default)]
12pub struct ConfigLoaded;
13
14#[derive(Debug, Clone, Copy, Default)]
17pub struct ConfigValidated;
18
19#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
21#[serde(rename_all = "lowercase")]
22pub enum MarkdownFlavor {
23 #[serde(rename = "standard", alias = "none", alias = "")]
25 #[default]
26 Standard,
27 #[serde(rename = "mkdocs")]
29 MkDocs,
30 #[serde(rename = "mdx")]
32 MDX,
33 #[serde(rename = "quarto")]
35 Quarto,
36 #[serde(rename = "obsidian")]
38 Obsidian,
39 #[serde(rename = "kramdown")]
41 Kramdown,
42}
43
44fn markdown_flavor_schema(_gen: &mut schemars::SchemaGenerator) -> schemars::Schema {
46 schemars::json_schema!({
47 "description": "Markdown flavor/dialect. Accepts: standard, gfm, mkdocs, mdx, quarto, obsidian, kramdown. Aliases: commonmark/github map to standard, qmd/rmd/rmarkdown map to quarto, jekyll maps to kramdown.",
48 "type": "string",
49 "enum": ["standard", "gfm", "github", "commonmark", "mkdocs", "mdx", "quarto", "qmd", "rmd", "rmarkdown", "obsidian", "kramdown", "jekyll"]
50 })
51}
52
53impl schemars::JsonSchema for MarkdownFlavor {
54 fn schema_name() -> std::borrow::Cow<'static, str> {
55 std::borrow::Cow::Borrowed("MarkdownFlavor")
56 }
57
58 fn json_schema(generator: &mut schemars::SchemaGenerator) -> schemars::Schema {
59 markdown_flavor_schema(generator)
60 }
61}
62
63impl fmt::Display for MarkdownFlavor {
64 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
65 match self {
66 MarkdownFlavor::Standard => write!(f, "standard"),
67 MarkdownFlavor::MkDocs => write!(f, "mkdocs"),
68 MarkdownFlavor::MDX => write!(f, "mdx"),
69 MarkdownFlavor::Quarto => write!(f, "quarto"),
70 MarkdownFlavor::Obsidian => write!(f, "obsidian"),
71 MarkdownFlavor::Kramdown => write!(f, "kramdown"),
72 }
73 }
74}
75
76impl FromStr for MarkdownFlavor {
77 type Err = String;
78
79 fn from_str(s: &str) -> Result<Self, Self::Err> {
80 match s.to_lowercase().as_str() {
81 "standard" | "" | "none" => Ok(MarkdownFlavor::Standard),
82 "mkdocs" => Ok(MarkdownFlavor::MkDocs),
83 "mdx" => Ok(MarkdownFlavor::MDX),
84 "quarto" | "qmd" | "rmd" | "rmarkdown" => Ok(MarkdownFlavor::Quarto),
85 "obsidian" => Ok(MarkdownFlavor::Obsidian),
86 "kramdown" | "jekyll" => Ok(MarkdownFlavor::Kramdown),
87 "gfm" | "github" | "commonmark" => Ok(MarkdownFlavor::Standard),
91 _ => Err(format!("Unknown markdown flavor: {s}")),
92 }
93 }
94}
95
96impl MarkdownFlavor {
97 pub fn from_extension(ext: &str) -> Self {
99 match ext.to_lowercase().as_str() {
100 "mdx" => Self::MDX,
101 "qmd" => Self::Quarto,
102 "rmd" => Self::Quarto,
103 "kramdown" => Self::Kramdown,
104 _ => Self::Standard,
105 }
106 }
107
108 pub fn from_path(path: &std::path::Path) -> Self {
110 path.extension()
111 .and_then(|e| e.to_str())
112 .map(Self::from_extension)
113 .unwrap_or(Self::Standard)
114 }
115
116 pub fn supports_esm_blocks(self) -> bool {
118 matches!(self, Self::MDX)
119 }
120
121 pub fn supports_jsx(self) -> bool {
123 matches!(self, Self::MDX)
124 }
125
126 pub fn supports_auto_references(self) -> bool {
128 matches!(self, Self::MkDocs)
129 }
130
131 pub fn supports_kramdown_syntax(self) -> bool {
133 matches!(self, Self::Kramdown)
134 }
135
136 pub fn name(self) -> &'static str {
138 match self {
139 Self::Standard => "Standard",
140 Self::MkDocs => "MkDocs",
141 Self::MDX => "MDX",
142 Self::Quarto => "Quarto",
143 Self::Obsidian => "Obsidian",
144 Self::Kramdown => "Kramdown",
145 }
146 }
147}
148
149pub fn normalize_key(key: &str) -> String {
151 if key.len() == 5 && key.to_ascii_lowercase().starts_with("md") && key[2..].chars().all(|c| c.is_ascii_digit()) {
153 key.to_ascii_uppercase()
154 } else {
155 key.replace('_', "-").to_ascii_lowercase()
156 }
157}
158
159pub(super) fn warn_comma_without_brace_in_pattern(pattern: &str, config_file: &str) {
163 if pattern.contains(',') && !pattern.contains('{') {
164 eprintln!("Warning: Pattern \"{pattern}\" in {config_file} contains a comma but no braces.");
165 eprintln!(" To match multiple files, use brace expansion: \"{{{pattern}}}\"");
166 eprintln!(" Or use separate entries for each file.");
167 }
168}