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}