ftml/tree/
container.rs

1/*
2 * tree/container.rs
3 *
4 * ftml - Library to parse Wikidot text
5 * Copyright (C) 2019-2025 Wikijump Team
6 *
7 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU Affero General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU Affero General Public License for more details.
16 *
17 * You should have received a copy of the GNU Affero General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 */
20
21//! Representation of generic syntax elements which wrap other elements.
22
23use super::clone::elements_to_owned;
24use super::{Alignment, AttributeMap, Element, Heading, HtmlTag};
25use crate::next_index::{NextIndex, TableOfContentsIndex};
26use strum_macros::IntoStaticStr;
27
28#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
29#[serde(rename_all = "kebab-case")]
30pub struct Container<'t> {
31    #[serde(rename = "type")]
32    ctype: ContainerType,
33    attributes: AttributeMap<'t>,
34    elements: Vec<Element<'t>>,
35}
36
37impl<'t> Container<'t> {
38    #[inline]
39    pub fn new(
40        ctype: ContainerType,
41        elements: Vec<Element<'t>>,
42        attributes: AttributeMap<'t>,
43    ) -> Self {
44        Container {
45            ctype,
46            attributes,
47            elements,
48        }
49    }
50
51    #[inline]
52    pub fn ctype(&self) -> ContainerType {
53        self.ctype
54    }
55
56    #[inline]
57    pub fn elements(&self) -> &[Element<'t>] {
58        &self.elements
59    }
60
61    #[inline]
62    pub fn attributes(&self) -> &AttributeMap<'t> {
63        &self.attributes
64    }
65
66    #[inline]
67    pub fn attributes_mut(&mut self) -> &mut AttributeMap<'t> {
68        &mut self.attributes
69    }
70
71    pub fn to_owned(&self) -> Container<'static> {
72        Container {
73            ctype: self.ctype,
74            attributes: self.attributes.to_owned(),
75            elements: elements_to_owned(&self.elements),
76        }
77    }
78}
79
80impl<'t> From<Container<'t>> for Vec<Element<'t>> {
81    #[inline]
82    fn from(container: Container<'t>) -> Vec<Element<'t>> {
83        let Container { elements, .. } = container;
84
85        elements
86    }
87}
88
89#[derive(
90    Serialize, Deserialize, IntoStaticStr, Debug, Copy, Clone, Hash, PartialEq, Eq,
91)]
92#[serde(rename_all = "kebab-case")]
93pub enum ContainerType {
94    Bold,
95    Italics,
96    Underline,
97    Superscript,
98    Subscript,
99    Strikethrough,
100    Monospace,
101    Span,
102    Div,
103    Mark,
104    Blockquote,
105    Insertion,
106    Deletion,
107    Hidden,
108    Invisible,
109    Size,
110    Ruby,
111    RubyText,
112    Paragraph,
113    Align(Alignment),
114    Header(Heading),
115}
116
117impl ContainerType {
118    #[inline]
119    pub fn name(self) -> &'static str {
120        self.into()
121    }
122
123    #[inline]
124    pub fn html_tag(self, indexer: &mut dyn NextIndex<TableOfContentsIndex>) -> HtmlTag {
125        match self {
126            ContainerType::Bold => HtmlTag::new("strong"),
127            ContainerType::Italics => HtmlTag::new("em"),
128            ContainerType::Underline => HtmlTag::new("u"),
129            ContainerType::Superscript => HtmlTag::new("sup"),
130            ContainerType::Subscript => HtmlTag::new("sub"),
131            ContainerType::Strikethrough => HtmlTag::new("s"),
132            ContainerType::Monospace => HtmlTag::with_class("code", "wj-monospace"),
133            ContainerType::Span => HtmlTag::new("span"),
134            ContainerType::Div => HtmlTag::new("div"),
135            ContainerType::Mark => HtmlTag::new("mark"),
136            ContainerType::Blockquote => HtmlTag::new("blockquote"),
137            ContainerType::Insertion => HtmlTag::new("ins"),
138            ContainerType::Deletion => HtmlTag::new("del"),
139            ContainerType::Hidden => HtmlTag::with_class("span", "wj-hidden"),
140            ContainerType::Invisible => HtmlTag::with_class("span", "wj-invisible"),
141            ContainerType::Size => HtmlTag::new("span"),
142            ContainerType::Ruby => HtmlTag::new("ruby"),
143            ContainerType::RubyText => HtmlTag::new("rt"),
144            ContainerType::Paragraph => HtmlTag::new("p"),
145            ContainerType::Align(alignment) => {
146                HtmlTag::with_class("div", alignment.html_class())
147            }
148            ContainerType::Header(heading) => heading.html_tag(indexer),
149        }
150    }
151
152    /// Determines if this container type is able to be embedded in a paragraph.
153    ///
154    /// See `Element::paragraph_safe()`, as the same caveats apply.
155    #[inline]
156    pub fn paragraph_safe(self) -> bool {
157        match self {
158            ContainerType::Bold => true,
159            ContainerType::Italics => true,
160            ContainerType::Underline => true,
161            ContainerType::Superscript => true,
162            ContainerType::Subscript => true,
163            ContainerType::Strikethrough => true,
164            ContainerType::Monospace => true,
165            ContainerType::Span => true,
166            ContainerType::Div => false,
167            ContainerType::Mark => true,
168            ContainerType::Blockquote => false,
169            ContainerType::Insertion => true,
170            ContainerType::Deletion => true,
171            ContainerType::Hidden => true,
172            ContainerType::Invisible => true,
173            ContainerType::Size => true,
174            ContainerType::Ruby => true,
175            ContainerType::RubyText => true,
176            ContainerType::Paragraph => false,
177            ContainerType::Align(_) => false,
178            ContainerType::Header(_) => false,
179        }
180    }
181}