cookie_monster/cookie/
mod.rs1use std::{
2 borrow::Cow,
3 fmt::{self, Debug},
4 time::Duration,
5};
6
7mod builder;
8mod domain;
9pub(crate) mod expires;
10mod parse;
11mod path;
12pub(crate) mod same_site;
13mod serialize;
14
15#[cfg(feature = "percent-encode")]
16mod encoding;
17
18pub use builder::CookieBuilder;
19use expires::Expires;
20
21use crate::{SameSite, util::TinyStr};
22
23#[derive(Default, Clone)]
25pub struct Cookie {
26 raw_value: Option<String>,
28 name: TinyStr,
29 value: TinyStr,
30 expires: Expires,
31 max_age: Option<u64>,
32 domain: Option<TinyStr>,
33 path: Option<TinyStr>,
34 secure: bool,
35 http_only: bool,
36 partitioned: bool,
37 same_site: Option<SameSite>,
38}
39
40impl Cookie {
41 pub fn new<N, V>(name: N, value: V) -> Cookie
53 where
54 N: Into<Cow<'static, str>>,
55 V: Into<Cow<'static, str>>,
56 {
57 Self::new_inner(TinyStr::from(name), TinyStr::from(value))
58 }
59
60 pub fn remove<N>(name: N) -> Cookie
80 where
81 N: Into<Cow<'static, str>>,
82 {
83 Cookie::new(name, "").into_remove()
84 }
85
86 pub(crate) fn into_remove(mut self) -> Self {
87 self.set_expires(Expires::remove());
88 self.set_max_age_secs(0);
89 self.set_value("");
90 self
91 }
92
93 fn new_inner(name: TinyStr, value: TinyStr) -> Cookie {
94 Cookie {
95 name,
96 value,
97 ..Default::default()
98 }
99 }
100
101 pub fn build<N, V>(name: N, value: V) -> CookieBuilder
117 where
118 N: Into<Cow<'static, str>>,
119 V: Into<Cow<'static, str>>,
120 {
121 CookieBuilder::new(name, value)
122 }
123
124 pub fn named<N>(name: N) -> CookieBuilder
137 where
138 N: Into<Cow<'static, str>>,
139 {
140 Self::build(name, "")
141 }
142
143 #[inline]
145 pub fn name(&self) -> &str {
146 self.name.as_str(self.raw_value.as_deref())
147 }
148
149 #[inline]
151 pub fn set_name<N: Into<Cow<'static, str>>>(&mut self, name: N) {
152 self.name = TinyStr::from(name)
153 }
154
155 #[inline]
157 pub fn value(&self) -> &str {
158 self.value.as_str(self.raw_value.as_deref())
159 }
160
161 #[inline]
163 pub fn set_value<V: Into<Cow<'static, str>>>(&mut self, value: V) {
164 self.value = TinyStr::from(value)
165 }
166
167 #[inline]
169 pub fn set_expires<E: Into<Expires>>(&mut self, expires: E) {
170 self.expires = expires.into();
171 }
172
173 #[inline]
176 pub fn max_age(&self) -> Option<Duration> {
177 self.max_age.map(Duration::from_secs)
178 }
179
180 #[inline]
182 pub fn max_age_secs(&self) -> Option<u64> {
183 self.max_age
184 }
185
186 #[inline]
188 pub fn set_max_age(&mut self, max_age: Duration) {
189 self.set_max_age_secs(max_age.as_secs());
190 }
191
192 #[inline]
194 pub fn set_max_age_secs(&mut self, max_age_secs: u64) {
195 self.max_age = Some(max_age_secs);
196 }
197
198 #[inline]
200 pub fn unset_max_age(&mut self) {
201 self.max_age = None;
202 }
203
204 #[inline]
206 pub fn domain(&self) -> Option<&str> {
207 self.domain
208 .as_ref()
209 .map(|s| s.as_str(self.raw_value.as_deref()))
210 }
211
212 pub(crate) fn domain_sanitized(&self) -> Option<&str> {
213 self.domain().map(|d| d.strip_prefix('.').unwrap_or(d))
214 }
215
216 #[inline]
218 pub fn set_domain<D: Into<Cow<'static, str>>>(&mut self, domain: D) {
219 self.domain = Some(TinyStr::from(domain))
220 }
221
222 #[inline]
224 pub fn unset_domain(&mut self) {
225 self.domain = None
226 }
227
228 #[inline]
230 pub fn path(&self) -> Option<&str> {
231 self.path
232 .as_ref()
233 .map(|val| val.as_str(self.raw_value.as_deref()))
234 }
235
236 #[inline]
238 pub fn set_path<D: Into<Cow<'static, str>>>(&mut self, path: D) {
239 self.path = Some(TinyStr::from(path))
240 }
241
242 #[inline]
244 pub fn unset_path(&mut self) {
245 self.path = None
246 }
247
248 #[inline]
250 pub fn secure(&self) -> bool {
251 self.secure
252 }
253
254 #[inline]
256 pub fn set_secure(&mut self, secure: bool) {
257 self.secure = secure
258 }
259
260 #[inline]
262 pub fn http_only(&self) -> bool {
263 self.http_only
264 }
265
266 #[inline]
268 pub fn set_http_only(&mut self, http_only: bool) {
269 self.http_only = http_only
270 }
271
272 #[inline]
274 pub fn partitioned(&self) -> bool {
275 self.partitioned
276 }
277
278 #[inline]
280 pub fn set_partitioned(&mut self, partitioned: bool) {
281 self.partitioned = partitioned;
282 }
283
284 #[inline]
286 pub fn same_site(&self) -> Option<SameSite> {
287 self.same_site
288 }
289
290 #[inline]
292 pub fn set_same_site<S: Into<Option<SameSite>>>(&mut self, same_site: S) {
293 self.same_site = same_site.into();
294 }
295}
296
297impl fmt::Display for Cookie {
298 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
299 write!(f, "{:?}", self)
300 }
301}
302
303impl Debug for Cookie {
304 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
305 let mut debug = f.debug_struct("Cookie");
306
307 debug
308 .field("name", &self.name())
309 .field("value", &self.value())
310 .field("max_age", &self.max_age())
311 .field("domain", &self.domain())
312 .field("path", &self.path())
313 .field("secure", &self.secure())
314 .field("http_only", &self.http_only())
315 .field("partitioned", &self.partitioned())
316 .field("expires", &self.expires)
317 .finish()
318 }
319}
320
321impl PartialEq<Cookie> for Cookie {
322 fn eq(&self, other: &Cookie) -> bool {
323 if self.name() != other.name()
324 || self.value() != other.value()
325 || self.secure() != other.secure()
326 || self.http_only() != other.http_only()
327 || self.partitioned() != other.partitioned()
328 || self.max_age() != other.max_age()
329 || self.same_site() != other.same_site()
330 || self.expires != other.expires
331 {
332 return false;
333 }
334
335 if !opt_str_eq(self.domain_sanitized(), other.domain_sanitized()) {
336 return false;
337 }
338
339 if !opt_str_eq(self.path(), other.path()) {
340 return false;
341 }
342
343 true
344 }
345}
346
347fn opt_str_eq(left: Option<&str>, right: Option<&str>) -> bool {
348 match (left, right) {
349 (None, None) => true,
350 (Some(l), Some(r)) => l.eq_ignore_ascii_case(r),
351 _ => false,
352 }
353}