safari_binarycookies/
model.rs1use core::fmt;
4
5use time::OffsetDateTime;
6
7pub type CookieIter<'a> = core::iter::FlatMap<
10 core::slice::Iter<'a, Page>,
11 &'a Vec<Cookie>,
12 fn(&'a Page) -> &'a Vec<Cookie>,
13>;
14
15#[derive(Debug, Clone, PartialEq, Eq, Hash)]
17#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
21#[non_exhaustive]
22pub struct BinaryCookies {
23 pub pages: Vec<Page>,
25 pub checksum: [u8; 8],
28}
29
30impl BinaryCookies {
31 pub fn cookies(&self) -> impl Iterator<Item = &Cookie> {
36 self.iter()
37 }
38
39 pub fn iter(&self) -> CookieIter<'_> {
45 self.pages.iter().flat_map(|page| &page.cookies)
46 }
47}
48
49impl<'a> IntoIterator for &'a BinaryCookies {
50 type Item = &'a Cookie;
51 type IntoIter = CookieIter<'a>;
52
53 fn into_iter(self) -> Self::IntoIter {
54 self.iter()
55 }
56}
57
58#[derive(Debug, Clone, PartialEq, Eq, Hash)]
60#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
61#[non_exhaustive]
62pub struct Page {
63 pub cookies: Vec<Cookie>,
65 pub offsets: Vec<u32>,
68}
69
70impl Page {
71 pub fn iter(&self) -> core::slice::Iter<'_, Cookie> {
75 self.cookies.iter()
76 }
77}
78
79impl<'a> IntoIterator for &'a Page {
80 type Item = &'a Cookie;
81 type IntoIter = core::slice::Iter<'a, Cookie>;
82
83 fn into_iter(self) -> Self::IntoIter {
84 self.cookies.iter()
85 }
86}
87
88#[derive(Debug, Clone, PartialEq, Eq, Hash)]
95#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
96#[non_exhaustive]
97pub struct Cookie {
98 pub domain: String,
100 pub name: String,
102 pub path: String,
104 pub value: String,
106 pub comment: Option<String>,
109 pub flags: Flags,
111 #[cfg_attr(feature = "serde", serde(with = "time::serde::rfc3339"))]
113 pub expires: OffsetDateTime,
114 #[cfg_attr(feature = "serde", serde(with = "time::serde::rfc3339"))]
116 pub creation: OffsetDateTime,
117}
118
119impl Cookie {
120 #[must_use]
122 pub const fn is_secure(&self) -> bool {
123 self.flags.is_secure()
124 }
125
126 #[must_use]
128 pub const fn is_http_only(&self) -> bool {
129 self.flags.is_http_only()
130 }
131
132 #[must_use]
138 pub const fn expires_unix(&self) -> i64 {
139 self.expires.unix_timestamp()
140 }
141
142 #[must_use]
146 pub const fn creation_unix(&self) -> i64 {
147 self.creation.unix_timestamp()
148 }
149}
150
151#[derive(Clone, Copy, PartialEq, Eq, Hash)]
156#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
157#[non_exhaustive]
158pub struct Flags(u32);
159
160impl Flags {
161 pub const SECURE: u32 = 0x1;
163 pub const HTTP_ONLY: u32 = 0x4;
165
166 pub(crate) const fn new(bits: u32) -> Self {
167 Self(bits)
168 }
169
170 #[must_use]
172 pub const fn bits(self) -> u32 {
173 self.0
174 }
175
176 #[must_use]
178 pub const fn contains(self, bit: u32) -> bool {
179 self.0 & bit == bit
180 }
181
182 #[must_use]
184 pub const fn is_secure(self) -> bool {
185 self.contains(Self::SECURE)
186 }
187
188 #[must_use]
190 pub const fn is_http_only(self) -> bool {
191 self.contains(Self::HTTP_ONLY)
192 }
193}
194
195impl fmt::Debug for Flags {
196 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
197 f.debug_struct("Flags")
198 .field("bits", &format_args!("{:#x}", self.0))
199 .field("secure", &self.is_secure())
200 .field("http_only", &self.is_http_only())
201 .finish()
202 }
203}
204
205#[cfg(test)]
206mod tests {
207 use super::*;
208
209 fn assert_send_sync<T: Send + Sync>() {}
210
211 #[test]
212 fn public_types_are_send_and_sync() {
213 assert_send_sync::<BinaryCookies>();
214 assert_send_sync::<Page>();
215 assert_send_sync::<Cookie>();
216 assert_send_sync::<Flags>();
217 assert_send_sync::<crate::Error>();
218 assert_send_sync::<crate::Cookies<'static>>();
219 }
220
221 #[test]
222 fn flags_read_bits_via_bitmask() {
223 assert!(!Flags::new(0x0).is_secure() && !Flags::new(0x0).is_http_only());
224 assert!(Flags::new(0x1).is_secure() && !Flags::new(0x1).is_http_only());
225 assert!(!Flags::new(0x4).is_secure() && Flags::new(0x4).is_http_only());
226 assert!(Flags::new(0x5).is_secure() && Flags::new(0x5).is_http_only());
227 assert_eq!(Flags::new(0x2).bits(), 0x2);
228 }
229}