1use std::fmt::Display;
4use std::str::FromStr;
5
6use serde::{
7 Deserialize, Serialize,
8 de::{self, Deserializer, MapAccess, Visitor},
9 ser::{SerializeMap, Serializer},
10};
11
12use crate::{Error, Positioning, SourceLocation};
13
14use super::inlines::InlineNode;
15use super::location::{Location, Position};
16use super::metadata::BlockMetadata;
17use super::title::Title;
18
19#[derive(Clone, Debug, PartialEq)]
23pub enum Source {
24 Path(std::path::PathBuf),
26 Url(url::Url),
28 Name(String),
30}
31
32impl Source {
33 #[must_use]
38 pub fn get_filename(&self) -> Option<&str> {
39 match self {
40 Source::Path(path) => path.file_name().and_then(|os_str| os_str.to_str()),
41 Source::Url(url) => url
42 .path_segments()
43 .and_then(std::iter::Iterator::last)
44 .filter(|s| !s.is_empty()),
45 Source::Name(name) => Some(name.as_str()),
46 }
47 }
48}
49
50impl FromStr for Source {
51 type Err = Error;
52
53 fn from_str(value: &str) -> Result<Self, Self::Err> {
54 if value.starts_with("http://")
56 || value.starts_with("https://")
57 || value.starts_with("ftp://")
58 || value.starts_with("irc://")
59 || value.starts_with("mailto:")
60 {
61 url::Url::parse(value).map(Source::Url).map_err(|e| {
62 Error::Parse(
63 Box::new(SourceLocation {
64 file: None,
65 positioning: Positioning::Position(Position::default()),
66 }),
67 format!("invalid URL: {e}"),
68 )
69 })
70 } else if value.contains('/') || value.contains('\\') || value.contains('.') {
71 Ok(Source::Path(std::path::PathBuf::from(value)))
74 } else {
75 Ok(Source::Name(value.to_string()))
77 }
78 }
79}
80
81impl Display for Source {
82 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
83 match self {
84 Source::Path(path) => write!(f, "{}", path.display()),
85 Source::Url(url) => {
86 let url_str = url.as_str();
90 if url.path() == "/" && !url_str.ends_with("://") {
91 write!(f, "{}", url_str.trim_end_matches('/'))
92 } else {
93 write!(f, "{url}")
94 }
95 }
96 Source::Name(name) => write!(f, "{name}"),
97 }
98 }
99}
100
101impl Serialize for Source {
102 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
103 where
104 S: Serializer,
105 {
106 let mut state = serializer.serialize_map(None)?;
107 match self {
108 Source::Path(path) => {
109 state.serialize_entry("type", "path")?;
110 state.serialize_entry("value", &path.display().to_string())?;
111 }
112 Source::Url(url) => {
113 state.serialize_entry("type", "url")?;
114 state.serialize_entry("value", url.as_str())?;
115 }
116 Source::Name(name) => {
117 state.serialize_entry("type", "name")?;
118 state.serialize_entry("value", name)?;
119 }
120 }
121 state.end()
122 }
123}
124
125impl<'de> Deserialize<'de> for Source {
126 fn deserialize<D>(deserializer: D) -> Result<Source, D::Error>
127 where
128 D: Deserializer<'de>,
129 {
130 struct SourceVisitor;
131
132 impl<'de> Visitor<'de> for SourceVisitor {
133 type Value = Source;
134
135 fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
136 formatter.write_str("a Source object with type and value fields")
137 }
138
139 fn visit_map<V>(self, mut map: V) -> Result<Source, V::Error>
140 where
141 V: MapAccess<'de>,
142 {
143 let mut source_type: Option<String> = None;
144 let mut value: Option<String> = None;
145
146 while let Some(key) = map.next_key::<String>()? {
147 match key.as_str() {
148 "type" => {
149 if source_type.is_some() {
150 return Err(de::Error::duplicate_field("type"));
151 }
152 source_type = Some(map.next_value()?);
153 }
154 "value" => {
155 if value.is_some() {
156 return Err(de::Error::duplicate_field("value"));
157 }
158 value = Some(map.next_value()?);
159 }
160 _ => {
161 let _ = map.next_value::<de::IgnoredAny>()?;
162 }
163 }
164 }
165
166 let source_type = source_type.ok_or_else(|| de::Error::missing_field("type"))?;
167 let value = value.ok_or_else(|| de::Error::missing_field("value"))?;
168
169 match source_type.as_str() {
170 "path" => Ok(Source::Path(std::path::PathBuf::from(value))),
171 "url" => url::Url::parse(&value)
172 .map(Source::Url)
173 .map_err(|e| de::Error::custom(format!("invalid URL: {e}"))),
174 "name" => Ok(Source::Name(value)),
175 _ => Err(de::Error::custom(format!(
176 "unexpected source type: {source_type}"
177 ))),
178 }
179 }
180 }
181
182 deserializer.deserialize_map(SourceVisitor)
183 }
184}
185
186#[derive(Clone, Debug, PartialEq)]
188#[non_exhaustive]
189pub struct Audio {
190 pub title: Title,
191 pub source: Source,
192 pub metadata: BlockMetadata,
193 pub location: Location,
194}
195
196impl Audio {
197 #[must_use]
199 pub fn new(source: Source, location: Location) -> Self {
200 Self {
201 title: Title::default(),
202 source,
203 metadata: BlockMetadata::default(),
204 location,
205 }
206 }
207
208 #[must_use]
210 pub fn with_title(mut self, title: Title) -> Self {
211 self.title = title;
212 self
213 }
214
215 #[must_use]
217 pub fn with_metadata(mut self, metadata: BlockMetadata) -> Self {
218 self.metadata = metadata;
219 self
220 }
221}
222
223#[derive(Clone, Debug, PartialEq)]
225#[non_exhaustive]
226pub struct Video {
227 pub title: Title,
228 pub sources: Vec<Source>,
229 pub metadata: BlockMetadata,
230 pub location: Location,
231}
232
233impl Video {
234 #[must_use]
236 pub fn new(sources: Vec<Source>, location: Location) -> Self {
237 Self {
238 title: Title::default(),
239 sources,
240 metadata: BlockMetadata::default(),
241 location,
242 }
243 }
244
245 #[must_use]
247 pub fn with_title(mut self, title: Title) -> Self {
248 self.title = title;
249 self
250 }
251
252 #[must_use]
254 pub fn with_metadata(mut self, metadata: BlockMetadata) -> Self {
255 self.metadata = metadata;
256 self
257 }
258}
259
260#[derive(Clone, Debug, PartialEq)]
262#[non_exhaustive]
263pub struct Image {
264 pub title: Title,
265 pub source: Source,
266 pub metadata: BlockMetadata,
267 pub location: Location,
268}
269
270impl Image {
271 #[must_use]
273 pub fn new(source: Source, location: Location) -> Self {
274 Self {
275 title: Title::default(),
276 source,
277 metadata: BlockMetadata::default(),
278 location,
279 }
280 }
281
282 #[must_use]
284 pub fn with_title(mut self, title: Title) -> Self {
285 self.title = title;
286 self
287 }
288
289 #[must_use]
291 pub fn with_metadata(mut self, metadata: BlockMetadata) -> Self {
292 self.metadata = metadata;
293 self
294 }
295}
296
297impl Serialize for Audio {
298 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
299 where
300 S: Serializer,
301 {
302 let mut state = serializer.serialize_map(None)?;
303 state.serialize_entry("name", "audio")?;
304 state.serialize_entry("type", "block")?;
305 state.serialize_entry("form", "macro")?;
306 if !self.metadata.is_default() {
307 state.serialize_entry("metadata", &self.metadata)?;
308 }
309 if !self.title.is_empty() {
310 state.serialize_entry("title", &self.title)?;
311 }
312 state.serialize_entry("source", &self.source)?;
313 state.serialize_entry("location", &self.location)?;
314 state.end()
315 }
316}
317
318impl Serialize for Image {
319 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
320 where
321 S: Serializer,
322 {
323 let mut state = serializer.serialize_map(None)?;
324 state.serialize_entry("name", "image")?;
325 state.serialize_entry("type", "block")?;
326 state.serialize_entry("form", "macro")?;
327 if !self.metadata.is_default() {
328 state.serialize_entry("metadata", &self.metadata)?;
329 }
330 if !self.title.is_empty() {
331 state.serialize_entry("title", &self.title)?;
332 }
333 state.serialize_entry("source", &self.source)?;
334 state.serialize_entry("location", &self.location)?;
335 state.end()
336 }
337}
338
339impl Serialize for Video {
340 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
341 where
342 S: Serializer,
343 {
344 let mut state = serializer.serialize_map(None)?;
345 state.serialize_entry("name", "video")?;
346 state.serialize_entry("type", "block")?;
347 state.serialize_entry("form", "macro")?;
348 if !self.metadata.is_default() {
349 state.serialize_entry("metadata", &self.metadata)?;
350 }
351 if !self.title.is_empty() {
352 state.serialize_entry("title", &self.title)?;
353 }
354 if !self.sources.is_empty() {
355 state.serialize_entry("sources", &self.sources)?;
356 }
357 state.serialize_entry("location", &self.location)?;
358 state.end()
359 }
360}
361
362impl<'de> Deserialize<'de> for Audio {
363 fn deserialize<D>(deserializer: D) -> Result<Audio, D::Error>
364 where
365 D: Deserializer<'de>,
366 {
367 #[derive(Deserialize)]
368 #[serde(field_identifier, rename_all = "lowercase")]
369 enum Field {
370 Metadata,
371 Title,
372 Source,
373 Location,
374 }
375
376 struct AudioVisitor;
377
378 impl<'de> Visitor<'de> for AudioVisitor {
379 type Value = Audio;
380
381 fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
382 formatter.write_str("struct Audio")
383 }
384
385 fn visit_map<V>(self, mut map: V) -> Result<Audio, V::Error>
386 where
387 V: MapAccess<'de>,
388 {
389 let mut metadata = None;
390 let mut title: Option<Vec<InlineNode>> = None;
391 let mut source = None;
392 let mut location = None;
393
394 while let Some(key) = map.next_key()? {
395 match key {
396 Field::Metadata => {
397 metadata = Some(map.next_value()?);
398 }
399 Field::Title => {
400 title = Some(map.next_value()?);
401 }
402 Field::Source => {
403 source = Some(map.next_value()?);
404 }
405 Field::Location => {
406 location = Some(map.next_value()?);
407 }
408 }
409 }
410
411 Ok(Audio {
412 title: title.unwrap_or_default().into(),
413 source: source.ok_or_else(|| serde::de::Error::missing_field("source"))?,
414 metadata: metadata.unwrap_or_default(),
415 location: location
416 .ok_or_else(|| serde::de::Error::missing_field("location"))?,
417 })
418 }
419 }
420
421 deserializer.deserialize_struct(
422 "Audio",
423 &["metadata", "title", "source", "location"],
424 AudioVisitor,
425 )
426 }
427}
428
429impl<'de> Deserialize<'de> for Image {
430 fn deserialize<D>(deserializer: D) -> Result<Image, D::Error>
431 where
432 D: Deserializer<'de>,
433 {
434 #[derive(Deserialize)]
435 #[serde(field_identifier, rename_all = "lowercase")]
436 enum Field {
437 Metadata,
438 Title,
439 Source,
440 Location,
441 }
442
443 struct ImageVisitor;
444
445 impl<'de> Visitor<'de> for ImageVisitor {
446 type Value = Image;
447
448 fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
449 formatter.write_str("struct Image")
450 }
451
452 fn visit_map<V>(self, mut map: V) -> Result<Image, V::Error>
453 where
454 V: MapAccess<'de>,
455 {
456 let mut metadata = None;
457 let mut title: Option<Vec<InlineNode>> = None;
458 let mut source = None;
459 let mut location = None;
460
461 while let Some(key) = map.next_key()? {
462 match key {
463 Field::Metadata => {
464 metadata = Some(map.next_value()?);
465 }
466 Field::Title => {
467 title = Some(map.next_value()?);
468 }
469 Field::Source => {
470 source = Some(map.next_value()?);
471 }
472 Field::Location => {
473 location = Some(map.next_value()?);
474 }
475 }
476 }
477
478 Ok(Image {
479 title: title.unwrap_or_default().into(),
480 source: source.ok_or_else(|| serde::de::Error::missing_field("source"))?,
481 metadata: metadata.unwrap_or_default(),
482 location: location
483 .ok_or_else(|| serde::de::Error::missing_field("location"))?,
484 })
485 }
486 }
487
488 deserializer.deserialize_struct(
489 "Image",
490 &["metadata", "title", "source", "location"],
491 ImageVisitor,
492 )
493 }
494}
495
496impl<'de> Deserialize<'de> for Video {
498 fn deserialize<D>(deserializer: D) -> Result<Video, D::Error>
499 where
500 D: Deserializer<'de>,
501 {
502 #[derive(Deserialize)]
503 #[serde(field_identifier, rename_all = "lowercase")]
504 enum Field {
505 Metadata,
506 Title,
507 Sources,
508 Location,
509 }
510
511 struct VideoVisitor;
512
513 impl<'de> Visitor<'de> for VideoVisitor {
514 type Value = Video;
515
516 fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
517 formatter.write_str("struct Video")
518 }
519
520 fn visit_map<V>(self, mut map: V) -> Result<Video, V::Error>
521 where
522 V: MapAccess<'de>,
523 {
524 let mut metadata = None;
525 let mut title: Option<Vec<InlineNode>> = None;
526 let mut sources = None;
527 let mut location = None;
528
529 while let Some(key) = map.next_key()? {
530 match key {
531 Field::Metadata => metadata = Some(map.next_value()?),
532 Field::Title => title = Some(map.next_value()?),
533 Field::Sources => sources = Some(map.next_value()?),
534 Field::Location => location = Some(map.next_value()?),
535 }
536 }
537
538 Ok(Video {
539 title: title.unwrap_or_default().into(),
540 sources: sources.unwrap_or_default(),
541 metadata: metadata.unwrap_or_default(),
542 location: location
543 .ok_or_else(|| serde::de::Error::missing_field("location"))?,
544 })
545 }
546 }
547
548 deserializer.deserialize_struct(
549 "Video",
550 &["metadata", "title", "sources", "location"],
551 VideoVisitor,
552 )
553 }
554}