build_html/html_container.rs
1//! Defines the `HtmlContainer` Trait
2
3use std::iter::empty;
4
5use crate::{Container, Html, HtmlChild, HtmlElement, HtmlTag, Table};
6
7/// An HTML element that can contain other HTML elements
8///
9/// The methods on this trait are implemented generically, allowing any type (or combination of
10/// types) implementing [`ToString`] to be passed in. For example, we can pass an `Ipv4Addr` object
11/// into `with_paragraph` directly:
12///
13/// ```
14/// # use build_html::*;
15/// # use std::net::Ipv4Addr;
16/// let content = HtmlElement::new(HtmlTag::Div)
17/// .with_paragraph(Ipv4Addr::new(127, 0, 0, 1))
18/// .to_html_string();
19/// assert_eq!(content, "<div><p>127.0.0.1</p></div>")
20/// ```
21///
22/// Attributes can be passed in using any type that implements [`IntoIterator`] for 2-tuples of
23/// objects implementing `ToString`. That includes (as of Rust 1.53) arrays of `&str`s, which are
24/// very handy when content is known. For more dynamic attribute action,
25/// [`HashMap`](std::collections::HashMap)s can also be used.
26///
27/// ```
28/// # use build_html::*;
29/// let content = HtmlElement::new(HtmlTag::Div)
30/// .with_paragraph_attr("123", [("id", "paragraph"), ("class", "action")])
31/// .to_html_string();
32/// assert_eq!(content, r#"<div><p id="paragraph" class="action">123</p></div>"#)
33/// ```
34///
35/// There are two different ways of interacting with `HtmlContainer` objects which will suit
36/// different use cases. The first is using the *with* API, which consumes the calling container.
37/// Because the calling container is consumed and returned, it can be chained effectively. This
38/// makes it very useful for building containers whose content is known ahead of time, and for
39/// building content using iterators. For example:
40///
41/// ```
42/// # use build_html::*;
43/// // Aggregating content
44/// let list_items = (0..3)
45/// .map(|x| format!("List item {}", x))
46/// .fold(Container::new(ContainerType::OrderedList), |a, n| a.with_paragraph(n));
47///
48/// // Defining content literally
49/// let content = HtmlElement::new(HtmlTag::Div)
50/// .with_paragraph("paragraph text")
51/// .with_container(list_items)
52/// .to_html_string();
53///
54/// assert_eq!(
55/// content,
56/// concat!(
57/// "<div><p>paragraph text</p><ol>",
58/// "<li><p>List item 0</p></li>",
59/// "<li><p>List item 1</p></li>",
60/// "<li><p>List item 2</p></li></ol></div>"
61/// )
62/// )
63/// ```
64///
65/// The other is using the *add* API, which acts on mutable references. This method is effective for
66/// more imperative programming and more delicate control flow.
67///
68/// ```
69/// # use build_html::*;
70/// let mut container = HtmlElement::new(HtmlTag::Div);
71/// if true {
72/// container.add_paragraph("optional content");
73/// }
74/// for i in 0..3 {
75/// container.add_paragraph(format!("Item: {}", i));
76/// }
77/// assert_eq!(
78/// container.to_html_string(),
79/// concat!(
80/// "<div><p>optional content</p>",
81/// "<p>Item: 0</p><p>Item: 1</p>",
82/// "<p>Item: 2</p></div>"
83/// )
84/// );
85/// ```
86pub trait HtmlContainer: Html + Sized {
87 /// Adds the specified HTML element to this container
88 ///
89 /// This method can be used as an escape hatch to insert arbitrary types into the HTML document,
90 /// helping to make up for those types which are not supported natively by this library. This
91 /// can be done by defining your own types that implement the [`Html`] trait.
92 ///
93 /// If you need a simple one-off, it may be more convenient to insert the element as a raw
94 /// string using [`add_raw`](HtmlContainer::add_raw) method instead
95 ///
96 /// # Example
97 /// ```
98 /// # use build_html::*;
99 /// #[derive(Debug)]
100 /// struct Span {
101 /// content: String
102 /// }
103 ///
104 /// impl Span {
105 /// pub fn new(content: impl ToString) -> Self {
106 /// Span { content: content.to_string() }
107 /// }
108 /// }
109 ///
110 /// impl Html for Span {
111 /// fn to_html_string(&self) -> String {
112 /// format!("<span>{}</span>", self.content)
113 /// }
114 /// }
115 ///
116 /// let mut content = HtmlElement::new(HtmlTag::Div);
117 /// content.add_html(Span::new("inner"));
118 /// assert_eq!(content.to_html_string(), "<div><span>inner</span></div>");
119 /// ```
120 fn add_html<H: Html>(&mut self, html: H);
121
122 /// Consumes the container, returning it with the specified HTML element added to it
123 ///
124 /// This method can be used as an escape hatch to insert arbitrary types into the HTML document,
125 /// helping to make up for those types which are not supported natively by this library. This
126 /// can be done by defining your own types that implement the [`Html`] trait.
127 ///
128 /// If you need a simple one-off, it may be more convenient to insert the element as a raw
129 /// string using [`with_raw`](HtmlContainer::with_raw) method instead
130 ///
131 /// # Example
132 /// ```
133 /// # use build_html::*;
134 /// #[derive(Debug)]
135 /// struct Span {
136 /// content: String
137 /// }
138 ///
139 /// impl Span {
140 /// pub fn new(content: impl ToString) -> Self {
141 /// Span { content: content.to_string() }
142 /// }
143 /// }
144 ///
145 /// impl Html for Span {
146 /// fn to_html_string(&self) -> String {
147 /// format!("<span>{}</span>", self.content)
148 /// }
149 /// }
150 ///
151 /// let content = HtmlElement::new(HtmlTag::Div)
152 /// .with_html(Span::new("inner"))
153 /// .to_html_string();
154 /// assert_eq!(content, "<div><span>inner</span></div>");
155 /// ```
156 #[inline]
157 fn with_html<H: Html>(mut self, html: H) -> Self {
158 self.add_html(html);
159 self
160 }
161
162 /// Add the container to this HTML Container
163 ///
164 /// # Example
165 /// ```
166 /// # use build_html::*;
167 /// let mut content = Container::default();
168 /// content.add_container(Container::new(ContainerType::Main).with_paragraph("Inside"));
169 /// assert_eq!(content.to_html_string(), "<div><main><p>Inside</p></main></div>");
170 /// ```
171 #[inline]
172 fn add_container(&mut self, container: Container) {
173 self.add_html(container)
174 }
175
176 /// Nest the specified container within this container
177 ///
178 /// # Example
179 /// ```
180 /// # use build_html::*;
181 /// let content = Container::default()
182 /// .with_header(1, "Content Outside")
183 /// .with_container(
184 /// Container::new(ContainerType::Main)
185 /// .with_paragraph("Content Inside")
186 /// )
187 /// .to_html_string();
188 ///
189 /// assert_eq!(
190 /// content,
191 /// "<div><h1>Content Outside</h1><main><p>Content Inside</p></main></div>"
192 /// );
193 /// ```
194 #[inline]
195 fn with_container(self, container: Container) -> Self {
196 self.with_html(container)
197 }
198
199 /// Add the specified `Table` to this container
200 ///
201 /// # Example
202 /// ```
203 /// # use build_html::*;
204 /// let table = Table::from([
205 /// [1, 2, 3],
206 /// [4, 5, 6]
207 /// ]).with_header_row(['A', 'B', 'C']);
208 /// let mut container = HtmlElement::new(HtmlTag::Div);
209 /// container.add_table(table);
210 ///
211 /// assert_eq!(
212 /// container.to_html_string(),
213 /// concat!(
214 /// "<div><table><thead>",
215 /// "<tr><th>A</th><th>B</th><th>C</th></tr>",
216 /// "</thead><tbody>",
217 /// "<tr><td>1</td><td>2</td><td>3</td></tr>",
218 /// "<tr><td>4</td><td>5</td><td>6</td></tr>",
219 /// "</tbody></table></div>"
220 /// )
221 /// );
222 /// ```
223 fn add_table(&mut self, table: Table) {
224 self.add_html(table);
225 }
226
227 /// Nest the specified `Table` within this container
228 ///
229 /// # Example
230 /// ```
231 /// # use build_html::*;
232 /// let content = HtmlElement::new(HtmlTag::Div)
233 /// .with_table(
234 /// Table::from(&[
235 /// [1, 2, 3],
236 /// [4, 5, 6]
237 /// ])
238 /// .with_header_row(&['A', 'B', 'C'])
239 /// )
240 /// .to_html_string();
241 ///
242 /// assert_eq!(
243 /// content,
244 /// concat!(
245 /// "<div><table><thead>",
246 /// "<tr><th>A</th><th>B</th><th>C</th></tr>",
247 /// "</thead><tbody>",
248 /// "<tr><td>1</td><td>2</td><td>3</td></tr>",
249 /// "<tr><td>4</td><td>5</td><td>6</td></tr>",
250 /// "</tbody></table></div>"
251 /// )
252 /// );
253 /// ```
254 fn with_table(self, table: Table) -> Self {
255 self.with_html(table)
256 }
257
258 /// Adds a header tag with the designated level to this container
259 ///
260 /// # Example
261 /// ```
262 /// # use build_html::*;
263 /// let mut content = HtmlElement::new(HtmlTag::Div);
264 /// content.add_header(1, "Header Text");
265 /// assert_eq!(content.to_html_string(), r#"<div><h1>Header Text</h1></div>"#);
266 /// ```
267 fn add_header(&mut self, level: u8, text: impl ToString) {
268 self.add_header_attr(level, text, empty::<(&str, &str)>());
269 }
270
271 /// Adds a header tag with the designated level to this container
272 ///
273 /// # Example
274 /// ```
275 /// # use build_html::*;
276 /// let content = HtmlElement::new(HtmlTag::Div)
277 /// .with_header(1, "Header Text")
278 /// .to_html_string();
279 ///
280 /// assert_eq!(content, r#"<div><h1>Header Text</h1></div>"#);
281 /// ```
282 fn with_header(self, level: u8, text: impl ToString) -> Self {
283 self.with_header_attr(level, text, empty::<(&str, &str)>())
284 }
285
286 /// Adds a header tag with the designated level and attributes to this container.
287 ///
288 /// # Example
289 /// ```
290 /// # use build_html::*;
291 /// let mut content = HtmlElement::new(HtmlTag::Div);
292 /// content.add_header_attr(1, "Header Text", std::iter::once(("id", "main-header")));
293 /// assert_eq!(content.to_html_string(), r#"<div><h1 id="main-header">Header Text</h1></div>"#);
294 /// ```
295 fn add_header_attr<A, S>(&mut self, level: u8, text: impl ToString, attr: A)
296 where
297 A: IntoIterator<Item = (S, S)>,
298 S: ToString,
299 {
300 let tag = match level {
301 1 => HtmlTag::Heading1,
302 2 => HtmlTag::Heading2,
303 3 => HtmlTag::Heading3,
304 4 => HtmlTag::Heading4,
305 5 => HtmlTag::Heading5,
306 6 => HtmlTag::Heading6,
307 _ => panic!("'{}' is not a valid html heading level", level),
308 };
309
310 let mut element = HtmlElement::new(tag).with_child(HtmlChild::Raw(text.to_string()));
311 for (k, v) in attr {
312 element.add_attribute(k, v)
313 }
314
315 self.add_html(element);
316 }
317
318 /// Adds a header tag with the designated level and attributes to this container.
319 ///
320 /// # Example
321 /// ```
322 /// # use build_html::*;
323 /// let content = HtmlElement::new(HtmlTag::Div)
324 /// .with_header_attr(1, "Header Text", std::iter::once(("id", "main-header")))
325 /// .to_html_string();
326 ///
327 /// assert_eq!(content, r#"<div><h1 id="main-header">Header Text</h1></div>"#);
328 /// ```
329 fn with_header_attr<A, S>(mut self, level: u8, text: impl ToString, attr: A) -> Self
330 where
331 A: IntoIterator<Item = (S, S)>,
332 S: ToString,
333 {
334 self.add_header_attr(level, text, attr);
335 self
336 }
337
338 /// Adds an `<img>` tag to this container
339 ///
340 /// # Example
341 /// ```
342 /// # use build_html::*;
343 /// let mut content = HtmlElement::new(HtmlTag::Div);
344 /// content.add_image("myimage.png", "a test image");
345 /// assert_eq!(
346 /// content.to_html_string(),
347 /// r#"<div><img src="myimage.png" alt="a test image"/></div>"#
348 /// );
349 /// ```
350 fn add_image(&mut self, src: impl ToString, alt: impl ToString) {
351 self.add_image_attr(src, alt, empty::<(&str, &str)>());
352 }
353
354 /// Adds an `<img>` tag to this container
355 ///
356 /// # Example
357 /// ```
358 /// # use build_html::*;
359 /// let content = HtmlElement::new(HtmlTag::Div)
360 /// .with_image("myimage.png", "a test image")
361 /// .to_html_string();
362 ///
363 /// assert_eq!(content, r#"<div><img src="myimage.png" alt="a test image"/></div>"#);
364 /// ```
365 fn with_image(self, src: impl ToString, alt: impl ToString) -> Self {
366 self.with_image_attr(src, alt, empty::<(&str, &str)>())
367 }
368
369 /// Adds an `<img>` tag with the specified attributes to this container
370 ///
371 /// # Example
372 /// ```
373 /// # use build_html::*;
374 /// # use std::collections::BTreeMap;
375 /// let mut attrs = BTreeMap::new();
376 /// attrs.insert("id", "sample-image");
377 /// let mut content = HtmlElement::new(HtmlTag::Div);
378 /// content.add_image_attr("myimage.png", "a test image", attrs);
379 ///
380 /// assert_eq!(
381 /// content.to_html_string(),
382 /// r#"<div><img src="myimage.png" alt="a test image" id="sample-image"/></div>"#
383 /// );
384 /// ```
385 fn add_image_attr<A, S>(&mut self, src: impl ToString, alt: impl ToString, attr: A)
386 where
387 A: IntoIterator<Item = (S, S)>,
388 S: ToString,
389 {
390 let mut element = HtmlElement::new(HtmlTag::Image)
391 .with_attribute("src", src)
392 .with_attribute("alt", alt);
393 for (k, v) in attr {
394 element.add_attribute(k, v);
395 }
396
397 self.add_html(element);
398 }
399
400 /// Adds an `<img>` tag with the specified attributes to this container
401 ///
402 /// # Example
403 /// ```
404 /// # use build_html::*;
405 /// # use std::collections::BTreeMap;
406 /// let mut attrs = BTreeMap::new();
407 /// attrs.insert("id", "sample-image");
408 /// let content = HtmlElement::new(HtmlTag::Div)
409 /// .with_image_attr("myimage.png", "a test image", attrs)
410 /// .to_html_string();
411 ///
412 /// assert_eq!(
413 /// content,
414 /// r#"<div><img src="myimage.png" alt="a test image" id="sample-image"/></div>"#
415 /// );
416 /// ```
417 fn with_image_attr<A, S>(mut self, src: impl ToString, alt: impl ToString, attr: A) -> Self
418 where
419 A: IntoIterator<Item = (S, S)>,
420 S: ToString,
421 {
422 self.add_image_attr(src, alt, attr);
423 self
424 }
425
426 /// Adds an `<a>` tag to this container
427 ///
428 /// # Example
429 /// ```
430 /// # use build_html::*;
431 /// let mut content = HtmlElement::new(HtmlTag::Div);
432 /// content.add_link("https://rust-lang.org/", "Rust Homepage");
433 ///
434 /// assert_eq!(
435 /// content.to_html_string(),
436 /// r#"<div><a href="https://rust-lang.org/">Rust Homepage</a></div>"#
437 /// );
438 /// ```
439 fn add_link(&mut self, href: impl ToString, text: impl ToString) {
440 self.add_link_attr(href, text, empty::<(&str, &str)>());
441 }
442
443 /// Adds an `<a>` tag to this container
444 ///
445 /// # Example
446 /// ```
447 /// # use build_html::*;
448 /// let content = HtmlElement::new(HtmlTag::Div)
449 /// .with_link("https://rust-lang.org/", "Rust Homepage")
450 /// .to_html_string();
451 ///
452 /// assert_eq!(content, r#"<div><a href="https://rust-lang.org/">Rust Homepage</a></div>"#)
453 /// ```
454 fn with_link(self, href: impl ToString, text: impl ToString) -> Self {
455 self.with_link_attr(href, text, empty::<(&str, &str)>())
456 }
457
458 /// Adds an `<a>` tag with the specified attributes to this container
459 ///
460 /// # Example
461 /// ```
462 /// # use build_html::*;
463 /// let mut content = HtmlElement::new(HtmlTag::Div);
464 /// content.add_link_attr("https://rust-lang.org/", "Rust Homepage", [("class", "links")]);
465 ///
466 /// assert_eq!(
467 /// content.to_html_string(),
468 /// r#"<div><a href="https://rust-lang.org/" class="links">Rust Homepage</a></div>"#
469 /// );
470 /// ```
471 fn add_link_attr<A, S>(&mut self, href: impl ToString, text: impl ToString, attr: A)
472 where
473 A: IntoIterator<Item = (S, S)>,
474 S: ToString,
475 {
476 let mut element = HtmlElement::new(HtmlTag::Link)
477 .with_attribute("href", href)
478 .with_child(HtmlChild::Raw(text.to_string()));
479 for (k, v) in attr {
480 element.add_attribute(k, v);
481 }
482 self.add_html(element);
483 }
484
485 /// Adds an `<a>` tag with the specified attributes to this container
486 ///
487 /// # Example
488 /// ```
489 /// # use build_html::*;
490 /// let content = HtmlElement::new(HtmlTag::Div)
491 /// .with_link_attr("https://rust-lang.org/", "Rust Homepage", [("class", "links")])
492 /// .to_html_string();
493 ///
494 /// assert_eq!(
495 /// content,
496 /// r#"<div><a href="https://rust-lang.org/" class="links">Rust Homepage</a></div>"#
497 /// )
498 /// ```
499 fn with_link_attr<A, S>(mut self, href: impl ToString, text: impl ToString, attr: A) -> Self
500 where
501 A: IntoIterator<Item = (S, S)>,
502 S: ToString,
503 {
504 self.add_link_attr(href, text, attr);
505 self
506 }
507
508 /// Adds a `<p>` tag element to this Container
509 ///
510 /// # Example
511 /// ```
512 /// # use build_html::*;
513 /// let mut content = HtmlElement::new(HtmlTag::Div);
514 /// content.add_paragraph("This is sample paragraph text");
515 /// assert_eq!(content.to_html_string(), r#"<div><p>This is sample paragraph text</p></div>"#);
516 /// ```
517 fn add_paragraph(&mut self, text: impl ToString) {
518 self.add_paragraph_attr(text, empty::<(&str, &str)>());
519 }
520
521 /// Adds a `<p>` tag element to this Container
522 ///
523 /// # Example
524 /// ```
525 /// # use build_html::*;
526 /// let content = HtmlElement::new(HtmlTag::Div)
527 /// .with_paragraph("This is sample paragraph text")
528 /// .to_html_string();
529 ///
530 /// assert_eq!(content, r#"<div><p>This is sample paragraph text</p></div>"#);
531 /// ```
532 fn with_paragraph(self, text: impl ToString) -> Self {
533 self.with_paragraph_attr(text, empty::<(&str, &str)>())
534 }
535
536 /// Adds a `<p>` tag element with the specified attributes to this Container
537 ///
538 /// # Example
539 /// ```
540 /// # use build_html::*;
541 /// let mut content = HtmlElement::new(HtmlTag::Div);
542 /// content.add_paragraph_attr("This is sample paragraph text", [("class", "text")]);
543 /// assert_eq!(
544 /// content.to_html_string(),
545 /// r#"<div><p class="text">This is sample paragraph text</p></div>"#
546 /// );
547 /// ```
548 fn add_paragraph_attr<A, S>(&mut self, text: impl ToString, attr: A)
549 where
550 A: IntoIterator<Item = (S, S)>,
551 S: ToString,
552 {
553 let mut element =
554 HtmlElement::new(HtmlTag::ParagraphText).with_child(HtmlChild::Raw(text.to_string()));
555 for (k, v) in attr {
556 element.add_attribute(k, v);
557 }
558 self.add_html(element);
559 }
560
561 /// Adds a `<p>` tag element with the specified attributes to this Container
562 ///
563 /// # Example
564 /// ```
565 /// # use build_html::*;
566 /// let content = HtmlElement::new(HtmlTag::Div)
567 /// .with_paragraph_attr("This is sample paragraph text", [("class", "text")])
568 /// .to_html_string();
569 ///
570 /// assert_eq!(content, r#"<div><p class="text">This is sample paragraph text</p></div>"#)
571 /// ```
572 fn with_paragraph_attr<A, S>(mut self, text: impl ToString, attr: A) -> Self
573 where
574 A: IntoIterator<Item = (S, S)>,
575 S: ToString,
576 {
577 self.add_paragraph_attr(text, attr);
578 self
579 }
580
581 /// Adds a `<pre>` tag element to this container
582 ///
583 /// # Example
584 /// ```
585 /// # use build_html::*;
586 /// let mut content = HtmlElement::new(HtmlTag::Div);
587 /// content.add_preformatted("This | is preformatted => text");
588 /// assert_eq!(
589 /// content.to_html_string(),
590 /// r#"<div><pre>This | is preformatted => text</pre></div>"#
591 /// );
592 /// ```
593 fn add_preformatted(&mut self, text: impl ToString) {
594 self.add_preformatted_attr(text, empty::<(&str, &str)>());
595 }
596
597 /// Adds a `<pre>` tag element to this container
598 ///
599 /// # Example
600 /// ```
601 /// # use build_html::*;
602 /// let content = HtmlElement::new(HtmlTag::Div)
603 /// .with_preformatted("This | is preformatted => text")
604 /// .to_html_string();
605 ///
606 /// assert_eq!(content, r#"<div><pre>This | is preformatted => text</pre></div>"#);
607 /// ```
608 fn with_preformatted(self, text: impl ToString) -> Self {
609 self.with_preformatted_attr(text, empty::<(&str, &str)>())
610 }
611
612 /// Adds a `<pre>` tag element with the specified attributes to this container
613 ///
614 /// # Example
615 /// ```
616 /// # use build_html::*;
617 /// let mut content = HtmlElement::new(HtmlTag::Div);
618 /// content.add_preformatted_attr("This | is preformatted => text", [("id", "code")]);
619 /// assert_eq!(
620 /// content.to_html_string(),
621 /// r#"<div><pre id="code">This | is preformatted => text</pre></div>"#
622 /// );
623 /// ```
624 fn add_preformatted_attr<A, S>(&mut self, text: impl ToString, attr: A)
625 where
626 A: IntoIterator<Item = (S, S)>,
627 S: ToString,
628 {
629 let mut element = HtmlElement::new(HtmlTag::PreformattedText)
630 .with_child(HtmlChild::Raw(text.to_string()));
631 for (k, v) in attr {
632 element.add_attribute(k, v);
633 }
634 self.add_html(element);
635 }
636
637 /// Adds a `<pre>` tag element with the specified attributes to this container
638 ///
639 /// # Example
640 /// ```
641 /// # use build_html::*;
642 /// let content = HtmlElement::new(HtmlTag::Div)
643 /// .with_preformatted_attr("This | is preformatted => text", [("id", "code")])
644 /// .to_html_string();
645 ///
646 /// assert_eq!(content, r#"<div><pre id="code">This | is preformatted => text</pre></div>"#)
647 /// ```
648 fn with_preformatted_attr<A, S>(mut self, text: impl ToString, attr: A) -> Self
649 where
650 A: IntoIterator<Item = (S, S)>,
651 S: ToString,
652 {
653 self.add_preformatted_attr(text, attr);
654 self
655 }
656
657 /// Add raw content to the container. This content is pasted directly into the HTML
658 ///
659 /// This is intended to be used as an escape hatch for one-off insertions. If you want a more
660 /// reusable escape hatch, consider writing your own type implementing the [`Html`] trait. You
661 /// can then use [`add_html`](HtmlContainer::add_html) to insert boxed instances into the
662 /// container. See the documentation for that method for examples.
663 ///
664 /// # Example
665 /// ```
666 /// # use build_html::*;
667 /// let mut content = HtmlElement::new(HtmlTag::Div);
668 /// content.add_raw(r#"<video width="250"><source src="video.mp4" type="video/mp4"></video>"#);
669 /// assert_eq!(
670 /// content.to_html_string(),
671 /// r#"<div><video width="250"><source src="video.mp4" type="video/mp4"></video></div>"#
672 /// );
673 /// ```
674 fn add_raw(&mut self, content: impl ToString) {
675 self.add_html(content.to_string());
676 }
677
678 /// Add raw content to this container. The content is pasted directly into the HTML
679 ///
680 /// This is intended to be used as an escape hatch for one-off insertions. If you want a more
681 /// reusable escape hatch, consider writing your own type implementing the [`Html`] trait. You
682 /// can then use [`with_html`](HtmlContainer::with_html) to insert boxed instances into the
683 /// container. See the documentation for that method for examples.
684 ///
685 /// # Example
686 /// ```
687 /// # use build_html::*;
688 /// let content = HtmlElement::new(HtmlTag::Div)
689 /// .with_raw(r#"<video width="250"><source src="video.mp4" type="video/mp4"></video>"#)
690 /// .to_html_string();
691 ///
692 /// assert_eq!(
693 /// content,
694 /// r#"<div><video width="250"><source src="video.mp4" type="video/mp4"></video></div>"#
695 /// );
696 /// ```
697 fn with_raw(self, content: impl ToString) -> Self {
698 self.with_html(content.to_string())
699 }
700}