nmd_core/
compilable_text.rs

1pub mod compilable_text_part;
2
3
4use compilable_text_part::{CompilableTextPart, CompilableTextPartType};
5use getset::{Getters, MutGetters, Setters};
6use serde::Serialize;
7use thiserror::Error;
8use crate::{codex::{modifier::{ModifierIdentifier, ModifiersBucket}, Codex}, compilation::{compilable::Compilable, compilation_configuration::{compilation_configuration_overlay::CompilationConfigurationOverLay, CompilationConfiguration}, compilation_error::CompilationError, compilation_outcome::CompilationOutcome, compilation_rule::CompilationRule}, output_format::OutputFormat, resource::bucket::Bucket, utility::nmd_unique_identifier::NmdUniqueIdentifier};
9
10
11#[derive(Debug, Clone)]
12pub enum PartsSliceElaborationPolicy {
13    DontTakeBorderFixedParts,
14    TakeLeftFixedParts,
15    TakeRightFixedParts,
16    TakeLeftAndRightFixedParts,
17}
18
19#[derive(Debug, Clone)]
20enum ElaborationPosition {
21    BeforeRange,
22    InRange,
23    AfterRange,
24}
25
26
27#[derive(Error, Debug)]
28pub enum CompilableError {
29    #[error("compilable content {0} has an overflow using {1} -> {2}")]
30    ContentOverflow(String, usize, usize),
31}
32
33
34#[derive(Debug, Clone, Getters, MutGetters, Setters, Serialize)]
35pub struct CompilableText {
36
37    #[getset(get = "pub", get_mut = "pub", set = "pub")]
38    parts: Vec<CompilableTextPart>,
39
40    #[getset(get = "pub", get_mut = "pub", set = "pub")]
41    nuid: Option<NmdUniqueIdentifier>,
42}
43
44
45
46impl From<CompilableTextPart> for CompilableText {
47    fn from(value: CompilableTextPart) -> Self {
48        Self::new(vec![value])
49    }
50}
51
52impl From<Vec<CompilableTextPart>> for CompilableText {
53    fn from(value: Vec<CompilableTextPart>) -> Self {
54        Self::new(value)
55    }
56}
57
58impl Into<Vec<CompilableTextPart>> for CompilableText {
59    fn into(self) -> Vec<CompilableTextPart> {
60        self.parts  
61    }
62}
63
64impl Into<String> for CompilableText {
65    fn into(self) -> String {
66        self.content()
67    }
68}
69
70impl From<String> for CompilableText {
71    fn from(value: String) -> Self {
72        Self::from(CompilableTextPart::new_compilable(
73            value,
74            ModifiersBucket::None
75        ))
76    }
77}
78
79impl From<&str> for CompilableText {
80    fn from(value: &str) -> Self {
81        Self::from(CompilableTextPart::new_compilable(
82            value.to_string(),
83            ModifiersBucket::None
84        ))
85    }
86}
87
88impl CompilableText {
89
90    pub fn new_empty() -> Self {
91        Self {
92            parts: Vec::new(),
93            nuid: None,
94        }
95    }
96
97    pub fn new(parts: Vec<CompilableTextPart>) -> Self {
98
99        Self {
100            parts,
101            nuid: None,
102        }
103    }
104
105    pub fn new_with_nuid(parts: Vec<CompilableTextPart>, nuid: Option<NmdUniqueIdentifier>) -> Self {
106        Self {
107            parts,
108            nuid
109        }
110    }
111
112    /// content usable in regex. It's the string obtained concatenating compilable parts
113    pub fn compilable_content(&self) -> String {
114
115        self.compilable_content_with_ends_positions().0
116    }
117
118    pub fn compilable_content_with_ends_positions(&self) -> (String, Vec<usize>) {
119        let mut compilable_content = String::new();
120        let mut ends: Vec<usize> = Vec::new();
121        let mut last_end: usize = 0;
122
123        self.parts.iter().for_each(|part| {
124            match part.part_type() {
125                CompilableTextPartType::Fixed => (),
126                CompilableTextPartType::Compilable { incompatible_modifiers: _ } => {
127
128                    let content = part.content();
129
130                    ends.push(last_end + content.len());
131                    last_end = *ends.last().unwrap();
132
133                    compilable_content.push_str(&content);
134                },
135            }
136        });
137
138        (compilable_content, ends)
139    }
140
141    /// string generated using all parts contents 
142    pub fn content(&self) -> String {
143
144        let mut content = String::new();
145
146        self.parts.iter().for_each(|part| content.push_str(part.content()));
147
148        content
149    }
150
151    /// this method calls `parts_slice_with_explicit_policy` using policy `TakeLeftAndRightFixedParts`
152    pub fn parts_slice(&self, start: usize, end: usize) -> Result<Vec<CompilableTextPart>, CompilableError> {
153        self.parts_slice_with_explicit_policy(start, end, PartsSliceElaborationPolicy::TakeLeftAndRightFixedParts)
154    }
155
156    /// parts between two positions in `compilable_content`.
157    /// If start or end are in the middle of a compilable part, it will be split.
158    /// 
159    /// **`start` is included, but `end` is excluded** as typically behavior of `end()` methods.
160    pub fn parts_slice_with_explicit_policy(&self, start: usize, end: usize, elaboration_policy: PartsSliceElaborationPolicy) -> Result<Vec<CompilableTextPart>, CompilableError> {
161
162        let (compilable_content, ends) = self.compilable_content_with_ends_positions();
163
164        if end > compilable_content.len() {
165            return Err(CompilableError::ContentOverflow(compilable_content, start, end))
166        }
167
168        let mut parts_slice: Vec<CompilableTextPart> = Vec::new();
169
170        let mut start_part_position_in_compilable_content: usize = 0; 
171        let mut end_part_position_in_compilable_content: usize;
172
173        let mut elaboration_position = ElaborationPosition::BeforeRange;
174
175        let mut left_fixed_parts: Vec<&CompilableTextPart> = Vec::new();
176        let mut right_fixed_parts: Vec<&CompilableTextPart> = Vec::new();
177
178        let mut index: usize = 0;
179        let mut compilable_parts_index: usize = 0;
180
181        while index < self.parts.len() {
182
183            let part = &self.parts[index];
184
185            index += 1;
186
187            match part.part_type() {
188                CompilableTextPartType::Fixed => {
189
190                    match elaboration_position {
191                        ElaborationPosition::BeforeRange => left_fixed_parts.push(part),
192                        ElaborationPosition::InRange => parts_slice.push(part.clone()),
193                        ElaborationPosition::AfterRange => right_fixed_parts.push(part),
194                    }
195                },
196                CompilableTextPartType::Compilable { incompatible_modifiers: _ } => {
197
198                    end_part_position_in_compilable_content = ends[compilable_parts_index];
199
200                    compilable_parts_index += 1;
201
202                    if start_part_position_in_compilable_content == end {
203
204                        elaboration_position = ElaborationPosition::AfterRange;
205                    }
206
207                    match elaboration_position {
208
209                        ElaborationPosition::BeforeRange => {
210                            if start_part_position_in_compilable_content <= start
211                                && start < end_part_position_in_compilable_content {     // start matching
212        
213                                if start_part_position_in_compilable_content < start {      // there is a pre-match compilable part segment
214                                    left_fixed_parts.clear();
215                                } 
216                                
217                                let part = CompilableTextPart::new(
218                                    compilable_content[start..end_part_position_in_compilable_content.min(end)].to_string(),
219                                    part.part_type().clone()
220                                );
221                                
222                                parts_slice.push(part);
223        
224                                if end < end_part_position_in_compilable_content {         // start and end are in the same part
225                                    break;              
226                                }
227        
228                                elaboration_position = ElaborationPosition::InRange;
229
230                                start_part_position_in_compilable_content = end_part_position_in_compilable_content.min(end);
231
232                                if start_part_position_in_compilable_content < end_part_position_in_compilable_content {
233
234                                    index -= 1;
235                                    compilable_parts_index -= 1;
236                                    
237                                    continue;
238                                }
239                            
240                            } else {        // no matching in this part
241        
242                                left_fixed_parts.clear();
243                            }
244                        },
245
246                        ElaborationPosition::InRange => {
247                            if end <= end_part_position_in_compilable_content {      // the end is in this part
248
249                                let content = compilable_content[start_part_position_in_compilable_content..end].to_string();
250
251                                if !content.is_empty() {
252                                    // take last part segment
253                                    let part = CompilableTextPart::new(
254                                        content,
255                                        part.part_type().clone()
256                                    );
257                                    
258                                    parts_slice.push(part);
259                                }
260
261                                if end < end_part_position_in_compilable_content {
262                                    break;
263                                }
264
265                                elaboration_position = ElaborationPosition::AfterRange;
266
267                            } else {
268                                let part = CompilableTextPart::new(
269                                    compilable_content[start_part_position_in_compilable_content..end_part_position_in_compilable_content].to_string(),
270                                    part.part_type().clone()
271                                );
272                                
273                                parts_slice.push(part);
274                            }
275                        },
276                        
277                        ElaborationPosition::AfterRange => break,
278                    }
279
280                    if start_part_position_in_compilable_content == end {
281
282                        elaboration_position = ElaborationPosition::AfterRange;
283                    }
284                    
285                    start_part_position_in_compilable_content = end_part_position_in_compilable_content;
286                },
287            }
288
289        }
290
291        match elaboration_policy {
292            PartsSliceElaborationPolicy::DontTakeBorderFixedParts => (),
293            PartsSliceElaborationPolicy::TakeLeftFixedParts => left_fixed_parts.into_iter().for_each(|p| parts_slice.insert(0, p.clone())),
294            PartsSliceElaborationPolicy::TakeRightFixedParts => right_fixed_parts.into_iter().for_each(|p| parts_slice.push(p.clone())),
295            PartsSliceElaborationPolicy::TakeLeftAndRightFixedParts => {
296
297                left_fixed_parts.into_iter().for_each(|p| parts_slice.insert(0, p.clone()));
298
299                right_fixed_parts.into_iter().for_each(|p| parts_slice.push(p.clone()));
300            },
301        }
302
303        Ok(parts_slice)
304    }
305}
306
307impl CompilableText {
308
309    /// Compile parts and return the new compiled parts or `None` if there are not matches using
310    /// provided rule
311    fn compile_with_compilation_rule(&mut self, (rule_identifier, rule): (&ModifierIdentifier, &Box<dyn CompilationRule>), format: &OutputFormat, compilation_configuration: &CompilationConfiguration, compilation_configuration_overlay: CompilationConfigurationOverLay) -> Result<(), CompilationError> {
312    
313        let parts = self.parts();
314
315        let mut compilable_content = String::new();
316        let mut compilable_content_end_parts_positions: Vec<usize> = Vec::new();
317
318        parts.iter()
319                .filter(|part| {
320                    match &part.part_type() {
321                        CompilableTextPartType::Fixed => false,
322                        CompilableTextPartType::Compilable{ incompatible_modifiers } => {
323                            if incompatible_modifiers.contains(&rule_identifier) {
324                                return false
325                            } else {
326                                return true
327                            }
328                        },
329                    }
330                })
331                .for_each(|part| {
332
333                    compilable_content.push_str(part.content());
334
335                    let last_pos = *compilable_content_end_parts_positions.last().unwrap_or(&0);
336
337                    compilable_content_end_parts_positions.push(last_pos + part.content().len());
338                });
339
340        let matches = rule.find_iter(&compilable_content);
341
342        if matches.len() == 0 {
343            log::debug!("'{}' => no matches with {:?} -> {:?}", compilable_content, rule_identifier, rule.search_pattern());
344            
345            return Ok(());
346        }
347
348        log::debug!("'{}' => there is a match with {:?} -> {:?}", compilable_content, rule_identifier, rule.search_pattern());
349
350        let mut compiled_parts: Vec<CompilableTextPart> = Vec::new();     // final output
351
352        let mut parts_index: usize = 0;
353        let mut compilable_parts_index: usize = 0;
354
355        // only for compilable parts
356        let mut part_start_position_in_compilable_content: usize = 0;
357        let mut part_end_position_in_compilable_content: usize;
358
359        let mut match_index: usize = 0;
360
361        while parts_index < parts.len() {      // there are other parts
362
363            let match_start_end: Option<(usize, usize)>;        // start and end
364
365            if match_index < matches.len() {
366
367                let current_evaluated_match = matches[match_index];
368
369                match_index += 1;    
370            
371                match_start_end = Some((
372                    current_evaluated_match.start(),
373                    current_evaluated_match.end()
374                ));
375
376            } else {
377
378                match_start_end = None;
379            }
380
381            let mut match_found = false;
382
383            let mut matched_parts: Vec<CompilableTextPart> = Vec::new();
384            
385            'parts_loop: while parts_index < parts.len() {
386
387                let part = &parts[parts_index];
388
389                parts_index += 1;   // for next iteration
390
391                match part.part_type() {
392                    CompilableTextPartType::Fixed => {
393
394                        if let Some((_start, _end)) = match_start_end {
395
396                            if match_found {        // matching end cannot be in a fixed part
397
398                                matched_parts.push(part.clone());
399        
400                                continue 'parts_loop;
401                            
402                            } else {
403                                
404                                compiled_parts.push(part.clone());      // direct in compiled_parts
405    
406                                continue 'parts_loop;
407                            }
408                        
409                        } else {
410                            compiled_parts.push(part.clone());      // direct in compiled_parts
411
412                            continue 'parts_loop;
413                        }
414                    },
415                    CompilableTextPartType::Compilable{ incompatible_modifiers } => {
416
417                        if incompatible_modifiers.contains(rule_identifier) {
418                            compiled_parts.push(part.clone());      // direct in compiled_parts
419
420                            continue 'parts_loop;
421                        }
422
423                        part_end_position_in_compilable_content = compilable_content_end_parts_positions[compilable_parts_index];
424                        
425                        compilable_parts_index += 1;
426
427                        if let Some((match_start, match_end)) = match_start_end {
428
429                            if !match_found && part_end_position_in_compilable_content <= match_start {      // there is no match in this part
430                            
431                                let sub_part = &compilable_content[part_start_position_in_compilable_content..part_end_position_in_compilable_content];
432
433                                compiled_parts.push(CompilableTextPart::new(
434                                    sub_part.to_string(),
435                                    CompilableTextPartType::Compilable{ incompatible_modifiers: incompatible_modifiers.clone() }
436                                ));
437    
438                            } else {
439                                // ...part has a match
440    
441                                if !match_found     // first part in which current match is found
442                                    && part_start_position_in_compilable_content <= match_start
443                                    && match_start < part_end_position_in_compilable_content {
444
445                                    // === pre-matched part ==
446                                    let pre_matched_part = &compilable_content[part_start_position_in_compilable_content..match_start];
447                                                                            
448                                    if !pre_matched_part.is_empty() {
449                                        compiled_parts.push(CompilableTextPart::new(
450                                            pre_matched_part.to_string(),
451                                            CompilableTextPartType::Compilable{ incompatible_modifiers: incompatible_modifiers.clone() }
452                                        ));
453                                    }
454
455                                    part_start_position_in_compilable_content = match_start;
456
457                                    // === matched part ===
458                                    let matched_part = &compilable_content[part_start_position_in_compilable_content..part_end_position_in_compilable_content.min(match_end)];
459
460                                    matched_parts.push(CompilableTextPart::new(
461                                        matched_part.to_string(),
462                                        CompilableTextPartType::Compilable{ incompatible_modifiers: incompatible_modifiers.clone() }
463                                    ));
464                                }
465                                
466                                if match_end <= part_end_position_in_compilable_content {       // matching end is in this part
467
468                                    if match_found {   // the matching end is in another part respect of matching start
469
470                                        let matched_part = &compilable_content[part_start_position_in_compilable_content..match_end];
471
472                                        matched_parts.push(CompilableTextPart::new(
473                                            matched_part.to_string(),
474                                            CompilableTextPartType::Compilable{ incompatible_modifiers: incompatible_modifiers.clone() }
475                                        ));
476                                    }
477
478                                    // compile and append found matched parts
479                                    compiled_parts.append(
480                                        &mut rule.compile(
481                                            &CompilableText::from(matched_parts),
482                                            format,
483                                            compilation_configuration,
484                                            compilation_configuration_overlay.clone()
485                                        )?.parts_mut() 
486                                    );
487
488                                    // re-start next parts loop from this part
489                                    parts_index -= 1;       
490                                    compilable_parts_index -= 1;
491
492                                    part_start_position_in_compilable_content = match_end;
493
494                                    break 'parts_loop;
495
496                                } else {
497
498                                    if match_found {        // this part is a compilable part in the middle of matched parts
499
500                                        let matched_part = &compilable_content[part_start_position_in_compilable_content..part_end_position_in_compilable_content];
501
502                                        matched_parts.push(CompilableTextPart::new(
503                                            matched_part.to_string(),
504                                            CompilableTextPartType::Compilable{ incompatible_modifiers: incompatible_modifiers.clone() }
505                                        ));
506                                    }
507                                }
508
509                                match_found = true;     // update to check if match is found in next iterations
510                            }
511
512                        } else {
513                            
514                            let part = &compilable_content[part_start_position_in_compilable_content..part_end_position_in_compilable_content];
515                                                                            
516                            if !part.is_empty() {
517                                compiled_parts.push(CompilableTextPart::new(
518                                    part.to_string(),
519                                    CompilableTextPartType::Compilable{ incompatible_modifiers: incompatible_modifiers.clone() }
520                                ));
521                            }
522                        }
523        
524                        // update start position
525                        part_start_position_in_compilable_content = part_end_position_in_compilable_content;
526                    }
527
528                }
529            }
530        }
531
532        self.set_parts(compiled_parts);
533        
534        Ok(())
535    }
536}
537
538impl Compilable for CompilableText {
539
540    fn standard_compile(&mut self, format: &OutputFormat, codex: &Codex, compilation_configuration: &CompilationConfiguration, compilation_configuration_overlay: CompilationConfigurationOverLay) -> Result<CompilationOutcome, CompilationError> {
541        
542        let excluded_modifiers = compilation_configuration_overlay.excluded_modifiers().clone();
543
544        log::debug!("start to compile content:\n{:?}\nexcluding: {:?}", self, excluded_modifiers);
545
546        if excluded_modifiers == Bucket::All {
547            log::debug!("compilation of content:\n{:?} is skipped because are excluded all modifiers", self);
548            
549            return Ok(CompilationOutcome::from(self.content()))
550        }
551
552        for (codex_identifier, (text_modifier, text_rule)) in codex.text_modifiers() {
553
554            if excluded_modifiers.contains(codex_identifier) {
555
556                log::debug!("{:?} is skipped", text_modifier);
557                continue;
558            }
559
560            self.compile_with_compilation_rule((codex_identifier, text_rule), format, compilation_configuration, compilation_configuration_overlay.clone())?;
561        }
562
563        Ok(CompilationOutcome::from(self.content()))
564    }
565}
566
567
568#[cfg(test)]
569mod test {
570    use std::collections::HashSet;
571
572    use crate::{codex::{modifier::{standard_text_modifier::StandardTextModifier, ModifiersBucket}, Codex}, compilable_text::{compilable_text_part::{CompilableTextPart, CompilableTextPartType}, PartsSliceElaborationPolicy}, compilation::{compilation_configuration::{compilation_configuration_overlay::CompilationConfigurationOverLay, CompilationConfiguration}, compilable::Compilable}, output_format::OutputFormat};
573
574    use super::CompilableText;
575
576
577    #[test]
578    fn parts_between_positions_in_cfc() {
579        let compilable = CompilableText::new(vec![
580            CompilableTextPart::new(
581                String::from("this is a string with 35 characters"),
582                CompilableTextPartType::Compilable { incompatible_modifiers: ModifiersBucket::None }
583            ),
584            CompilableTextPart::new(
585                String::from("this is the fixed part"),
586                CompilableTextPartType::Fixed
587            ),
588            CompilableTextPart::new(
589                String::from("end of the content"),
590                CompilableTextPartType::Compilable { incompatible_modifiers: ModifiersBucket::None }
591            ),
592        ]);
593
594        let start1: usize = 5;
595        let start2: usize = 25;
596
597        let end1: usize = 16;
598        let end2: usize = 38;
599
600        let parts_slice = compilable.parts_slice(start1, end1).unwrap();
601
602        assert_eq!(parts_slice.len(), 1);
603        assert_eq!(parts_slice[0].content(), &String::from("is a string"));
604
605        let parts_slice = compilable.parts_slice(start2, end2).unwrap();
606
607        assert_eq!(parts_slice.len(), 3);
608        assert_eq!(parts_slice[0].content(), &String::from("characters"));
609        assert_eq!(parts_slice[1].content(), &String::from("this is the fixed part"));
610        assert_eq!(parts_slice[2].content(), &String::from("end"));
611    }
612
613    #[test]
614    fn parts_between_positions_in_cfcfc() {
615        let compilable = CompilableText::new(vec![
616            CompilableTextPart::new_compilable(String::from("c1"), ModifiersBucket::None),
617            CompilableTextPart::new_fixed(String::from("f1")),
618            CompilableTextPart::new_compilable(String::from("c2"), ModifiersBucket::None),
619            CompilableTextPart::new_fixed(String::from("f2")),
620            CompilableTextPart::new_compilable(String::from("c3"), ModifiersBucket::None),
621        ]);
622
623        let start: usize = 1;
624        let end: usize = 5;
625
626        let parts_slice = compilable.parts_slice(start, end).unwrap();
627
628        assert_eq!(parts_slice.len(), 5);
629        assert_eq!(parts_slice[0].content(), &String::from("1"));
630        assert_eq!(parts_slice[1].content(), &String::from("f1"));
631        assert_eq!(parts_slice[2].content(), &String::from("c2"));
632        assert_eq!(parts_slice[3].content(), &String::from("f2"));
633        assert_eq!(parts_slice[4].content(), &String::from("c"));
634
635        let compilable = CompilableText::new(vec![
636            CompilableTextPart::new_compilable(String::from("c1"), ModifiersBucket::None),
637            CompilableTextPart::new_fixed(String::from("f1")),
638            CompilableTextPart::new_compilable(String::from("c2"), ModifiersBucket::None),
639            CompilableTextPart::new_fixed(String::from("f2")),
640            CompilableTextPart::new_compilable(String::from("c3"), ModifiersBucket::None),
641        ]);
642
643        let start: usize = 1;
644        let end: usize = 4;
645
646        let parts_slice = compilable.parts_slice(start, end).unwrap();
647
648        assert_eq!(parts_slice.len(), 4);
649        assert_eq!(parts_slice[0].content(), &String::from("1"));
650        assert_eq!(parts_slice[1].content(), &String::from("f1"));
651        assert_eq!(parts_slice[2].content(), &String::from("c2"));
652        assert_eq!(parts_slice[3].content(), &String::from("f2"));
653    }
654
655    #[test]
656    fn parts_between_positions_in_cfcfc_with_explicit_policy() {
657        let compilable = CompilableText::new(vec![
658            CompilableTextPart::new_fixed(String::from("f-1")),
659            CompilableTextPart::new_compilable(String::from("c0"), ModifiersBucket::None),
660            CompilableTextPart::new_fixed(String::from("f0")),
661            CompilableTextPart::new_compilable(String::from("*"), ModifiersBucket::None),
662            CompilableTextPart::new_fixed(String::from("f1")),
663            CompilableTextPart::new_compilable(String::from("c2"), ModifiersBucket::None),
664            CompilableTextPart::new_fixed(String::from("f2")),
665            CompilableTextPart::new_compilable(String::from("*"), ModifiersBucket::None),
666            CompilableTextPart::new_fixed(String::from("f3")),
667            CompilableTextPart::new_compilable(String::from("c3"), ModifiersBucket::None),
668            CompilableTextPart::new_fixed(String::from("f4")),
669        ]);
670
671        let start: usize = 3;
672        let end: usize = 5;
673
674        // ==== take left and right ====
675        let parts_slice = compilable.parts_slice_with_explicit_policy(start, end, PartsSliceElaborationPolicy::TakeLeftAndRightFixedParts).unwrap();
676
677        assert_eq!(parts_slice.len(), 3);
678        assert_eq!(parts_slice[0].content(), &String::from("f1"));
679        assert_eq!(parts_slice[1].content(), &String::from("c2"));
680        assert_eq!(parts_slice[2].content(), &String::from("f2"));
681
682        // ==== take left ====
683        let parts_slice = compilable.parts_slice_with_explicit_policy(start, end, PartsSliceElaborationPolicy::TakeLeftFixedParts).unwrap();
684
685        assert_eq!(parts_slice.len(), 2);
686        assert_eq!(parts_slice[0].content(), &String::from("f1"));
687        assert_eq!(parts_slice[1].content(), &String::from("c2"));
688
689        // ==== take right ====
690        let parts_slice = compilable.parts_slice_with_explicit_policy(start, end, PartsSliceElaborationPolicy::TakeRightFixedParts).unwrap();
691
692        assert_eq!(parts_slice.len(), 2);
693        assert_eq!(parts_slice[0].content(), &String::from("c2"));
694        assert_eq!(parts_slice[1].content(), &String::from("f2"));
695
696        // ==== no take ====
697        let parts_slice = compilable.parts_slice_with_explicit_policy(start, end, PartsSliceElaborationPolicy::DontTakeBorderFixedParts).unwrap();
698
699        assert_eq!(parts_slice.len(), 1);
700        assert_eq!(parts_slice[0].content(), &String::from("c2"));
701    }
702
703    #[test]
704    fn compile_nested_modifiers() {
705
706        let mut codex = Codex::of_html();
707
708        codex.retain(HashSet::from([
709            StandardTextModifier::BoldStarVersion.identifier(),
710            StandardTextModifier::BoldUnderscoreVersion.identifier(),
711            StandardTextModifier::ItalicStarVersion.identifier(),
712            StandardTextModifier::ItalicUnderscoreVersion.identifier(),
713            StandardTextModifier::InlineCode.identifier(),
714        ]));
715
716        let compilation_configuration = CompilationConfiguration::default();
717
718        let content = "A piece of **bold text**, *italic text*, `a **(fake) bold text** which must be not parsed` and *nested **bold text***";
719
720        let mut outcome = CompilableText::from(content);
721        
722        outcome.compile(&OutputFormat::Html, &codex, &compilation_configuration, CompilationConfigurationOverLay::default()).unwrap();       
723
724        assert_eq!(outcome.content(), concat!(
725            "A piece of ",
726            r#"<strong class="bold">bold text</strong>, "#,
727            r#"<em class="italic">italic text</em>, "#,
728            r#"<code class="language-markup inline-code">a **(fake) bold text** which must be not parsed</code>"#,
729            r#" and "#,
730            r#"<em class="italic">nested <strong class="bold">bold text</strong></em>"#,
731        ));
732    }
733
734}
735
736
737
738
739
740
741