1use core::marker::PhantomData;
3
4use super::HtmlTagWriter;
5
6impl<T: SimpleHtmlTagWriter<CustomTy>, CustomTy> HtmlTagWriter<CustomTy> for T
7where
8 CustomTy: Clone + 'static,
9{
10 fn match_tag(&self, tag: &str) -> bool {
11 Self::TAGS.iter().any(|x| x.eq_ignore_ascii_case(tag))
12 }
13
14 fn open_tag(
15 &self,
16 tk_writer: &dyn super::HtmlTokenWriter<CustomTy>,
17 token: &crate::Token<'_, CustomTy>,
18 out: &mut String,
19 ) {
20 if Self::TAGS.iter().any(|x| token.is_open_argless(x)) && Self::HTML_OPEN.is_some() {
22 out.push_str(Self::HTML_OPEN.unwrap())
23 } else {
24 tk_writer.write_token(token, out)
25 }
26 }
27
28 fn close_tag<'a>(
29 &self,
30 tk_writer: &dyn super::HtmlTokenWriter<CustomTy>,
31 _: &crate::Token<'a, CustomTy>,
32 close_token: &crate::Token<'a, CustomTy>,
33 out: &mut String,
34 ) {
35 if Self::TAGS.iter().any(|x| close_token.is_close_argless(x)) && Self::HTML_CLOSE.is_some()
36 {
37 out.push_str(Self::HTML_CLOSE.unwrap())
38 } else {
39 tk_writer.write_token(close_token, out)
40 }
41 }
42
43 fn standalone_tag(
44 &self,
45 tk_writer: &dyn super::HtmlTokenWriter<CustomTy>,
46 token: &crate::Token<'_, CustomTy>,
47 out: &mut String,
48 ) {
49 if Self::TAGS.iter().any(|x| token.is_standalone_argless(x))
50 && Self::HTML_STANDALONE.is_some()
51 {
52 out.push_str(Self::HTML_STANDALONE.unwrap())
53 } else {
54 tk_writer.write_token(token, out)
55 }
56 }
57}
58
59trait SimpleHtmlTagWriter<CustomTy>
60where
61 CustomTy: Clone,
62{
63 const TAGS: &'static [&'static str];
64
65 const HTML_OPEN: Option<&'static str>;
66
67 const HTML_CLOSE: Option<&'static str>;
68
69 const HTML_STANDALONE: Option<&'static str>;
70}
71
72macro_rules! simple_tag {
73 ($doc:expr, $name:ident, $tags:expr, $open:expr, $close:expr) => {
74 #[derive(Copy, Clone, Debug, Default)]
75 #[doc = $doc]
76 #[doc = "<br/>"]
77 #[doc = "This matches the following BBCode tags: `"]
78 #[doc = stringify!($tags)]
79 #[doc = "`"]
80 #[doc = "# Exact output"]
81 #[doc = "This tag converts exactly to"]
82 #[doc = "```html"]
83 #[doc = $open]
84 #[doc = " contents"]
85 #[doc = $close]
86 #[doc = "```"]
87 pub struct $name<CustomTy = ()> {
88 _custom_ty: PhantomData<CustomTy>,
89 }
90
91 impl<CustomTy> SimpleHtmlTagWriter<CustomTy> for $name<CustomTy>
92 where
93 CustomTy: Clone,
94 {
95 const TAGS: &'static [&'static str] = &$tags;
96 const HTML_CLOSE: Option<&'static str> = Some($close);
97 const HTML_OPEN: Option<&'static str> = Some($open);
98 const HTML_STANDALONE: Option<&'static str> = None;
99 }
100 };
101}
102
103macro_rules! simple_standalone_tag {
104 ($doc:expr, $name:ident, $tags:expr, $standalone:expr) => {
105 #[derive(Copy, Clone, Debug, Default)]
106 #[doc = $doc]
107 #[doc = "<br/>"]
108 #[doc = "This matches the following BBCode tags: `"]
109 #[doc = stringify!($tags)]
110 #[doc = "`"]
111 #[doc = "# Exact output"]
112 #[doc = "This tag converts exactly to"]
113 #[doc = "```html"]
114 #[doc = $standalone]
115 #[doc = "```"]
116 pub struct $name<CustomTy = ()> {
117 _custom_ty: PhantomData<CustomTy>,
118 }
119
120 impl<CustomTy> SimpleHtmlTagWriter<CustomTy> for $name<CustomTy>
121 where
122 CustomTy: Clone,
123 {
124 const TAGS: &'static [&'static str] = &$tags;
125 const HTML_CLOSE: Option<&'static str> = None;
126 const HTML_OPEN: Option<&'static str> = None;
127 const HTML_STANDALONE: Option<&'static str> = Some($standalone);
128 }
129 };
130}
131
132simple_tag! {
134 "A bold tag with no arguments, which converts directly to HTML5 `<b>`.",
135 BoldTag, ["b", "bold"], "<b>", "</b>"
136}
137simple_tag! {
138 "An italic tag with no arguments, which converts directly to HTML5 `<i>`.",
139 ItalicTag, ["i", "italic"], "<i>", "</i>"
140}
141simple_tag! {
142 "An underline tag no arguments, which converts directly to HTML5 `<u>`.",
143 UnderlineTag, ["u", "underline", "under"], "<u>", "</u>"
144}
145simple_standalone_tag! {
146 "A linebreak tag with no arguments, which converts directly into HTML5 `<br/>`.",
147 LinebreakTag, ["br"], "<br/>"
148}
149simple_tag! {
150 "A block quote tag with no arguments, which converts directly to HTML5 `<blockquote>`.",
151 BlockQuoteTag, ["quote", "blockquote"], "<blockquote>", "</blockquote>"
152}
153simple_tag! {
154 "Inline quote tag with no arguments, which converts directly to HTML5 `<q>`.",
155 QuoteTag, ["q"], "<q>", "</q>"
156}
157simple_tag! {
158 "Subscript tag with no arguments, which converts directly to HTML5 `<sub>`.",
159 SubscriptTag, ["sub", "subscript", "small"], "<sub>", "</sub>"
160}
161simple_tag! {
162 "Superscript tag with no arguments, which converts directly to HTML5 `<sup>`.",
163 SuperscriptTag, ["sup", "super", "superscript"], "<sup>", "</sup>"
164}
165simple_tag! {
166 "Header (tier 1) tag with no arguments, which converts directly to HTML5 `<h1>`.",
167 Header1Tag, ["h1", "title"], "<h1>", "</h1>"
168}
169simple_tag! {
170 "Header (tier 2) tag with no arguments, which converts directly to HTML5 `<h2>`.",
171 Header2Tag, ["h2", "topic"], "<h2>", "</h2>"
172}
173simple_tag! {
174 "Header (tier 3) tag with no arguments, which converts directly to HTML5 `<h3>`.",
175 Header3Tag, ["h3", "subtopic"], "<h3>", "</h3>"
176}
177simple_tag! {
178 "Header (tier 4) tag with no arguments, which converts directly to HTML5 `<h4>`.",
179 Header4Tag, ["h4"], "<h4>", "</h4>"
180}
181simple_tag! {
182 "Header (tier 5) tag with no arguments, which converts directly to HTML5 `<h5>`.",
183 Header5Tag, ["h5"], "<h5>", "</h5>"
184}
185simple_tag! {
186 "Header (tier 6) tag with no arguments, which converts directly to HTML5 `<h6>`.",
187 Header6Tag, ["h6"], "<h6>", "</h6>"
188}
189simple_tag! {
190 "Centering tag with no arguments, which converts to a div with styling to horizontally center it.",
191 CenterTag, ["center"], "<div style=\"display: flex; justify-content: center;\"><div>", "</div></div>"
192}
193simple_tag! {
194 "Left-align tag with no arguments, which converts to a div with styling to left-align it.",
195 LeftTag, ["left"], "<div style=\"display: flex; justify-content: left;\"><div>", "</div></div>"
196}
197simple_tag! {
198 "Right-align tag with no arguments, which converts to a div with styling to right-align it.",
199 RightTag, ["right"], "<div style=\"display: flex; justify-content: right;\"><div>", "</div></div>"
200}
201simple_tag! {
202 "Preformatted styling tag with no arguments, which converts directly to HTML5 `<pre>`.",
203 PreformattedTag, ["pre", "codeblock"], "<pre>", "</pre>"
204}
205simple_tag! {
206 "Code styling tag with no arguments, which converts directly to HTML5 `<code>`.",
207 CodeTag, ["code"], "<code>", "</code>"
208}
209simple_tag! {
210 "Keypress styling tag with no arguments, which converts directly to HTML5 `<kbd>`.",
211 KbdTag, ["kbd"], "<kbd>", "</kbd>"
212}
213
214macro_rules! tag_list {
215 ($ct:ident; $($tag:ident),*) => {
216 {
217 let v: Vec<Box<dyn HtmlTagWriter<$ct>>> = vec![
218 $(
219 Box::new($tag::default()),
220 )*
221 ];
222
223 v
224 }
225 };
226}
227
228pub fn all_core_v1_tags<CustomTy>() -> Vec<Box<dyn HtmlTagWriter<CustomTy>>>
251where
252 CustomTy: Clone + Default + 'static,
253{
254 tag_list! {CustomTy;
255 BoldTag,
256 ItalicTag,
257 UnderlineTag,
258 LinebreakTag,
259 QuoteTag,
260 BlockQuoteTag,
261 SubscriptTag,
262 SuperscriptTag,
263 Header1Tag,
264 Header2Tag,
265 Header3Tag,
266 Header4Tag,
267 Header5Tag,
268 Header6Tag,
269 CenterTag
270 }
271}