1use crate::{
2 HasSpan, Parser, Span,
3 attributes::{Attrlist, AttrlistContext},
4 blocks::{ChildBlocks, ContentModel, IsBlock, caption, metadata::BlockMetadata},
5 content::{Content, substitute_attributes_in_macro_target},
6 span::MatchedItem,
7 strings::CowStr,
8 warnings::{MatchAndWarnings, Warning, WarningType},
9};
10
11#[derive(Clone, Debug, Eq, Hash, PartialEq)]
13pub struct MediaBlock<'src> {
14 type_: MediaType,
15 target: Span<'src>,
16 resolved_target: CowStr<'src>,
17 macro_attrlist: Attrlist<'src>,
18 source: Span<'src>,
19 title_source: Option<Span<'src>>,
20 title: Option<Content<'src>>,
21 caption: Option<String>,
22 number: Option<usize>,
23 anchor: Option<Span<'src>>,
24 anchor_reftext: Option<Span<'src>>,
25 attrlist: Option<Attrlist<'src>>,
26}
27
28#[derive(Clone, Copy, Debug, Eq, PartialEq)]
31pub(crate) enum TargetResolution {
32 Keep,
34
35 Drop,
38}
39
40#[derive(Clone, Copy, Eq, Hash, PartialEq)]
42pub enum MediaType {
43 Image,
45
46 Video,
48
49 Audio,
51}
52
53impl std::fmt::Debug for MediaType {
54 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
55 match self {
56 MediaType::Image => write!(f, "MediaType::Image"),
57 MediaType::Video => write!(f, "MediaType::Video"),
58 MediaType::Audio => write!(f, "MediaType::Audio"),
59 }
60 }
61}
62
63impl<'src> MediaBlock<'src> {
64 pub fn child_blocks(&'src self) -> ChildBlocks<'src> {
71 ChildBlocks::empty()
72 }
73
74 pub(crate) fn title_content_mut(&mut self) -> Option<&mut Content<'src>> {
82 self.title.as_mut()
83 }
84
85 pub(crate) fn parse(
86 metadata: &BlockMetadata<'src>,
87 parser: &mut Parser,
88 ) -> MatchAndWarnings<'src, Option<MatchedItem<'src, Self>>> {
89 let line = metadata.block_start.take_normalized_line();
90
91 if !line.item.ends_with(']') {
93 return MatchAndWarnings {
94 item: None,
95 warnings: vec![],
96 };
97 }
98
99 let Some(name) = line.item.take_block_macro_name() else {
100 return MatchAndWarnings {
101 item: None,
102 warnings: vec![],
103 };
104 };
105
106 let type_ = match name.item.data() {
107 "image" => MediaType::Image,
108 "video" => MediaType::Video,
109 "audio" => MediaType::Audio,
110 _ => {
111 return MatchAndWarnings {
112 item: None,
113 warnings: vec![],
114 };
115 }
116 };
117
118 let Some(colons) = name.after.take_prefix("::") else {
119 return MatchAndWarnings {
120 item: None,
121 warnings: vec![Warning {
122 source: name.after,
123 warning: WarningType::MacroMissingSeparator,
124 origin: None,
125 }],
126 };
127 };
128
129 let target = colons.after.take_while(|c| c != '[');
131
132 if target.item.is_empty() {
133 return MatchAndWarnings {
134 item: None,
135 warnings: vec![Warning {
136 source: target.after,
137 warning: WarningType::MediaMacroMissingTarget,
138 origin: None,
139 }],
140 };
141 }
142
143 let Some(open_brace) = target.after.take_prefix("[") else {
144 return MatchAndWarnings {
145 item: None,
146 warnings: vec![Warning {
147 source: target.after,
148 warning: WarningType::MacroMissingAttributeList,
149 origin: None,
150 }],
151 };
152 };
153
154 let attrlist = open_brace.after.slice(0..open_brace.after.len() - 1);
155
156 let macro_attrlist = Attrlist::parse(attrlist, parser, AttrlistContext::Inline);
159
160 let source: Span = metadata.source.trim_remainder(line.after);
161 let source = source.slice(0..source.trim().len());
162
163 MatchAndWarnings {
164 item: Some(MatchedItem {
165 item: Self {
166 type_,
167 target: target.item,
168
169 resolved_target: target.item.data().into(),
174 macro_attrlist: macro_attrlist.item.item,
175 source,
176 title_source: metadata.title_source,
177 title: metadata.title.clone(),
178
179 caption: None,
186 number: None,
187 anchor: metadata.anchor,
188 anchor_reftext: metadata.anchor_reftext,
189 attrlist: metadata.attrlist.clone(),
190 },
191
192 after: line.after.discard_empty_lines(),
193 }),
194 warnings: macro_attrlist.warnings,
195 }
196 }
197
198 pub fn type_(&self) -> MediaType {
200 self.type_
201 }
202
203 pub fn target(&'src self) -> Option<&'src Span<'src>> {
209 Some(&self.target)
210 }
211
212 pub fn resolved_target(&self) -> &str {
220 self.resolved_target.as_ref()
221 }
222
223 pub(crate) fn resolve_target(&mut self, parser: &Parser) -> TargetResolution {
235 match substitute_attributes_in_macro_target(self.target, parser) {
236 Some(resolved) => {
237 self.resolved_target = resolved;
238 TargetResolution::Keep
239 }
240 None => TargetResolution::Drop,
241 }
242 }
243
244 pub(crate) fn assign_caption(&mut self, parser: &mut Parser) {
260 if self.type_ != MediaType::Image {
261 return;
262 }
263
264 let explicit_caption = self
265 .macro_attrlist
266 .named_attribute("caption")
267 .or_else(|| {
268 self.attrlist
269 .as_ref()
270 .and_then(|attrlist| attrlist.named_attribute("caption"))
271 })
272 .map(|attr| attr.value().to_string());
273
274 let caption = caption::assign_caption(
275 parser,
276 "figure",
277 self.title.is_some(),
278 explicit_caption.as_deref(),
279 );
280 self.number = caption.as_ref().and_then(|c| c.number);
281 self.caption = caption.map(|c| c.prefix);
282 }
283
284 pub fn macro_attrlist(&'src self) -> &'src Attrlist<'src> {
294 &self.macro_attrlist
295 }
296}
297
298impl<'src> IsBlock<'src> for MediaBlock<'src> {
299 fn content_model(&self) -> ContentModel {
300 ContentModel::Empty
301 }
302
303 fn raw_context(&self) -> CowStr<'src> {
304 match self.type_ {
305 MediaType::Audio => "audio",
306 MediaType::Image => "image",
307 MediaType::Video => "video",
308 }
309 .into()
310 }
311
312 fn title_source(&'src self) -> Option<Span<'src>> {
313 self.title_source
314 }
315
316 fn title(&self) -> Option<&str> {
317 self.title.as_ref().map(Content::rendered_str)
318 }
319
320 fn caption(&self) -> Option<&str> {
321 self.caption.as_deref()
322 }
323
324 fn number(&self) -> Option<usize> {
325 self.number
326 }
327
328 fn id(&'src self) -> Option<&'src str> {
329 self.anchor()
335 .map(|a| a.data())
336 .or_else(|| self.attrlist().and_then(|attrlist| attrlist.id()))
337 .or_else(|| self.macro_attrlist.id())
338 }
339
340 fn anchor(&'src self) -> Option<Span<'src>> {
341 self.anchor
342 }
343
344 fn anchor_reftext(&'src self) -> Option<Span<'src>> {
345 self.anchor_reftext
346 }
347
348 fn attrlist(&'src self) -> Option<&'src Attrlist<'src>> {
349 self.attrlist.as_ref()
350 }
351}
352
353impl<'src> HasSpan<'src> for MediaBlock<'src> {
354 fn span(&self) -> Span<'src> {
355 self.source
356 }
357}
358
359#[cfg(test)]
360mod tests {
361 #![allow(clippy::unwrap_used)]
362
363 use std::ops::Deref;
364
365 use crate::{
366 blocks::{ContentModel, MediaType, metadata::BlockMetadata},
367 tests::prelude::*,
368 };
369
370 #[test]
371 fn impl_clone() {
372 let mut parser = Parser::default();
374
375 let b1 =
376 crate::blocks::MediaBlock::parse(&BlockMetadata::new("image::foo.jpg[]"), &mut parser)
377 .unwrap_if_no_warnings()
378 .unwrap()
379 .item;
380
381 let b2 = b1.clone();
382 assert_eq!(b1, b2);
383 }
384
385 #[test]
386 fn err_empty_source() {
387 let mut parser = Parser::default();
388 assert!(
389 crate::blocks::MediaBlock::parse(&BlockMetadata::new(""), &mut parser)
390 .unwrap_if_no_warnings()
391 .is_none()
392 );
393 }
394
395 #[test]
396 fn err_only_spaces() {
397 let mut parser = Parser::default();
398 assert!(
399 crate::blocks::MediaBlock::parse(&BlockMetadata::new(" "), &mut parser)
400 .unwrap_if_no_warnings()
401 .is_none()
402 );
403 }
404
405 #[test]
406 fn err_macro_name_not_word_char() {
407 let mut parser = Parser::default();
410 let maw = crate::blocks::MediaBlock::parse(
411 &BlockMetadata::new("#xyz::bar[blah,blap]"),
412 &mut parser,
413 );
414
415 assert!(maw.item.is_none());
416 assert!(maw.warnings.is_empty());
417 }
418
419 #[test]
420 fn err_missing_double_colon() {
421 let mut parser = Parser::default();
422 let maw = crate::blocks::MediaBlock::parse(
423 &BlockMetadata::new("image:bar[blah,blap]"),
424 &mut parser,
425 );
426
427 assert!(maw.item.is_none());
428
429 assert_eq!(
430 maw.warnings,
431 vec![Warning {
432 source: Span {
433 data: ":bar[blah,blap]",
434 line: 1,
435 col: 6,
436 offset: 5,
437 },
438 warning: WarningType::MacroMissingSeparator,
439 }]
440 );
441 }
442
443 #[test]
444 fn err_missing_macro_attrlist() {
445 let mut parser = Parser::default();
446 let maw = crate::blocks::MediaBlock::parse(
447 &BlockMetadata::new("image::barblah,blap]"),
448 &mut parser,
449 );
450
451 assert!(maw.item.is_none());
452
453 assert_eq!(
454 maw.warnings,
455 vec![Warning {
456 source: Span {
457 data: "",
458 line: 1,
459 col: 21,
460 offset: 20,
461 },
462 warning: WarningType::MacroMissingAttributeList,
463 }]
464 );
465 }
466
467 #[test]
468 fn err_unknown_type() {
469 let mut parser = Parser::default();
470 assert!(
471 crate::blocks::MediaBlock::parse(&BlockMetadata::new("imagex::bar[]"), &mut parser)
472 .unwrap_if_no_warnings()
473 .is_none()
474 );
475 }
476
477 #[test]
478 fn err_no_attr_list() {
479 let mut parser = Parser::default();
480 assert!(
481 crate::blocks::MediaBlock::parse(&BlockMetadata::new("image::bar"), &mut parser)
482 .unwrap_if_no_warnings()
483 .is_none()
484 );
485 }
486
487 #[test]
488 fn err_attr_list_not_closed() {
489 let mut parser = Parser::default();
490 assert!(
491 crate::blocks::MediaBlock::parse(&BlockMetadata::new("image::bar[blah"), &mut parser)
492 .unwrap_if_no_warnings()
493 .is_none()
494 );
495 }
496
497 #[test]
498 fn err_unexpected_after_attr_list() {
499 let mut parser = Parser::default();
500 assert!(
501 crate::blocks::MediaBlock::parse(
502 &BlockMetadata::new("image::bar[blah]bonus"),
503 &mut parser
504 )
505 .unwrap_if_no_warnings()
506 .is_none()
507 );
508 }
509
510 #[test]
511 fn simplest_block_macro() {
512 let mut parser = Parser::default();
513
514 let mi = crate::blocks::MediaBlock::parse(&BlockMetadata::new("image::[]"), &mut parser);
515 assert!(mi.item.is_none());
516
517 assert_eq!(
518 mi.warnings,
519 vec![Warning {
520 source: Span {
521 data: "[]",
522 line: 1,
523 col: 8,
524 offset: 7,
525 },
526 warning: WarningType::MediaMacroMissingTarget,
527 }]
528 );
529 }
530
531 #[test]
532 fn has_target() {
533 let mut parser = Parser::default();
534
535 let mi = crate::blocks::MediaBlock::parse(&BlockMetadata::new("image::bar[]"), &mut parser)
536 .unwrap_if_no_warnings()
537 .unwrap();
538
539 assert_eq!(
540 mi.item,
541 MediaBlock {
542 type_: MediaType::Image,
543 target: Span {
544 data: "bar",
545 line: 1,
546 col: 8,
547 offset: 7,
548 },
549 macro_attrlist: Attrlist {
550 attributes: &[],
551 anchor: None,
552 source: Span {
553 data: "",
554 line: 1,
555 col: 12,
556 offset: 11,
557 }
558 },
559 source: Span {
560 data: "image::bar[]",
561 line: 1,
562 col: 1,
563 offset: 0,
564 },
565 title_source: None,
566 title: None,
567 caption: None,
568 number: None,
569 anchor: None,
570 anchor_reftext: None,
571 attrlist: None,
572 }
573 );
574
575 assert_eq!(
576 mi.after,
577 Span {
578 data: "",
579 line: 1,
580 col: 13,
581 offset: 12
582 }
583 );
584
585 assert_eq!(mi.item.content_model(), ContentModel::Empty);
586 assert_eq!(mi.item.raw_context().deref(), "image");
587 assert!(mi.item.child_blocks().next().is_none());
588 assert!(mi.item.title_source().is_none());
589 assert!(mi.item.title().is_none());
590 assert!(mi.item.anchor().is_none());
591 assert!(mi.item.anchor_reftext().is_none());
592 assert!(mi.item.attrlist().is_none());
593 assert_eq!(mi.item.substitution_group(), SubstitutionGroup::Normal);
594 }
595
596 #[test]
597 fn has_target_and_attrlist() {
598 let mut parser = Parser::default();
599
600 let mi =
601 crate::blocks::MediaBlock::parse(&BlockMetadata::new("image::bar[blah]"), &mut parser)
602 .unwrap_if_no_warnings()
603 .unwrap();
604
605 assert_eq!(
606 mi.item,
607 MediaBlock {
608 type_: MediaType::Image,
609 target: Span {
610 data: "bar",
611 line: 1,
612 col: 8,
613 offset: 7,
614 },
615 macro_attrlist: Attrlist {
616 attributes: &[ElementAttribute {
617 name: None,
618 shorthand_items: &["blah"],
619 value: "blah"
620 }],
621 anchor: None,
622 source: Span {
623 data: "blah",
624 line: 1,
625 col: 12,
626 offset: 11,
627 }
628 },
629 source: Span {
630 data: "image::bar[blah]",
631 line: 1,
632 col: 1,
633 offset: 0,
634 },
635 title_source: None,
636 title: None,
637 caption: None,
638 number: None,
639 anchor: None,
640 anchor_reftext: None,
641 attrlist: None,
642 }
643 );
644
645 assert_eq!(
646 mi.after,
647 Span {
648 data: "",
649 line: 1,
650 col: 17,
651 offset: 16
652 }
653 );
654 }
655
656 #[test]
657 fn audio() {
658 let mut parser = Parser::default();
659
660 let mi = crate::blocks::MediaBlock::parse(&BlockMetadata::new("audio::bar[]"), &mut parser)
661 .unwrap_if_no_warnings()
662 .unwrap();
663
664 assert_eq!(
665 mi.item,
666 MediaBlock {
667 type_: MediaType::Audio,
668 target: Span {
669 data: "bar",
670 line: 1,
671 col: 8,
672 offset: 7,
673 },
674 macro_attrlist: Attrlist {
675 attributes: &[],
676 anchor: None,
677 source: Span {
678 data: "",
679 line: 1,
680 col: 12,
681 offset: 11,
682 }
683 },
684 source: Span {
685 data: "audio::bar[]",
686 line: 1,
687 col: 1,
688 offset: 0,
689 },
690 title_source: None,
691 title: None,
692 caption: None,
693 number: None,
694 anchor: None,
695 anchor_reftext: None,
696 attrlist: None,
697 }
698 );
699
700 assert_eq!(
701 mi.after,
702 Span {
703 data: "",
704 line: 1,
705 col: 13,
706 offset: 12
707 }
708 );
709
710 assert_eq!(mi.item.content_model(), ContentModel::Empty);
711 assert_eq!(mi.item.raw_context().deref(), "audio");
712 assert!(mi.item.child_blocks().next().is_none());
713 assert!(mi.item.title_source().is_none());
714 assert!(mi.item.title().is_none());
715 assert!(mi.item.anchor().is_none());
716 assert!(mi.item.anchor_reftext().is_none());
717 assert!(mi.item.attrlist().is_none());
718 assert_eq!(mi.item.substitution_group(), SubstitutionGroup::Normal);
719 }
720
721 #[test]
722 fn video() {
723 let mut parser = Parser::default();
724
725 let mi = crate::blocks::MediaBlock::parse(&BlockMetadata::new("video::bar[]"), &mut parser)
726 .unwrap_if_no_warnings()
727 .unwrap();
728
729 assert_eq!(
730 mi.item,
731 MediaBlock {
732 type_: MediaType::Video,
733 target: Span {
734 data: "bar",
735 line: 1,
736 col: 8,
737 offset: 7,
738 },
739 macro_attrlist: Attrlist {
740 attributes: &[],
741 anchor: None,
742 source: Span {
743 data: "",
744 line: 1,
745 col: 12,
746 offset: 11,
747 }
748 },
749 source: Span {
750 data: "video::bar[]",
751 line: 1,
752 col: 1,
753 offset: 0,
754 },
755 title_source: None,
756 title: None,
757 caption: None,
758 number: None,
759 anchor: None,
760 anchor_reftext: None,
761 attrlist: None,
762 }
763 );
764
765 assert_eq!(
766 mi.after,
767 Span {
768 data: "",
769 line: 1,
770 col: 13,
771 offset: 12
772 }
773 );
774
775 assert_eq!(mi.item.content_model(), ContentModel::Empty);
776 assert_eq!(mi.item.raw_context().deref(), "video");
777 assert!(mi.item.child_blocks().next().is_none());
778 assert!(mi.item.title_source().is_none());
779 assert!(mi.item.title().is_none());
780 assert!(mi.item.anchor().is_none());
781 assert!(mi.item.anchor_reftext().is_none());
782 assert!(mi.item.attrlist().is_none());
783 assert_eq!(mi.item.substitution_group(), SubstitutionGroup::Normal);
784 }
785
786 #[test]
787 fn err_duplicate_comma() {
788 let mut parser = Parser::default();
789 let maw = crate::blocks::MediaBlock::parse(
790 &BlockMetadata::new("image::bar[blah,,blap]"),
791 &mut parser,
792 );
793
794 let mi = maw.item.unwrap().clone();
795
796 assert_eq!(
797 mi.item,
798 MediaBlock {
799 type_: MediaType::Image,
800 target: Span {
801 data: "bar",
802 line: 1,
803 col: 8,
804 offset: 7,
805 },
806 macro_attrlist: Attrlist {
807 attributes: &[
808 ElementAttribute {
809 name: None,
810 shorthand_items: &["blah"],
811 value: "blah"
812 },
813 ElementAttribute {
814 name: None,
815 shorthand_items: &[],
816 value: "blap"
817 }
818 ],
819 anchor: None,
820 source: Span {
821 data: "blah,,blap",
822 line: 1,
823 col: 12,
824 offset: 11,
825 }
826 },
827 source: Span {
828 data: "image::bar[blah,,blap]",
829 line: 1,
830 col: 1,
831 offset: 0,
832 },
833 title_source: None,
834 title: None,
835 caption: None,
836 number: None,
837 anchor: None,
838 anchor_reftext: None,
839 attrlist: None,
840 }
841 );
842
843 assert_eq!(
844 mi.after,
845 Span {
846 data: "",
847 line: 1,
848 col: 23,
849 offset: 22
850 }
851 );
852
853 assert_eq!(
854 maw.warnings,
855 vec![Warning {
856 source: Span {
857 data: "blah,,blap",
858 line: 1,
859 col: 12,
860 offset: 11,
861 },
862 warning: WarningType::EmptyAttributeValue,
863 }]
864 );
865 }
866
867 mod target_resolution {
868 #![allow(clippy::indexing_slicing)]
869
870 use crate::{
871 blocks::{MediaBlock, media::TargetResolution, metadata::BlockMetadata},
872 parser::ModificationContext,
873 tests::prelude::*,
874 warnings::WarningType,
875 };
876
877 fn resolve<'i>(input: &'i str, parser: &mut Parser) -> Option<MediaBlock<'i>> {
881 let mut block = MediaBlock::parse(&BlockMetadata::new(input), parser)
882 .unwrap_if_no_warnings()
883 .unwrap()
884 .item;
885
886 match block.resolve_target(parser) {
887 TargetResolution::Keep => Some(block),
888 TargetResolution::Drop => None,
889 }
890 }
891
892 fn parser_with_mode(mode: &str) -> Parser {
893 Parser::default().with_intrinsic_attribute(
894 "attribute-missing",
895 mode,
896 ModificationContext::Anywhere,
897 )
898 }
899
900 #[test]
901 fn target_without_reference_is_unchanged() {
902 let mut p = Parser::default();
904 let block = resolve("image::foo.png[]", &mut p).unwrap();
905 assert_eq!(block.resolved_target(), "foo.png");
906 }
907
908 #[test]
909 fn resolves_a_defined_reference() {
910 let mut p = Parser::default().with_intrinsic_attribute(
911 "name",
912 "bar",
913 ModificationContext::Anywhere,
914 );
915 let block = resolve("image::pre-{name}-post.png[]", &mut p).unwrap();
916 assert_eq!(block.resolved_target(), "pre-bar-post.png");
917 }
918
919 #[test]
920 fn skip_leaves_a_missing_reference_in_place() {
921 let mut p = Parser::default();
923 let block = resolve("image::a{missing}b.png[]", &mut p).unwrap();
924 assert_eq!(block.resolved_target(), "a{missing}b.png");
925 assert!(p.take_substitution_warnings().is_empty());
926 }
927
928 #[test]
929 fn drop_removes_only_the_missing_reference() {
930 let mut p = parser_with_mode("drop");
931 let block = resolve("image::a{missing}b.png[]", &mut p).unwrap();
932 assert_eq!(block.resolved_target(), "ab.png");
933 }
934
935 #[test]
936 fn warn_leaves_the_reference_and_records_a_warning() {
937 let mut p = parser_with_mode("warn");
938 let block = resolve("image::a{missing}b.png[]", &mut p).unwrap();
939 assert_eq!(block.resolved_target(), "a{missing}b.png");
940
941 let warnings = p.take_substitution_warnings();
942 assert_eq!(warnings.len(), 1);
943 assert_eq!(
944 warnings[0].warning,
945 WarningType::SkippingReferenceToMissingAttribute("missing".to_string())
946 );
947 }
948
949 #[test]
950 fn drop_line_drops_the_whole_block() {
951 let mut p = parser_with_mode("drop-line");
952 assert!(resolve("image::a{missing}b.png[]", &mut p).is_none());
953 }
954
955 #[test]
956 fn drop_line_keeps_a_block_whose_reference_resolves() {
957 let mut p = parser_with_mode("drop-line").with_intrinsic_attribute(
958 "name",
959 "bar",
960 ModificationContext::Anywhere,
961 );
962 let block = resolve("image::{name}.png[]", &mut p).unwrap();
963 assert_eq!(block.resolved_target(), "bar.png");
964 }
965
966 #[test]
967 fn drop_line_drops_a_top_level_block_but_keeps_following_blocks() {
968 let doc = Parser::default().parse(
971 ":attribute-missing: drop-line\n\nimage::{unresolved}[]\n\nparagraph after\n",
972 );
973
974 assert_css(&doc, ".imageblock", 0);
975 assert_css(&doc, ".paragraph", 1);
976 }
977
978 #[test]
979 fn drop_line_drops_a_top_level_audio_block() {
980 let doc = Parser::default().parse(
983 ":attribute-missing: drop-line\n\naudio::{unresolved}[]\n\nparagraph after\n",
984 );
985
986 assert_css(&doc, ".audioblock", 0);
987 assert_css(&doc, ".paragraph", 1);
988 }
989
990 #[test]
991 fn escaped_missing_reference_never_drops_the_block() {
992 let mut p = parser_with_mode("drop-line");
997 let block = resolve("image::a\\{missing}b.png[]", &mut p).unwrap();
998 assert_eq!(block.resolved_target(), "a{missing}b.png");
999 assert!(p.take_substitution_warnings().is_empty());
1000 }
1001 }
1002
1003 mod media_type {
1004 mod impl_debug {
1005 use crate::blocks::MediaType;
1006
1007 #[test]
1008 fn image() {
1009 let media_type = MediaType::Image;
1010 let debug_output = format!("{:?}", media_type);
1011 assert_eq!(debug_output, "MediaType::Image");
1012 }
1013
1014 #[test]
1015 fn video() {
1016 let media_type = MediaType::Video;
1017 let debug_output = format!("{:?}", media_type);
1018 assert_eq!(debug_output, "MediaType::Video");
1019 }
1020
1021 #[test]
1022 fn audio() {
1023 let media_type = MediaType::Audio;
1024 let debug_output = format!("{:?}", media_type);
1025 assert_eq!(debug_output, "MediaType::Audio");
1026 }
1027 }
1028 }
1029}