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}