http_type/cookie/
impl.rs

1use crate::*;
2
3/// Implementation for `CookieBuilder`.
4impl CookieBuilder {
5    /// Creates a new cookie builder instance.
6    ///
7    /// # Arguments
8    ///
9    /// - `AsRef<str>` - The cookie name type.
10    /// - `AsRef<str>` - The cookie value type.
11    ///
12    /// # Returns
13    ///
14    /// - `CookieBuilder` - A new builder instance.
15    #[inline(always)]
16    pub fn new<N, V>(name: N, value: V) -> Self
17    where
18        N: AsRef<str>,
19        V: AsRef<str>,
20    {
21        Self {
22            name: name.as_ref().to_owned(),
23            value: value.as_ref().to_owned(),
24            ..Default::default()
25        }
26    }
27
28    /// Parses a `Set-Cookie` header string into a `CookieBuilder`.
29    ///
30    /// This method takes a `Set-Cookie` header string and extracts the various
31    /// attributes of a cookie, populating a `CookieBuilder` instance.
32    ///
33    /// # Arguments
34    ///
35    /// - `AsRef<str>` - The `Set-Cookie` header string to parse.
36    ///
37    /// # Returns
38    ///
39    /// A `CookieBuilder` instance populated with the parsed cookie attributes.
40    pub fn parse<C>(cookie: C) -> Self
41    where
42        C: AsRef<str>,
43    {
44        let mut cookie_builder: Self = Self::default();
45        let parts: Vec<&str> = cookie.as_ref().split(SEMICOLON).collect();
46        if parts.is_empty() {
47            return cookie_builder;
48        }
49        if let Some(name_value_pair) = parts.first() {
50            let name_value_pair: &str = name_value_pair.trim();
51            if let Some((name, value)) = name_value_pair.split_once(EQUAL) {
52                cookie_builder.name = name.trim().to_string();
53                cookie_builder.value = value.trim().to_string();
54            } else if !name_value_pair.is_empty() {
55                cookie_builder.name = name_value_pair.to_string();
56                cookie_builder.value = String::new();
57            }
58        }
59        for part in parts.iter().skip(1) {
60            let part: &str = part.trim();
61            if part.is_empty() {
62                continue;
63            }
64            if let Some((key, value)) = part.split_once(EQUAL) {
65                let key_lowercase: String = key.trim().to_lowercase();
66                let value: String = value.trim().to_string();
67                match key_lowercase.as_str() {
68                    COOKIE_EXPIRES_LOWERCASE => {
69                        cookie_builder.expires = value;
70                    }
71                    COOKIE_MAX_AGE_LOWERCASE => {
72                        if let Ok(max_age_value) = value.parse::<i64>() {
73                            cookie_builder.max_age = max_age_value;
74                        }
75                    }
76                    COOKIE_DOMAIN_LOWERCASE => {
77                        cookie_builder.domain = value;
78                    }
79                    COOKIE_PATH_LOWERCASE => {
80                        cookie_builder.path = value;
81                    }
82                    COOKIE_SAME_SITE_LOWERCASE => {
83                        cookie_builder.same_site = value;
84                    }
85                    _ => {}
86                }
87            } else {
88                let attribute_lowercase: String = part.to_lowercase();
89                match attribute_lowercase.as_str() {
90                    COOKIE_SECURE_LOWERCASE => {
91                        cookie_builder.secure = true;
92                    }
93                    COOKIE_HTTP_ONLY_LOWERCASE => {
94                        cookie_builder.http_only = true;
95                    }
96                    _ => {}
97                }
98            }
99        }
100        cookie_builder
101    }
102
103    /// Builds the cookie string according to the `Set-Cookie` header format.
104    ///
105    /// Only includes attributes that have been set with non-empty values.
106    ///
107    /// # Returns
108    ///
109    /// - `String` - A formatted cookie string ready to be sent in a `Set-Cookie` header.
110    pub fn build(&self) -> String {
111        if self.name.is_empty() {
112            return String::new();
113        }
114        let mut cookie_string: String = format!("{}={}", self.name, self.value);
115
116        if !self.expires.is_empty() {
117            cookie_string.push_str(COOKIE_EXPIRES_ATTRIBUTE_LOWERCASE);
118            cookie_string.push_str(&self.expires);
119        }
120        if self.max_age > 0 {
121            cookie_string.push_str(COOKIE_MAX_AGE_ATTRIBUTE_LOWERCASE);
122            cookie_string.push_str(&self.max_age.to_string());
123        }
124        if !self.domain.is_empty() {
125            cookie_string.push_str(COOKIE_DOMAIN_ATTRIBUTE_LOWERCASE);
126            cookie_string.push_str(&self.domain);
127        }
128        if !self.path.is_empty() {
129            cookie_string.push_str(COOKIE_PATH_ATTRIBUTE_LOWERCASE);
130            cookie_string.push_str(&self.path);
131        }
132        if self.secure {
133            cookie_string.push_str(COOKIE_SECURE_ATTRIBUTE_LOWERCASE);
134        }
135        if self.http_only {
136            cookie_string.push_str(COOKIE_HTTP_ONLY_ATTRIBUTE_LOWERCASE);
137        }
138        if !self.same_site.is_empty() {
139            cookie_string.push_str(COOKIE_SAME_SITE_ATTRIBUTE_LOWERCASE);
140            cookie_string.push_str(&self.same_site);
141        }
142        cookie_string
143    }
144}
145
146/// Implementation for `Cookie`.
147impl Cookie {
148    /// Parses a `Cookie` header string into a collection of key-value pairs.
149    ///
150    /// This method takes a `Cookie` header string (typically from a `Cookie` request header)
151    /// and parses it into a map of cookie names to their values.
152    ///
153    /// # Arguments
154    ///
155    /// - `AsRef<str>` - The `Cookie` header string to parse.
156    ///
157    /// # Returns
158    ///
159    /// A `Cookies` collection (a hash map) containing all parsed cookie key-value pairs.
160    pub fn parse<C>(cookie: C) -> Cookies
161    where
162        C: AsRef<str>,
163    {
164        let cookie_ref: &str = cookie.as_ref();
165        let mut cookies: Cookies = hash_map_xx_hash3_64();
166        if cookie_ref.trim().is_empty() {
167            return cookies;
168        }
169        let parts: Vec<&str> = cookie_ref.split(SEMICOLON).collect();
170        for part in parts {
171            let part: &str = part.trim();
172            if part.is_empty() {
173                continue;
174            }
175            if let Some((name, value)) = part.split_once(EQUAL) {
176                let name: String = name.trim().to_string();
177                let value: String = value.trim().to_string();
178                if !name.is_empty() {
179                    cookies.insert(name, value);
180                }
181            } else if !part.is_empty() {
182                cookies.insert(part.to_string(), String::new());
183            }
184        }
185        cookies
186    }
187}