1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
use super::{FitIn, ResponseMode, Settings, Trim};
use crate::server::Security;
use base64ct::{Base64Url, Encoding};
use hmac::Mac;

impl Settings {
    fn build_path(&self, image_uri: &str) -> String {
        let mut path = vec![];

        if let Some(resp) = &self.response {
            path.push(
                match resp {
                    ResponseMode::Metadata => "meta",
                    ResponseMode::Debug => "debug",
                }
                .to_owned(),
            );
        }

        if let Some(orientation) = &self.trim {
            path.push(
                match orientation {
                    Trim::TopLeft => "trim:top-left",
                    Trim::BottomRight => "trim:bottom-right",
                }
                .to_owned(),
            );
        }

        if let Some(crop) = &self.crop {
            path.push(crop.to_string());
        }

        if let Some(fit_in) = &self.fit_in {
            path.push(
                match fit_in {
                    FitIn::Default => "fit-in",
                    FitIn::Adaptive => "adaptive-fit-in",
                    FitIn::Full => "full-fit-in",
                }
                .to_owned(),
            );
        }

        if let Some(resize) = &self.resize {
            path.push(resize.to_string());
        }

        if let Some(h_align) = &self.h_align {
            path.push(h_align.as_ref().to_owned());
        }

        if let Some(v_align) = &self.v_align {
            path.push(v_align.as_ref().to_owned());
        }

        if self.smart {
            path.push("smart".to_owned());
        }

        if !self.filters.is_empty() {
            let filters = self
                .filters
                .iter()
                .map(ToString::to_string)
                .collect::<Vec<_>>()
                .join(":");

            path.push(format!("filters:{filters}"));
        }

        path.push(image_uri.to_owned());

        path.join("/")
    }

    pub fn to_path(&self, image_uri: &str) -> String {
        let path = self.build_path(image_uri);

        let security = match &self.server.security {
            Security::Unsafe => "unsafe",
            Security::Hmac(hmac) => {
                let mut mac = hmac.clone();
                mac.update(path.as_bytes());

                let signature = mac.finalize().into_bytes();
                &Base64Url::encode_string(&signature)
            }
        };

        format!("/{security}/{path}")
    }

    pub fn to_url(&self, image_uri: &str) -> String {
        format!("{}{}", self.server.origin, self.to_path(image_uri))
    }
}