nmd_core/
codex.rs

1//! `Codex` is a set of associations used to transform the text using a `Compiler`
2
3
4pub mod modifier;
5
6
7use std::collections::HashSet;
8use std::fmt::Debug;
9use std::sync::Arc;
10use getset::{Getters, Setters};
11use indexmap::IndexMap;
12use modifier::base_modifier::BaseModifier;
13use modifier::{Modifier, ModifierIdentifier};
14use self::modifier::standard_paragraph_modifier::StandardParagraphModifier;
15use self::modifier::standard_text_modifier::StandardTextModifier;
16use crate::assembler::html_assembler::HtmlAssembler;
17use crate::assembler::Assembler;
18use crate::compilable_text::compilable_text_part::CompilableTextPart;
19use crate::compilable_text::CompilableText;
20use crate::compilation::compilation_rule::replacement_rule::replacement_rule_part::closure_replacement_rule_part::ClosureReplacementRuleReplacerPart;
21use crate::compilation::compilation_rule::replacement_rule::replacement_rule_part::fixed_replacement_rule_part::FixedReplacementRuleReplacerPart;
22use crate::compilation::compilation_rule::replacement_rule::replacement_rule_part::single_capture_group_replacement_rule_part::SingleCaptureGroupReplacementRuleReplacerPart;
23use crate::compilation::compilation_rule::replacement_rule::ReplacementRule;
24use crate::dossier::document::chapter::paragraph::paragraph_loading_rule::block_quote_paragraph_loading_rule::BlockQuoteParagraphLoadingRule;
25use crate::dossier::document::chapter::paragraph::paragraph_loading_rule::common_paragraph_loading_rule::CommonParagraphLoadingRule;
26use crate::dossier::document::chapter::paragraph::paragraph_loading_rule::focus_block_paragraph_loading_rule::FocusBlockParagraphLoadingRule;
27use crate::dossier::document::chapter::paragraph::paragraph_loading_rule::image_paragraph_loading_rule::ImageParagraphLoadingRule;
28use crate::dossier::document::chapter::paragraph::paragraph_loading_rule::list_paragraph_loading_rule::ListParagraphLoadingRule;
29use crate::dossier::document::chapter::paragraph::paragraph_loading_rule::metadata_wrapper_paragraph_loading_rule::MetadataWrapperParagraphLoadingRule;
30use crate::dossier::document::chapter::paragraph::paragraph_loading_rule::replacement_rule_paragraph_loading_rule::ReplacementRuleParagraphLoadingRule;
31use crate::dossier::document::chapter::paragraph::paragraph_loading_rule::table_paragraph_loading_rule::TableParagraphLoadingRule;
32use crate::dossier::document::chapter::paragraph::paragraph_loading_rule::{MultiParagraphLoadingRule, ParagraphLoadingRule};
33use crate::output_format::OutputFormat;
34use crate::resource::resource_reference::ResourceReference;
35use crate::utility::text_utility;
36use super::compilation::compilation_rule::constants::ESCAPE_HTML;
37use super::compilation::compilation_rule::html_cite_rule::HtmlCiteRule;
38use super::compilation::compilation_rule::html_greek_letter_rule::HtmlGreekLettersRule;
39use super::compilation::compilation_rule::reference_rule::ReferenceRule;
40use super::compilation::compilation_rule::CompilationRule;
41
42
43pub type TextModifierOrderedMap = IndexMap<ModifierIdentifier, (Box<dyn Modifier>, Box<dyn CompilationRule>)>;
44pub type ParagraphModifierOrderedMap = IndexMap<ModifierIdentifier, (Box<dyn Modifier>, Box<dyn ParagraphLoadingRule>)>;
45pub type FallbackParagraph = (ModifierIdentifier, Box<dyn MultiParagraphLoadingRule>);
46
47
48/// Ordered collection of rules
49/// A **rule** is defined as the actual text transformation
50#[derive(Debug, Getters, Setters)]
51pub struct Codex {
52
53    #[getset(get = "pub", set = "pub")]
54    text_modifiers: TextModifierOrderedMap,
55
56    #[getset(get = "pub", set = "pub")]
57    paragraph_modifiers: ParagraphModifierOrderedMap,
58
59    #[getset(get = "pub", set = "pub")]
60    fallback_paragraph: Option<FallbackParagraph>,
61    
62    #[getset(get = "pub", set = "pub")]
63    assembler: Box<dyn Assembler>
64}
65
66impl Codex {
67
68    pub fn from(format: &OutputFormat) -> Self {
69        match format {
70            OutputFormat::Html => Self::of_html()
71        }
72    }
73
74    /// Create a new `Codex`
75    pub fn new(text_modifiers: TextModifierOrderedMap, paragraph_modifiers: ParagraphModifierOrderedMap,
76                fallback_paragraph_modifier: Option<FallbackParagraph>,
77                assembler: Box<dyn Assembler>,) -> Self {
78
79        Self {
80            text_modifiers,
81            paragraph_modifiers,
82            fallback_paragraph: fallback_paragraph_modifier,
83            assembler
84        }
85    }
86
87    /// Remove all modifiers and rules except those passed 
88    pub fn retain(&mut self, identifiers: HashSet<ModifierIdentifier>) {
89
90        self.text_modifiers.retain(|id, _| identifiers.contains(id));
91        self.paragraph_modifiers.retain(|id, _| identifiers.contains(id));
92    }
93
94
95    /// Remove modifiers and rules 
96    pub fn remove(&mut self, identifiers: HashSet<ModifierIdentifier>) {
97        identifiers.iter().for_each(|id: &ModifierIdentifier| {
98
99            self.text_modifiers.shift_remove(id);
100            self.paragraph_modifiers.shift_remove(id);
101        });
102    }
103
104    /// Standard HTML `Codex`
105    pub fn of_html() -> Self {
106
107        let text_rules: TextModifierOrderedMap = TextModifierOrderedMap::from([
108            (
109                StandardTextModifier::InlineCode.identifier().clone(),
110                (
111                    Box::new(Into::<BaseModifier>::into(StandardTextModifier::InlineCode)) as Box<dyn Modifier>,
112                    Box::new(ReplacementRule::new(
113                        StandardTextModifier::InlineCode.modifier_pattern().clone(),
114                        vec![
115                            Arc::new(FixedReplacementRuleReplacerPart::new(String::from(r#"<code class="language-markup inline-code">"#))),
116                            Arc::new(SingleCaptureGroupReplacementRuleReplacerPart::new(1, ESCAPE_HTML.clone(), StandardTextModifier::InlineCode.incompatible_modifiers())),
117                            Arc::new(FixedReplacementRuleReplacerPart::new(String::from(r#"</code>"#))),
118                        ]
119                    )) as Box<dyn CompilationRule>
120                ) as (Box<dyn Modifier>, Box<dyn CompilationRule>)
121            ),
122            (
123                StandardTextModifier::InlineMath.identifier().clone(),
124                (
125                    Box::new(Into::<BaseModifier>::into(StandardTextModifier::InlineMath)),
126                    Box::new(ReplacementRule::new(
127                        StandardTextModifier::InlineMath.modifier_pattern().clone(),
128                        vec![
129                            Arc::new(FixedReplacementRuleReplacerPart::new(String::from(r#"<span class="inline-math">$"#))),
130                            Arc::new(SingleCaptureGroupReplacementRuleReplacerPart::new(1, ESCAPE_HTML.clone(), StandardTextModifier::InlineMath.incompatible_modifiers())),
131                            Arc::new(FixedReplacementRuleReplacerPart::new(String::from(r#"$</span>"#))),
132                        ]
133                    ))
134                )
135            ),
136            (
137                StandardTextModifier::Comment.identifier().clone(),
138                (
139                    Box::new(Into::<BaseModifier>::into(StandardTextModifier::Comment)),
140                    Box::new(ReplacementRule::new(
141                        StandardTextModifier::Comment.modifier_pattern().clone(),
142                        vec![
143                            Arc::new(FixedReplacementRuleReplacerPart::new(String::from(r#"<!-- "#))),
144                            Arc::new(SingleCaptureGroupReplacementRuleReplacerPart::new(1, ESCAPE_HTML.clone(), StandardTextModifier::Comment.incompatible_modifiers())),
145                            Arc::new(FixedReplacementRuleReplacerPart::new(String::from(r#" -->"#))),
146                        ]
147                    ))
148                )
149            ),
150            (
151                StandardTextModifier::GreekLetter.identifier().clone(),
152                (
153                    Box::new(Into::<BaseModifier>::into(StandardTextModifier::GreekLetter)),
154                    Box::new(HtmlGreekLettersRule::new())
155                ),
156            ),
157            (
158                StandardTextModifier::Todo.identifier().clone(),
159                (
160                    Box::new(Into::<BaseModifier>::into(StandardTextModifier::Todo)) as Box<dyn Modifier>,
161                    Box::new(ReplacementRule::new(
162                        StandardTextModifier::Todo.modifier_pattern().clone(),
163                        vec![
164                            Arc::new(FixedReplacementRuleReplacerPart::new(String::from(r#"<div class="todo"><div class="todo-title"></div><div class="todo-description">"#))),
165                            Arc::new(SingleCaptureGroupReplacementRuleReplacerPart::new(1, ESCAPE_HTML.clone(), StandardTextModifier::Todo.incompatible_modifiers())),
166                            Arc::new(FixedReplacementRuleReplacerPart::new(String::from(r#"</div></div>"#))),
167                        ]
168                    )) as Box<dyn CompilationRule>,
169                ) as (Box<dyn Modifier>, Box<dyn CompilationRule>)
170            ),
171            (
172                StandardTextModifier::Bookmark.identifier().clone(),
173                (
174                    Box::new(Into::<BaseModifier>::into(StandardTextModifier::Bookmark)),
175                    Box::new(ReplacementRule::new(
176                    StandardTextModifier::Bookmark.modifier_pattern().clone(),
177                    vec![
178                        Arc::new(ClosureReplacementRuleReplacerPart::new(Arc::new(|captures, _, _, _, cco| {
179
180                            let mut identifier_class = String::new();
181                            let mut id_attr = String::new();
182                            if let Some(raw_id) = captures.get(2) {
183                                id_attr = format!(
184                                    r#"id="{}""#,
185                                    ResourceReference::of_internal_from_without_sharp(raw_id.as_str(), cco.document_name().as_ref())?.build(),
186                                );
187
188                                identifier_class = String::from("identifier");
189                            }
190    
191                            Ok(CompilableText::from(vec![
192                                CompilableTextPart::new_fixed(format!(
193                                    r#"<div class="{} bookmark" {}><div class="bookmark-title">"#,
194                                    identifier_class,
195                                    id_attr
196                                ))
197                            ]))
198                        }))),
199                        Arc::new(SingleCaptureGroupReplacementRuleReplacerPart::new(1, ESCAPE_HTML.clone(), StandardTextModifier::Bookmark.incompatible_modifiers())),
200                        Arc::new(FixedReplacementRuleReplacerPart::new(String::from(r#"</div><div class="bookmark-description">"#))),
201                        Arc::new(SingleCaptureGroupReplacementRuleReplacerPart::new(3, ESCAPE_HTML.clone(), StandardTextModifier::Bookmark.incompatible_modifiers())),
202                        Arc::new(FixedReplacementRuleReplacerPart::new(String::from(r#"</div></div>"#))),
203                    ])),
204                )
205            ),
206            (
207                StandardTextModifier::AbridgedBookmark.identifier().clone(),
208                (
209                    Box::new(Into::<BaseModifier>::into(StandardTextModifier::AbridgedBookmark)),
210                    Box::new(ReplacementRule::new(
211                        StandardTextModifier::AbridgedBookmark.modifier_pattern().clone(),
212                        vec![
213                            Arc::new(ClosureReplacementRuleReplacerPart::new(Arc::new(|captures, _, _, _, cco| {
214    
215                                let mut identifier_class = String::new();
216                                let mut id_attr = String::new();
217                                if let Some(raw_id) = captures.get(2) {
218                                    id_attr = format!(
219                                        r#"id="{}""#,
220                                        ResourceReference::of_internal_from_without_sharp(raw_id.as_str(), cco.document_name().as_ref())?.build(),
221                                    );
222    
223                                    identifier_class = String::from("identifier");
224                                }
225        
226                                Ok(CompilableText::from(vec![
227                                    CompilableTextPart::new_fixed(format!(
228                                        r#"<div class="{} abridged-bookmark" {}><div class="abridged-bookmark-title">"#,
229                                        identifier_class,
230                                        id_attr,
231                                    ))
232                                ]))
233                            }))),
234                            Arc::new(SingleCaptureGroupReplacementRuleReplacerPart::new(1, ESCAPE_HTML.clone(), StandardTextModifier::AbridgedBookmark.incompatible_modifiers())),
235                            Arc::new(FixedReplacementRuleReplacerPart::new(String::from(r#"</div></div>"#)))
236                        ]
237                    ))
238                )
239            ),
240            (
241                StandardTextModifier::EmbeddedStyle.identifier().clone(),
242                (
243                    Box::new(Into::<BaseModifier>::into(StandardTextModifier::EmbeddedStyle)),
244                    Box::new(ReplacementRule::new(
245                        StandardTextModifier::EmbeddedStyle.modifier_pattern().clone(),
246                        vec![
247                            Arc::new(ClosureReplacementRuleReplacerPart::new(Arc::new(|captures, _, _, _, cco| {
248    
249                                let (styles, classes) = text_utility::split_styles_and_classes(captures.get(3).unwrap().as_str());
250    
251                                let mut identifier_class = String::new();
252                                let mut id_attr = String::new();
253                                if let Some(raw_id) = captures.get(2) {
254                                    id_attr = format!(
255                                        r#"id="{}""#,
256                                        ResourceReference::of_internal_from_without_sharp(raw_id.as_str(), cco.document_name().as_ref())?.build(),
257                                    );
258    
259                                    identifier_class = String::from("identifier");
260                                } 
261    
262                                Ok(CompilableText::from(vec![
263                                    CompilableTextPart::new_fixed(format!(
264                                        r#"<span class="{} embedded-style {}" {} style="{}">"#,
265                                        identifier_class,
266                                        classes.unwrap_or(String::new()),
267                                        id_attr,
268                                        styles.unwrap_or(String::new())
269                                    ))
270                                ]))
271                            }))),
272                            Arc::new(SingleCaptureGroupReplacementRuleReplacerPart::new(1, ESCAPE_HTML.clone(), StandardTextModifier::EmbeddedStyle.incompatible_modifiers())),
273                            Arc::new(FixedReplacementRuleReplacerPart::new(String::from(r#"</span>"#)))
274                        ]
275                    ))
276                )
277            ),
278            (
279                StandardTextModifier::AbridgedEmbeddedStyle.identifier().clone(),
280                (
281                    Box::new(Into::<BaseModifier>::into(StandardTextModifier::AbridgedEmbeddedStyle)),
282                    Box::new(ReplacementRule::new(
283                        StandardTextModifier::AbridgedEmbeddedStyle.modifier_pattern().clone(),
284                        vec![
285                            Arc::new(ClosureReplacementRuleReplacerPart::new(Arc::new(|captures, _, _, _, cco| {
286    
287                                let mut identifier_class = String::new();
288                                let mut id_attr = String::new();
289                                if let Some(raw_id) = captures.get(2) {
290                                    id_attr = format!(
291                                        r#"id="{}""#,
292                                        ResourceReference::of_internal_from_without_sharp(raw_id.as_str(), cco.document_name().as_ref())?.build(),
293                                    );
294    
295                                    identifier_class = String::from("identifier");
296                                } 
297        
298                                let mut color_style = String::new();
299                                if let Some(color) = captures.get(4) {
300                                    color_style = format!("color: {};", color.as_str());
301                                }
302    
303                                let mut bg_style = String::new();
304                                if let Some(bg) = captures.get(5) {
305                                    bg_style = format!("background-color: {};", bg.as_str());
306                                }
307    
308                                let mut font_style = String::new();
309                                if let Some(font) = captures.get(6) {
310                                    font_style = format!("font-family: {};", font.as_str());
311                                }
312    
313                                Ok(CompilableText::from(vec![
314                                    CompilableTextPart::new_fixed(format!(
315                                        r#"<span class="{} abridged-embedded-style" {} style="{} {} {}">"#,
316                                        identifier_class,
317                                        id_attr,
318                                        color_style,
319                                        bg_style,
320                                        font_style,
321                                    ))
322                                ]))
323                            }))),
324                            Arc::new(SingleCaptureGroupReplacementRuleReplacerPart::new(1, ESCAPE_HTML.clone(), StandardTextModifier::AbridgedEmbeddedStyle.incompatible_modifiers())),
325                            Arc::new(FixedReplacementRuleReplacerPart::new(String::from(r#"</span>"#)))
326                        ]
327                    ))
328                )
329            ),
330            (
331                StandardTextModifier::Identifier.identifier().clone(),
332                (
333                    Box::new(Into::<BaseModifier>::into(StandardTextModifier::Identifier)),
334                    Box::new(ReplacementRule::new(
335                        StandardTextModifier::Identifier.modifier_pattern().clone(),
336                        vec![
337                            Arc::new(ClosureReplacementRuleReplacerPart::new(Arc::new(|captures, _, _, _, cco| {
338        
339                                Ok(CompilableText::from(vec![
340                                    CompilableTextPart::new_fixed(format!(
341                                        r#"<span class="identifier" id="{}">"#,
342                                        ResourceReference::of_internal_from_without_sharp(captures.get(2).unwrap().as_str(), cco.document_name().as_ref())?.build(),
343                                    ))
344                                ]))
345                            }))),
346                            Arc::new(SingleCaptureGroupReplacementRuleReplacerPart::new(1, ESCAPE_HTML.clone(), StandardTextModifier::Identifier.incompatible_modifiers())),
347                            Arc::new(FixedReplacementRuleReplacerPart::new(String::from(r#"</span>"#)))
348                        ]
349                    ))
350                )
351            ),
352            (
353                StandardTextModifier::Highlight.identifier().clone(),
354                (
355                    Box::new(Into::<BaseModifier>::into(StandardTextModifier::Highlight)),
356                    Box::new(ReplacementRule::new(
357                        StandardTextModifier::Highlight.modifier_pattern().clone(),
358                        vec![
359                            Arc::new(FixedReplacementRuleReplacerPart::new(String::from(r#"<mark class="highlight">"#))),
360                            Arc::new(SingleCaptureGroupReplacementRuleReplacerPart::new(1, ESCAPE_HTML.clone(), StandardTextModifier::Highlight.incompatible_modifiers())),
361                            Arc::new(FixedReplacementRuleReplacerPart::new(String::from(r#"</mark>"#))),
362                        ]
363                    ))
364                )
365            ),
366            (
367                StandardTextModifier::BoldStarVersion.identifier().clone(),
368                (
369                    Box::new(Into::<BaseModifier>::into(StandardTextModifier::BoldStarVersion)),
370                    Box::new(ReplacementRule::new(
371                        StandardTextModifier::BoldStarVersion.modifier_pattern().clone(),
372                        vec![
373                            Arc::new(FixedReplacementRuleReplacerPart::new(String::from(r#"<strong class="bold">"#))),
374                            Arc::new(SingleCaptureGroupReplacementRuleReplacerPart::new(1, ESCAPE_HTML.clone(), StandardTextModifier::BoldStarVersion.incompatible_modifiers())),
375                            Arc::new(FixedReplacementRuleReplacerPart::new(String::from(r#"</strong>"#))),
376                        ]
377                    ))
378                )
379            ),
380            (
381                StandardTextModifier::BoldUnderscoreVersion.identifier().clone(),
382                (
383                    Box::new(Into::<BaseModifier>::into(StandardTextModifier::BoldUnderscoreVersion)),
384                    Box::new(ReplacementRule::new(
385                        StandardTextModifier::BoldUnderscoreVersion.modifier_pattern().clone(),
386                        vec![
387                            Arc::new(FixedReplacementRuleReplacerPart::new(String::from(r#"<strong class="bold">"#))),
388                            Arc::new(SingleCaptureGroupReplacementRuleReplacerPart::new(1, ESCAPE_HTML.clone(), StandardTextModifier::BoldUnderscoreVersion.incompatible_modifiers())),
389                            Arc::new(FixedReplacementRuleReplacerPart::new(String::from(r#"</strong>"#))),
390                        ]
391                    ))
392                )
393            ),
394            (
395                StandardTextModifier::ItalicStarVersion.identifier().clone(),
396                (
397                    Box::new(Into::<BaseModifier>::into(StandardTextModifier::ItalicStarVersion)),
398                    Box::new(ReplacementRule::new(
399                        StandardTextModifier::ItalicStarVersion.modifier_pattern().clone(),
400                        vec![
401                            Arc::new(FixedReplacementRuleReplacerPart::new(String::from(r#"<em class="italic">"#))),
402                            Arc::new(SingleCaptureGroupReplacementRuleReplacerPart::new(1, ESCAPE_HTML.clone(), StandardTextModifier::ItalicStarVersion.incompatible_modifiers())),
403                            Arc::new(FixedReplacementRuleReplacerPart::new(String::from(r#"</em>"#))),
404                        ]
405                    ))
406                )
407            ),
408            (
409                StandardTextModifier::ItalicUnderscoreVersion.identifier().clone(),
410                (
411                    Box::new(Into::<BaseModifier>::into(StandardTextModifier::ItalicUnderscoreVersion)),
412                    Box::new(ReplacementRule::new(
413                        StandardTextModifier::ItalicUnderscoreVersion.modifier_pattern().clone(),
414                        vec![
415                            Arc::new(FixedReplacementRuleReplacerPart::new(String::from(r#"<em class="italic">"#))),
416                            Arc::new(SingleCaptureGroupReplacementRuleReplacerPart::new(1, ESCAPE_HTML.clone(), StandardTextModifier::ItalicUnderscoreVersion.incompatible_modifiers())),
417                            Arc::new(FixedReplacementRuleReplacerPart::new(String::from(r#"</em>"#))),
418                        ]
419                    ))
420                )
421            ),
422            (
423                StandardTextModifier::Strikethrough.identifier().clone(),
424                (
425                    Box::new(Into::<BaseModifier>::into(StandardTextModifier::Strikethrough)),
426                    Box::new(ReplacementRule::new(
427                        StandardTextModifier::Strikethrough.modifier_pattern().clone(),
428                        vec![
429                            Arc::new(FixedReplacementRuleReplacerPart::new(String::from(r#"<del class="strikethrough">"#))),
430                            Arc::new(SingleCaptureGroupReplacementRuleReplacerPart::new(1, ESCAPE_HTML.clone(), StandardTextModifier::Strikethrough.incompatible_modifiers())),
431                            Arc::new(FixedReplacementRuleReplacerPart::new(String::from(r#"</del>"#))),
432                        ]
433                    ))
434                )
435            ),
436            (
437                StandardTextModifier::Underlined.identifier().clone(),
438                (
439                    Box::new(Into::<BaseModifier>::into(StandardTextModifier::Underlined)),
440                    Box::new(ReplacementRule::new(
441                        StandardTextModifier::Underlined.modifier_pattern().clone(),
442                        vec![
443                            Arc::new(FixedReplacementRuleReplacerPart::new(String::from(r#"<u class="underlined">"#))),
444                            Arc::new(SingleCaptureGroupReplacementRuleReplacerPart::new(1, ESCAPE_HTML.clone(), StandardTextModifier::Underlined.incompatible_modifiers())),
445                            Arc::new(FixedReplacementRuleReplacerPart::new(String::from(r#"</u>"#))),
446                        ]
447                    ))
448                )
449            ),
450            (
451                StandardTextModifier::Superscript.identifier().clone(),
452                (
453                    Box::new(Into::<BaseModifier>::into(StandardTextModifier::Superscript)),
454                    Box::new(ReplacementRule::new(
455                        StandardTextModifier::Superscript.modifier_pattern().clone(),
456                        vec![
457                            Arc::new(FixedReplacementRuleReplacerPart::new(String::from(r#"<sup class="superscript">"#))),
458                            Arc::new(SingleCaptureGroupReplacementRuleReplacerPart::new(1, ESCAPE_HTML.clone(), StandardTextModifier::Superscript.incompatible_modifiers())),
459                            Arc::new(FixedReplacementRuleReplacerPart::new(String::from(r#"</sup>"#))),
460                        ]
461                    ))
462                )
463            ),
464            (
465                StandardTextModifier::Subscript.identifier().clone(),
466                (
467                    Box::new(Into::<BaseModifier>::into(StandardTextModifier::Subscript)),
468                    Box::new(ReplacementRule::new(
469                        StandardTextModifier::Subscript.modifier_pattern().clone(),
470                        vec![
471                            Arc::new(FixedReplacementRuleReplacerPart::new(String::from(r#"<sub class="subscript">"#))),
472                            Arc::new(SingleCaptureGroupReplacementRuleReplacerPart::new(1, ESCAPE_HTML.clone(), StandardTextModifier::Subscript.incompatible_modifiers())),
473                            Arc::new(FixedReplacementRuleReplacerPart::new(String::from(r#"</sub>"#))),
474                        ]
475                    ))
476                )
477            ),
478            (
479                StandardTextModifier::Link.identifier().clone(),
480                (
481                    Box::new(Into::<BaseModifier>::into(StandardTextModifier::Link)),
482                    Box::new(ReplacementRule::new(
483                        StandardTextModifier::Link.modifier_pattern().clone(),
484                        vec![
485                            Arc::new(ClosureReplacementRuleReplacerPart::new(Arc::new(|captures, _, _, _, cco| {
486        
487                                Ok(CompilableText::from(vec![
488                                    CompilableTextPart::new_fixed(format!(
489                                        r#"<a href="{}" class="link">"#,
490                                        ResourceReference::of(captures.get(2).unwrap().as_str(), cco.document_name().as_ref())?.build(),
491                                    ))
492                                ]))
493                            }))),
494                            Arc::new(SingleCaptureGroupReplacementRuleReplacerPart::new(1, ESCAPE_HTML.clone(), StandardTextModifier::Link.incompatible_modifiers())),
495                            Arc::new(FixedReplacementRuleReplacerPart::new(String::from(r#"</a>"#)))
496                        ]
497                    ))
498                )
499            ),
500            (
501                StandardTextModifier::Checkbox.identifier().clone(),
502                (
503                    Box::new(Into::<BaseModifier>::into(StandardTextModifier::Checkbox)),
504                    Box::new(ReplacementRule::new(
505                        StandardTextModifier::Checkbox.modifier_pattern().clone(),
506                        vec![
507                            Arc::new(FixedReplacementRuleReplacerPart::new(String::from(r#"<div class="checkbox checkbox-unchecked"></div>"#))),
508                        ]
509                    ))
510                )
511            ),
512            (
513                StandardTextModifier::CheckboxChecked.identifier().clone(),
514                (
515                    Box::new(Into::<BaseModifier>::into(StandardTextModifier::CheckboxChecked)),
516                    Box::new(ReplacementRule::new(
517                        StandardTextModifier::CheckboxChecked.modifier_pattern().clone(),
518                        vec![
519                            Arc::new(FixedReplacementRuleReplacerPart::new(String::from(r#"<div class="checkbox checkbox-checked"></div>"#))),
520                        ]
521                    ))
522                )
523            ),
524            (
525                StandardTextModifier::Emoji.identifier().clone(),
526                (
527                    Box::new(Into::<BaseModifier>::into(StandardTextModifier::Emoji)),
528                    Box::new(ReplacementRule::new(
529                        StandardTextModifier::Emoji.modifier_pattern().clone(),
530                        vec![
531                            Arc::new(ClosureReplacementRuleReplacerPart::new(Arc::new(|captures, _, _, _, _| {
532        
533                                Ok(CompilableText::from(vec![
534                                    CompilableTextPart::new_fixed(format!(
535                                        r#"<i class="em-svg em-{}" aria-role="presentation"></i>"#,
536                                        captures.get(1).unwrap().as_str(),
537                                    ))
538                                ]))
539                            }))),
540                        ]
541                    ))
542                )
543            ),
544            (
545                StandardTextModifier::Escape.identifier().clone(),
546                (
547                    Box::new(Into::<BaseModifier>::into(StandardTextModifier::Escape)) as Box<dyn Modifier>,
548                    Box::new(ReplacementRule::new(
549                        StandardTextModifier::Escape.modifier_pattern().clone(),
550                        vec![
551                            Arc::new(SingleCaptureGroupReplacementRuleReplacerPart::new(1, ESCAPE_HTML.clone(), StandardTextModifier::Escape.incompatible_modifiers())),
552                        ]
553                    )) as Box<dyn CompilationRule>
554                ) as (Box<dyn Modifier>, Box<dyn CompilationRule>)
555            ),
556            (
557                StandardTextModifier::Reference.identifier().clone(),
558                (
559                    Box::new(Into::<BaseModifier>::into(StandardTextModifier::Reference)),
560                    Box::new(ReferenceRule::new())
561                )
562            ),
563            (
564                StandardTextModifier::Cite.identifier().clone(),
565                (
566                    Box::new(Into::<BaseModifier>::into(StandardTextModifier::Cite)),
567                    Box::new(HtmlCiteRule::new())
568                )
569            ),
570        ]);
571
572        let paragraph_rules: ParagraphModifierOrderedMap = ParagraphModifierOrderedMap::from([
573            (
574                StandardParagraphModifier::CodeBlock.identifier().clone(),
575                (
576                    Box::new(Into::<BaseModifier>::into(StandardParagraphModifier::CodeBlock)) as Box<dyn Modifier>,
577                    Box::new(ReplacementRuleParagraphLoadingRule::new(
578                        ReplacementRule::new(
579                            StandardParagraphModifier::CodeBlock.modifier_pattern().clone(),
580                            vec![
581                                Arc::new(ClosureReplacementRuleReplacerPart::new(Arc::new(|captures, compilable, _, _, _| {
582    
583                                    let mut lang_class = String::from("language-markup");
584                                    if let Some(lang) = captures.get(1) {
585                                        lang_class = format!("language-{}", lang.as_str());
586                                    }
587    
588                                    Ok(CompilableText::from(vec![
589                                        CompilableTextPart::new_fixed(format!(
590                                            r#"<pre{}><code class="{} code-block">{}</code></pre>"#,
591                                            text_utility::html_nuid_tag_or_nothing(compilable.nuid().as_ref()),
592                                            lang_class,
593                                            text_utility::replace(captures.get(2).unwrap().as_str(), &ESCAPE_HTML),
594                                        )
595                                    )
596                                    ]))
597                                }))),
598                            ]
599                        )
600                    )) as Box<dyn ParagraphLoadingRule>
601                ) as (Box<dyn Modifier>, Box<dyn ParagraphLoadingRule>)
602            ),
603            (
604                StandardParagraphModifier::MathBlock.identifier().clone(),
605                (
606                    Box::new(Into::<BaseModifier>::into(StandardParagraphModifier::MathBlock)) as Box<dyn Modifier>,
607                    Box::new(ReplacementRuleParagraphLoadingRule::new(
608                        ReplacementRule::new(
609                            StandardParagraphModifier::MathBlock.modifier_pattern().clone(),
610                            vec![
611                                Arc::new(ClosureReplacementRuleReplacerPart::new(Arc::new(|captures, compilable, _, _, _| {
612    
613                                    Ok(CompilableText::from(vec![
614                                        CompilableTextPart::new_fixed(format!(
615                                            r#"<p class="math-block"{}>$${}$$</p>"#,
616                                            text_utility::html_nuid_tag_or_nothing(compilable.nuid().as_ref()),
617                                            text_utility::replace(captures.get(1).unwrap().as_str(), &ESCAPE_HTML),
618                                        )
619                                    )
620                                    ]))
621                                }))),
622                            ]
623                        )
624                    ))
625                )
626            ),
627            (
628                StandardParagraphModifier::EmbeddedParagraphStyle.identifier().clone(),
629                (
630                    Box::new(Into::<BaseModifier>::into(StandardParagraphModifier::EmbeddedParagraphStyle)) as Box<dyn Modifier>,
631                    Box::new(MetadataWrapperParagraphLoadingRule::new(
632                        StandardParagraphModifier::EmbeddedParagraphStyle.modifier_pattern_regex().clone(),
633                        1,
634                        Some(2),
635                        Some(3),
636                        Some(Arc::new(|style, there_is_id| {
637    
638                            if there_is_id {
639    
640                                text_utility::split_styles_and_classes_with_default(style, (None, Some(String::from("identifier embedded-paragraph-style"))))
641                            
642                            } else {
643    
644                                text_utility::split_styles_and_classes_with_default(style, (None, Some(String::from("embedded-paragraph-style"))))
645                            }
646    
647                        })),
648                    ))
649                )
650            ),
651            (
652                StandardParagraphModifier::ParagraphIdentifier.identifier().clone(),
653                (
654                    Box::new(Into::<BaseModifier>::into(StandardParagraphModifier::ParagraphIdentifier)) as Box<dyn Modifier>,
655                    Box::new(MetadataWrapperParagraphLoadingRule::new(
656                        StandardParagraphModifier::ParagraphIdentifier.modifier_pattern_regex().clone(),
657                        1,
658                        Some(2),
659                        None,
660                        None,
661                    ))
662                )
663            ),
664            (
665                    StandardParagraphModifier::Table.identifier(),
666                    (
667                        Box::new(Into::<BaseModifier>::into(StandardParagraphModifier::Table)) as Box<dyn Modifier>,
668                        Box::new(TableParagraphLoadingRule::new()) as Box<dyn ParagraphLoadingRule>
669                    ) as (Box<dyn Modifier>, Box<dyn ParagraphLoadingRule>)
670            ),
671            (
672                StandardParagraphModifier::ExtendedBlockQuote.identifier().clone(),
673                (
674                    Box::new(Into::<BaseModifier>::into(StandardParagraphModifier::ExtendedBlockQuote)) as Box<dyn Modifier>,
675                    Box::new(BlockQuoteParagraphLoadingRule::new()),
676                )
677            ),
678            (
679                StandardParagraphModifier::FocusBlock.identifier().clone(),
680                (
681                    Box::new(Into::<BaseModifier>::into(StandardParagraphModifier::FocusBlock)) as Box<dyn Modifier>,
682                    Box::new(FocusBlockParagraphLoadingRule::new(StandardParagraphModifier::FocusBlock.modifier_pattern_regex().clone())),
683                )
684            ),
685            (
686                StandardParagraphModifier::List.identifier().clone(),
687                (
688                    Box::new(Into::<BaseModifier>::into(StandardParagraphModifier::List)) as Box<dyn Modifier>,
689                    Box::new(ListParagraphLoadingRule::new()),
690                )
691            ),
692            (
693                StandardParagraphModifier::MultilineTodo.identifier().clone(),
694                (
695                    Box::new(Into::<BaseModifier>::into(StandardParagraphModifier::MultilineTodo)) as Box<dyn Modifier>,
696                    Box::new(ReplacementRuleParagraphLoadingRule::new(
697                        ReplacementRule::new(
698                            StandardParagraphModifier::MultilineTodo.modifier_pattern().clone(),
699                            vec![
700                                Arc::new(ClosureReplacementRuleReplacerPart::new(Arc::new(|_, compilable, _, _, _| {
701    
702                                    Ok(CompilableText::from(CompilableTextPart::new_fixed(format!(
703                                            r#"<div class="todo multiline-todo"{}><div class="todo-title"></div><div class="todo-description">"#,
704                                            text_utility::html_nuid_tag_or_nothing(compilable.nuid().as_ref()),
705                                        ))
706                                    ))
707                                }))),
708                                Arc::new(SingleCaptureGroupReplacementRuleReplacerPart::new(1, ESCAPE_HTML.clone(), StandardParagraphModifier::MultilineTodo.incompatible_modifiers())),
709                                Arc::new(FixedReplacementRuleReplacerPart::new(String::from(r#"</div>"#)))
710                            ]
711                        )
712                    ))
713                )
714            ),
715            (
716                StandardParagraphModifier::Todo.identifier().clone(),
717                (
718                    Box::new(Into::<BaseModifier>::into(StandardParagraphModifier::Todo)) as Box<dyn Modifier>,
719                    Box::new(ReplacementRuleParagraphLoadingRule::new(
720                        ReplacementRule::new(
721                            StandardParagraphModifier::Todo.modifier_pattern().clone(),
722                            vec![
723                                Arc::new(ClosureReplacementRuleReplacerPart::new(Arc::new(|_, compilable, _, _, _| {
724    
725                                    Ok(CompilableText::from(CompilableTextPart::new_fixed(format!(
726                                            r#"<div class="todo"{}><div class="todo-title"></div>"#,
727                                            text_utility::html_nuid_tag_or_nothing(compilable.nuid().as_ref()),
728                                        ))
729                                    ))
730                                }))),
731                                Arc::new(SingleCaptureGroupReplacementRuleReplacerPart::new(1, ESCAPE_HTML.clone(), StandardParagraphModifier::Todo.incompatible_modifiers())),
732                                Arc::new(FixedReplacementRuleReplacerPart::new(String::from(r#"</div>"#)))
733                            ]
734                        )
735                    ))
736                )
737            ),
738            (
739                StandardParagraphModifier::AbridgedTodo.identifier().clone(),
740                (
741                    Box::new(Into::<BaseModifier>::into(StandardParagraphModifier::AbridgedTodo)) as Box<dyn Modifier>,
742                    Box::new(ReplacementRuleParagraphLoadingRule::new(
743                        ReplacementRule::new(
744                            StandardParagraphModifier::AbridgedTodo.modifier_pattern().clone(),
745                            vec![
746                                Arc::new(ClosureReplacementRuleReplacerPart::new(Arc::new(|_, compilable, _, _, _| {
747    
748                                    Ok(CompilableText::from(CompilableTextPart::new_fixed(format!(
749                                            r#"<div class="todo abridged-todo"{}><div class="todo-title"></div></div>"#,
750                                            text_utility::html_nuid_tag_or_nothing(compilable.nuid().as_ref()),
751                                        ))
752                                    ))
753                                }))),
754                            ]
755                        )
756                    ))
757                )
758            ),
759            (
760                StandardParagraphModifier::MultiImage.identifier().clone(),
761                (
762                    Box::new(Into::<BaseModifier>::into(StandardParagraphModifier::MultiImage)) as Box<dyn Modifier>,
763                    Box::new(ImageParagraphLoadingRule::MultiImage)
764                )
765            ),
766            (
767                StandardParagraphModifier::Image.identifier().clone(),
768                (
769                    Box::new(Into::<BaseModifier>::into(StandardParagraphModifier::Image)) as Box<dyn Modifier>,
770                    Box::new(ImageParagraphLoadingRule::SingleImage)
771                )
772            ),
773            (
774                StandardParagraphModifier::AbridgedImage.identifier().clone(),
775                (
776                    Box::new(Into::<BaseModifier>::into(StandardParagraphModifier::AbridgedImage)) as Box<dyn Modifier>,
777                    Box::new(ImageParagraphLoadingRule::AbridgedImage)
778                )
779            ),
780            (
781                    StandardParagraphModifier::PageBreak.identifier().clone(),
782                    (
783                        Box::new(Into::<BaseModifier>::into(StandardParagraphModifier::PageBreak)) as Box<dyn Modifier>,
784                        Box::new(ReplacementRuleParagraphLoadingRule::new(
785                            ReplacementRule::new(
786                                StandardParagraphModifier::PageBreak.modifier_pattern().clone(),
787                                vec![
788                                    Arc::new(FixedReplacementRuleReplacerPart::new(String::from(r#"<div class="page-break"></div>"#)))
789                                ]
790                            )
791                        ))
792                    )
793            ),
794            (
795                StandardParagraphModifier::LineBreakDash.identifier().clone(),
796                (
797                    Box::new(Into::<BaseModifier>::into(StandardParagraphModifier::LineBreakDash)) as Box<dyn Modifier>,
798                    Box::new(ReplacementRuleParagraphLoadingRule::new(
799                        ReplacementRule::new(
800                            StandardParagraphModifier::LineBreakDash.modifier_pattern().clone(),
801                            vec![
802                                Arc::new(ClosureReplacementRuleReplacerPart::new(Arc::new(|_, compilable, _, _, _| {
803    
804                                    Ok(CompilableText::from(vec![
805                                        CompilableTextPart::new_fixed(format!(
806                                            r#"<hr class="line-break line-break-dash"{}>"#,
807                                            text_utility::html_nuid_tag_or_nothing(compilable.nuid().as_ref()),
808                                        )
809                                    )
810                                    ]))
811                                }))),
812                            ]
813                        )
814                    ))
815                )
816            ),
817            (
818                StandardParagraphModifier::LineBreakStar.identifier().clone(),
819                (
820                    Box::new(Into::<BaseModifier>::into(StandardParagraphModifier::LineBreakStar)) as Box<dyn Modifier>,
821                    Box::new(ReplacementRuleParagraphLoadingRule::new(
822                        ReplacementRule::new(
823                            StandardParagraphModifier::LineBreakDash.modifier_pattern().clone(),
824                            vec![
825                                Arc::new(ClosureReplacementRuleReplacerPart::new(Arc::new(|_, compilable, _, _, _| {
826    
827                                    Ok(CompilableText::from(vec![
828                                        CompilableTextPart::new_fixed(format!(
829                                            r#"<hr class="line-break line-break-star"{}>"#,
830                                            text_utility::html_nuid_tag_or_nothing(compilable.nuid().as_ref()),
831                                        )
832                                    )
833                                    ]))
834                                }))),
835                            ]
836                        )
837                    ))
838                )
839            ),
840            (
841                StandardParagraphModifier::LineBreakPlus.identifier().clone(),
842                (
843                    Box::new(Into::<BaseModifier>::into(StandardParagraphModifier::LineBreakPlus)) as Box<dyn Modifier>,
844                    Box::new(ReplacementRuleParagraphLoadingRule::new(
845                        ReplacementRule::new(
846                            StandardParagraphModifier::LineBreakDash.modifier_pattern().clone(),
847                            vec![
848                                Arc::new(ClosureReplacementRuleReplacerPart::new(Arc::new(|_, compilable, _, _, _| {
849    
850                                    Ok(CompilableText::from(vec![
851                                        CompilableTextPart::new_fixed(format!(
852                                            r#"<hr class="line-break line-break-plus"{}>"#,
853                                            text_utility::html_nuid_tag_or_nothing(compilable.nuid().as_ref()),
854                                        )
855                                    )
856                                    ]))
857                                }))),
858                            ]
859                        )
860                    ))
861                )
862            ),
863            (
864                StandardParagraphModifier::CommentBlock.identifier().clone(),
865                (
866                    Box::new(Into::<BaseModifier>::into(StandardParagraphModifier::CommentBlock)) as Box<dyn Modifier>,
867                    Box::new(ReplacementRuleParagraphLoadingRule::new(
868                        ReplacementRule::new(
869                            StandardParagraphModifier::CommentBlock.modifier_pattern().clone(),
870                            vec![
871                                Arc::new(FixedReplacementRuleReplacerPart::new(String::from(r#"<!--"#))),
872                                Arc::new(SingleCaptureGroupReplacementRuleReplacerPart::new(1, Vec::new(), StandardParagraphModifier::CommentBlock.incompatible_modifiers())),
873                                Arc::new(FixedReplacementRuleReplacerPart::new(String::from(r#"-->"#))),
874                            ]
875                        )
876                    ))
877                )
878            ),
879        ]);
880
881        Self::new(
882            text_rules,
883            paragraph_rules,
884            Some(
885                (
886                    StandardParagraphModifier::CommonParagraph.identifier().clone(),
887                    Box::new(CommonParagraphLoadingRule::new())
888                ),
889            ),
890            Box::new(HtmlAssembler::new())
891        )
892    }
893}
894
895#[cfg(test)]
896mod test {
897
898    use indexmap::IndexMap;
899    use modifier::base_modifier::BaseModifier;
900    use super::*;
901
902
903    #[test]
904    fn correct_order() {
905
906        let codex = Codex::new(
907            IndexMap::new(),
908            IndexMap::from([
909                (
910                    String::from("a"),
911                    (
912                        Box::new(Into::<BaseModifier>::into(StandardTextModifier::BoldStarVersion)) as Box<dyn Modifier>,
913                        Box::new(ReplacementRuleParagraphLoadingRule::new(
914                            ReplacementRule::new(
915                                String::from("fake"),
916                                vec![
917                                ]
918                            )
919                        )) as Box<dyn ParagraphLoadingRule>
920                    ) as (Box<dyn Modifier>, Box<dyn ParagraphLoadingRule>)
921                ),
922                (
923                    String::from("b"),
924                    (
925                        Box::new(Into::<BaseModifier>::into(StandardTextModifier::BoldStarVersion)) as Box<dyn Modifier>,
926                        Box::new(ReplacementRuleParagraphLoadingRule::new(
927                            ReplacementRule::new(
928                                String::from("fake"),
929                                vec![
930                                ]
931                            )
932                        )) as Box<dyn ParagraphLoadingRule>
933                    ) as (Box<dyn Modifier>, Box<dyn ParagraphLoadingRule>)
934                ),
935                (
936                    String::from("e"),
937                    (
938                        Box::new(Into::<BaseModifier>::into(StandardTextModifier::BoldStarVersion)) as Box<dyn Modifier>,
939                        Box::new(ReplacementRuleParagraphLoadingRule::new(
940                            ReplacementRule::new(
941                                String::from("fake"),
942                                vec![
943                                ]
944                            )
945                        )) as Box<dyn ParagraphLoadingRule>
946                    ) as (Box<dyn Modifier>, Box<dyn ParagraphLoadingRule>)
947                ),
948                (
949                    String::from("c"),
950                    (
951                        Box::new(Into::<BaseModifier>::into(StandardTextModifier::BoldStarVersion)) as Box<dyn Modifier>,
952                        Box::new(ReplacementRuleParagraphLoadingRule::new(
953                            ReplacementRule::new(
954                                String::from("fake"),
955                                vec![
956                                ]
957                            )
958                        )) as Box<dyn ParagraphLoadingRule>
959                    ) as (Box<dyn Modifier>, Box<dyn ParagraphLoadingRule>)
960                ),
961                (
962                    String::from("i"),
963                    (
964                        Box::new(Into::<BaseModifier>::into(StandardTextModifier::BoldStarVersion)) as Box<dyn Modifier>,
965                        Box::new(ReplacementRuleParagraphLoadingRule::new(
966                            ReplacementRule::new(
967                                String::from("fake"),
968                                vec![
969                                ]
970                            )
971                        )) as Box<dyn ParagraphLoadingRule>
972                    ) as (Box<dyn Modifier>, Box<dyn ParagraphLoadingRule>)
973                ),
974            ]),
975            None,
976            Box::new(HtmlAssembler::new())
977        );
978
979
980        let ids: Vec<String> = codex.paragraph_modifiers.into_iter().map(|tm| tm.0).collect();
981
982        assert_eq!(ids.join(""), "abeci");
983    }
984}