asciidoc_parser/blocks/
media.rs1use crate::{
2 HasSpan, Parser, Span,
3 attributes::{Attrlist, AttrlistContext},
4 blocks::{ContentModel, IsBlock, metadata::BlockMetadata},
5 span::MatchedItem,
6 strings::CowStr,
7 warnings::{MatchAndWarnings, Warning, WarningType},
8};
9
10#[derive(Clone, Debug, Eq, PartialEq)]
12pub struct MediaBlock<'src> {
13 type_: MediaType,
14 target: Span<'src>,
15 macro_attrlist: Attrlist<'src>,
16 source: Span<'src>,
17 title_source: Option<Span<'src>>,
18 title: Option<String>,
19 anchor: Option<Span<'src>>,
20 attrlist: Option<Attrlist<'src>>,
21}
22
23#[derive(Clone, Copy, Eq, PartialEq)]
25pub enum MediaType {
26 Image,
28
29 Video,
31
32 Audio,
34}
35
36impl std::fmt::Debug for MediaType {
37 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
38 match self {
39 MediaType::Image => write!(f, "MediaType::Image"),
40 MediaType::Video => write!(f, "MediaType::Video"),
41 MediaType::Audio => write!(f, "MediaType::Audio"),
42 }
43 }
44}
45
46impl<'src> MediaBlock<'src> {
47 pub(crate) fn parse(
48 metadata: &BlockMetadata<'src>,
49 parser: &mut Parser,
50 ) -> MatchAndWarnings<'src, Option<MatchedItem<'src, Self>>> {
51 let line = metadata.block_start.take_normalized_line();
52
53 if !line.item.ends_with(']') {
55 return MatchAndWarnings {
56 item: None,
57 warnings: vec![],
58 };
59 }
60
61 let Some(name) = line.item.take_ident() else {
62 return MatchAndWarnings {
63 item: None,
64 warnings: vec![],
65 };
66 };
67
68 let type_ = match name.item.data() {
69 "image" => MediaType::Image,
70 "video" => MediaType::Video,
71 "audio" => MediaType::Audio,
72 _ => {
73 return MatchAndWarnings {
74 item: None,
75 warnings: vec![],
76 };
77 }
78 };
79
80 let Some(colons) = name.after.take_prefix("::") else {
81 return MatchAndWarnings {
82 item: None,
83 warnings: vec![Warning {
84 source: name.after,
85 warning: WarningType::MacroMissingDoubleColon,
86 }],
87 };
88 };
89
90 let target = colons.after.take_while(|c| c != '[');
92
93 if target.item.is_empty() {
94 return MatchAndWarnings {
95 item: None,
96 warnings: vec![Warning {
97 source: target.after,
98 warning: WarningType::MediaMacroMissingTarget,
99 }],
100 };
101 }
102
103 let Some(open_brace) = target.after.take_prefix("[") else {
104 return MatchAndWarnings {
105 item: None,
106 warnings: vec![Warning {
107 source: target.after,
108 warning: WarningType::MacroMissingAttributeList,
109 }],
110 };
111 };
112
113 let attrlist = open_brace.after.slice(0..open_brace.after.len() - 1);
114 let macro_attrlist = Attrlist::parse(attrlist, parser, AttrlistContext::Inline);
117
118 let source: Span = metadata.source.trim_remainder(line.after);
119 let source = source.slice(0..source.trim().len());
120
121 MatchAndWarnings {
122 item: Some(MatchedItem {
123 item: Self {
124 type_,
125 target: target.item,
126 macro_attrlist: macro_attrlist.item.item,
127 source,
128 title_source: metadata.title_source,
129 title: metadata.title.clone(),
130 anchor: metadata.anchor,
131 attrlist: metadata.attrlist.clone(),
132 },
133
134 after: line.after.discard_empty_lines(),
135 }),
136 warnings: macro_attrlist.warnings,
137 }
138 }
139
140 pub fn type_(&self) -> MediaType {
142 self.type_
143 }
144
145 pub fn target(&'src self) -> Option<&'src Span<'src>> {
147 Some(&self.target)
148 }
149
150 pub fn macro_attrlist(&'src self) -> &'src Attrlist<'src> {
160 &self.macro_attrlist
161 }
162}
163
164impl<'src> IsBlock<'src> for MediaBlock<'src> {
165 fn content_model(&self) -> ContentModel {
166 ContentModel::Empty
167 }
168
169 fn raw_context(&self) -> CowStr<'src> {
170 match self.type_ {
171 MediaType::Audio => "audio",
172 MediaType::Image => "image",
173 MediaType::Video => "video",
174 }
175 .into()
176 }
177
178 fn title_source(&'src self) -> Option<Span<'src>> {
179 self.title_source
180 }
181
182 fn title(&self) -> Option<&str> {
183 self.title.as_deref()
184 }
185
186 fn anchor(&'src self) -> Option<Span<'src>> {
187 self.anchor
188 }
189
190 fn attrlist(&'src self) -> Option<&'src Attrlist<'src>> {
191 self.attrlist.as_ref()
192 }
193}
194
195impl<'src> HasSpan<'src> for MediaBlock<'src> {
196 fn span(&self) -> Span<'src> {
197 self.source
198 }
199}