http_type/cookie/
impl.rs

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