acdc_parser/model/
admonition.rs

1//! Admonition types for `AsciiDoc` documents.
2
3use std::fmt::Display;
4use std::str::FromStr;
5
6use serde::{
7    Deserialize, Serialize,
8    ser::{SerializeMap, Serializer},
9};
10
11use crate::{Error, Positioning, SourceLocation};
12
13use super::Block;
14use super::location::{Location, Position};
15use super::metadata::BlockMetadata;
16use super::title::Title;
17
18/// An `Admonition` represents an admonition in a document.
19#[derive(Clone, Debug, PartialEq)]
20#[non_exhaustive]
21pub struct Admonition {
22    pub metadata: BlockMetadata,
23    pub variant: AdmonitionVariant,
24    pub blocks: Vec<Block>,
25    pub title: Title,
26    pub location: Location,
27}
28
29/// The variant/type of an admonition block.
30#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
31#[serde(rename_all = "lowercase")]
32pub enum AdmonitionVariant {
33    Note,
34    Tip,
35    Important,
36    Caution,
37    Warning,
38}
39
40impl Display for AdmonitionVariant {
41    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
42        match self {
43            AdmonitionVariant::Note => write!(f, "note"),
44            AdmonitionVariant::Tip => write!(f, "tip"),
45            AdmonitionVariant::Important => write!(f, "important"),
46            AdmonitionVariant::Caution => write!(f, "caution"),
47            AdmonitionVariant::Warning => write!(f, "warning"),
48        }
49    }
50}
51
52impl FromStr for AdmonitionVariant {
53    type Err = Error;
54
55    fn from_str(variant: &str) -> Result<Self, Self::Err> {
56        match variant.to_lowercase().as_str() {
57            "note" => Ok(AdmonitionVariant::Note),
58            "tip" => Ok(AdmonitionVariant::Tip),
59            "important" => Ok(AdmonitionVariant::Important),
60            "caution" => Ok(AdmonitionVariant::Caution),
61            "warning" => Ok(AdmonitionVariant::Warning),
62            _ => Err(Error::Parse(
63                Box::new(SourceLocation {
64                    file: None,
65                    positioning: Positioning::Position(Position::default()),
66                }),
67                format!("unknown admonition variant: {variant}"),
68            )),
69        }
70    }
71}
72
73impl Admonition {
74    /// Create a new admonition with the given variant, blocks, and location.
75    #[must_use]
76    pub fn new(variant: AdmonitionVariant, blocks: Vec<Block>, location: Location) -> Self {
77        Self {
78            metadata: BlockMetadata::default(),
79            variant,
80            blocks,
81            title: Title::default(),
82            location,
83        }
84    }
85
86    /// Set the metadata.
87    #[must_use]
88    pub fn with_metadata(mut self, metadata: BlockMetadata) -> Self {
89        self.metadata = metadata;
90        self
91    }
92
93    /// Set the title.
94    #[must_use]
95    pub fn with_title(mut self, title: Title) -> Self {
96        self.title = title;
97        self
98    }
99}
100
101impl Serialize for Admonition {
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        state.serialize_entry("name", "admonition")?;
108        state.serialize_entry("type", "block")?;
109        state.serialize_entry("variant", &self.variant)?;
110        if !self.metadata.is_default() {
111            state.serialize_entry("metadata", &self.metadata)?;
112        }
113        if !self.title.is_empty() {
114            state.serialize_entry("title", &self.title)?;
115        }
116
117        if !self.blocks.is_empty() {
118            state.serialize_entry("blocks", &self.blocks)?;
119        }
120        state.serialize_entry("location", &self.location)?;
121        state.end()
122    }
123}