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

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,477 +0,0 @@
1
- #![allow(clippy::cast_precision_loss, clippy::cast_sign_loss, clippy::unused_self)]
2
- //! Type definitions for metadata extraction.
3
-
4
- use std::collections::BTreeMap;
5
-
6
- /// Text directionality of document content.
7
- ///
8
- /// Corresponds to the HTML `dir` attribute and `bdi` element directionality.
9
- #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
10
- #[cfg_attr(feature = "metadata", derive(serde::Serialize, serde::Deserialize))]
11
- pub enum TextDirection {
12
- /// Left-to-right text flow (default for Latin scripts)
13
- #[cfg_attr(feature = "metadata", serde(rename = "ltr"))]
14
- LeftToRight,
15
- /// Right-to-left text flow (Hebrew, Arabic, Urdu, etc.)
16
- #[cfg_attr(feature = "metadata", serde(rename = "rtl"))]
17
- RightToLeft,
18
- /// Automatic directionality detection
19
- #[cfg_attr(feature = "metadata", serde(rename = "auto"))]
20
- Auto,
21
- }
22
-
23
- impl std::fmt::Display for TextDirection {
24
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
25
- match self {
26
- Self::LeftToRight => write!(f, "ltr"),
27
- Self::RightToLeft => write!(f, "rtl"),
28
- Self::Auto => write!(f, "auto"),
29
- }
30
- }
31
- }
32
-
33
- impl TextDirection {
34
- /// Parse a text direction from string value.
35
- ///
36
- /// # Arguments
37
- ///
38
- /// * `s` - Direction string ("ltr", "rtl", or "auto")
39
- ///
40
- /// # Returns
41
- ///
42
- /// `Some(TextDirection)` if valid, `None` otherwise.
43
- ///
44
- /// # Examples
45
- ///
46
- /// ```
47
- /// # use html_to_markdown_rs::metadata::TextDirection;
48
- /// assert_eq!(TextDirection::parse("ltr"), Some(TextDirection::LeftToRight));
49
- /// assert_eq!(TextDirection::parse("rtl"), Some(TextDirection::RightToLeft));
50
- /// assert_eq!(TextDirection::parse("auto"), Some(TextDirection::Auto));
51
- /// assert_eq!(TextDirection::parse("invalid"), None);
52
- /// ```
53
- #[must_use]
54
- pub fn parse(s: &str) -> Option<Self> {
55
- if s.eq_ignore_ascii_case("ltr") {
56
- return Some(Self::LeftToRight);
57
- }
58
- if s.eq_ignore_ascii_case("rtl") {
59
- return Some(Self::RightToLeft);
60
- }
61
- if s.eq_ignore_ascii_case("auto") {
62
- return Some(Self::Auto);
63
- }
64
- None
65
- }
66
- }
67
-
68
- /// Link classification based on href value and document context.
69
- ///
70
- /// Used to categorize links during extraction for filtering and analysis.
71
- #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
72
- #[cfg_attr(feature = "metadata", derive(serde::Serialize, serde::Deserialize))]
73
- #[cfg_attr(feature = "metadata", serde(rename_all = "snake_case"))]
74
- pub enum LinkType {
75
- /// Anchor link within same document (href starts with #)
76
- Anchor,
77
- /// Internal link within same domain
78
- Internal,
79
- /// External link to different domain
80
- External,
81
- /// Email link (mailto:)
82
- Email,
83
- /// Phone link (tel:)
84
- Phone,
85
- /// Other protocol or unclassifiable
86
- Other,
87
- }
88
-
89
- impl std::fmt::Display for LinkType {
90
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
91
- match self {
92
- Self::Anchor => write!(f, "anchor"),
93
- Self::Internal => write!(f, "internal"),
94
- Self::External => write!(f, "external"),
95
- Self::Email => write!(f, "email"),
96
- Self::Phone => write!(f, "phone"),
97
- Self::Other => write!(f, "other"),
98
- }
99
- }
100
- }
101
-
102
- /// Image source classification for proper handling and processing.
103
- ///
104
- /// Determines whether an image is embedded (data URI), inline SVG, external, or relative.
105
- #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
106
- #[cfg_attr(feature = "metadata", derive(serde::Serialize, serde::Deserialize))]
107
- #[cfg_attr(feature = "metadata", serde(rename_all = "snake_case"))]
108
- pub enum ImageType {
109
- /// Data URI embedded image (base64 or other encoding)
110
- DataUri,
111
- /// Inline SVG element
112
- InlineSvg,
113
- /// External image URL (http/https)
114
- External,
115
- /// Relative image path
116
- Relative,
117
- }
118
-
119
- impl std::fmt::Display for ImageType {
120
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
121
- match self {
122
- Self::DataUri => write!(f, "data_uri"),
123
- Self::InlineSvg => write!(f, "inline_svg"),
124
- Self::External => write!(f, "external"),
125
- Self::Relative => write!(f, "relative"),
126
- }
127
- }
128
- }
129
-
130
- /// Structured data format type.
131
- ///
132
- /// Identifies the schema/format used for structured data markup.
133
- #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
134
- #[cfg_attr(feature = "metadata", derive(serde::Serialize, serde::Deserialize))]
135
- #[cfg_attr(feature = "metadata", serde(rename_all = "snake_case"))]
136
- pub enum StructuredDataType {
137
- /// JSON-LD (JSON for Linking Data) script blocks
138
- #[cfg_attr(feature = "metadata", serde(rename = "json_ld"))]
139
- JsonLd,
140
- /// HTML5 Microdata attributes (itemscope, itemtype, itemprop)
141
- Microdata,
142
- /// RDF in Attributes (RDFa) markup
143
- #[cfg_attr(feature = "metadata", serde(rename = "rdfa"))]
144
- RDFa,
145
- }
146
-
147
- impl std::fmt::Display for StructuredDataType {
148
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
149
- match self {
150
- Self::JsonLd => write!(f, "json_ld"),
151
- Self::Microdata => write!(f, "microdata"),
152
- Self::RDFa => write!(f, "rdfa"),
153
- }
154
- }
155
- }
156
-
157
- /// Document-level metadata extracted from `<head>` and top-level elements.
158
- ///
159
- /// Contains all metadata typically used by search engines, social media platforms,
160
- /// and browsers for document indexing and presentation.
161
- ///
162
- /// # Examples
163
- ///
164
- /// ```
165
- /// # use html_to_markdown_rs::metadata::DocumentMetadata;
166
- /// let doc = DocumentMetadata {
167
- /// title: Some("My Article".to_string()),
168
- /// description: Some("A great article about Rust".to_string()),
169
- /// keywords: vec!["rust".to_string(), "programming".to_string()],
170
- /// ..Default::default()
171
- /// };
172
- ///
173
- /// assert_eq!(doc.title, Some("My Article".to_string()));
174
- /// ```
175
- #[derive(Debug, Clone, Default)]
176
- #[cfg_attr(feature = "metadata", derive(serde::Serialize, serde::Deserialize))]
177
- pub struct DocumentMetadata {
178
- /// Document title from `<title>` tag
179
- pub title: Option<String>,
180
-
181
- /// Document description from `<meta name="description">` tag
182
- pub description: Option<String>,
183
-
184
- /// Document keywords from `<meta name="keywords">` tag, split on commas
185
- pub keywords: Vec<String>,
186
-
187
- /// Document author from `<meta name="author">` tag
188
- pub author: Option<String>,
189
-
190
- /// Canonical URL from `<link rel="canonical">` tag
191
- pub canonical_url: Option<String>,
192
-
193
- /// Base URL from `<base href="">` tag for resolving relative URLs
194
- pub base_href: Option<String>,
195
-
196
- /// Document language from `lang` attribute
197
- pub language: Option<String>,
198
-
199
- /// Document text direction from `dir` attribute
200
- pub text_direction: Option<TextDirection>,
201
-
202
- /// Open Graph metadata (og:* properties) for social media
203
- /// Keys like "title", "description", "image", "url", etc.
204
- pub open_graph: BTreeMap<String, String>,
205
-
206
- /// Twitter Card metadata (twitter:* properties)
207
- /// Keys like "card", "site", "creator", "title", "description", "image", etc.
208
- pub twitter_card: BTreeMap<String, String>,
209
-
210
- /// Additional meta tags not covered by specific fields
211
- /// Keys are meta name/property attributes, values are content
212
- pub meta_tags: BTreeMap<String, String>,
213
- }
214
-
215
- /// Header element metadata with hierarchy tracking.
216
- ///
217
- /// Captures heading elements (h1-h6) with their text content, identifiers,
218
- /// and position in the document structure.
219
- ///
220
- /// # Examples
221
- ///
222
- /// ```
223
- /// # use html_to_markdown_rs::metadata::HeaderMetadata;
224
- /// let header = HeaderMetadata {
225
- /// level: 1,
226
- /// text: "Main Title".to_string(),
227
- /// id: Some("main-title".to_string()),
228
- /// depth: 0,
229
- /// html_offset: 145,
230
- /// };
231
- ///
232
- /// assert_eq!(header.level, 1);
233
- /// assert!(header.is_valid());
234
- /// ```
235
- #[derive(Debug, Clone)]
236
- #[cfg_attr(feature = "metadata", derive(serde::Serialize, serde::Deserialize))]
237
- pub struct HeaderMetadata {
238
- /// Header level: 1 (h1) through 6 (h6)
239
- pub level: u8,
240
-
241
- /// Normalized text content of the header
242
- pub text: String,
243
-
244
- /// HTML id attribute if present
245
- pub id: Option<String>,
246
-
247
- /// Document tree depth at the header element
248
- pub depth: usize,
249
-
250
- /// Byte offset in original HTML document
251
- pub html_offset: usize,
252
- }
253
-
254
- impl HeaderMetadata {
255
- /// Validate that the header level is within valid range (1-6).
256
- ///
257
- /// # Returns
258
- ///
259
- /// `true` if level is 1-6, `false` otherwise.
260
- ///
261
- /// # Examples
262
- ///
263
- /// ```
264
- /// # use html_to_markdown_rs::metadata::HeaderMetadata;
265
- /// let valid = HeaderMetadata {
266
- /// level: 3,
267
- /// text: "Title".to_string(),
268
- /// id: None,
269
- /// depth: 2,
270
- /// html_offset: 100,
271
- /// };
272
- /// assert!(valid.is_valid());
273
- ///
274
- /// let invalid = HeaderMetadata {
275
- /// level: 7, // Invalid
276
- /// text: "Title".to_string(),
277
- /// id: None,
278
- /// depth: 2,
279
- /// html_offset: 100,
280
- /// };
281
- /// assert!(!invalid.is_valid());
282
- /// ```
283
- #[must_use]
284
- pub const fn is_valid(&self) -> bool {
285
- self.level >= 1 && self.level <= 6
286
- }
287
- }
288
-
289
- /// Hyperlink metadata with categorization and attributes.
290
- ///
291
- /// Represents `<a>` elements with parsed href values, text content, and link type classification.
292
- ///
293
- /// # Examples
294
- ///
295
- /// ```
296
- /// # use html_to_markdown_rs::metadata::{LinkMetadata, LinkType};
297
- /// let link = LinkMetadata {
298
- /// href: "https://example.com".to_string(),
299
- /// text: "Example".to_string(),
300
- /// title: Some("Visit Example".to_string()),
301
- /// link_type: LinkType::External,
302
- /// rel: vec!["nofollow".to_string()],
303
- /// attributes: Default::default(),
304
- /// };
305
- ///
306
- /// assert_eq!(link.link_type, LinkType::External);
307
- /// assert_eq!(link.text, "Example");
308
- /// ```
309
- #[derive(Debug, Clone)]
310
- #[cfg_attr(feature = "metadata", derive(serde::Serialize, serde::Deserialize))]
311
- pub struct LinkMetadata {
312
- /// The href URL value
313
- pub href: String,
314
-
315
- /// Link text content (normalized, concatenated if mixed with elements)
316
- pub text: String,
317
-
318
- /// Optional title attribute (often shown as tooltip)
319
- pub title: Option<String>,
320
-
321
- /// Link type classification
322
- pub link_type: LinkType,
323
-
324
- /// Rel attribute values (e.g., "nofollow", "stylesheet", "canonical")
325
- pub rel: Vec<String>,
326
-
327
- /// Additional HTML attributes
328
- pub attributes: BTreeMap<String, String>,
329
- }
330
-
331
- impl LinkMetadata {
332
- /// Classify a link based on href value.
333
- ///
334
- /// # Arguments
335
- ///
336
- /// * `href` - The href attribute value
337
- ///
338
- /// # Returns
339
- ///
340
- /// Appropriate [`LinkType`] based on protocol and content.
341
- ///
342
- /// # Examples
343
- ///
344
- /// ```
345
- /// # use html_to_markdown_rs::metadata::{LinkMetadata, LinkType};
346
- /// assert_eq!(LinkMetadata::classify_link("#section"), LinkType::Anchor);
347
- /// assert_eq!(LinkMetadata::classify_link("mailto:test@example.com"), LinkType::Email);
348
- /// assert_eq!(LinkMetadata::classify_link("tel:+1234567890"), LinkType::Phone);
349
- /// assert_eq!(LinkMetadata::classify_link("https://example.com"), LinkType::External);
350
- /// ```
351
- #[must_use]
352
- pub fn classify_link(href: &str) -> LinkType {
353
- if href.starts_with('#') {
354
- LinkType::Anchor
355
- } else if href.starts_with("mailto:") {
356
- LinkType::Email
357
- } else if href.starts_with("tel:") {
358
- LinkType::Phone
359
- } else if href.starts_with("http://") || href.starts_with("https://") {
360
- LinkType::External
361
- } else if href.starts_with('/') || href.starts_with("../") || href.starts_with("./") {
362
- LinkType::Internal
363
- } else {
364
- LinkType::Other
365
- }
366
- }
367
- }
368
-
369
- /// Image metadata with source and dimensions.
370
- ///
371
- /// Captures `<img>` elements and inline `<svg>` elements with metadata
372
- /// for image analysis and optimization.
373
- ///
374
- /// # Examples
375
- ///
376
- /// ```
377
- /// # use html_to_markdown_rs::metadata::{ImageMetadata, ImageType};
378
- /// let img = ImageMetadata {
379
- /// src: "https://example.com/image.jpg".to_string(),
380
- /// alt: Some("An example image".to_string()),
381
- /// title: Some("Example".to_string()),
382
- /// dimensions: Some((800, 600)),
383
- /// image_type: ImageType::External,
384
- /// attributes: Default::default(),
385
- /// };
386
- ///
387
- /// assert_eq!(img.image_type, ImageType::External);
388
- /// ```
389
- #[derive(Debug, Clone)]
390
- #[cfg_attr(feature = "metadata", derive(serde::Serialize, serde::Deserialize))]
391
- pub struct ImageMetadata {
392
- /// Image source (URL, data URI, or SVG content identifier)
393
- pub src: String,
394
-
395
- /// Alternative text from alt attribute (for accessibility)
396
- pub alt: Option<String>,
397
-
398
- /// Title attribute (often shown as tooltip)
399
- pub title: Option<String>,
400
-
401
- /// Image dimensions as (width, height) if available
402
- pub dimensions: Option<(u32, u32)>,
403
-
404
- /// Image type classification
405
- pub image_type: ImageType,
406
-
407
- /// Additional HTML attributes
408
- pub attributes: BTreeMap<String, String>,
409
- }
410
-
411
- /// Structured data block (JSON-LD, Microdata, or RDFa).
412
- ///
413
- /// Represents machine-readable structured data found in the document.
414
- /// JSON-LD blocks are collected as raw JSON strings for flexibility.
415
- ///
416
- /// # Examples
417
- ///
418
- /// ```
419
- /// # use html_to_markdown_rs::metadata::{StructuredData, StructuredDataType};
420
- /// let schema = StructuredData {
421
- /// data_type: StructuredDataType::JsonLd,
422
- /// raw_json: r#"{"@context":"https://schema.org","@type":"Article"}"#.to_string(),
423
- /// schema_type: Some("Article".to_string()),
424
- /// };
425
- ///
426
- /// assert_eq!(schema.data_type, StructuredDataType::JsonLd);
427
- /// ```
428
- #[derive(Debug, Clone)]
429
- #[cfg_attr(feature = "metadata", derive(serde::Serialize, serde::Deserialize))]
430
- pub struct StructuredData {
431
- /// Type of structured data (JSON-LD, Microdata, RDFa)
432
- pub data_type: StructuredDataType,
433
-
434
- /// Raw JSON string (for JSON-LD) or serialized representation
435
- pub raw_json: String,
436
-
437
- /// Schema type if detectable (e.g., "Article", "Event", "Product")
438
- pub schema_type: Option<String>,
439
- }
440
-
441
- /// Comprehensive metadata extraction result from HTML document.
442
- ///
443
- /// Contains all extracted metadata types in a single structure,
444
- /// suitable for serialization and transmission across language boundaries.
445
- ///
446
- /// # Examples
447
- ///
448
- /// ```
449
- /// # use html_to_markdown_rs::metadata::HtmlMetadata;
450
- /// let metadata = HtmlMetadata {
451
- /// document: Default::default(),
452
- /// headers: Vec::new(),
453
- /// links: Vec::new(),
454
- /// images: Vec::new(),
455
- /// structured_data: Vec::new(),
456
- /// };
457
- ///
458
- /// assert!(metadata.headers.is_empty());
459
- /// ```
460
- #[derive(Debug, Clone, Default)]
461
- #[cfg_attr(feature = "metadata", derive(serde::Serialize, serde::Deserialize))]
462
- pub struct HtmlMetadata {
463
- /// Document-level metadata (title, description, canonical, etc.)
464
- pub document: DocumentMetadata,
465
-
466
- /// Extracted header elements with hierarchy
467
- pub headers: Vec<HeaderMetadata>,
468
-
469
- /// Extracted hyperlinks with type classification
470
- pub links: Vec<LinkMetadata>,
471
-
472
- /// Extracted images with source and dimensions
473
- pub images: Vec<ImageMetadata>,
474
-
475
- /// Extracted structured data blocks
476
- pub structured_data: Vec<StructuredData>,
477
- }