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 Paragraph, LiteralBlock, DoctestBlock, MathBlock, Rubric, SubstitutionDefinition, Comment, Pending, Target, Raw, Image,
92 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,
124 Organization,
125 Address,
126 Contact,
127 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 TargetInline,
156 RawInline,
157 ImageInline
158});
159
160synonymous_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
213impl 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}