nmd_core/compilation/compilation_rule/
replacement_rule.rs1pub mod replacement_rule_part;
2
3
4use std::fmt::Debug;
5use std::sync::Arc;
6use getset::{Getters, Setters};
7use log;
8use regex::Regex;
9use replacement_rule_part::ReplacementRuleReplacerPart;
10use crate::compilable_text::CompilableText;
11use crate::compilation::compilation_configuration::compilation_configuration_overlay::CompilationConfigurationOverLay;
12use crate::compilation::compilation_configuration::CompilationConfiguration;
13use crate::output_format::OutputFormat;
14use super::CompilationRule;
15use crate::compilation::compilation_error::CompilationError;
16
17
18pub type ReplacementRuleParts = Vec<Arc<dyn ReplacementRuleReplacerPart>>;
19
20
21#[derive(Debug, Clone, Getters, Setters)]
23pub struct ReplacementRule {
24
25 #[getset(set)]
26 search_pattern: String,
27
28 #[getset(set)]
29 search_pattern_regex: Regex,
30
31 #[getset(get = "pub", set = "pub")]
32 replacer_parts: ReplacementRuleParts,
33}
34
35impl ReplacementRule {
36
37 pub fn new(searching_pattern: String, replacers: ReplacementRuleParts) -> Self {
39
40 log::debug!("created new compilation rule with search_pattern: '{}'", searching_pattern);
41
42 Self {
43 search_pattern_regex: Regex::new(&searching_pattern).unwrap(),
44 search_pattern: searching_pattern,
45 replacer_parts: replacers,
46 }
47 }
48
49}
50
51impl CompilationRule for ReplacementRule {
52
53 fn standard_compile(&self, compilable: &CompilableText, format: &OutputFormat, compilation_configuration: &CompilationConfiguration, compilation_configuration_overlay: CompilationConfigurationOverLay) -> Result<CompilableText, CompilationError> {
55
56 log::debug!("compile:\n{:#?}\nusing '{}'->'{:?}'", compilable, self.search_pattern(), self.replacer_parts);
57
58 let mut compiled_parts = Vec::new();
59
60 let compilable_content = compilable.compilable_content();
61
62 let captures_matches = self.search_pattern_regex.captures_iter(&compilable_content);
63
64 for captures in captures_matches {
65
66 for replacer_part in &self.replacer_parts {
67
68 compiled_parts.append(&mut replacer_part.compile(&captures, compilable, format, compilation_configuration, compilation_configuration_overlay.clone())?.into())
69 }
70 }
71
72 Ok(CompilableText::new(compiled_parts))
73 }
74
75 fn search_pattern(&self) -> &String {
76 &self.search_pattern
77 }
78
79 fn search_pattern_regex(&self) -> &Regex {
80 &self.search_pattern_regex
81 }
82}
83
84
85
86#[cfg(test)]
87mod test {
88
89 use std::sync::Arc;
90
91 use crate::{codex::modifier::{standard_text_modifier::StandardTextModifier, ModifiersBucket}, compilable_text::{compilable_text_part::{CompilableTextPart, CompilableTextPartType}, CompilableText}, compilation::{compilation_configuration::{compilation_configuration_overlay::CompilationConfigurationOverLay, CompilationConfiguration}, compilation_rule::{constants::ESCAPE_HTML, replacement_rule::{replacement_rule_part::{closure_replacement_rule_part::ClosureReplacementRuleReplacerPart, fixed_replacement_rule_part::FixedReplacementRuleReplacerPart, single_capture_group_replacement_rule_part::SingleCaptureGroupReplacementRuleReplacerPart, ReplacementRuleReplacerPart}, ReplacementRule}, CompilationRule}}, output_format::OutputFormat};
92
93
94 #[test]
95 fn bold_compiling() {
96
97 let replacement_rule = ReplacementRule::new(StandardTextModifier::BoldStarVersion.modifier_pattern(), vec![
99 Arc::new(FixedReplacementRuleReplacerPart::new(String::from("<strong>"))) as Arc<dyn ReplacementRuleReplacerPart>,
100 Arc::new(ClosureReplacementRuleReplacerPart::new(Arc::new(|captures, compilable, _, _, _| {
101
102 let capture1 = captures.get(1).unwrap();
103
104 let slice = compilable.parts_slice(capture1.start(), capture1.end())?;
105
106 Ok(CompilableText::new(slice))
107 }))),
108 Arc::new(FixedReplacementRuleReplacerPart::new(String::from("</strong>"))),
109 ]);
110
111 let text_to_compile = r"A piece of **bold text** and **bold text2**";
112 let compilation_configuration = CompilationConfiguration::default();
113
114 let compilable = CompilableText::new(
115 vec![
116 CompilableTextPart::new(
117 text_to_compile.to_string(),
118 CompilableTextPartType::Compilable { incompatible_modifiers: ModifiersBucket::None }
119 )
120 ]);
121
122 let outcome = replacement_rule.compile(&compilable, &OutputFormat::Html, &compilation_configuration, CompilationConfigurationOverLay::default()).unwrap();
123
124 assert_eq!(outcome.content(), r"<strong>bold text</strong><strong>bold text2</strong>");
125
126 let text_to_compile = r"A piece of text without bold text";
128
129 let compilable = CompilableText::new(
130 vec![
131 CompilableTextPart::new(
132 text_to_compile.to_string(),
133 CompilableTextPartType::Compilable { incompatible_modifiers: ModifiersBucket::None }
134 )
135 ]);
136
137 let outcome = replacement_rule.compile(&compilable, &OutputFormat::Html, &compilation_configuration, CompilationConfigurationOverLay::default()).unwrap();
138
139 assert_eq!(outcome.content(), r"");
140
141
142 }
143
144 #[test]
145 fn input_with_fixed_parts() {
146 let replacement_rule = ReplacementRule::new(StandardTextModifier::ItalicStarVersion.modifier_pattern(), vec![
147 Arc::new(FixedReplacementRuleReplacerPart::new(String::from("<em>"))) as Arc<dyn ReplacementRuleReplacerPart>,
148 Arc::new(SingleCaptureGroupReplacementRuleReplacerPart::new(1, ESCAPE_HTML.clone(), ModifiersBucket::None)),
149 Arc::new(FixedReplacementRuleReplacerPart::new(String::from("</em>"))),
150 ]);
151
152 let compilation_configuration = CompilationConfiguration::default();
153
154 let compilable = CompilableText::new(
156 vec![
157 CompilableTextPart::new(
158 String::from("*start "),
159 CompilableTextPartType::Compilable { incompatible_modifiers: ModifiersBucket::None }
160 ),
161 CompilableTextPart::new_fixed(String::from("<strong>")),
162 CompilableTextPart::new_compilable(String::from("fixed"), ModifiersBucket::None),
163 CompilableTextPart::new_fixed(String::from("</strong>")),
164 CompilableTextPart::new(
165 String::from(" end*"),
166 CompilableTextPartType::Compilable { incompatible_modifiers: ModifiersBucket::None }
167 ),
168 ]);
169
170 let outcome = replacement_rule.compile(&compilable, &OutputFormat::Html, &compilation_configuration, CompilationConfigurationOverLay::default()).unwrap();
171
172 assert_eq!(outcome.content(), r"<em>start <strong>fixed</strong> end</em>");
173
174
175 let compilable = CompilableText::new(
177 vec![
178 CompilableTextPart::new(
179 String::from("*start "),
180 CompilableTextPartType::Compilable { incompatible_modifiers: ModifiersBucket::None }
181 ),
182 CompilableTextPart::new_fixed(String::from("<strong>")),
183 CompilableTextPart::new_compilable(String::from("fixed"), ModifiersBucket::None),
184 CompilableTextPart::new_fixed(String::from("</strong>")),
185 CompilableTextPart::new(
186 String::from("*"),
187 CompilableTextPartType::Compilable { incompatible_modifiers: ModifiersBucket::None }
188 ),
189 ]);
190
191 let outcome = replacement_rule.compile(&compilable, &OutputFormat::Html, &compilation_configuration, CompilationConfigurationOverLay::default()).unwrap();
192
193 assert_eq!(outcome.content(), r"<em>start <strong>fixed</strong></em>");
194
195
196 let compilable = CompilableText::new(
198 vec![
199 CompilableTextPart::new(
200 String::from("*"),
201 CompilableTextPartType::Compilable { incompatible_modifiers: ModifiersBucket::None }
202 ),
203 CompilableTextPart::new_fixed(String::from("<strong>")),
204 CompilableTextPart::new_compilable(String::from("fixed"), ModifiersBucket::None),
205 CompilableTextPart::new_fixed(String::from("</strong>")),
206 CompilableTextPart::new(
207 String::from(" end*"),
208 CompilableTextPartType::Compilable { incompatible_modifiers: ModifiersBucket::None }
209 ),
210 ]);
211
212 let outcome = replacement_rule.compile(&compilable, &OutputFormat::Html, &compilation_configuration, CompilationConfigurationOverLay::default()).unwrap();
213
214 assert_eq!(outcome.content(), r"<em><strong>fixed</strong> end</em>");
215
216 }
217
218 }