document_tree/
element_categories.rs

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