document_tree/
element_categories.rs

1use std::fmt::{self, Debug, Formatter};
2
3use schemars::JsonSchema;
4use serde_derive::Serialize;
5
6#[allow(clippy::wildcard_imports)]
7use crate::elements::*;
8
9pub trait HasChildren<C> {
10    fn with_children(children: Vec<C>) -> Self;
11    fn children(&self) -> &Vec<C>;
12    fn children_mut(&mut self) -> &mut Vec<C>;
13    fn append_child<R: Into<C>>(&mut self, child: R) {
14        self.children_mut().push(child.into());
15    }
16    fn append_children<R: Into<C> + Clone>(&mut self, more: &[R]) {
17        let children = self.children_mut();
18        children.reserve(more.len());
19        for child in more {
20            children.push(child.clone().into());
21        }
22    }
23}
24
25macro_rules! impl_into {
26    ([ $( (($subcat:ident :: $entry:ident), $supcat:ident), )+ ]) => {
27        $( impl_into!($subcat::$entry => $supcat); )+
28    };
29    ($subcat:ident :: $entry:ident => $supcat:ident ) => {
30        impl From<$entry> for $supcat {
31            fn from(inner: $entry) -> Self {
32                $supcat::$subcat(Box::new(inner.into()))
33            }
34        }
35    };
36}
37
38macro_rules! synonymous_enum {
39    ( $subcat:ident : $($supcat:ident),+ ; $midcat:ident : $supsupcat:ident {
40        $($(#[$attr:meta])? $entry:ident),+ $(,)*
41    } ) => {
42        synonymous_enum!($subcat : $( $supcat ),+ , $midcat { $($(#[$attr])? $entry,)+ });
43        $( impl_into!($midcat::$entry => $supsupcat); )+
44    };
45    ( $subcat:ident : $($supcat:ident),+ {
46        $($(#[$attr:meta])? $entry:ident),+ $(,)*
47    } ) => {
48        synonymous_enum!($subcat { $($(#[$attr])? $entry,)* });
49        cartesian!(impl_into, [ $( ($subcat::$entry) ),+ ], [ $($supcat),+ ]);
50    };
51    ( $name:ident {
52        $($(#[$attr:meta])? $entry:ident),+ $(,)*
53    } ) => {
54        #[derive(Clone, PartialEq, Serialize, JsonSchema)]
55        #[serde(tag = "type")]
56        #[schemars(_unstable_ref_variants)]
57        pub enum $name { $(
58            $(#[$attr])?
59            $entry(Box<$entry>),
60        )* }
61
62        impl Debug for $name {
63            fn fmt(&self, fmt: &mut Formatter) -> Result<(), fmt::Error> {
64                match *self {
65                    $( $name::$entry(ref inner) => inner.fmt(fmt), )*
66                }
67            }
68        }
69
70        $( impl From<$entry> for $name {
71            fn from(inner: $entry) -> Self {
72                $name::$entry(Box::new(inner))
73            }
74        } )*
75    };
76}
77
78synonymous_enum!(StructuralSubElement {
79    Title,
80    Subtitle,
81    Decoration,
82    Docinfo,
83    #[serde(untagged)]
84    SubStructure
85});
86synonymous_enum!(SubStructure: StructuralSubElement {
87    Topic, Sidebar, Transition, Section, #[serde(untagged)] BodyElement
88});
89synonymous_enum!(BodyElement: SubTopic, SubSidebar, SubBlockQuote, SubFootnote, SubFigure; SubStructure: StructuralSubElement {
90    //Simple
91    Paragraph, LiteralBlock, DoctestBlock, MathBlock, Rubric, SubstitutionDefinition, Comment, Pending, Target, Raw, Image,
92    //Compound
93    Compound, Container,
94    BulletList, EnumeratedList, DefinitionList, FieldList, OptionList,
95    LineBlock, BlockQuote, Admonition, Attention, Hint, Note, Caution, Danger, Error, Important, Tip, Warning, Footnote, Citation, SystemMessage, Figure, Table
96});
97
98impl<'a> TryFrom<&'a StructuralSubElement> for &'a BodyElement {
99    type Error = ();
100
101    fn try_from(value: &'a StructuralSubElement) -> Result<Self, ()> {
102        match value {
103            StructuralSubElement::SubStructure(s) => s.as_ref().try_into(),
104            _ => Err(()),
105        }
106    }
107}
108
109impl<'a> TryFrom<&'a SubStructure> for &'a BodyElement {
110    type Error = ();
111
112    fn try_from(value: &'a SubStructure) -> Result<Self, ()> {
113        match value {
114            SubStructure::BodyElement(s) => Ok(s.as_ref()),
115            _ => Err(()),
116        }
117    }
118}
119
120synonymous_enum!(BibliographicElement {
121    Authors,
122    // author info, contained in Authors above:
123    Author,
124    Organization,
125    Address,
126    Contact,
127    // other:
128    Version,
129    Revision,
130    Status,
131    Date,
132    Copyright,
133    Field
134});
135
136synonymous_enum!(TextOrInlineElement {
137    String,
138    Emphasis,
139    Strong,
140    Literal,
141    Reference,
142    FootnoteReference,
143    CitationReference,
144    SubstitutionReference,
145    TitleReference,
146    Abbreviation,
147    Acronym,
148    Superscript,
149    Subscript,
150    Inline,
151    Problematic,
152    Generated,
153    Math,
154    //also have non-inline versions. Inline image is no figure child, inline target has content
155    TargetInline,
156    RawInline,
157    ImageInline
158});
159
160//--------------\\
161//Content Models\\
162//--------------\\
163
164synonymous_enum!(AuthorInfo {
165    Author,
166    Organization,
167    Address,
168    Contact
169});
170synonymous_enum!(DecorationElement { Header, Footer });
171synonymous_enum!(SubTopic { Title, BodyElement });
172synonymous_enum!(SubSidebar {
173    Topic,
174    Title,
175    Subtitle,
176    BodyElement
177});
178synonymous_enum!(SubDLItem {
179    Term,
180    Classifier,
181    Definition
182});
183synonymous_enum!(SubField {
184    FieldName,
185    FieldBody
186});
187synonymous_enum!(SubOptionListItem {
188    OptionGroup,
189    Description
190});
191synonymous_enum!(SubOption {
192    OptionString,
193    OptionArgument
194});
195synonymous_enum!(SubLineBlock { LineBlock, Line });
196synonymous_enum!(SubBlockQuote {
197    Attribution,
198    BodyElement
199});
200synonymous_enum!(SubFootnote { Label, BodyElement });
201synonymous_enum!(SubFigure {
202    Caption,
203    Legend,
204    BodyElement
205});
206synonymous_enum!(SubTable { Title, TableGroup });
207synonymous_enum!(SubTableGroup {
208    TableColspec,
209    TableHead,
210    TableBody
211});
212
213// indirect conversions
214impl From<SubTopic> for SubSidebar {
215    fn from(inner: SubTopic) -> Self {
216        match inner {
217            SubTopic::Title(e) => (*e).into(),
218            SubTopic::BodyElement(e) => (*e).into(),
219        }
220    }
221}
222
223impl From<SubTopic> for StructuralSubElement {
224    fn from(inner: SubTopic) -> Self {
225        match inner {
226            SubTopic::Title(e) => (*e).into(),
227            SubTopic::BodyElement(e) => (*e).into(),
228        }
229    }
230}
231
232impl From<SubSidebar> for StructuralSubElement {
233    fn from(inner: SubSidebar) -> Self {
234        match inner {
235            SubSidebar::Topic(e) => (*e).into(),
236            SubSidebar::Title(e) => (*e).into(),
237            SubSidebar::Subtitle(e) => (*e).into(),
238            SubSidebar::BodyElement(e) => (*e).into(),
239        }
240    }
241}
242
243impl From<AuthorInfo> for BibliographicElement {
244    fn from(inner: AuthorInfo) -> Self {
245        match inner {
246            AuthorInfo::Author(e) => (*e).into(),
247            AuthorInfo::Organization(e) => (*e).into(),
248            AuthorInfo::Address(e) => (*e).into(),
249            AuthorInfo::Contact(e) => (*e).into(),
250        }
251    }
252}
253
254#[cfg(test)]
255mod conversion_tests {
256    use super::*;
257    use std::default::Default;
258
259    #[test]
260    fn basic() {
261        let _: BodyElement = Paragraph::default().into();
262    }
263
264    #[test]
265    fn more() {
266        let _: SubStructure = Paragraph::default().into();
267    }
268
269    #[test]
270    fn even_more() {
271        let _: StructuralSubElement = Paragraph::default().into();
272    }
273
274    #[test]
275    fn super_() {
276        let be: BodyElement = Paragraph::default().into();
277        let _: StructuralSubElement = be.into();
278    }
279}