html_site_generator/html/
image.rs

1use std::io::Write;
2
3use anyhow::Result;
4use derive_builder::Builder;
5use html_site_generator_macro::DeriveSetHtmlAttributes;
6
7use super::hyperlink::ReferrerPolicy;
8use super::link::CrossOrigin;
9use super::{IntoHtmlNode, IsParagraph};
10use crate::attributes::HtmlAttributes;
11
12/// Specifies whether a browser should load an image immediately or to defer loading of images until some conditions are met
13#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
14pub enum Loading {
15    Eager,
16    Lazy,
17}
18
19impl Loading {
20    fn to_html_string(&self) -> &'static str {
21        match self {
22            Loading::Eager => "eager",
23            Loading::Lazy => "lazy",
24        }
25    }
26}
27
28/// HTML Attribute: [`<img>`](https://www.w3schools.com/tags/tag_img.asp)
29#[derive(Debug, Builder, DeriveSetHtmlAttributes)]
30pub struct Image {
31    /// Specifies an alternate text for an image
32    #[builder(setter(strip_option, into), default)]
33    alt: Option<String>,
34
35    /// Allow images from third-party sites that allow cross-origin access to be used with canvas
36    #[builder(setter(strip_option), default)]
37    cross_origin: Option<CrossOrigin>,
38
39    /// Specifies the height of an image
40    #[builder(setter(strip_option), default)]
41    height: Option<u32>,
42
43    /// Specifies an image as a server-side image map
44    #[builder(setter(strip_option), default = "false")]
45    ismap: bool,
46
47    /// Specifies whether a browser should load an image immediately or to defer loading of images until some conditions are met
48    #[builder(setter(strip_option), default)]
49    loading: Option<Loading>,
50
51    // TODO not really supported https://www.w3schools.com/tags/att_img_longdesc.asp
52    /// Specifies a URL to a detailed description of an image
53    // #[builder(setter(strip_option, into), default)]
54    // long_desc: Option<String>,
55
56    /// Specifies which referrer information to use when fetching an image
57    #[builder(setter(strip_option), default)]
58    referrer_policy: Option<ReferrerPolicy>,
59
60    // TODO add logic to check with `src_set`, https://www.dofactory.com/html/img/sizes
61    /// Specifies image sizes for different page layouts
62    // #[builder(setter(strip_option, into), default)]
63    // sizes: Option<String>,
64
65    /// Specifies the path to the image
66    #[builder(setter(strip_option, into), default)]
67    src: Option<String>,
68
69    // TODO add logic to check with `sizes`, https://www.dofactory.com/html/img/sizes
70    /// Specifies a list of image files to use in different situations
71    // #[builder(setter(strip_option, into), default)]
72    // src_set: Option<Vec<String>>,
73
74    /// Specifies an image as a client-side image map
75    // TODO implement html tag `map` before implementing this here in `Image`
76    // #[builder(setter(strip_option, into), default)]
77    // usemap: Option<String>,
78
79    /// Specifies the width of an image
80    #[builder(setter(strip_option), default)]
81    width: Option<u32>,
82
83    #[builder(setter(skip))]
84    _attributes: HtmlAttributes,
85}
86
87impl IntoHtmlNode for Image {
88    fn transform_into_html_node(&self, buffer: &mut dyn Write) -> Result<()> {
89        write!(buffer, "<img")?;
90        self._attributes.transform_into_html_node(buffer)?;
91
92        if let Some(value) = &self.alt {
93            write!(buffer, " alt=\"{}\"", value)?;
94        }
95        if let Some(value) = &self.cross_origin {
96            write!(buffer, " crossorigin=\"{}\"", value.to_html_string())?;
97        }
98        if let Some(value) = &self.height {
99            write!(buffer, " height=\"{}\"", value)?;
100        }
101        if let Some(value) = &self.loading {
102            write!(buffer, " loading=\"{}\"", value.to_html_string())?;
103        }
104        if let Some(value) = &self.referrer_policy {
105            write!(buffer, " referrerpolicy=\"{}\"", value.to_html_string())?;
106        }
107        if let Some(value) = &self.src {
108            write!(buffer, " src=\"{}\"", value)?;
109        }
110        if let Some(value) = &self.width {
111            write!(buffer, " width=\"{}\"", value)?;
112        }
113
114        if self.ismap {
115            write!(buffer, " ismap")?;
116        }
117
118        write!(buffer, ">")?;
119
120        Ok(())
121    }
122}
123
124// TODO I'm not quit sure if `Image` implements the trait `IsParagraph`.
125// But at the moment this is the only way to use `Hyperlink` in a `Paragraph`
126// Maybe there is another, and better way, to do this?
127// same as `Hyperlink`, `LineBreak`
128impl IsParagraph for Image {
129    fn to_raw(&self) -> String {
130        let mut vec = Vec::new();
131
132        self.transform_into_html_node(&mut vec).unwrap();
133
134        String::from_utf8(vec).unwrap()
135    }
136}