html-to-markdown 3.4.0 → 3.6.0.pre.rc.24

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (188) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE +21 -0
  3. data/README.md +347 -0
  4. data/Steepfile +10 -2
  5. data/ext/html_to_markdown_rb/Cargo.toml +3 -2
  6. data/ext/html_to_markdown_rb/extconf.rb +5 -5
  7. data/ext/html_to_markdown_rb/native/Cargo.lock +962 -0
  8. data/ext/html_to_markdown_rb/native/Cargo.toml +6 -11
  9. data/ext/html_to_markdown_rb/native/extconf.rb +14 -0
  10. data/ext/html_to_markdown_rb/src/lib.rs +1715 -646
  11. data/lib/html_to_markdown/native.rb +913 -37
  12. data/lib/html_to_markdown/version.rb +3 -3
  13. data/lib/html_to_markdown.rb +9 -4
  14. data/lib/html_to_markdown_rb.so +0 -0
  15. data/sig/types.rbs +59 -292
  16. metadata +32 -179
  17. data/ext/html_to_markdown_rb/Makefile +0 -592
  18. data/lib/bin/html-to-markdown +0 -0
  19. data/vendor/Cargo.toml +0 -33
  20. data/vendor/html-to-markdown-rs/Cargo.toml +0 -54
  21. data/vendor/html-to-markdown-rs/README.md +0 -278
  22. data/vendor/html-to-markdown-rs/examples/basic.rs +0 -24
  23. data/vendor/html-to-markdown-rs/examples/table.rs +0 -25
  24. data/vendor/html-to-markdown-rs/examples/test_deser.rs +0 -12
  25. data/vendor/html-to-markdown-rs/examples/test_escape.rs +0 -58
  26. data/vendor/html-to-markdown-rs/examples/test_inline_formatting.rs +0 -113
  27. data/vendor/html-to-markdown-rs/examples/test_lists.rs +0 -39
  28. data/vendor/html-to-markdown-rs/examples/test_semantic_tags.rs +0 -89
  29. data/vendor/html-to-markdown-rs/examples/test_tables.rs +0 -100
  30. data/vendor/html-to-markdown-rs/examples/test_task_lists.rs +0 -61
  31. data/vendor/html-to-markdown-rs/examples/test_whitespace.rs +0 -34
  32. data/vendor/html-to-markdown-rs/src/convert_api.rs +0 -349
  33. data/vendor/html-to-markdown-rs/src/converter/block/blockquote.rs +0 -178
  34. data/vendor/html-to-markdown-rs/src/converter/block/container.rs +0 -114
  35. data/vendor/html-to-markdown-rs/src/converter/block/div.rs +0 -149
  36. data/vendor/html-to-markdown-rs/src/converter/block/heading.rs +0 -428
  37. data/vendor/html-to-markdown-rs/src/converter/block/horizontal_rule.rs +0 -103
  38. data/vendor/html-to-markdown-rs/src/converter/block/line_break.rs +0 -89
  39. data/vendor/html-to-markdown-rs/src/converter/block/mod.rs +0 -10
  40. data/vendor/html-to-markdown-rs/src/converter/block/paragraph.rs +0 -140
  41. data/vendor/html-to-markdown-rs/src/converter/block/preformatted.rs +0 -298
  42. data/vendor/html-to-markdown-rs/src/converter/block/table/builder.rs +0 -453
  43. data/vendor/html-to-markdown-rs/src/converter/block/table/caption.rs +0 -44
  44. data/vendor/html-to-markdown-rs/src/converter/block/table/cell.rs +0 -276
  45. data/vendor/html-to-markdown-rs/src/converter/block/table/cells.rs +0 -336
  46. data/vendor/html-to-markdown-rs/src/converter/block/table/layout.rs +0 -58
  47. data/vendor/html-to-markdown-rs/src/converter/block/table/mod.rs +0 -266
  48. data/vendor/html-to-markdown-rs/src/converter/block/table/scanner.rs +0 -146
  49. data/vendor/html-to-markdown-rs/src/converter/block/table/utils.rs +0 -34
  50. data/vendor/html-to-markdown-rs/src/converter/block/unknown.rs +0 -138
  51. data/vendor/html-to-markdown-rs/src/converter/context.rs +0 -208
  52. data/vendor/html-to-markdown-rs/src/converter/dom_context.rs +0 -337
  53. data/vendor/html-to-markdown-rs/src/converter/form/elements.rs +0 -770
  54. data/vendor/html-to-markdown-rs/src/converter/form/mod.rs +0 -82
  55. data/vendor/html-to-markdown-rs/src/converter/format/djot.rs +0 -64
  56. data/vendor/html-to-markdown-rs/src/converter/format/markdown.rs +0 -59
  57. data/vendor/html-to-markdown-rs/src/converter/format/mod.rs +0 -43
  58. data/vendor/html-to-markdown-rs/src/converter/handlers/blockquote.rs +0 -173
  59. data/vendor/html-to-markdown-rs/src/converter/handlers/code_block.rs +0 -434
  60. data/vendor/html-to-markdown-rs/src/converter/handlers/graphic.rs +0 -234
  61. data/vendor/html-to-markdown-rs/src/converter/handlers/image.rs +0 -282
  62. data/vendor/html-to-markdown-rs/src/converter/handlers/link.rs +0 -316
  63. data/vendor/html-to-markdown-rs/src/converter/handlers/mod.rs +0 -26
  64. data/vendor/html-to-markdown-rs/src/converter/inline/code.rs +0 -306
  65. data/vendor/html-to-markdown-rs/src/converter/inline/emphasis.rs +0 -345
  66. data/vendor/html-to-markdown-rs/src/converter/inline/link.rs +0 -428
  67. data/vendor/html-to-markdown-rs/src/converter/inline/mod.rs +0 -237
  68. data/vendor/html-to-markdown-rs/src/converter/inline/ruby.rs +0 -337
  69. data/vendor/html-to-markdown-rs/src/converter/inline/semantic/marks.rs +0 -566
  70. data/vendor/html-to-markdown-rs/src/converter/inline/semantic/mod.rs +0 -86
  71. data/vendor/html-to-markdown-rs/src/converter/inline/semantic/typography.rs +0 -558
  72. data/vendor/html-to-markdown-rs/src/converter/list/definition.rs +0 -232
  73. data/vendor/html-to-markdown-rs/src/converter/list/item.rs +0 -332
  74. data/vendor/html-to-markdown-rs/src/converter/list/mod.rs +0 -70
  75. data/vendor/html-to-markdown-rs/src/converter/list/ordered.rs +0 -201
  76. data/vendor/html-to-markdown-rs/src/converter/list/unordered.rs +0 -195
  77. data/vendor/html-to-markdown-rs/src/converter/list/utils.rs +0 -314
  78. data/vendor/html-to-markdown-rs/src/converter/main.rs +0 -710
  79. data/vendor/html-to-markdown-rs/src/converter/main_helpers.rs +0 -452
  80. data/vendor/html-to-markdown-rs/src/converter/media/embedded.rs +0 -393
  81. data/vendor/html-to-markdown-rs/src/converter/media/graphic.rs +0 -4
  82. data/vendor/html-to-markdown-rs/src/converter/media/image.rs +0 -183
  83. data/vendor/html-to-markdown-rs/src/converter/media/mod.rs +0 -87
  84. data/vendor/html-to-markdown-rs/src/converter/media/svg.rs +0 -280
  85. data/vendor/html-to-markdown-rs/src/converter/metadata.rs +0 -220
  86. data/vendor/html-to-markdown-rs/src/converter/mod.rs +0 -156
  87. data/vendor/html-to-markdown-rs/src/converter/plain_text.rs +0 -516
  88. data/vendor/html-to-markdown-rs/src/converter/preprocessing_helpers.rs +0 -201
  89. data/vendor/html-to-markdown-rs/src/converter/reference_collector.rs +0 -69
  90. data/vendor/html-to-markdown-rs/src/converter/semantic/attributes.rs +0 -269
  91. data/vendor/html-to-markdown-rs/src/converter/semantic/definition_list.rs +0 -266
  92. data/vendor/html-to-markdown-rs/src/converter/semantic/figure.rs +0 -391
  93. data/vendor/html-to-markdown-rs/src/converter/semantic/mod.rs +0 -112
  94. data/vendor/html-to-markdown-rs/src/converter/semantic/sectioning.rs +0 -85
  95. data/vendor/html-to-markdown-rs/src/converter/semantic/summary.rs +0 -324
  96. data/vendor/html-to-markdown-rs/src/converter/text/mod.rs +0 -8
  97. data/vendor/html-to-markdown-rs/src/converter/text/processing.rs +0 -56
  98. data/vendor/html-to-markdown-rs/src/converter/text_node.rs +0 -269
  99. data/vendor/html-to-markdown-rs/src/converter/utility/attributes.rs +0 -151
  100. data/vendor/html-to-markdown-rs/src/converter/utility/caching.rs +0 -74
  101. data/vendor/html-to-markdown-rs/src/converter/utility/content.rs +0 -271
  102. data/vendor/html-to-markdown-rs/src/converter/utility/mod.rs +0 -17
  103. data/vendor/html-to-markdown-rs/src/converter/utility/preprocessing.rs +0 -1002
  104. data/vendor/html-to-markdown-rs/src/converter/utility/serialization.rs +0 -126
  105. data/vendor/html-to-markdown-rs/src/converter/utility/siblings.rs +0 -97
  106. data/vendor/html-to-markdown-rs/src/converter/visitor_hooks.rs +0 -189
  107. data/vendor/html-to-markdown-rs/src/error.rs +0 -43
  108. data/vendor/html-to-markdown-rs/src/exports.rs +0 -24
  109. data/vendor/html-to-markdown-rs/src/inline_images.rs +0 -336
  110. data/vendor/html-to-markdown-rs/src/lib.rs +0 -139
  111. data/vendor/html-to-markdown-rs/src/metadata/collector.rs +0 -457
  112. data/vendor/html-to-markdown-rs/src/metadata/config.rs +0 -394
  113. data/vendor/html-to-markdown-rs/src/metadata/extraction.rs +0 -398
  114. data/vendor/html-to-markdown-rs/src/metadata/mod.rs +0 -288
  115. data/vendor/html-to-markdown-rs/src/metadata/types.rs +0 -477
  116. data/vendor/html-to-markdown-rs/src/options/conversion.rs +0 -559
  117. data/vendor/html-to-markdown-rs/src/options/inline_image.rs +0 -111
  118. data/vendor/html-to-markdown-rs/src/options/mod.rs +0 -20
  119. data/vendor/html-to-markdown-rs/src/options/preprocessing.rs +0 -201
  120. data/vendor/html-to-markdown-rs/src/options/validation.rs +0 -416
  121. data/vendor/html-to-markdown-rs/src/prelude.rs +0 -1
  122. data/vendor/html-to-markdown-rs/src/rcdom.rs +0 -487
  123. data/vendor/html-to-markdown-rs/src/text.rs +0 -358
  124. data/vendor/html-to-markdown-rs/src/types/document.rs +0 -191
  125. data/vendor/html-to-markdown-rs/src/types/mod.rs +0 -17
  126. data/vendor/html-to-markdown-rs/src/types/result.rs +0 -54
  127. data/vendor/html-to-markdown-rs/src/types/structure_builder.rs +0 -791
  128. data/vendor/html-to-markdown-rs/src/types/structure_collector.rs +0 -483
  129. data/vendor/html-to-markdown-rs/src/types/tables.rs +0 -52
  130. data/vendor/html-to-markdown-rs/src/types/warnings.rs +0 -33
  131. data/vendor/html-to-markdown-rs/src/validation.rs +0 -158
  132. data/vendor/html-to-markdown-rs/src/visitor/default_impl.rs +0 -63
  133. data/vendor/html-to-markdown-rs/src/visitor/mod.rs +0 -41
  134. data/vendor/html-to-markdown-rs/src/visitor/traits.rs +0 -370
  135. data/vendor/html-to-markdown-rs/src/visitor/types.rs +0 -319
  136. data/vendor/html-to-markdown-rs/src/visitor_helpers/helpers/callbacks/mod.rs +0 -1
  137. data/vendor/html-to-markdown-rs/src/visitor_helpers/helpers/content.rs +0 -126
  138. data/vendor/html-to-markdown-rs/src/visitor_helpers/helpers/mod.rs +0 -27
  139. data/vendor/html-to-markdown-rs/src/visitor_helpers/helpers/state.rs +0 -110
  140. data/vendor/html-to-markdown-rs/src/visitor_helpers/helpers/traversal.rs +0 -250
  141. data/vendor/html-to-markdown-rs/src/visitor_helpers.rs +0 -597
  142. data/vendor/html-to-markdown-rs/src/wrapper/sync.rs +0 -413
  143. data/vendor/html-to-markdown-rs/src/wrapper/utils.rs +0 -290
  144. data/vendor/html-to-markdown-rs/src/wrapper.rs +0 -9
  145. data/vendor/html-to-markdown-rs/tests/br_in_inline_test.rs +0 -87
  146. data/vendor/html-to-markdown-rs/tests/commonmark_compliance_test.rs +0 -297
  147. data/vendor/html-to-markdown-rs/tests/djot_output_test.rs +0 -153
  148. data/vendor/html-to-markdown-rs/tests/exclude_selectors_test.rs +0 -132
  149. data/vendor/html-to-markdown-rs/tests/integration_test.rs +0 -631
  150. data/vendor/html-to-markdown-rs/tests/issue_121_regressions.rs +0 -49
  151. data/vendor/html-to-markdown-rs/tests/issue_127_regressions.rs +0 -58
  152. data/vendor/html-to-markdown-rs/tests/issue_128_regressions.rs +0 -17
  153. data/vendor/html-to-markdown-rs/tests/issue_131_regressions.rs +0 -41
  154. data/vendor/html-to-markdown-rs/tests/issue_134_regressions.rs +0 -40
  155. data/vendor/html-to-markdown-rs/tests/issue_139_regressions.rs +0 -26
  156. data/vendor/html-to-markdown-rs/tests/issue_140_regressions.rs +0 -185
  157. data/vendor/html-to-markdown-rs/tests/issue_143_regressions.rs +0 -100
  158. data/vendor/html-to-markdown-rs/tests/issue_145_regressions.rs +0 -133
  159. data/vendor/html-to-markdown-rs/tests/issue_146_regressions.rs +0 -144
  160. data/vendor/html-to-markdown-rs/tests/issue_176_regressions.rs +0 -62
  161. data/vendor/html-to-markdown-rs/tests/issue_190_regressions.rs +0 -128
  162. data/vendor/html-to-markdown-rs/tests/issue_199_regressions.rs +0 -20
  163. data/vendor/html-to-markdown-rs/tests/issue_200_regressions.rs +0 -62
  164. data/vendor/html-to-markdown-rs/tests/issue_212_regressions.rs +0 -68
  165. data/vendor/html-to-markdown-rs/tests/issue_216_217_regressions.rs +0 -87
  166. data/vendor/html-to-markdown-rs/tests/issue_336_regressions.rs +0 -74
  167. data/vendor/html-to-markdown-rs/tests/issue_339_regressions.rs +0 -92
  168. data/vendor/html-to-markdown-rs/tests/issue_347_regressions.rs +0 -154
  169. data/vendor/html-to-markdown-rs/tests/issue_348_visitor_plain.rs +0 -93
  170. data/vendor/html-to-markdown-rs/tests/json_ld_script_extraction.rs +0 -44
  171. data/vendor/html-to-markdown-rs/tests/lists_test.rs +0 -199
  172. data/vendor/html-to-markdown-rs/tests/plain_output_test.rs +0 -273
  173. data/vendor/html-to-markdown-rs/tests/preprocessing_tests.rs +0 -61
  174. data/vendor/html-to-markdown-rs/tests/reference_links_test.rs +0 -169
  175. data/vendor/html-to-markdown-rs/tests/sectioning_elements_test.rs +0 -137
  176. data/vendor/html-to-markdown-rs/tests/skip_images_test.rs +0 -522
  177. data/vendor/html-to-markdown-rs/tests/tables_test.rs +0 -743
  178. data/vendor/html-to-markdown-rs/tests/test_custom_elements.rs +0 -41
  179. data/vendor/html-to-markdown-rs/tests/test_issue_187.rs +0 -204
  180. data/vendor/html-to-markdown-rs/tests/test_issue_218.rs +0 -68
  181. data/vendor/html-to-markdown-rs/tests/test_issue_277.rs +0 -77
  182. data/vendor/html-to-markdown-rs/tests/test_max_depth.rs +0 -82
  183. data/vendor/html-to-markdown-rs/tests/test_nested_simple.rs +0 -45
  184. data/vendor/html-to-markdown-rs/tests/test_script_style_stripping.rs +0 -396
  185. data/vendor/html-to-markdown-rs/tests/test_spa_bisect.rs +0 -34
  186. data/vendor/html-to-markdown-rs/tests/visitor_code_integration_test.rs +0 -121
  187. data/vendor/html-to-markdown-rs/tests/visitor_integration_test.rs +0 -1190
  188. data/vendor/html-to-markdown-rs/tests/xml_tables_test.rs +0 -372
@@ -1,201 +0,0 @@
1
- #![allow(clippy::cast_precision_loss, clippy::cast_sign_loss, clippy::unused_self)]
2
-
3
- //! HTML preprocessing configuration options.
4
- //!
5
- //! This module provides configuration for document cleanup before conversion,
6
- //! including preset levels and granular control over element removal.
7
-
8
- use crate::options::validation::normalize_token;
9
-
10
- /// HTML preprocessing aggressiveness level.
11
- ///
12
- /// Controls the extent of cleanup performed before conversion. Higher levels remove more elements.
13
- #[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
14
- pub enum PreprocessingPreset {
15
- /// Minimal cleanup. Remove only essential noise (scripts, styles).
16
- Minimal,
17
- /// Standard cleanup. Default. Removes navigation, forms, and other auxiliary content.
18
- #[default]
19
- Standard,
20
- /// Aggressive cleanup. Remove extensive non-content elements and structure.
21
- Aggressive,
22
- }
23
-
24
- impl PreprocessingPreset {
25
- /// Parse a preprocessing preset from a string.
26
- ///
27
- /// Accepts "minimal", "aggressive", or defaults to Standard.
28
- /// Input is normalized (lowercased, alphanumeric only).
29
- #[must_use]
30
- pub fn parse(value: &str) -> Self {
31
- match normalize_token(value).as_str() {
32
- "minimal" => Self::Minimal,
33
- "aggressive" => Self::Aggressive,
34
- _ => Self::Standard,
35
- }
36
- }
37
- }
38
-
39
- /// HTML preprocessing options for document cleanup before conversion.
40
- #[derive(Debug, Clone)]
41
- #[cfg_attr(
42
- any(feature = "serde", feature = "metadata"),
43
- derive(serde::Serialize, serde::Deserialize)
44
- )]
45
- #[cfg_attr(any(feature = "serde", feature = "metadata"), serde(default, deny_unknown_fields))]
46
- pub struct PreprocessingOptions {
47
- /// Enable HTML preprocessing globally
48
- pub enabled: bool,
49
-
50
- /// Preprocessing preset level (Minimal, Standard, Aggressive)
51
- pub preset: PreprocessingPreset,
52
-
53
- /// Remove navigation elements (nav, breadcrumbs, menus, sidebars)
54
- pub remove_navigation: bool,
55
-
56
- /// Remove form elements (forms, inputs, buttons, etc.)
57
- pub remove_forms: bool,
58
- }
59
-
60
- /// Partial update for `PreprocessingOptions`.
61
- ///
62
- /// This struct uses `Option<T>` to represent optional fields that can be selectively updated.
63
- /// Only specified fields (Some values) will override existing options; None values leave the
64
- /// corresponding fields unchanged when applied via [`PreprocessingOptions::apply_update`].
65
- #[derive(Debug, Clone, Default)]
66
- #[cfg_attr(
67
- any(feature = "serde", feature = "metadata"),
68
- derive(serde::Serialize, serde::Deserialize)
69
- )]
70
- #[cfg_attr(any(feature = "serde", feature = "metadata"), serde(deny_unknown_fields))]
71
- pub struct PreprocessingOptionsUpdate {
72
- /// Optional global preprocessing enablement override
73
- pub enabled: Option<bool>,
74
-
75
- /// Optional preprocessing preset level override (Minimal, Standard, Aggressive)
76
- pub preset: Option<PreprocessingPreset>,
77
-
78
- /// Optional navigation element removal override (nav, breadcrumbs, menus, sidebars)
79
- pub remove_navigation: Option<bool>,
80
-
81
- /// Optional form element removal override (forms, inputs, buttons, etc.)
82
- pub remove_forms: Option<bool>,
83
- }
84
-
85
- impl Default for PreprocessingOptions {
86
- fn default() -> Self {
87
- Self {
88
- enabled: true,
89
- preset: PreprocessingPreset::default(),
90
- remove_navigation: true,
91
- remove_forms: true,
92
- }
93
- }
94
- }
95
-
96
- impl PreprocessingOptions {
97
- /// Apply a partial update to these preprocessing options.
98
- ///
99
- /// Any specified fields in the update will override the current values.
100
- /// Unspecified fields (None) are left unchanged.
101
- ///
102
- /// # Arguments
103
- ///
104
- /// * `update` - Partial preprocessing options update
105
- #[allow(clippy::needless_pass_by_value)]
106
- pub const fn apply_update(&mut self, update: PreprocessingOptionsUpdate) {
107
- if let Some(enabled) = update.enabled {
108
- self.enabled = enabled;
109
- }
110
- if let Some(preset) = update.preset {
111
- self.preset = preset;
112
- }
113
- if let Some(remove_navigation) = update.remove_navigation {
114
- self.remove_navigation = remove_navigation;
115
- }
116
- if let Some(remove_forms) = update.remove_forms {
117
- self.remove_forms = remove_forms;
118
- }
119
- }
120
-
121
- /// Create new preprocessing options from a partial update.
122
- ///
123
- /// Creates a new `PreprocessingOptions` struct with defaults, then applies the update.
124
- /// Fields not specified in the update keep their default values.
125
- ///
126
- /// # Arguments
127
- ///
128
- /// * `update` - Partial preprocessing options update
129
- ///
130
- /// # Returns
131
- ///
132
- /// New `PreprocessingOptions` with specified updates applied to defaults
133
- #[must_use]
134
- pub fn from_update(update: PreprocessingOptionsUpdate) -> Self {
135
- let mut options = Self::default();
136
- options.apply_update(update);
137
- options
138
- }
139
- }
140
-
141
- impl From<PreprocessingOptionsUpdate> for PreprocessingOptions {
142
- fn from(update: PreprocessingOptionsUpdate) -> Self {
143
- Self::from_update(update)
144
- }
145
- }
146
-
147
- #[cfg(any(feature = "serde", feature = "metadata"))]
148
- mod serde_impls {
149
- use super::PreprocessingPreset;
150
- use serde::{Deserialize, Serializer};
151
-
152
- impl<'de> Deserialize<'de> for PreprocessingPreset {
153
- fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
154
- where
155
- D: serde::Deserializer<'de>,
156
- {
157
- let value = String::deserialize(deserializer)?;
158
- Ok(Self::parse(&value))
159
- }
160
- }
161
-
162
- impl serde::Serialize for PreprocessingPreset {
163
- fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
164
- where
165
- S: Serializer,
166
- {
167
- let s = match self {
168
- Self::Minimal => "minimal",
169
- Self::Standard => "standard",
170
- Self::Aggressive => "aggressive",
171
- };
172
- serializer.serialize_str(s)
173
- }
174
- }
175
- }
176
-
177
- #[cfg(all(test, any(feature = "serde", feature = "metadata")))]
178
- mod tests {
179
- use super::*;
180
-
181
- #[test]
182
- fn test_preprocessing_options_serde() {
183
- let options = PreprocessingOptions {
184
- enabled: true,
185
- preset: PreprocessingPreset::Aggressive,
186
- remove_navigation: false,
187
- ..Default::default()
188
- };
189
-
190
- // Serialize to JSON
191
- let json = serde_json::to_string(&options).expect("Failed to serialize");
192
-
193
- // Deserialize back
194
- let deserialized: PreprocessingOptions = serde_json::from_str(&json).expect("Failed to deserialize");
195
-
196
- // Verify values
197
- assert!(deserialized.enabled);
198
- assert_eq!(deserialized.preset, PreprocessingPreset::Aggressive);
199
- assert!(!deserialized.remove_navigation);
200
- }
201
- }
@@ -1,416 +0,0 @@
1
- //! Validation and parsing utilities for option enums.
2
- //!
3
- //! This module provides parsing and serialization logic for configuration
4
- //! enums (HeadingStyle, ListIndentType, etc.) with string conversion support.
5
-
6
- /// Heading style options for Markdown output.
7
- ///
8
- /// Controls how headings (h1-h6) are rendered in the output Markdown.
9
- #[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
10
- pub enum HeadingStyle {
11
- /// Underlined style (=== for h1, --- for h2).
12
- Underlined,
13
- /// ATX style (# for h1, ## for h2, etc.). Default.
14
- #[default]
15
- Atx,
16
- /// ATX closed style (# title #, with closing hashes).
17
- AtxClosed,
18
- }
19
-
20
- impl HeadingStyle {
21
- /// Parse a heading style from a string.
22
- ///
23
- /// Accepts "atx", "atxclosed", or defaults to Underlined.
24
- /// Input is normalized (lowercased, alphanumeric only).
25
- #[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
- /// List indentation character type.
36
- ///
37
- /// Controls whether list items are indented with spaces or tabs.
38
- #[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
39
- pub enum ListIndentType {
40
- /// Use spaces for indentation. Default. Width controlled by `list_indent_width`.
41
- #[default]
42
- Spaces,
43
- /// Use tabs for indentation.
44
- Tabs,
45
- }
46
-
47
- impl ListIndentType {
48
- /// Parse a list indentation type from a string.
49
- ///
50
- /// Accepts "tabs" or defaults to Spaces.
51
- /// Input is normalized (lowercased, alphanumeric only).
52
- #[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
- /// Whitespace handling strategy during conversion.
62
- ///
63
- /// Determines how sequences of whitespace characters (spaces, tabs, newlines) are processed.
64
- #[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
65
- pub enum WhitespaceMode {
66
- /// Collapse multiple whitespace characters to single spaces. Default. Matches browser behavior.
67
- #[default]
68
- Normalized,
69
- /// Preserve all whitespace exactly as it appears in the HTML.
70
- Strict,
71
- }
72
-
73
- impl WhitespaceMode {
74
- /// Parse a whitespace mode from a string.
75
- ///
76
- /// Accepts "strict" or defaults to Normalized.
77
- /// Input is normalized (lowercased, alphanumeric only).
78
- #[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
- /// Line break syntax in Markdown output.
88
- ///
89
- /// Controls how soft line breaks (from `<br>` or line breaks in source) are rendered.
90
- #[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
91
- pub enum NewlineStyle {
92
- /// Two trailing spaces at end of line. Default. Standard Markdown syntax.
93
- #[default]
94
- Spaces,
95
- /// Backslash at end of line. Alternative Markdown syntax.
96
- Backslash,
97
- }
98
-
99
- impl NewlineStyle {
100
- /// Parse a newline style from a string.
101
- ///
102
- /// Accepts "backslash" or defaults to Spaces.
103
- /// Input is normalized (lowercased, alphanumeric only).
104
- #[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
- /// Code block fence style in Markdown output.
114
- ///
115
- /// Determines how code blocks (`<pre><code>`) are rendered in Markdown.
116
- #[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
117
- pub enum CodeBlockStyle {
118
- /// Indented code blocks (4 spaces). `CommonMark` standard.
119
- Indented,
120
- /// Fenced code blocks with backticks (```). Default (GFM). Supports language hints.
121
- #[default]
122
- Backticks,
123
- /// Fenced code blocks with tildes (~~~). Supports language hints.
124
- Tildes,
125
- }
126
-
127
- impl CodeBlockStyle {
128
- /// Parse a code block style from a string.
129
- ///
130
- /// Accepts "backticks", "tildes", or defaults to Indented.
131
- /// Input is normalized (lowercased, alphanumeric only).
132
- #[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
- /// Highlight rendering style for `<mark>` elements.
143
- ///
144
- /// Controls how highlighted text is rendered in Markdown output.
145
- #[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
146
- pub enum HighlightStyle {
147
- /// Double equals syntax (==text==). Default. Pandoc-compatible.
148
- #[default]
149
- DoubleEqual,
150
- /// Preserve as HTML (==text==). Original HTML tag.
151
- Html,
152
- /// Render as bold (**text**). Uses strong emphasis.
153
- Bold,
154
- /// Strip formatting, render as plain text. No markup.
155
- None,
156
- }
157
-
158
- impl HighlightStyle {
159
- /// Parse a highlight style from a string.
160
- ///
161
- /// Accepts "doubleequal", "html", "bold", "none", or defaults to None.
162
- /// Input is normalized (lowercased, alphanumeric only).
163
- #[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
- /// Link rendering style in Markdown output.
176
- ///
177
- /// Controls whether links and images use inline `[text](url)` syntax or
178
- /// reference-style `[text][1]` syntax with definitions collected at the end.
179
- #[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
180
- pub enum LinkStyle {
181
- /// Inline links: `[text](url)`. Default.
182
- #[default]
183
- Inline,
184
- /// Reference-style links: `[text][1]` with `[1]: url` at end of document.
185
- Reference,
186
- }
187
-
188
- impl LinkStyle {
189
- /// Parse a link style from a string.
190
- ///
191
- /// Accepts "reference" or defaults to Inline.
192
- /// Input is normalized (lowercased, alphanumeric only).
193
- #[must_use]
194
- pub fn parse(value: &str) -> Self {
195
- match normalize_token(value).as_str() {
196
- "reference" => Self::Reference,
197
- _ => Self::Inline,
198
- }
199
- }
200
- }
201
-
202
- /// Output format for conversion.
203
- ///
204
- /// Specifies the target markup language format for the conversion output.
205
- #[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
206
- pub enum OutputFormat {
207
- /// Standard Markdown (CommonMark compatible). Default.
208
- #[default]
209
- Markdown,
210
- /// Djot lightweight markup language.
211
- Djot,
212
- /// Plain text output (no markup, visible text only).
213
- Plain,
214
- }
215
-
216
- impl OutputFormat {
217
- /// Parse an output format from a string.
218
- ///
219
- /// Accepts "djot" or defaults to Markdown.
220
- /// Input is normalized (lowercased, alphanumeric only).
221
- #[must_use]
222
- pub fn parse(value: &str) -> Self {
223
- match normalize_token(value).as_str() {
224
- "djot" => Self::Djot,
225
- "plain" | "plaintext" | "text" => Self::Plain,
226
- _ => Self::Markdown,
227
- }
228
- }
229
- }
230
-
231
- /// Normalize a configuration string by lowercasing and removing non-alphanumeric characters.
232
- pub(crate) fn normalize_token(value: &str) -> String {
233
- let mut out = String::with_capacity(value.len());
234
- for ch in value.chars() {
235
- if ch.is_ascii_alphanumeric() {
236
- out.push(ch.to_ascii_lowercase());
237
- }
238
- }
239
- out
240
- }
241
-
242
- #[cfg(any(feature = "serde", feature = "metadata"))]
243
- mod serde_impls {
244
- use super::{
245
- CodeBlockStyle, HeadingStyle, HighlightStyle, LinkStyle, ListIndentType, NewlineStyle, OutputFormat,
246
- WhitespaceMode,
247
- };
248
- use serde::{Deserialize, Serialize, Serializer};
249
-
250
- macro_rules! impl_deserialize_from_parse {
251
- ($ty:ty, $parser:expr) => {
252
- impl<'de> Deserialize<'de> for $ty {
253
- fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
254
- where
255
- D: serde::Deserializer<'de>,
256
- {
257
- let value = String::deserialize(deserializer)?;
258
- Ok($parser(&value))
259
- }
260
- }
261
- };
262
- }
263
-
264
- impl_deserialize_from_parse!(HeadingStyle, HeadingStyle::parse);
265
- impl_deserialize_from_parse!(ListIndentType, ListIndentType::parse);
266
- impl_deserialize_from_parse!(WhitespaceMode, WhitespaceMode::parse);
267
- impl_deserialize_from_parse!(NewlineStyle, NewlineStyle::parse);
268
- impl_deserialize_from_parse!(CodeBlockStyle, CodeBlockStyle::parse);
269
- impl_deserialize_from_parse!(HighlightStyle, HighlightStyle::parse);
270
- impl_deserialize_from_parse!(LinkStyle, LinkStyle::parse);
271
- impl_deserialize_from_parse!(OutputFormat, OutputFormat::parse);
272
-
273
- // Serialize implementations that convert enum variants to their string representations
274
- impl Serialize for HeadingStyle {
275
- fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
276
- where
277
- S: Serializer,
278
- {
279
- let s = match self {
280
- Self::Underlined => "underlined",
281
- Self::Atx => "atx",
282
- Self::AtxClosed => "atxclosed",
283
- };
284
- serializer.serialize_str(s)
285
- }
286
- }
287
-
288
- impl Serialize for ListIndentType {
289
- fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
290
- where
291
- S: Serializer,
292
- {
293
- let s = match self {
294
- Self::Spaces => "spaces",
295
- Self::Tabs => "tabs",
296
- };
297
- serializer.serialize_str(s)
298
- }
299
- }
300
-
301
- impl Serialize for WhitespaceMode {
302
- fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
303
- where
304
- S: Serializer,
305
- {
306
- let s = match self {
307
- Self::Normalized => "normalized",
308
- Self::Strict => "strict",
309
- };
310
- serializer.serialize_str(s)
311
- }
312
- }
313
-
314
- impl Serialize for NewlineStyle {
315
- fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
316
- where
317
- S: Serializer,
318
- {
319
- let s = match self {
320
- Self::Spaces => "spaces",
321
- Self::Backslash => "backslash",
322
- };
323
- serializer.serialize_str(s)
324
- }
325
- }
326
-
327
- impl Serialize for CodeBlockStyle {
328
- fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
329
- where
330
- S: Serializer,
331
- {
332
- let s = match self {
333
- Self::Indented => "indented",
334
- Self::Backticks => "backticks",
335
- Self::Tildes => "tildes",
336
- };
337
- serializer.serialize_str(s)
338
- }
339
- }
340
-
341
- impl Serialize for HighlightStyle {
342
- fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
343
- where
344
- S: Serializer,
345
- {
346
- let s = match self {
347
- Self::DoubleEqual => "doubleequal",
348
- Self::Html => "html",
349
- Self::Bold => "bold",
350
- Self::None => "none",
351
- };
352
- serializer.serialize_str(s)
353
- }
354
- }
355
-
356
- impl Serialize for LinkStyle {
357
- fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
358
- where
359
- S: Serializer,
360
- {
361
- let s = match self {
362
- Self::Inline => "inline",
363
- Self::Reference => "reference",
364
- };
365
- serializer.serialize_str(s)
366
- }
367
- }
368
-
369
- impl Serialize for OutputFormat {
370
- fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
371
- where
372
- S: Serializer,
373
- {
374
- let s = match self {
375
- Self::Markdown => "markdown",
376
- Self::Djot => "djot",
377
- Self::Plain => "plain",
378
- };
379
- serializer.serialize_str(s)
380
- }
381
- }
382
- }
383
-
384
- #[cfg(all(test, any(feature = "serde", feature = "metadata")))]
385
- mod tests {
386
- use super::*;
387
-
388
- #[test]
389
- fn test_enum_serialization() {
390
- // Test that enums serialize to lowercase strings
391
- let heading = HeadingStyle::AtxClosed;
392
- let json = serde_json::to_string(&heading).expect("Failed to serialize");
393
- assert_eq!(json, r#""atxclosed""#);
394
-
395
- let list_indent = ListIndentType::Tabs;
396
- let json = serde_json::to_string(&list_indent).expect("Failed to serialize");
397
- assert_eq!(json, r#""tabs""#);
398
-
399
- let whitespace = WhitespaceMode::Strict;
400
- let json = serde_json::to_string(&whitespace).expect("Failed to serialize");
401
- assert_eq!(json, r#""strict""#);
402
- }
403
-
404
- #[test]
405
- fn test_enum_deserialization() {
406
- // Test that enums deserialize from strings (case insensitive)
407
- let heading: HeadingStyle = serde_json::from_str(r#""atxclosed""#).expect("Failed");
408
- assert_eq!(heading, HeadingStyle::AtxClosed);
409
-
410
- let heading: HeadingStyle = serde_json::from_str(r#""ATXCLOSED""#).expect("Failed");
411
- assert_eq!(heading, HeadingStyle::AtxClosed);
412
-
413
- let list_indent: ListIndentType = serde_json::from_str(r#""tabs""#).expect("Failed");
414
- assert_eq!(list_indent, ListIndentType::Tabs);
415
- }
416
- }
@@ -1 +0,0 @@
1
- //! Prelude module for convenient internal imports.