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
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
//! Represents a collection of Request Headers

use std::collections::HashMap;

use log::error;
use reqwest::header::{HeaderMap, HeaderName, HeaderValue};

use crate::api::models::api_error::SquareApiError;
use crate::config;
use crate::http::client::config::SquareHttpClientConfig;

/// A collection of Request Headers
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Headers {
    /// The request headers as key-value pairs
    pub headers: HashMap<String, String>,
}

impl Headers {
    /// Indicates whether the headers include the User Agent header
    pub fn has_user_agent(&self) -> bool {
        self.headers.contains_key("user-agent")
    }

    /// Sets the User Agent header
    pub fn set_user_agent(&mut self, user_agent: &str) -> Option<String> {
        self.insert("user-agent", user_agent)
    }

    /// Adds a request header to the collection
    pub fn insert(&mut self, header_name: &str, header_value: &str) -> Option<String> {
        self.headers
            .insert(String::from(header_name), String::from(header_value))
    }
}

impl Default for Headers {
    /// The default set of request headers
    /// * Content-Type: application/json
    /// * Square-Version
    /// * accept: application/json
    /// * user-agent
    /// * Authorization
    fn default() -> Self {
        let mut headers = HashMap::new();

        headers.insert(String::from("Content-Type"), String::from("application/json"));
        headers.insert(String::from("Square-Version"), config::DEFAULT_API_VERSION.to_string());
        headers.insert(String::from("accept"), String::from("application/json"));
        headers.insert(String::from("user-agent"), SquareHttpClientConfig::default_user_agent());
        headers.insert(String::from("Authorization"), config::get_default_authorization_token());

        Self { headers }
    }
}

impl TryFrom<&Headers> for HeaderMap {
    type Error = SquareApiError;

    /// Converts `Headers` into Reqwest lib's `HeaderMap`
    fn try_from(headers: &Headers) -> Result<Self, Self::Error> {
        let mut header_map = Self::new();
        for (k, v) in &headers.headers {
            let header_name = HeaderName::from_bytes(k.as_bytes()).map_err(|e| {
                let msg = format!("Error generating {} header name: {}", k, e);
                error!("{}", msg);
                SquareApiError::new(&msg)
            })?;
            let header_value = HeaderValue::from_bytes(v.as_bytes()).map_err(|e| {
                let msg = format!("Error generating {} header value for header {}: {}", v, k, e);
                error!("{}", msg);
                SquareApiError::new(&msg)
            })?;
            header_map.insert(header_name, header_value);
        }

        Ok(header_map)
    }
}

#[cfg(test)]
mod tests {
    use std::collections::HashMap;

    use reqwest::header::HeaderMap;

    use crate::config;
    use crate::http::client::config::SquareHttpClientConfig;
    use crate::http::headers::Headers;

    #[test]
    fn headers_default() {
        let headers = Headers::default();
        assert_eq!(
            headers.headers.get("Content-Type"),
            Some(&String::from("application/json"))
        );
        assert_eq!(headers.headers.get("Square-Version"), Some(&String::from("2023-09-25")));
        assert_eq!(headers.headers.get("accept"), Some(&String::from("application/json")));
        assert_eq!(
            headers.headers.get("user-agent"),
            Some(&SquareHttpClientConfig::default_user_agent())
        );
        assert_eq!(
            headers.headers.get("Authorization"),
            Some(&config::get_default_authorization_token())
        );
        assert!(headers.has_user_agent());
    }

    #[test]
    fn headers_has_user_agent() {
        let mut headers = Headers::default();
        headers.headers = HashMap::new();
        assert!(!headers.has_user_agent());
        headers.set_user_agent("some-user-agent");
        assert!(headers.has_user_agent());
    }

    #[test]
    fn headers_set_user_agent() {
        let mut headers = Headers::default();
        assert!(!(headers.headers.get("user-agent") == Some(&String::from("some-user-agent"))));
        headers.set_user_agent("some-user-agent");
        assert_eq!(
            Some(&String::from("some-user-agent")),
            headers.headers.get("user-agent")
        );
    }

    #[test]
    fn try_from_ok() {
        assert!(HeaderMap::try_from(&Headers::default()).is_ok());
    }

    #[test]
    fn try_from_error() {
        let mut headers = Headers::default();
        headers.headers = HashMap::new();
        headers
            .headers
            .insert(String::from("some_faulty_code\u{1234}"), String::from("some_value"));
        assert!(HeaderMap::try_from(&headers).is_err());
    }
}