meta_language/document_formatting/
profile.rs1use crate::language_profile::LanguageProfile;
14use crate::link_network::LinkType;
15
16pub const DOCUMENT_FORMATS: &[&str] = &["txt", "Markdown", "HTML", "PDF", "DOCX"];
19
20pub const CROSS_FORMAT_CONCEPT_IDS: &[&str] = &[
26 "heading",
27 "paragraph",
28 "bullet-list",
29 "ordered-list",
30 "list-item",
31 "strong-emphasis",
32 "emphasis",
33 "hyperlink",
34];
35
36pub const CROSS_FORMAT_CONCEPTS: &[&str] = &[
44 "heading",
45 "paragraph",
46 "bullet-list",
47 "ordered-list",
48 "list-item",
49 "strong",
50 "emphasis",
51 "hyperlink",
52];
53
54#[must_use]
62pub fn document_format_profile(format: &str) -> Option<LanguageProfile> {
63 let canonical = canonical_document_format(format)?;
64 let profile = base_profile(canonical);
65 Some(match canonical {
66 "txt" => txt_profile(profile),
67 "Markdown" => markdown_profile(profile),
68 "HTML" => html_profile(profile),
69 "PDF" => pdf_profile(profile),
70 "DOCX" => docx_profile(profile),
71 _ => unreachable!("canonical_document_format only yields known formats"),
72 })
73}
74
75#[must_use]
80pub fn canonical_document_format(format: &str) -> Option<&'static str> {
81 match format.to_ascii_lowercase().as_str() {
82 "txt" | "text" | "plain-text" | "plaintext" => Some("txt"),
83 "markdown" | "md" => Some("Markdown"),
84 "html" | "htm" => Some("HTML"),
85 "pdf" => Some("PDF"),
86 "docx" => Some("DOCX"),
87 _ => None,
88 }
89}
90
91fn base_profile(canonical: &str) -> LanguageProfile {
92 LanguageProfile::new(canonical, canonical)
93 .with_link_type(LinkType::Document)
94 .with_link_type(LinkType::Concept)
95 .with_link_type(LinkType::Token)
96}
97
98fn with_supported<'a>(
99 mut profile: LanguageProfile,
100 concepts: impl IntoIterator<Item = &'a str>,
101) -> LanguageProfile {
102 for concept in concepts {
103 profile = profile.with_concept(concept);
104 }
105 profile
106}
107
108fn txt_profile(profile: LanguageProfile) -> LanguageProfile {
109 with_supported(profile, ["paragraph"])
110 .with_concept_fallback(
111 "heading",
112 "flattened to a plain paragraph (heading level dropped)",
113 )
114 .with_concept_fallback(
115 "bullet-list",
116 "flattened to plain lines with a `- ` marker per item",
117 )
118 .with_concept_fallback(
119 "ordered-list",
120 "flattened to plain lines with a `N. ` marker per item",
121 )
122 .with_concept_fallback("list-item", "rendered as a single plain line")
123 .with_concept_fallback("strong-emphasis", "rendered as unstyled plain text")
124 .with_concept_fallback("emphasis", "rendered as unstyled plain text")
125 .with_concept_fallback("hyperlink", "rendered as its visible text (URL dropped)")
126}
127
128fn markdown_profile(profile: LanguageProfile) -> LanguageProfile {
129 with_supported(
130 profile,
131 [
132 "heading",
133 "paragraph",
134 "bullet-list",
135 "list-item",
136 "strong-emphasis",
137 "emphasis",
138 "hyperlink",
139 ],
140 )
141 .with_concept_fallback(
142 "ordered-list",
143 "rendered with bullet `- ` markers (ordering not preserved by the Markdown profile)",
144 )
145}
146
147fn html_profile(profile: LanguageProfile) -> LanguageProfile {
148 with_supported(profile, CROSS_FORMAT_CONCEPT_IDS.iter().copied())
150}
151
152fn pdf_profile(profile: LanguageProfile) -> LanguageProfile {
153 with_supported(
154 profile,
155 [
156 "heading",
157 "paragraph",
158 "bullet-list",
159 "ordered-list",
160 "list-item",
161 "strong-emphasis",
162 "emphasis",
163 ],
164 )
165 .with_concept_fallback(
166 "hyperlink",
167 "rendered as its visible text, unstyled (URL dropped)",
168 )
169}
170
171fn docx_profile(profile: LanguageProfile) -> LanguageProfile {
172 with_supported(
173 profile,
174 [
175 "heading",
176 "paragraph",
177 "bullet-list",
178 "ordered-list",
179 "list-item",
180 "strong-emphasis",
181 "emphasis",
182 ],
183 )
184 .with_concept_fallback(
185 "hyperlink",
186 "rendered as its visible text, unstyled (URL dropped)",
187 )
188}