html_to_markdown_rs/options/
validation.rs1#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
10pub enum HeadingStyle {
11 Underlined,
13 #[default]
15 Atx,
16 AtxClosed,
18}
19
20impl HeadingStyle {
21 #[must_use]
26 pub fn parse(value: &str) -> Self {
27 match normalize_token(value).as_str() {
28 "atx" => Self::Atx,
29 "atxclosed" => Self::AtxClosed,
30 _ => Self::Underlined,
31 }
32 }
33}
34
35#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
39pub enum ListIndentType {
40 #[default]
42 Spaces,
43 Tabs,
45}
46
47impl ListIndentType {
48 #[must_use]
53 pub fn parse(value: &str) -> Self {
54 match normalize_token(value).as_str() {
55 "tabs" => Self::Tabs,
56 _ => Self::Spaces,
57 }
58 }
59}
60
61#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
65pub enum WhitespaceMode {
66 #[default]
68 Normalized,
69 Strict,
71}
72
73impl WhitespaceMode {
74 #[must_use]
79 pub fn parse(value: &str) -> Self {
80 match normalize_token(value).as_str() {
81 "strict" => Self::Strict,
82 _ => Self::Normalized,
83 }
84 }
85}
86
87#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
91pub enum NewlineStyle {
92 #[default]
94 Spaces,
95 Backslash,
97}
98
99impl NewlineStyle {
100 #[must_use]
105 pub fn parse(value: &str) -> Self {
106 match normalize_token(value).as_str() {
107 "backslash" => Self::Backslash,
108 _ => Self::Spaces,
109 }
110 }
111}
112
113#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
117pub enum CodeBlockStyle {
118 #[default]
120 Indented,
121 Backticks,
123 Tildes,
125}
126
127impl CodeBlockStyle {
128 #[must_use]
133 pub fn parse(value: &str) -> Self {
134 match normalize_token(value).as_str() {
135 "backticks" => Self::Backticks,
136 "tildes" => Self::Tildes,
137 _ => Self::Indented,
138 }
139 }
140}
141
142#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
146pub enum HighlightStyle {
147 #[default]
149 DoubleEqual,
150 Html,
152 Bold,
154 None,
156}
157
158impl HighlightStyle {
159 #[must_use]
164 pub fn parse(value: &str) -> Self {
165 match normalize_token(value).as_str() {
166 "doubleequal" => Self::DoubleEqual,
167 "html" => Self::Html,
168 "bold" => Self::Bold,
169 "none" => Self::None,
170 _ => Self::None,
171 }
172 }
173}
174
175#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
179pub enum OutputFormat {
180 #[default]
182 Markdown,
183 Djot,
185}
186
187impl OutputFormat {
188 #[must_use]
193 pub fn parse(value: &str) -> Self {
194 match normalize_token(value).as_str() {
195 "djot" => Self::Djot,
196 _ => Self::Markdown,
197 }
198 }
199}
200
201pub(crate) fn normalize_token(value: &str) -> String {
203 let mut out = String::with_capacity(value.len());
204 for ch in value.chars() {
205 if ch.is_ascii_alphanumeric() {
206 out.push(ch.to_ascii_lowercase());
207 }
208 }
209 out
210}
211
212#[cfg(any(feature = "serde", feature = "metadata"))]
213mod serde_impls {
214 use super::{
215 CodeBlockStyle, HeadingStyle, HighlightStyle, ListIndentType, NewlineStyle, OutputFormat, WhitespaceMode,
216 };
217 use serde::{Deserialize, Serialize, Serializer};
218
219 macro_rules! impl_deserialize_from_parse {
220 ($ty:ty, $parser:expr) => {
221 impl<'de> Deserialize<'de> for $ty {
222 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
223 where
224 D: serde::Deserializer<'de>,
225 {
226 let value = String::deserialize(deserializer)?;
227 Ok($parser(&value))
228 }
229 }
230 };
231 }
232
233 impl_deserialize_from_parse!(HeadingStyle, HeadingStyle::parse);
234 impl_deserialize_from_parse!(ListIndentType, ListIndentType::parse);
235 impl_deserialize_from_parse!(WhitespaceMode, WhitespaceMode::parse);
236 impl_deserialize_from_parse!(NewlineStyle, NewlineStyle::parse);
237 impl_deserialize_from_parse!(CodeBlockStyle, CodeBlockStyle::parse);
238 impl_deserialize_from_parse!(HighlightStyle, HighlightStyle::parse);
239 impl_deserialize_from_parse!(OutputFormat, OutputFormat::parse);
240
241 impl Serialize for HeadingStyle {
243 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
244 where
245 S: Serializer,
246 {
247 let s = match self {
248 Self::Underlined => "underlined",
249 Self::Atx => "atx",
250 Self::AtxClosed => "atxclosed",
251 };
252 serializer.serialize_str(s)
253 }
254 }
255
256 impl Serialize for ListIndentType {
257 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
258 where
259 S: Serializer,
260 {
261 let s = match self {
262 Self::Spaces => "spaces",
263 Self::Tabs => "tabs",
264 };
265 serializer.serialize_str(s)
266 }
267 }
268
269 impl Serialize for WhitespaceMode {
270 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
271 where
272 S: Serializer,
273 {
274 let s = match self {
275 Self::Normalized => "normalized",
276 Self::Strict => "strict",
277 };
278 serializer.serialize_str(s)
279 }
280 }
281
282 impl Serialize for NewlineStyle {
283 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
284 where
285 S: Serializer,
286 {
287 let s = match self {
288 Self::Spaces => "spaces",
289 Self::Backslash => "backslash",
290 };
291 serializer.serialize_str(s)
292 }
293 }
294
295 impl Serialize for CodeBlockStyle {
296 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
297 where
298 S: Serializer,
299 {
300 let s = match self {
301 Self::Indented => "indented",
302 Self::Backticks => "backticks",
303 Self::Tildes => "tildes",
304 };
305 serializer.serialize_str(s)
306 }
307 }
308
309 impl Serialize for HighlightStyle {
310 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
311 where
312 S: Serializer,
313 {
314 let s = match self {
315 Self::DoubleEqual => "doubleequal",
316 Self::Html => "html",
317 Self::Bold => "bold",
318 Self::None => "none",
319 };
320 serializer.serialize_str(s)
321 }
322 }
323
324 impl Serialize for OutputFormat {
325 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
326 where
327 S: Serializer,
328 {
329 let s = match self {
330 Self::Markdown => "markdown",
331 Self::Djot => "djot",
332 };
333 serializer.serialize_str(s)
334 }
335 }
336}
337
338#[cfg(all(test, any(feature = "serde", feature = "metadata")))]
339mod tests {
340 use super::*;
341
342 #[test]
343 fn test_enum_serialization() {
344 let heading = HeadingStyle::AtxClosed;
346 let json = serde_json::to_string(&heading).expect("Failed to serialize");
347 assert_eq!(json, r#""atxclosed""#);
348
349 let list_indent = ListIndentType::Tabs;
350 let json = serde_json::to_string(&list_indent).expect("Failed to serialize");
351 assert_eq!(json, r#""tabs""#);
352
353 let whitespace = WhitespaceMode::Strict;
354 let json = serde_json::to_string(&whitespace).expect("Failed to serialize");
355 assert_eq!(json, r#""strict""#);
356 }
357
358 #[test]
359 fn test_enum_deserialization() {
360 let heading: HeadingStyle = serde_json::from_str(r#""atxclosed""#).expect("Failed");
362 assert_eq!(heading, HeadingStyle::AtxClosed);
363
364 let heading: HeadingStyle = serde_json::from_str(r#""ATXCLOSED""#).expect("Failed");
365 assert_eq!(heading, HeadingStyle::AtxClosed);
366
367 let list_indent: ListIndentType = serde_json::from_str(r#""tabs""#).expect("Failed");
368 assert_eq!(list_indent, ListIndentType::Tabs);
369 }
370}