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]
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: false,
29            http_only: false,
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.expires = Some(value);
76                    }
77                    COOKIE_MAX_AGE_LOWERCASE => {
78                        if let Ok(max_age_value) = value.parse::<i64>() {
79                            cookie_builder.max_age = Some(max_age_value);
80                        }
81                    }
82                    COOKIE_DOMAIN_LOWERCASE => {
83                        cookie_builder.domain = Some(value);
84                    }
85                    COOKIE_PATH_LOWERCASE => {
86                        cookie_builder.path = Some(value);
87                    }
88                    COOKIE_SAME_SITE_LOWERCASE => {
89                        cookie_builder.same_site = Some(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 = true;
98                    }
99                    COOKIE_HTTP_ONLY_LOWERCASE => {
100                        cookie_builder.http_only = 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]
119    pub fn 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    /// - `i64` - The maximum age in seconds.
132    ///
133    /// # Returns
134    ///
135    /// The `CookieBuilder` instance for method chaining.
136    #[inline]
137    pub fn max_age(&mut self, max_age: i64) -> &mut Self {
138        self.max_age = Some(max_age);
139        self
140    }
141
142    /// Sets the domain for the cookie.
143    ///
144    /// # Arguments
145    ///
146    /// - `AsRef<str>` - The domain for the cookie.
147    ///
148    /// # Returns
149    ///
150    /// The `CookieBuilder` instance for method chaining.
151    #[inline]
152    pub fn domain<D>(&mut self, domain: D) -> &mut Self
153    where
154        D: AsRef<str>,
155    {
156        self.domain = Some(domain.as_ref().to_owned());
157        self
158    }
159
160    /// Sets the path for the cookie.
161    ///
162    /// # Arguments
163    ///
164    /// - `AsRef<str>` - The path for the cookie.
165    ///
166    /// # Returns
167    ///
168    /// The `CookieBuilder` instance for method chaining.
169    #[inline]
170    pub fn path<T>(&mut self, path: T) -> &mut Self
171    where
172        T: AsRef<str>,
173    {
174        self.path = Some(path.as_ref().to_owned());
175        self
176    }
177
178    /// Sets the `Secure` flag for the cookie.
179    ///
180    /// This flag indicates that the cookie should only be transmitted over secure (HTTPS) connections.
181    ///
182    /// # Returns
183    ///
184    /// The `CookieBuilder` instance for method chaining.
185    #[inline]
186    pub fn secure(&mut self) -> &mut Self {
187        self.secure = true;
188        self
189    }
190
191    /// Sets the `HttpOnly` flag for the cookie.
192    ///
193    /// This flag prevents client-side JavaScript from accessing the cookie.
194    ///
195    /// # Returns
196    ///
197    /// The `CookieBuilder` instance for method chaining.
198    #[inline]
199    pub fn http_only(&mut self) -> &mut Self {
200        self.http_only = true;
201        self
202    }
203
204    /// Sets the `SameSite` policy for the cookie.
205    ///
206    /// # Arguments
207    ///
208    /// - `AsRef<str>` - The `SameSite` policy.
209    ///
210    /// # Returns
211    ///
212    /// The `CookieBuilder` instance for method chaining.
213    #[inline]
214    pub fn same_site<T>(&mut self, same_site: T) -> &mut Self
215    where
216        T: AsRef<str>,
217    {
218        self.same_site = Some(same_site.as_ref().to_owned());
219        self
220    }
221
222    /// Builds the cookie string according to the `Set-Cookie` header format.
223    ///
224    /// # Returns
225    ///
226    /// - `String` - A formatted cookie string ready to be sent in a `Set-Cookie` header.
227    pub fn build(&self) -> String {
228        if self.name.is_empty() {
229            return String::new();
230        }
231        let mut cookie_string: String = format!("{}={}", self.name, self.value);
232        if let Some(ref expires_value) = self.expires {
233            cookie_string.push_str(COOKIE_EXPIRES_ATTRIBUTE_LOWERCASE);
234            cookie_string.push_str(expires_value);
235        }
236        if let Some(max_age_value) = self.max_age {
237            cookie_string.push_str(COOKIE_MAX_AGE_ATTRIBUTE_LOWERCASE);
238            cookie_string.push_str(&max_age_value.to_string());
239        }
240        if let Some(ref domain_value) = self.domain {
241            cookie_string.push_str(COOKIE_DOMAIN_ATTRIBUTE_LOWERCASE);
242            cookie_string.push_str(domain_value);
243        }
244        if let Some(ref path_value) = self.path {
245            cookie_string.push_str(COOKIE_PATH_ATTRIBUTE_LOWERCASE);
246            cookie_string.push_str(path_value);
247        }
248        if self.secure {
249            cookie_string.push_str(COOKIE_SECURE_ATTRIBUTE_LOWERCASE);
250        }
251        if self.http_only {
252            cookie_string.push_str(COOKIE_HTTP_ONLY_ATTRIBUTE_LOWERCASE);
253        }
254        if let Some(ref same_site_value) = self.same_site {
255            cookie_string.push_str(COOKIE_SAME_SITE_ATTRIBUTE_LOWERCASE);
256            cookie_string.push_str(same_site_value);
257        }
258        cookie_string
259    }
260}
261
262/// Implementation for `Cookie`.
263impl Cookie {
264    /// Parses a `Cookie` header string into a collection of key-value pairs.
265    ///
266    /// This method takes a `Cookie` header string (typically from a `Cookie` request header)
267    /// and parses it into a map of cookie names to their values.
268    ///
269    /// # Arguments
270    ///
271    /// - `AsRef<str>` - The `Cookie` header string to parse.
272    ///
273    /// # Returns
274    ///
275    /// A `Cookies` collection (a hash map) containing all parsed cookie key-value pairs.
276    pub fn parse<C>(cookie: C) -> Cookies
277    where
278        C: AsRef<str>,
279    {
280        let cookie_ref: &str = cookie.as_ref();
281        let mut cookies: Cookies = hash_map_xx_hash3_64();
282        if cookie_ref.trim().is_empty() {
283            return cookies;
284        }
285        let parts: Vec<&str> = cookie_ref.split(SEMICOLON).collect();
286        for part in parts {
287            let part: &str = part.trim();
288            if part.is_empty() {
289                continue;
290            }
291            if let Some((name, value)) = part.split_once(EQUAL) {
292                let name: String = name.trim().to_string();
293                let value: String = value.trim().to_string();
294                if !name.is_empty() {
295                    cookies.insert(name, value);
296                }
297            } else if !part.is_empty() {
298                cookies.insert(part.to_string(), String::new());
299            }
300        }
301        cookies
302    }
303}