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