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            expires: None,
25            max_age: None,
26            domain: None,
27            path: None,
28            secure: None,
29            http_only: None,
30            same_site: None,
31        }
32    }
33
34    /// Parses a `Set-Cookie` header string into a `CookieBuilder`.
35    ///
36    /// This method takes a `Set-Cookie` header string and extracts the various
37    /// attributes of a cookie, populating a `CookieBuilder` instance.
38    ///
39    /// # Arguments
40    ///
41    /// - `AsRef<str>` - The `Set-Cookie` header string to parse.
42    ///
43    /// # Returns
44    ///
45    /// A `CookieBuilder` instance populated with the parsed cookie attributes.
46    pub fn parse<C>(cookie: C) -> Self
47    where
48        C: AsRef<str>,
49    {
50        let mut cookie_builder: Self = Self::default();
51        let parts: Vec<&str> = cookie.as_ref().split(SEMICOLON).collect();
52        if parts.is_empty() {
53            return cookie_builder;
54        }
55        if let Some(name_value_pair) = parts.first() {
56            let name_value_pair: &str = name_value_pair.trim();
57            if let Some((name, value)) = name_value_pair.split_once(EQUAL) {
58                cookie_builder.name = name.trim().to_string();
59                cookie_builder.value = value.trim().to_string();
60            } else if !name_value_pair.is_empty() {
61                cookie_builder.name = name_value_pair.to_string();
62                cookie_builder.value = String::new();
63            }
64        }
65        for part in parts.iter().skip(1) {
66            let part: &str = part.trim();
67            if part.is_empty() {
68                continue;
69            }
70            if let Some((key, value)) = part.split_once(EQUAL) {
71                let key_lowercase: String = key.trim().to_lowercase();
72                let value: String = value.trim().to_string();
73                match key_lowercase.as_str() {
74                    COOKIE_EXPIRES_LOWERCASE => {
75                        cookie_builder.set_expires(value);
76                    }
77                    COOKIE_MAX_AGE_LOWERCASE => {
78                        if let Ok(max_age_value) = value.parse::<i64>() {
79                            cookie_builder.set_max_age(max_age_value);
80                        }
81                    }
82                    COOKIE_DOMAIN_LOWERCASE => {
83                        cookie_builder.set_domain(value);
84                    }
85                    COOKIE_PATH_LOWERCASE => {
86                        cookie_builder.set_path(value);
87                    }
88                    COOKIE_SAME_SITE_LOWERCASE => {
89                        cookie_builder.set_same_site(value);
90                    }
91                    _ => {}
92                }
93            } else {
94                let attribute_lowercase: String = part.to_lowercase();
95                match attribute_lowercase.as_str() {
96                    COOKIE_SECURE_LOWERCASE => {
97                        cookie_builder.secure = Some(true);
98                    }
99                    COOKIE_HTTP_ONLY_LOWERCASE => {
100                        cookie_builder.http_only = Some(true);
101                    }
102                    _ => {}
103                }
104            }
105        }
106        cookie_builder
107    }
108
109    /// Sets the expiration date for the cookie.
110    ///
111    /// # Arguments
112    ///
113    /// - `AsRef<str>` - The expiration date string.
114    ///
115    /// # Returns
116    ///
117    /// The `CookieBuilder` instance for method chaining.
118    #[inline(always)]
119    pub fn set_expires<E>(&mut self, expires: E) -> &mut Self
120    where
121        E: AsRef<str>,
122    {
123        self.expires = Some(expires.as_ref().to_owned());
124        self
125    }
126
127    /// Sets the maximum age for the cookie in seconds.
128    ///
129    /// # Arguments
130    ///
131    /// - `Into<i64>` - The maximum age in seconds.
132    ///
133    /// # Returns
134    ///
135    /// The `CookieBuilder` instance for method chaining.
136    #[inline(always)]
137    pub fn set_max_age<M>(&mut self, max_age: M) -> &mut Self
138    where
139        M: Into<i64>,
140    {
141        self.max_age = Some(max_age.into());
142        self
143    }
144
145    /// Sets the domain for the cookie.
146    ///
147    /// # Arguments
148    ///
149    /// - `AsRef<str>` - The domain for the cookie.
150    ///
151    /// # Returns
152    ///
153    /// The `CookieBuilder` instance for method chaining.
154    #[inline(always)]
155    pub fn set_domain<D>(&mut self, domain: D) -> &mut Self
156    where
157        D: AsRef<str>,
158    {
159        self.domain = Some(domain.as_ref().to_owned());
160        self
161    }
162
163    /// Sets the path for the cookie.
164    ///
165    /// # Arguments
166    ///
167    /// - `AsRef<str>` - The path for the cookie.
168    ///
169    /// # Returns
170    ///
171    /// The `CookieBuilder` instance for method chaining.
172    #[inline(always)]
173    pub fn set_path<T>(&mut self, path: T) -> &mut Self
174    where
175        T: AsRef<str>,
176    {
177        self.path = Some(path.as_ref().to_owned());
178        self
179    }
180
181    /// Sets the `Secure` flag for the cookie.
182    ///
183    /// This flag indicates that the cookie should only be transmitted over secure (HTTPS) connections.
184    ///
185    /// # Returns
186    ///
187    /// The `CookieBuilder` instance for method chaining.
188    #[inline(always)]
189    pub fn secure(&mut self) -> &mut Self {
190        self.secure = Some(true);
191        self
192    }
193
194    /// Sets the `HttpOnly` flag for the cookie.
195    ///
196    /// This flag prevents client-side JavaScript from accessing the cookie.
197    ///
198    /// # Returns
199    ///
200    /// The `CookieBuilder` instance for method chaining.
201    #[inline(always)]
202    pub fn http_only(&mut self) -> &mut Self {
203        self.http_only = Some(true);
204        self
205    }
206
207    /// Explicitly disables the `Secure` flag for the cookie.
208    ///
209    /// This method explicitly sets the `Secure` flag to `false`, which may be useful
210    /// for overriding default secure settings or for testing purposes.
211    ///
212    /// # Returns
213    ///
214    /// The `CookieBuilder` instance for method chaining.
215    #[inline(always)]
216    pub fn disable_secure(&mut self) -> &mut Self {
217        self.secure = Some(false);
218        self
219    }
220
221    /// Explicitly disables the `HttpOnly` flag for the cookie.
222    ///
223    /// This method explicitly sets the `HttpOnly` flag to `false`, which may be useful
224    /// for overriding default HttpOnly settings or for testing purposes.
225    ///
226    /// # Returns
227    ///
228    /// The `CookieBuilder` instance for method chaining.
229    #[inline(always)]
230    pub fn disable_http_only(&mut self) -> &mut Self {
231        self.http_only = Some(false);
232        self
233    }
234
235    /// Sets the `SameSite` policy for the cookie.
236    ///
237    /// # Arguments
238    ///
239    /// - `AsRef<str>` - The `SameSite` policy.
240    ///
241    /// # Returns
242    ///
243    /// The `CookieBuilder` instance for method chaining.
244    #[inline(always)]
245    pub fn set_same_site<T>(&mut self, same_site: T) -> &mut Self
246    where
247        T: AsRef<str>,
248    {
249        self.same_site = Some(same_site.as_ref().to_owned());
250        self
251    }
252
253    /// Builds the cookie string according to the `Set-Cookie` header format.
254    ///
255    /// # Returns
256    ///
257    /// - `String` - A formatted cookie string ready to be sent in a `Set-Cookie` header.
258    pub fn build(&self) -> String {
259        if self.get_name().is_empty() {
260            return String::new();
261        }
262        let mut cookie_string: String = format!("{}={}", self.get_name(), self.get_value());
263        if let Some(expires_value) = self.try_get_expires() {
264            cookie_string.push_str(COOKIE_EXPIRES_ATTRIBUTE_LOWERCASE);
265            cookie_string.push_str(expires_value);
266        }
267        if let Some(max_age_value) = self.try_get_max_age() {
268            cookie_string.push_str(COOKIE_MAX_AGE_ATTRIBUTE_LOWERCASE);
269            cookie_string.push_str(&max_age_value.to_string());
270        }
271        if let Some(domain_value) = self.try_get_domain() {
272            cookie_string.push_str(COOKIE_DOMAIN_ATTRIBUTE_LOWERCASE);
273            cookie_string.push_str(domain_value);
274        }
275        if let Some(path_value) = self.try_get_path() {
276            cookie_string.push_str(COOKIE_PATH_ATTRIBUTE_LOWERCASE);
277            cookie_string.push_str(path_value);
278        }
279        if let Some(true) = self.try_get_secure() {
280            cookie_string.push_str(COOKIE_SECURE_ATTRIBUTE_LOWERCASE);
281        }
282        if let Some(true) = self.try_get_http_only() {
283            cookie_string.push_str(COOKIE_HTTP_ONLY_ATTRIBUTE_LOWERCASE);
284        }
285        if let Some(same_site_value) = self.try_get_same_site() {
286            cookie_string.push_str(COOKIE_SAME_SITE_ATTRIBUTE_LOWERCASE);
287            cookie_string.push_str(same_site_value);
288        }
289        cookie_string
290    }
291}
292
293/// Implementation for `Cookie`.
294impl Cookie {
295    /// Parses a `Cookie` header string into a collection of key-value pairs.
296    ///
297    /// This method takes a `Cookie` header string (typically from a `Cookie` request header)
298    /// and parses it into a map of cookie names to their values.
299    ///
300    /// # Arguments
301    ///
302    /// - `AsRef<str>` - The `Cookie` header string to parse.
303    ///
304    /// # Returns
305    ///
306    /// A `Cookies` collection (a hash map) containing all parsed cookie key-value pairs.
307    pub fn parse<C>(cookie: C) -> Cookies
308    where
309        C: AsRef<str>,
310    {
311        let cookie_ref: &str = cookie.as_ref();
312        let mut cookies: Cookies = hash_map_xx_hash3_64();
313        if cookie_ref.trim().is_empty() {
314            return cookies;
315        }
316        let parts: Vec<&str> = cookie_ref.split(SEMICOLON).collect();
317        for part in parts {
318            let part: &str = part.trim();
319            if part.is_empty() {
320                continue;
321            }
322            if let Some((name, value)) = part.split_once(EQUAL) {
323                let name: String = name.trim().to_string();
324                let value: String = value.trim().to_string();
325                if !name.is_empty() {
326                    cookies.insert(name, value);
327                }
328            } else if !part.is_empty() {
329                cookies.insert(part.to_string(), String::new());
330            }
331        }
332        cookies
333    }
334}