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