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