document_tree/
elements.rs

1use serde_derive::Serialize;
2use std::path::PathBuf;
3
4use crate::attribute_types::{CanBeEmpty, ID, NameToken};
5#[allow(clippy::wildcard_imports)]
6use crate::element_categories::*;
7use crate::extra_attributes::{self, ExtraAttributes};
8
9//-----------------\\
10//Element hierarchy\\
11//-----------------\\
12
13pub trait Element {
14    /// A list containing one or more unique identifier keys
15    fn ids(&self) -> &Vec<ID>;
16    fn ids_mut(&mut self) -> &mut Vec<ID>;
17    /// a list containing the names of an element, typically originating from the element's title or content.
18    /// Each name in names must be unique; if there are name conflicts (two or more elements want to the same name),
19    /// the contents will be transferred to the dupnames attribute on the duplicate elements.
20    /// An element may have at most one of the names or dupnames attributes, but not both.
21    fn names(&self) -> &Vec<NameToken>;
22    fn names_mut(&mut self) -> &mut Vec<NameToken>;
23    fn source(&self) -> &Option<PathBuf>;
24    fn source_mut(&mut self) -> &mut Option<PathBuf>;
25    fn classes(&self) -> &Vec<String>;
26    fn classes_mut(&mut self) -> &mut Vec<String>;
27}
28
29#[derive(Debug, Default, PartialEq, Serialize, Clone)]
30pub struct CommonAttributes {
31    #[serde(skip_serializing_if = "CanBeEmpty::is_empty")]
32    ids: Vec<ID>,
33    #[serde(skip_serializing_if = "CanBeEmpty::is_empty")]
34    names: Vec<NameToken>,
35    #[serde(skip_serializing_if = "CanBeEmpty::is_empty")]
36    source: Option<PathBuf>,
37    #[serde(skip_serializing_if = "CanBeEmpty::is_empty")]
38    classes: Vec<String>,
39    //TODO: dupnames
40}
41
42//----\\
43//impl\\
44//----\\
45
46macro_rules! impl_element {
47    ($name:ident) => {
48        impl Element for $name {
49            fn ids(&self) -> &Vec<ID> {
50                &self.common.ids
51            }
52            fn ids_mut(&mut self) -> &mut Vec<ID> {
53                &mut self.common.ids
54            }
55            fn names(&self) -> &Vec<NameToken> {
56                &self.common.names
57            }
58            fn names_mut(&mut self) -> &mut Vec<NameToken> {
59                &mut self.common.names
60            }
61            fn source(&self) -> &Option<PathBuf> {
62                &self.common.source
63            }
64            fn source_mut(&mut self) -> &mut Option<PathBuf> {
65                &mut self.common.source
66            }
67            fn classes(&self) -> &Vec<String> {
68                &self.common.classes
69            }
70            fn classes_mut(&mut self) -> &mut Vec<String> {
71                &mut self.common.classes
72            }
73        }
74    };
75}
76
77macro_rules! impl_children {
78    ($name:ident, $childtype:ident) => {
79        impl HasChildren<$childtype> for $name {
80            #[allow(clippy::needless_update)]
81            fn with_children(children: Vec<$childtype>) -> $name {
82                $name {
83                    children,
84                    ..Default::default()
85                }
86            }
87            fn children(&self) -> &Vec<$childtype> {
88                &self.children
89            }
90            fn children_mut(&mut self) -> &mut Vec<$childtype> {
91                &mut self.children
92            }
93        }
94    };
95}
96
97macro_rules! impl_extra { ($name:ident $($more:tt)*) => (
98    impl ExtraAttributes<extra_attributes::$name> for $name {
99        #[allow(clippy::needless_update)]
100        fn with_extra(extra: extra_attributes::$name) -> $name { $name { common: Default::default(), extra $($more)* } }
101        fn extra    (&    self) -> &    extra_attributes::$name { &    self.extra }
102        fn extra_mut(&mut self) -> &mut extra_attributes::$name { &mut self.extra }
103    }
104)}
105
106#[allow(dead_code)]
107trait HasExtraAndChildren<C, A> {
108    fn with_extra_and_children(extra: A, children: Vec<C>) -> Self;
109}
110
111impl<T, C, A> HasExtraAndChildren<C, A> for T
112where
113    T: HasChildren<C> + ExtraAttributes<A>,
114{
115    #[allow(clippy::needless_update)]
116    fn with_extra_and_children(extra: A, mut children: Vec<C>) -> Self {
117        let mut r = Self::with_extra(extra);
118        r.children_mut().append(&mut children);
119        r
120    }
121}
122
123macro_rules! impl_new {(
124    $(#[$attr:meta])*
125    pub struct $name:ident { $(
126        $(#[$fattr:meta])*
127        $field:ident : $typ:path
128    ),* $(,)* }
129) => (
130    $(#[$attr])*
131    #[derive(Debug,PartialEq,Serialize,Clone)]
132    pub struct $name { $(
133        $(#[$fattr])* $field: $typ,
134    )* }
135    impl $name {
136        #[must_use]
137        pub fn new( $( $field: $typ, )* ) -> $name { $name { $( $field, )* } }
138    }
139)}
140
141macro_rules! impl_elem {
142    ($name:ident) => {
143        impl_new!(
144            #[derive(Default)]
145            pub struct $name {
146                #[serde(flatten)]
147                common: CommonAttributes,
148            }
149        );
150        impl_element!($name);
151    };
152    ($name:ident; +) => {
153        impl_new!(
154            #[derive(Default)]
155            pub struct $name {
156                #[serde(flatten)]
157                common: CommonAttributes,
158                #[serde(flatten)]
159                extra: extra_attributes::$name,
160            }
161        );
162        impl_element!($name);
163        impl_extra!($name, ..Default::default());
164    };
165    ($name:ident; *) => {
166        //same as above with no default
167        impl_new!(
168            pub struct $name {
169                #[serde(flatten)]
170                common: CommonAttributes,
171                #[serde(flatten)]
172                extra: extra_attributes::$name,
173            }
174        );
175        impl_element!($name);
176        impl_extra!($name);
177    };
178    ($name:ident, $childtype:ident) => {
179        impl_new!(
180            #[derive(Default)]
181            pub struct $name {
182                #[serde(flatten)]
183                common: CommonAttributes,
184                children: Vec<$childtype>,
185            }
186        );
187        impl_element!($name);
188        impl_children!($name, $childtype);
189    };
190    ($name:ident, $childtype:ident; +) => {
191        impl_new!(
192            #[derive(Default)]
193            pub struct $name {
194                #[serde(flatten)]
195                common: CommonAttributes,
196                #[serde(flatten)]
197                extra: extra_attributes::$name,
198                children: Vec<$childtype>,
199            }
200        );
201        impl_element!($name);
202        impl_extra!($name, ..Default::default());
203        impl_children!($name, $childtype);
204    };
205}
206
207macro_rules! impl_elems { ( $( ($($args:tt)*) )* ) => (
208    $( impl_elem!($($args)*); )*
209)}
210
211#[derive(Default, Debug, Serialize)]
212pub struct Document {
213    children: Vec<StructuralSubElement>,
214}
215impl_children!(Document, StructuralSubElement);
216
217impl_elems!(
218    //structual elements
219    (Section, StructuralSubElement)
220    (Topic,   SubTopic)
221    (Sidebar, SubSidebar)
222
223    //structural subelements
224    (Title,      TextOrInlineElement)
225    (Subtitle,   TextOrInlineElement)
226    (Decoration, DecorationElement)
227    (Docinfo,    BibliographicElement)
228    (Transition)
229
230    //bibliographic elements
231    (Author,       TextOrInlineElement)
232    (Authors,      AuthorInfo)
233    (Organization, TextOrInlineElement)
234    (Address,      TextOrInlineElement; +)
235    (Contact,      TextOrInlineElement)
236    (Version,      TextOrInlineElement)
237    (Revision,     TextOrInlineElement)
238    (Status,       TextOrInlineElement)
239    (Date,         TextOrInlineElement)
240    (Copyright,    TextOrInlineElement)
241    (Field,        SubField)
242
243    //decoration elements
244    (Header, BodyElement)
245    (Footer, BodyElement)
246
247    //simple body elements
248    (Paragraph,              TextOrInlineElement)
249    (LiteralBlock,           TextOrInlineElement; +)
250    (DoctestBlock,           TextOrInlineElement; +)
251    (MathBlock,              String)
252    (Rubric,                 TextOrInlineElement)
253    (SubstitutionDefinition, TextOrInlineElement; +)
254    (Comment,                TextOrInlineElement; +)
255    (Pending)
256    (Target; +)
257    (Raw, String; +)
258    (Image; *)
259
260    //compound body elements
261    (Compound,  BodyElement)
262    (Container, BodyElement)
263
264    (BulletList,     ListItem; +)
265    (EnumeratedList, ListItem; +)
266    (DefinitionList, DefinitionListItem)
267    (FieldList,      Field)
268    (OptionList,     OptionListItem)
269
270    (LineBlock,     SubLineBlock)
271    (BlockQuote,    SubBlockQuote)
272    (Admonition,    SubTopic)
273    (Attention,     BodyElement)
274    (Hint,          BodyElement)
275    (Note,          BodyElement)
276    (Caution,       BodyElement)
277    (Danger,        BodyElement)
278    (Error,         BodyElement)
279    (Important,     BodyElement)
280    (Tip,           BodyElement)
281    (Warning,       BodyElement)
282    (Footnote,      SubFootnote; +)
283    (Citation,      SubFootnote; +)
284    (SystemMessage, BodyElement; +)
285    (Figure,        SubFigure;   +)
286    (Table,         SubTable;    +)
287
288    //table elements
289    (TableGroup, SubTableGroup; +)
290    (TableHead,  TableRow;      +)
291    (TableBody,  TableRow;      +)
292    (TableRow,   TableEntry;    +)
293    (TableEntry, BodyElement;   +)
294    (TableColspec; +)
295
296    //body sub elements
297    (ListItem, BodyElement)
298
299    (DefinitionListItem, SubDLItem)
300    (Term,               TextOrInlineElement)
301    (Classifier,         TextOrInlineElement)
302    (Definition,         BodyElement)
303
304    (FieldName, TextOrInlineElement)
305    (FieldBody, BodyElement)
306
307    (OptionListItem, SubOptionListItem)
308    (OptionGroup,    Option_)
309    (Description,    BodyElement)
310    (Option_,        SubOption)
311    (OptionString,   String)
312    (OptionArgument, String; +)
313
314    (Line,        TextOrInlineElement)
315    (Attribution, TextOrInlineElement)
316    (Label,       TextOrInlineElement)
317
318    (Caption, TextOrInlineElement)
319    (Legend,  BodyElement)
320
321    //inline elements
322    (Emphasis,              TextOrInlineElement)
323    (Literal,               String)
324    (Reference,             TextOrInlineElement; +)
325    (Strong,                TextOrInlineElement)
326    (FootnoteReference,     TextOrInlineElement; +)
327    (CitationReference,     TextOrInlineElement; +)
328    (SubstitutionReference, TextOrInlineElement; +)
329    (TitleReference,        TextOrInlineElement)
330    (Abbreviation,          TextOrInlineElement)
331    (Acronym,               TextOrInlineElement)
332    (Superscript,           TextOrInlineElement)
333    (Subscript,             TextOrInlineElement)
334    (Inline,                TextOrInlineElement)
335    (Problematic,           TextOrInlineElement; +)
336    (Generated,             TextOrInlineElement)
337    (Math,                  String)
338
339    //also have non-inline versions. Inline image is no figure child, inline target has content
340    (TargetInline, String; +)
341    (RawInline,    String; +)
342    (ImageInline; *)
343
344    //text element = String
345);
346
347impl<'a> From<&'a str> for TextOrInlineElement {
348    fn from(s: &'a str) -> Self {
349        s.to_owned().into()
350    }
351}
352
353impl Footnote {
354    /// Get the footnote’s label node, if available
355    ///
356    /// # Errors
357    /// Returns an error if the footnote has no label
358    pub fn get_label(&self) -> Result<&str, anyhow::Error> {
359        use anyhow::{Context, bail};
360
361        let SubFootnote::Label(e) = self
362            .children()
363            .first()
364            .context("Footnote has no children")?
365        else {
366            bail!("Non-auto footnote has no label");
367        };
368        match e
369            .children()
370            .first()
371            .context("Footnote label has no child")?
372        {
373            TextOrInlineElement::String(s) => Ok(s.as_ref()),
374            _ => bail!("Footnote label is not a string"),
375        }
376    }
377}