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, 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, 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_ident() 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_ident() {
407 let mut parser = Parser::default();
408 let maw = crate::blocks::MediaBlock::parse(
409 &BlockMetadata::new("98xyz::bar[blah,blap]"),
410 &mut parser,
411 );
412
413 assert!(maw.item.is_none());
414 assert!(maw.warnings.is_empty());
415 }
416
417 #[test]
418 fn err_missing_double_colon() {
419 let mut parser = Parser::default();
420 let maw = crate::blocks::MediaBlock::parse(
421 &BlockMetadata::new("image:bar[blah,blap]"),
422 &mut parser,
423 );
424
425 assert!(maw.item.is_none());
426
427 assert_eq!(
428 maw.warnings,
429 vec![Warning {
430 source: Span {
431 data: ":bar[blah,blap]",
432 line: 1,
433 col: 6,
434 offset: 5,
435 },
436 warning: WarningType::MacroMissingSeparator,
437 }]
438 );
439 }
440
441 #[test]
442 fn err_missing_macro_attrlist() {
443 let mut parser = Parser::default();
444 let maw = crate::blocks::MediaBlock::parse(
445 &BlockMetadata::new("image::barblah,blap]"),
446 &mut parser,
447 );
448
449 assert!(maw.item.is_none());
450
451 assert_eq!(
452 maw.warnings,
453 vec![Warning {
454 source: Span {
455 data: "",
456 line: 1,
457 col: 21,
458 offset: 20,
459 },
460 warning: WarningType::MacroMissingAttributeList,
461 }]
462 );
463 }
464
465 #[test]
466 fn err_unknown_type() {
467 let mut parser = Parser::default();
468 assert!(
469 crate::blocks::MediaBlock::parse(&BlockMetadata::new("imagex::bar[]"), &mut parser)
470 .unwrap_if_no_warnings()
471 .is_none()
472 );
473 }
474
475 #[test]
476 fn err_no_attr_list() {
477 let mut parser = Parser::default();
478 assert!(
479 crate::blocks::MediaBlock::parse(&BlockMetadata::new("image::bar"), &mut parser)
480 .unwrap_if_no_warnings()
481 .is_none()
482 );
483 }
484
485 #[test]
486 fn err_attr_list_not_closed() {
487 let mut parser = Parser::default();
488 assert!(
489 crate::blocks::MediaBlock::parse(&BlockMetadata::new("image::bar[blah"), &mut parser)
490 .unwrap_if_no_warnings()
491 .is_none()
492 );
493 }
494
495 #[test]
496 fn err_unexpected_after_attr_list() {
497 let mut parser = Parser::default();
498 assert!(
499 crate::blocks::MediaBlock::parse(
500 &BlockMetadata::new("image::bar[blah]bonus"),
501 &mut parser
502 )
503 .unwrap_if_no_warnings()
504 .is_none()
505 );
506 }
507
508 #[test]
509 fn simplest_block_macro() {
510 let mut parser = Parser::default();
511
512 let mi = crate::blocks::MediaBlock::parse(&BlockMetadata::new("image::[]"), &mut parser);
513 assert!(mi.item.is_none());
514
515 assert_eq!(
516 mi.warnings,
517 vec![Warning {
518 source: Span {
519 data: "[]",
520 line: 1,
521 col: 8,
522 offset: 7,
523 },
524 warning: WarningType::MediaMacroMissingTarget,
525 }]
526 );
527 }
528
529 #[test]
530 fn has_target() {
531 let mut parser = Parser::default();
532
533 let mi = crate::blocks::MediaBlock::parse(&BlockMetadata::new("image::bar[]"), &mut parser)
534 .unwrap_if_no_warnings()
535 .unwrap();
536
537 assert_eq!(
538 mi.item,
539 MediaBlock {
540 type_: MediaType::Image,
541 target: Span {
542 data: "bar",
543 line: 1,
544 col: 8,
545 offset: 7,
546 },
547 macro_attrlist: Attrlist {
548 attributes: &[],
549 anchor: None,
550 source: Span {
551 data: "",
552 line: 1,
553 col: 12,
554 offset: 11,
555 }
556 },
557 source: Span {
558 data: "image::bar[]",
559 line: 1,
560 col: 1,
561 offset: 0,
562 },
563 title_source: None,
564 title: None,
565 caption: None,
566 number: None,
567 anchor: None,
568 anchor_reftext: None,
569 attrlist: None,
570 }
571 );
572
573 assert_eq!(
574 mi.after,
575 Span {
576 data: "",
577 line: 1,
578 col: 13,
579 offset: 12
580 }
581 );
582
583 assert_eq!(mi.item.content_model(), ContentModel::Empty);
584 assert_eq!(mi.item.raw_context().deref(), "image");
585 assert!(mi.item.child_blocks().next().is_none());
586 assert!(mi.item.title_source().is_none());
587 assert!(mi.item.title().is_none());
588 assert!(mi.item.anchor().is_none());
589 assert!(mi.item.anchor_reftext().is_none());
590 assert!(mi.item.attrlist().is_none());
591 assert_eq!(mi.item.substitution_group(), SubstitutionGroup::Normal);
592 }
593
594 #[test]
595 fn has_target_and_attrlist() {
596 let mut parser = Parser::default();
597
598 let mi =
599 crate::blocks::MediaBlock::parse(&BlockMetadata::new("image::bar[blah]"), &mut parser)
600 .unwrap_if_no_warnings()
601 .unwrap();
602
603 assert_eq!(
604 mi.item,
605 MediaBlock {
606 type_: MediaType::Image,
607 target: Span {
608 data: "bar",
609 line: 1,
610 col: 8,
611 offset: 7,
612 },
613 macro_attrlist: Attrlist {
614 attributes: &[ElementAttribute {
615 name: None,
616 shorthand_items: &["blah"],
617 value: "blah"
618 }],
619 anchor: None,
620 source: Span {
621 data: "blah",
622 line: 1,
623 col: 12,
624 offset: 11,
625 }
626 },
627 source: Span {
628 data: "image::bar[blah]",
629 line: 1,
630 col: 1,
631 offset: 0,
632 },
633 title_source: None,
634 title: None,
635 caption: None,
636 number: None,
637 anchor: None,
638 anchor_reftext: None,
639 attrlist: None,
640 }
641 );
642
643 assert_eq!(
644 mi.after,
645 Span {
646 data: "",
647 line: 1,
648 col: 17,
649 offset: 16
650 }
651 );
652 }
653
654 #[test]
655 fn audio() {
656 let mut parser = Parser::default();
657
658 let mi = crate::blocks::MediaBlock::parse(&BlockMetadata::new("audio::bar[]"), &mut parser)
659 .unwrap_if_no_warnings()
660 .unwrap();
661
662 assert_eq!(
663 mi.item,
664 MediaBlock {
665 type_: MediaType::Audio,
666 target: Span {
667 data: "bar",
668 line: 1,
669 col: 8,
670 offset: 7,
671 },
672 macro_attrlist: Attrlist {
673 attributes: &[],
674 anchor: None,
675 source: Span {
676 data: "",
677 line: 1,
678 col: 12,
679 offset: 11,
680 }
681 },
682 source: Span {
683 data: "audio::bar[]",
684 line: 1,
685 col: 1,
686 offset: 0,
687 },
688 title_source: None,
689 title: None,
690 caption: None,
691 number: None,
692 anchor: None,
693 anchor_reftext: None,
694 attrlist: None,
695 }
696 );
697
698 assert_eq!(
699 mi.after,
700 Span {
701 data: "",
702 line: 1,
703 col: 13,
704 offset: 12
705 }
706 );
707
708 assert_eq!(mi.item.content_model(), ContentModel::Empty);
709 assert_eq!(mi.item.raw_context().deref(), "audio");
710 assert!(mi.item.child_blocks().next().is_none());
711 assert!(mi.item.title_source().is_none());
712 assert!(mi.item.title().is_none());
713 assert!(mi.item.anchor().is_none());
714 assert!(mi.item.anchor_reftext().is_none());
715 assert!(mi.item.attrlist().is_none());
716 assert_eq!(mi.item.substitution_group(), SubstitutionGroup::Normal);
717 }
718
719 #[test]
720 fn video() {
721 let mut parser = Parser::default();
722
723 let mi = crate::blocks::MediaBlock::parse(&BlockMetadata::new("video::bar[]"), &mut parser)
724 .unwrap_if_no_warnings()
725 .unwrap();
726
727 assert_eq!(
728 mi.item,
729 MediaBlock {
730 type_: MediaType::Video,
731 target: Span {
732 data: "bar",
733 line: 1,
734 col: 8,
735 offset: 7,
736 },
737 macro_attrlist: Attrlist {
738 attributes: &[],
739 anchor: None,
740 source: Span {
741 data: "",
742 line: 1,
743 col: 12,
744 offset: 11,
745 }
746 },
747 source: Span {
748 data: "video::bar[]",
749 line: 1,
750 col: 1,
751 offset: 0,
752 },
753 title_source: None,
754 title: None,
755 caption: None,
756 number: None,
757 anchor: None,
758 anchor_reftext: None,
759 attrlist: None,
760 }
761 );
762
763 assert_eq!(
764 mi.after,
765 Span {
766 data: "",
767 line: 1,
768 col: 13,
769 offset: 12
770 }
771 );
772
773 assert_eq!(mi.item.content_model(), ContentModel::Empty);
774 assert_eq!(mi.item.raw_context().deref(), "video");
775 assert!(mi.item.child_blocks().next().is_none());
776 assert!(mi.item.title_source().is_none());
777 assert!(mi.item.title().is_none());
778 assert!(mi.item.anchor().is_none());
779 assert!(mi.item.anchor_reftext().is_none());
780 assert!(mi.item.attrlist().is_none());
781 assert_eq!(mi.item.substitution_group(), SubstitutionGroup::Normal);
782 }
783
784 #[test]
785 fn err_duplicate_comma() {
786 let mut parser = Parser::default();
787 let maw = crate::blocks::MediaBlock::parse(
788 &BlockMetadata::new("image::bar[blah,,blap]"),
789 &mut parser,
790 );
791
792 let mi = maw.item.unwrap().clone();
793
794 assert_eq!(
795 mi.item,
796 MediaBlock {
797 type_: MediaType::Image,
798 target: Span {
799 data: "bar",
800 line: 1,
801 col: 8,
802 offset: 7,
803 },
804 macro_attrlist: Attrlist {
805 attributes: &[
806 ElementAttribute {
807 name: None,
808 shorthand_items: &["blah"],
809 value: "blah"
810 },
811 ElementAttribute {
812 name: None,
813 shorthand_items: &[],
814 value: "blap"
815 }
816 ],
817 anchor: None,
818 source: Span {
819 data: "blah,,blap",
820 line: 1,
821 col: 12,
822 offset: 11,
823 }
824 },
825 source: Span {
826 data: "image::bar[blah,,blap]",
827 line: 1,
828 col: 1,
829 offset: 0,
830 },
831 title_source: None,
832 title: None,
833 caption: None,
834 number: None,
835 anchor: None,
836 anchor_reftext: None,
837 attrlist: None,
838 }
839 );
840
841 assert_eq!(
842 mi.after,
843 Span {
844 data: "",
845 line: 1,
846 col: 23,
847 offset: 22
848 }
849 );
850
851 assert_eq!(
852 maw.warnings,
853 vec![Warning {
854 source: Span {
855 data: "blah,,blap",
856 line: 1,
857 col: 12,
858 offset: 11,
859 },
860 warning: WarningType::EmptyAttributeValue,
861 }]
862 );
863 }
864
865 mod target_resolution {
866 #![allow(clippy::indexing_slicing)]
867
868 use crate::{
869 blocks::{MediaBlock, media::TargetResolution, metadata::BlockMetadata},
870 parser::ModificationContext,
871 tests::prelude::*,
872 warnings::WarningType,
873 };
874
875 fn resolve<'i>(input: &'i str, parser: &mut Parser) -> Option<MediaBlock<'i>> {
879 let mut block = MediaBlock::parse(&BlockMetadata::new(input), parser)
880 .unwrap_if_no_warnings()
881 .unwrap()
882 .item;
883
884 match block.resolve_target(parser) {
885 TargetResolution::Keep => Some(block),
886 TargetResolution::Drop => None,
887 }
888 }
889
890 fn parser_with_mode(mode: &str) -> Parser {
891 Parser::default().with_intrinsic_attribute(
892 "attribute-missing",
893 mode,
894 ModificationContext::Anywhere,
895 )
896 }
897
898 #[test]
899 fn target_without_reference_is_unchanged() {
900 let mut p = Parser::default();
902 let block = resolve("image::foo.png[]", &mut p).unwrap();
903 assert_eq!(block.resolved_target(), "foo.png");
904 }
905
906 #[test]
907 fn resolves_a_defined_reference() {
908 let mut p = Parser::default().with_intrinsic_attribute(
909 "name",
910 "bar",
911 ModificationContext::Anywhere,
912 );
913 let block = resolve("image::pre-{name}-post.png[]", &mut p).unwrap();
914 assert_eq!(block.resolved_target(), "pre-bar-post.png");
915 }
916
917 #[test]
918 fn skip_leaves_a_missing_reference_in_place() {
919 let mut p = Parser::default();
921 let block = resolve("image::a{missing}b.png[]", &mut p).unwrap();
922 assert_eq!(block.resolved_target(), "a{missing}b.png");
923 assert!(p.take_substitution_warnings().is_empty());
924 }
925
926 #[test]
927 fn drop_removes_only_the_missing_reference() {
928 let mut p = parser_with_mode("drop");
929 let block = resolve("image::a{missing}b.png[]", &mut p).unwrap();
930 assert_eq!(block.resolved_target(), "ab.png");
931 }
932
933 #[test]
934 fn warn_leaves_the_reference_and_records_a_warning() {
935 let mut p = parser_with_mode("warn");
936 let block = resolve("image::a{missing}b.png[]", &mut p).unwrap();
937 assert_eq!(block.resolved_target(), "a{missing}b.png");
938
939 let warnings = p.take_substitution_warnings();
940 assert_eq!(warnings.len(), 1);
941 assert_eq!(
942 warnings[0].warning,
943 WarningType::SkippingReferenceToMissingAttribute("missing".to_string())
944 );
945 }
946
947 #[test]
948 fn drop_line_drops_the_whole_block() {
949 let mut p = parser_with_mode("drop-line");
950 assert!(resolve("image::a{missing}b.png[]", &mut p).is_none());
951 }
952
953 #[test]
954 fn drop_line_keeps_a_block_whose_reference_resolves() {
955 let mut p = parser_with_mode("drop-line").with_intrinsic_attribute(
956 "name",
957 "bar",
958 ModificationContext::Anywhere,
959 );
960 let block = resolve("image::{name}.png[]", &mut p).unwrap();
961 assert_eq!(block.resolved_target(), "bar.png");
962 }
963
964 #[test]
965 fn drop_line_drops_a_top_level_block_but_keeps_following_blocks() {
966 let doc = Parser::default().parse(
969 ":attribute-missing: drop-line\n\nimage::{unresolved}[]\n\nparagraph after\n",
970 );
971
972 assert_css(&doc, ".imageblock", 0);
973 assert_css(&doc, ".paragraph", 1);
974 }
975
976 #[test]
977 fn drop_line_drops_a_top_level_audio_block() {
978 let doc = Parser::default().parse(
981 ":attribute-missing: drop-line\n\naudio::{unresolved}[]\n\nparagraph after\n",
982 );
983
984 assert_css(&doc, ".audioblock", 0);
985 assert_css(&doc, ".paragraph", 1);
986 }
987
988 #[test]
989 fn escaped_missing_reference_never_drops_the_block() {
990 let mut p = parser_with_mode("drop-line");
995 let block = resolve("image::a\\{missing}b.png[]", &mut p).unwrap();
996 assert_eq!(block.resolved_target(), "a{missing}b.png");
997 assert!(p.take_substitution_warnings().is_empty());
998 }
999 }
1000
1001 mod media_type {
1002 mod impl_debug {
1003 use crate::blocks::MediaType;
1004
1005 #[test]
1006 fn image() {
1007 let media_type = MediaType::Image;
1008 let debug_output = format!("{:?}", media_type);
1009 assert_eq!(debug_output, "MediaType::Image");
1010 }
1011
1012 #[test]
1013 fn video() {
1014 let media_type = MediaType::Video;
1015 let debug_output = format!("{:?}", media_type);
1016 assert_eq!(debug_output, "MediaType::Video");
1017 }
1018
1019 #[test]
1020 fn audio() {
1021 let media_type = MediaType::Audio;
1022 let debug_output = format!("{:?}", media_type);
1023 assert_eq!(debug_output, "MediaType::Audio");
1024 }
1025 }
1026 }
1027}