1use crate::{
2 Parser,
3 attributes::Attrlist,
4 content::{Content, Passthroughs, SubstitutionStep},
5};
6
7#[derive(Clone, Debug, Eq, PartialEq)]
13pub enum SubstitutionGroup {
14 Normal,
18
19 Title,
22
23 Header,
33
34 Verbatim,
38
39 Pass,
47
48 None,
51
52 AttributeEntryValue,
56
57 Custom(Vec<SubstitutionStep>),
67}
68
69impl SubstitutionGroup {
70 pub(crate) fn from_custom_string(start_from: Option<&Self>, mut custom: &str) -> Option<Self> {
75 custom = custom.trim();
76
77 if custom == "none" {
78 return Some(Self::None);
79 }
80
81 if custom == "n" || custom == "normal" {
82 return Some(Self::Normal);
83 }
84
85 if custom == "v" || custom == "verbatim" {
86 return Some(Self::Verbatim);
87 }
88
89 let mut steps: Vec<SubstitutionStep> = vec![];
90
91 for (count, mut step) in custom.split(",").enumerate() {
92 step = step.trim();
93
94 if step == "n" || step == "normal" {
95 steps = vec![
96 SubstitutionStep::SpecialCharacters,
97 SubstitutionStep::Quotes,
98 SubstitutionStep::AttributeReferences,
99 SubstitutionStep::CharacterReplacements,
100 SubstitutionStep::Macros,
101 SubstitutionStep::PostReplacement,
102 ];
103 continue;
104 }
105
106 if step == "v" || step == "verbatim" {
107 steps = vec![SubstitutionStep::SpecialCharacters];
108 continue;
109 }
110
111 let append = if step.starts_with('+') {
112 step = &step[1..];
113 true
114 } else {
115 false
116 };
117
118 let prepend = if !append && step.ends_with('+') {
119 step = &step[0..step.len() - 1];
120 true
121 } else {
122 false
123 };
124
125 let subtract = if !append && !prepend && step.starts_with('-') {
126 step = &step[1..];
127 true
128 } else {
129 false
130 };
131
132 if count == 0
133 && let Some(start_from) = start_from
134 && (append || prepend || subtract)
135 {
136 steps = start_from.steps().to_owned();
137 }
138
139 let step = match step {
140 "c" | "specialcharacters" | "specialchars" => SubstitutionStep::SpecialCharacters,
141 "q" | "quotes" => SubstitutionStep::Quotes,
142 "a" | "attributes" => SubstitutionStep::AttributeReferences,
143 "r" | "replacements" => SubstitutionStep::CharacterReplacements,
144 "m" | "macros" => SubstitutionStep::Macros,
145 "p" | "post_replacements" => SubstitutionStep::PostReplacement,
146 _ => {
147 return None;
148 }
149 };
150
151 if prepend {
152 steps.insert(0, step);
153 } else if append {
154 steps.push(step);
155 } else if subtract {
156 steps.retain(|s| s != &step);
157 } else {
158 steps.push(step);
159 }
160 }
161
162 Some(Self::Custom(steps))
163 }
164
165 pub(crate) fn apply(
166 &self,
167 content: &mut Content<'_>,
168 parser: &Parser,
169 attrlist: Option<&Attrlist>,
170 ) {
171 let steps = self.steps();
172
173 let passthroughs: Option<Passthroughs> =
174 if steps.contains(&SubstitutionStep::Macros) || self == &Self::Header {
175 Some(Passthroughs::extract_from(content))
176 } else {
177 None
178 };
179
180 for step in steps {
181 step.apply(content, parser, attrlist);
182 }
183
184 if let Some(passthroughs) = passthroughs {
185 passthroughs.restore_to(content, parser);
186 }
187
188 content.finalize_deferred(&*parser.renderer);
193 }
194
195 pub(crate) fn override_via_attrlist(&self, attrlist: Option<&Attrlist>) -> Self {
196 let mut result = self.clone();
197
198 if let Some(attrlist) = attrlist {
199 if let Some(block_style) = attrlist.nth_attribute(1).and_then(|a| a.block_style()) {
200 result = match block_style {
201 "pass" => SubstitutionGroup::None,
203 _ => result,
204 };
205 }
206
207 if let Some(sub_group) = attrlist
208 .named_attribute("subs")
209 .map(|attr| attr.value())
210 .and_then(|s| Self::from_custom_string(Some(self), s))
211 {
212 result = sub_group;
213 }
214 }
215
216 result
217 }
218
219 fn steps(&self) -> &[SubstitutionStep] {
220 match self {
221 Self::Normal | Self::Title => &[
222 SubstitutionStep::SpecialCharacters,
223 SubstitutionStep::Quotes,
224 SubstitutionStep::AttributeReferences,
225 SubstitutionStep::CharacterReplacements,
226 SubstitutionStep::Macros,
227 SubstitutionStep::PostReplacement,
228 ],
229
230 Self::Header | Self::AttributeEntryValue => &[
231 SubstitutionStep::SpecialCharacters,
232 SubstitutionStep::AttributeReferences,
233 ],
234
235 Self::Verbatim => &[SubstitutionStep::SpecialCharacters],
236
237 Self::Pass | Self::None => &[],
238
239 Self::Custom(steps) => steps,
240 }
241 }
242}
243
244#[cfg(test)]
245mod tests {
246 #![allow(clippy::unwrap_used)]
247
248 mod from_custom_string {
249 use crate::{
250 content::{Content, SubstitutionStep},
251 strings::CowStr,
252 tests::prelude::*,
253 };
254
255 #[test]
256 fn empty() {
257 assert_eq!(SubstitutionGroup::from_custom_string(None, ""), None);
258 }
259
260 #[test]
261 fn none() {
262 assert_eq!(
263 SubstitutionGroup::from_custom_string(None, "none"),
264 Some(SubstitutionGroup::None)
265 );
266
267 assert_eq!(SubstitutionGroup::from_custom_string(None, "nermal"), None);
268 }
269
270 #[test]
271 fn normal() {
272 assert_eq!(
273 SubstitutionGroup::from_custom_string(None, "n"),
274 Some(SubstitutionGroup::Normal)
275 );
276
277 assert_eq!(
278 SubstitutionGroup::from_custom_string(None, "normal"),
279 Some(SubstitutionGroup::Normal)
280 );
281
282 assert_eq!(SubstitutionGroup::from_custom_string(None, "nermal"), None);
283 }
284
285 #[test]
286 fn verbatim() {
287 assert_eq!(
288 SubstitutionGroup::from_custom_string(None, "v"),
289 Some(SubstitutionGroup::Verbatim)
290 );
291
292 assert_eq!(
293 SubstitutionGroup::from_custom_string(None, "verbatim"),
294 Some(SubstitutionGroup::Verbatim)
295 );
296
297 assert_eq!(
298 SubstitutionGroup::from_custom_string(None, "verboten"),
299 None
300 );
301 }
302
303 #[test]
304 fn special_chars() {
305 assert_eq!(
306 SubstitutionGroup::from_custom_string(None, "c"),
307 Some(SubstitutionGroup::Custom(vec![
308 SubstitutionStep::SpecialCharacters
309 ]))
310 );
311
312 assert_eq!(
313 SubstitutionGroup::from_custom_string(None, "specialchars"),
314 Some(SubstitutionGroup::Custom(vec![
315 SubstitutionStep::SpecialCharacters
316 ]))
317 );
318 }
319
320 #[test]
321 fn quotes() {
322 assert_eq!(
323 SubstitutionGroup::from_custom_string(None, "q"),
324 Some(SubstitutionGroup::Custom(vec![SubstitutionStep::Quotes]))
325 );
326
327 assert_eq!(
328 SubstitutionGroup::from_custom_string(None, "quotes"),
329 Some(SubstitutionGroup::Custom(vec![SubstitutionStep::Quotes]))
330 );
331 }
332
333 #[test]
334 fn attributes() {
335 assert_eq!(
336 SubstitutionGroup::from_custom_string(None, "a"),
337 Some(SubstitutionGroup::Custom(vec![
338 SubstitutionStep::AttributeReferences
339 ]))
340 );
341
342 assert_eq!(
343 SubstitutionGroup::from_custom_string(None, "attributes"),
344 Some(SubstitutionGroup::Custom(vec![
345 SubstitutionStep::AttributeReferences
346 ]))
347 );
348 }
349
350 #[test]
351 fn replacements() {
352 assert_eq!(
353 SubstitutionGroup::from_custom_string(None, "r"),
354 Some(SubstitutionGroup::Custom(vec![
355 SubstitutionStep::CharacterReplacements
356 ]))
357 );
358
359 assert_eq!(
360 SubstitutionGroup::from_custom_string(None, "replacements"),
361 Some(SubstitutionGroup::Custom(vec![
362 SubstitutionStep::CharacterReplacements
363 ]))
364 );
365 }
366
367 #[test]
368 fn macros() {
369 assert_eq!(
370 SubstitutionGroup::from_custom_string(None, "m"),
371 Some(SubstitutionGroup::Custom(vec![SubstitutionStep::Macros]))
372 );
373
374 assert_eq!(
375 SubstitutionGroup::from_custom_string(None, "macros"),
376 Some(SubstitutionGroup::Custom(vec![SubstitutionStep::Macros]))
377 );
378 }
379
380 #[test]
381 fn post_replacements() {
382 assert_eq!(
383 SubstitutionGroup::from_custom_string(None, "p"),
384 Some(SubstitutionGroup::Custom(vec![
385 SubstitutionStep::PostReplacement
386 ]))
387 );
388
389 assert_eq!(
390 SubstitutionGroup::from_custom_string(None, "post_replacements"),
391 Some(SubstitutionGroup::Custom(vec![
392 SubstitutionStep::PostReplacement
393 ]))
394 );
395 }
396
397 #[test]
398 fn multiple() {
399 assert_eq!(
400 SubstitutionGroup::from_custom_string(None, "q,a"),
401 Some(SubstitutionGroup::Custom(vec![
402 SubstitutionStep::Quotes,
403 SubstitutionStep::AttributeReferences
404 ]))
405 );
406
407 assert_eq!(
408 SubstitutionGroup::from_custom_string(None, "q, a"),
409 Some(SubstitutionGroup::Custom(vec![
410 SubstitutionStep::Quotes,
411 SubstitutionStep::AttributeReferences
412 ]))
413 );
414
415 assert_eq!(
416 SubstitutionGroup::from_custom_string(None, "quotes,attributes"),
417 Some(SubstitutionGroup::Custom(vec![
418 SubstitutionStep::Quotes,
419 SubstitutionStep::AttributeReferences
420 ]))
421 );
422
423 assert_eq!(
424 SubstitutionGroup::from_custom_string(None, "x,bogus,no such step"),
425 None
426 );
427 }
428
429 #[test]
430 fn subtraction() {
431 assert_eq!(
432 SubstitutionGroup::from_custom_string(None, "n,-r"),
433 Some(SubstitutionGroup::Custom(vec![
434 SubstitutionStep::SpecialCharacters,
435 SubstitutionStep::Quotes,
436 SubstitutionStep::AttributeReferences,
437 SubstitutionStep::Macros,
438 SubstitutionStep::PostReplacement,
439 ]))
440 );
441
442 assert_eq!(
443 SubstitutionGroup::from_custom_string(None, "n,-r,-r,-m"),
444 Some(SubstitutionGroup::Custom(vec![
445 SubstitutionStep::SpecialCharacters,
446 SubstitutionStep::Quotes,
447 SubstitutionStep::AttributeReferences,
448 SubstitutionStep::PostReplacement,
449 ]))
450 );
451
452 assert_eq!(
453 SubstitutionGroup::from_custom_string(None, "v,-r"),
454 Some(SubstitutionGroup::Custom(vec![
455 SubstitutionStep::SpecialCharacters,
456 ]))
457 );
458
459 assert_eq!(
460 SubstitutionGroup::from_custom_string(None, "v,-c"),
461 Some(SubstitutionGroup::Custom(vec![]))
462 );
463 }
464
465 #[test]
466 fn addition() {
467 assert_eq!(
468 SubstitutionGroup::from_custom_string(None, "n,r"),
469 Some(SubstitutionGroup::Custom(vec![
470 SubstitutionStep::SpecialCharacters,
471 SubstitutionStep::Quotes,
472 SubstitutionStep::AttributeReferences,
473 SubstitutionStep::CharacterReplacements,
474 SubstitutionStep::Macros,
475 SubstitutionStep::PostReplacement,
476 SubstitutionStep::CharacterReplacements,
477 ]))
478 );
479
480 assert_eq!(
481 SubstitutionGroup::from_custom_string(None, "v,m"),
482 Some(SubstitutionGroup::Custom(vec![
483 SubstitutionStep::SpecialCharacters,
484 SubstitutionStep::Macros,
485 ]))
486 );
487 }
488
489 #[test]
490 fn incremental() {
491 assert_eq!(
492 SubstitutionGroup::from_custom_string(None, "n,r"),
493 Some(SubstitutionGroup::Custom(vec![
494 SubstitutionStep::SpecialCharacters,
495 SubstitutionStep::Quotes,
496 SubstitutionStep::AttributeReferences,
497 SubstitutionStep::CharacterReplacements,
498 SubstitutionStep::Macros,
499 SubstitutionStep::PostReplacement,
500 SubstitutionStep::CharacterReplacements,
501 ]))
502 );
503
504 assert_eq!(
505 SubstitutionGroup::from_custom_string(None, "v,m"),
506 Some(SubstitutionGroup::Custom(vec![
507 SubstitutionStep::SpecialCharacters,
508 SubstitutionStep::Macros,
509 ]))
510 );
511 }
512
513 #[test]
514 fn prepend() {
515 assert_eq!(
516 SubstitutionGroup::from_custom_string(
517 Some(&SubstitutionGroup::Verbatim),
518 "attributes+"
519 ),
520 Some(SubstitutionGroup::Custom(vec![
521 SubstitutionStep::AttributeReferences,
522 SubstitutionStep::SpecialCharacters,
523 ]))
524 );
525
526 assert_eq!(
527 SubstitutionGroup::from_custom_string(None, "attributes+"),
528 Some(SubstitutionGroup::Custom(vec![
529 SubstitutionStep::AttributeReferences,
530 ]))
531 );
532 }
533
534 #[test]
535 fn append() {
536 assert_eq!(
537 SubstitutionGroup::from_custom_string(
538 Some(&SubstitutionGroup::Verbatim),
539 "+attributes"
540 ),
541 Some(SubstitutionGroup::Custom(vec![
542 SubstitutionStep::SpecialCharacters,
543 SubstitutionStep::AttributeReferences,
544 ]))
545 );
546
547 assert_eq!(
548 SubstitutionGroup::from_custom_string(None, "attributes+"),
549 Some(SubstitutionGroup::Custom(vec![
550 SubstitutionStep::AttributeReferences,
551 ]))
552 );
553 }
554
555 #[test]
556 fn subtract() {
557 assert_eq!(
558 SubstitutionGroup::from_custom_string(
559 Some(&SubstitutionGroup::Normal),
560 "-attributes"
561 ),
562 Some(SubstitutionGroup::Custom(vec![
563 SubstitutionStep::SpecialCharacters,
564 SubstitutionStep::Quotes,
565 SubstitutionStep::CharacterReplacements,
566 SubstitutionStep::Macros,
567 SubstitutionStep::PostReplacement,
568 ]))
569 );
570
571 assert_eq!(
572 SubstitutionGroup::from_custom_string(None, "-attributes"),
573 Some(SubstitutionGroup::Custom(vec![]))
574 );
575 }
576
577 #[test]
578 fn custom_group_with_macros_preserves_passthroughs() {
579 let custom_group = SubstitutionGroup::from_custom_string(None, "q,m").unwrap();
580
581 let mut content = Content::from(crate::Span::new(
582 "Text with +++pass<through>+++ icon:github[] content.",
583 ));
584 let p = Parser::default();
585 custom_group.apply(&mut content, &p, None);
586
587 assert!(!content.is_empty());
588 assert_eq!(
589 content.rendered,
590 CowStr::Boxed(
591 "Text with pass<through> <span class=\"icon\">[github]</span> content."
592 .to_string()
593 .into_boxed_str()
594 )
595 );
596 }
597 }
598
599 mod normal {
600 use crate::{content::Content, strings::CowStr, tests::prelude::*};
601
602 #[test]
603 fn empty() {
604 let mut content = Content::from(crate::Span::default());
605 let p = Parser::default();
606 SubstitutionGroup::Normal.apply(&mut content, &p, None);
607 assert!(content.is_empty());
608 assert_eq!(content.rendered, CowStr::Borrowed(""));
609 }
610
611 #[test]
612 fn basic_non_empty_span() {
613 let mut content = Content::from(crate::Span::new("blah"));
614 let p = Parser::default();
615 SubstitutionGroup::Normal.apply(&mut content, &p, None);
616 assert!(!content.is_empty());
617 assert_eq!(content.rendered, CowStr::Borrowed("blah"));
618 }
619
620 #[test]
621 fn match_lt_and_gt() {
622 let mut content = Content::from(crate::Span::new("bl<ah>"));
623 let p = Parser::default();
624 SubstitutionGroup::Normal.apply(&mut content, &p, None);
625 assert!(!content.is_empty());
626 assert_eq!(
627 content.rendered,
628 CowStr::Boxed("bl<ah>".to_string().into_boxed_str())
629 );
630 }
631
632 #[test]
633 fn match_amp() {
634 let mut content = Content::from(crate::Span::new("bl<a&h>"));
635 let p = Parser::default();
636 SubstitutionGroup::Normal.apply(&mut content, &p, None);
637 assert!(!content.is_empty());
638 assert_eq!(
639 content.rendered,
640 CowStr::Boxed("bl<a&h>".to_string().into_boxed_str())
641 );
642 }
643
644 #[test]
645 fn strong_word() {
646 let mut content = Content::from(crate::Span::new("One *word* is strong."));
647 let p = Parser::default();
648 SubstitutionGroup::Normal.apply(&mut content, &p, None);
649 assert!(!content.is_empty());
650 assert_eq!(
651 content.rendered,
652 CowStr::Boxed(
653 "One <strong>word</strong> is strong."
654 .to_string()
655 .into_boxed_str()
656 )
657 );
658 }
659
660 #[test]
661 fn strong_word_with_special_chars() {
662 let mut content = Content::from(crate::Span::new("One *wo<r>d* is strong."));
663 let p = Parser::default();
664 SubstitutionGroup::Normal.apply(&mut content, &p, None);
665 assert!(!content.is_empty());
666 assert_eq!(
667 content.rendered,
668 CowStr::Boxed(
669 "One <strong>wo<r>d</strong> is strong."
670 .to_string()
671 .into_boxed_str()
672 )
673 );
674 }
675
676 #[test]
677 fn marked_string_with_id() {
678 let mut content = Content::from(crate::Span::new(r#"[#id]#a few words#"#));
679 let p = Parser::default();
680 SubstitutionGroup::Normal.apply(&mut content, &p, None);
681 assert!(!content.is_empty());
682 assert_eq!(
683 content.rendered,
684 CowStr::Boxed(r#"<span id="id">a few words</span>"#.to_string().into_boxed_str())
685 );
686 }
687 }
688
689 mod attribute_entry_value {
690 use crate::{
691 content::Content, parser::ModificationContext, strings::CowStr, tests::prelude::*,
692 };
693
694 #[test]
695 fn empty() {
696 let mut content = Content::from(crate::Span::default());
697 let p = Parser::default();
698 SubstitutionGroup::AttributeEntryValue.apply(&mut content, &p, None);
699 assert!(content.is_empty());
700 assert_eq!(content.rendered, CowStr::Borrowed(""));
701 }
702
703 #[test]
704 fn basic_non_empty_span() {
705 let mut content = Content::from(crate::Span::new("blah"));
706 let p = Parser::default();
707 SubstitutionGroup::AttributeEntryValue.apply(&mut content, &p, None);
708 assert!(!content.is_empty());
709 assert_eq!(content.rendered, CowStr::Borrowed("blah"));
710 }
711
712 #[test]
713 fn match_lt_and_gt() {
714 let mut content = Content::from(crate::Span::new("bl<ah>"));
715 let p = Parser::default();
716 SubstitutionGroup::Normal.apply(&mut content, &p, None);
717 assert!(!content.is_empty());
718 assert_eq!(
719 content.rendered,
720 CowStr::Boxed("bl<ah>".to_string().into_boxed_str())
721 );
722 }
723
724 #[test]
725 fn match_amp() {
726 let mut content = Content::from(crate::Span::new("bl<a&h>"));
727 let p = Parser::default();
728 SubstitutionGroup::AttributeEntryValue.apply(&mut content, &p, None);
729 assert!(!content.is_empty());
730 assert_eq!(
731 content.rendered,
732 CowStr::Boxed("bl<a&h>".to_string().into_boxed_str())
733 );
734 }
735
736 #[test]
737 fn ignores_strong_word() {
738 let mut content = Content::from(crate::Span::new("One *word* is strong."));
739 let p = Parser::default();
740 SubstitutionGroup::AttributeEntryValue.apply(&mut content, &p, None);
741 assert!(!content.is_empty());
742 assert_eq!(
743 content.rendered,
744 CowStr::Boxed("One *word* is strong.".to_string().into_boxed_str())
745 );
746 }
747
748 #[test]
749 fn special_chars_and_attributes() {
750 let mut content = Content::from(crate::Span::new("bl<ah> {color}"));
751
752 let p = Parser::default().with_intrinsic_attribute(
753 "color",
754 "red",
755 ModificationContext::Anywhere,
756 );
757
758 SubstitutionGroup::AttributeEntryValue.apply(&mut content, &p, None);
759 assert!(!content.is_empty());
760 assert_eq!(
761 content.rendered,
762 CowStr::Boxed("bl<ah> red".to_string().into_boxed_str())
763 );
764 }
765 }
766
767 mod header {
768 use crate::{content::Content, strings::CowStr, tests::prelude::*};
769
770 #[test]
771 fn empty() {
772 let mut content = Content::from(crate::Span::default());
773 let p = Parser::default();
774 SubstitutionGroup::Header.apply(&mut content, &p, None);
775 assert!(content.is_empty());
776 assert_eq!(content.rendered, CowStr::Borrowed(""));
777 }
778
779 #[test]
780 fn basic_non_empty_span() {
781 let mut content = Content::from(crate::Span::new("blah"));
782 let p = Parser::default();
783 SubstitutionGroup::Header.apply(&mut content, &p, None);
784 assert!(!content.is_empty());
785 assert_eq!(content.rendered, CowStr::Borrowed("blah"));
786 }
787
788 #[test]
789 fn match_lt_and_gt() {
790 let mut content = Content::from(crate::Span::new("bl<ah>"));
791 let p = Parser::default();
792 SubstitutionGroup::Header.apply(&mut content, &p, None);
793 assert!(!content.is_empty());
794 assert_eq!(
795 content.rendered,
796 CowStr::Boxed("bl<ah>".to_string().into_boxed_str())
797 );
798 }
799
800 #[test]
801 fn match_amp() {
802 let mut content = Content::from(crate::Span::new("bl<a&h>"));
803 let p = Parser::default();
804 SubstitutionGroup::Header.apply(&mut content, &p, None);
805 assert!(!content.is_empty());
806 assert_eq!(
807 content.rendered,
808 CowStr::Boxed("bl<a&h>".to_string().into_boxed_str())
809 );
810 }
811
812 #[test]
813 fn ignores_strong_word() {
814 let mut content = Content::from(crate::Span::new("One *word* is strong."));
815 let p = Parser::default();
816 SubstitutionGroup::Header.apply(&mut content, &p, None);
817 assert!(!content.is_empty());
818 assert_eq!(content.rendered, CowStr::Borrowed("One *word* is strong."));
819 }
820
821 #[test]
822 fn ignores_strong_word_with_special_chars() {
823 let mut content = Content::from(crate::Span::new("One *wo<r>d* is strong."));
824 let p = Parser::default();
825 SubstitutionGroup::Header.apply(&mut content, &p, None);
826 assert!(!content.is_empty());
827 assert_eq!(
828 content.rendered,
829 CowStr::Boxed("One *wo<r>d* is strong.".to_string().into_boxed_str())
830 );
831 }
832
833 #[test]
834 fn ignores_marked_string_with_id() {
835 let mut content = Content::from(crate::Span::new(r#"[#id]#a few words#"#));
836 let p = Parser::default();
837 SubstitutionGroup::Header.apply(&mut content, &p, None);
838 assert!(!content.is_empty());
839 assert_eq!(content.rendered, CowStr::Borrowed("[#id]#a few words#"));
840 }
841 }
842
843 mod title {
844 use crate::{content::Content, strings::CowStr, tests::prelude::*};
845
846 #[test]
847 fn empty() {
848 let mut content = Content::from(crate::Span::default());
849 let p = Parser::default();
850 SubstitutionGroup::Title.apply(&mut content, &p, None);
851 assert!(content.is_empty());
852 assert_eq!(content.rendered, CowStr::Borrowed(""));
853 }
854
855 #[test]
856 fn basic_non_empty_span() {
857 let mut content = Content::from(crate::Span::new("blah"));
858 let p = Parser::default();
859 SubstitutionGroup::Title.apply(&mut content, &p, None);
860 assert!(!content.is_empty());
861 assert_eq!(content.rendered, CowStr::Borrowed("blah"));
862 }
863
864 #[test]
865 fn match_lt_and_gt() {
866 let mut content = Content::from(crate::Span::new("bl<ah>"));
867 let p = Parser::default();
868 SubstitutionGroup::Title.apply(&mut content, &p, None);
869 assert!(!content.is_empty());
870 assert_eq!(
871 content.rendered,
872 CowStr::Boxed("bl<ah>".to_string().into_boxed_str())
873 );
874 }
875
876 #[test]
877 fn match_amp() {
878 let mut content = Content::from(crate::Span::new("bl<a&h>"));
879 let p = Parser::default();
880 SubstitutionGroup::Title.apply(&mut content, &p, None);
881 assert!(!content.is_empty());
882 assert_eq!(
883 content.rendered,
884 CowStr::Boxed("bl<a&h>".to_string().into_boxed_str())
885 );
886 }
887
888 #[test]
889 fn strong_word() {
890 let mut content = Content::from(crate::Span::new("One *word* is strong."));
891 let p = Parser::default();
892 SubstitutionGroup::Title.apply(&mut content, &p, None);
893 assert!(!content.is_empty());
894 assert_eq!(
895 content.rendered,
896 CowStr::Boxed(
897 "One <strong>word</strong> is strong."
898 .to_string()
899 .into_boxed_str()
900 )
901 );
902 }
903
904 #[test]
905 fn strong_word_with_special_chars() {
906 let mut content = Content::from(crate::Span::new("One *wo<r>d* is strong."));
907 let p = Parser::default();
908 SubstitutionGroup::Title.apply(&mut content, &p, None);
909 assert!(!content.is_empty());
910 assert_eq!(
911 content.rendered,
912 CowStr::Boxed(
913 "One <strong>wo<r>d</strong> is strong."
914 .to_string()
915 .into_boxed_str()
916 )
917 );
918 }
919
920 #[test]
921 fn marked_string_with_id() {
922 let mut content = Content::from(crate::Span::new(r#"[#id]#a few words#"#));
923 let p = Parser::default();
924 SubstitutionGroup::Title.apply(&mut content, &p, None);
925 assert!(!content.is_empty());
926 assert_eq!(
927 content.rendered,
928 CowStr::Boxed(r#"<span id="id">a few words</span>"#.to_string().into_boxed_str())
929 );
930 }
931
932 #[test]
933 fn title_behaves_same_as_normal() {
934 let test_input = "One *wo<r>d* is strong with [#id]#marked text#.";
935
936 let mut title_content = Content::from(crate::Span::new(test_input));
937 let mut normal_content = Content::from(crate::Span::new(test_input));
938 let p = Parser::default();
939
940 SubstitutionGroup::Title.apply(&mut title_content, &p, None);
941 SubstitutionGroup::Normal.apply(&mut normal_content, &p, None);
942
943 assert_eq!(title_content.rendered, normal_content.rendered);
945 }
946 }
947}