use core::fmt;
use time::OffsetDateTime;
pub type CookieIter<'a> = core::iter::FlatMap<
core::slice::Iter<'a, Page>,
&'a Vec<Cookie>,
fn(&'a Page) -> &'a Vec<Cookie>,
>;
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[non_exhaustive]
pub struct BinaryCookies {
pub pages: Vec<Page>,
pub checksum: [u8; 8],
}
impl BinaryCookies {
pub fn cookies(&self) -> impl Iterator<Item = &Cookie> {
self.iter()
}
pub fn iter(&self) -> CookieIter<'_> {
self.pages.iter().flat_map(|page| &page.cookies)
}
}
impl<'a> IntoIterator for &'a BinaryCookies {
type Item = &'a Cookie;
type IntoIter = CookieIter<'a>;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[non_exhaustive]
pub struct Page {
pub cookies: Vec<Cookie>,
pub offsets: Vec<u32>,
}
impl Page {
pub fn iter(&self) -> core::slice::Iter<'_, Cookie> {
self.cookies.iter()
}
}
impl<'a> IntoIterator for &'a Page {
type Item = &'a Cookie;
type IntoIter = core::slice::Iter<'a, Cookie>;
fn into_iter(self) -> Self::IntoIter {
self.cookies.iter()
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[non_exhaustive]
pub struct Cookie {
pub domain: String,
pub name: String,
pub path: String,
pub value: String,
pub comment: Option<String>,
pub flags: Flags,
#[cfg_attr(feature = "serde", serde(with = "time::serde::rfc3339"))]
pub expires: OffsetDateTime,
#[cfg_attr(feature = "serde", serde(with = "time::serde::rfc3339"))]
pub creation: OffsetDateTime,
}
impl Cookie {
#[must_use]
pub const fn is_secure(&self) -> bool {
self.flags.is_secure()
}
#[must_use]
pub const fn is_http_only(&self) -> bool {
self.flags.is_http_only()
}
#[must_use]
pub const fn expires_unix(&self) -> i64 {
self.expires.unix_timestamp()
}
#[must_use]
pub const fn creation_unix(&self) -> i64 {
self.creation.unix_timestamp()
}
}
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[non_exhaustive]
pub struct Flags(u32);
impl Flags {
pub const SECURE: u32 = 0x1;
pub const HTTP_ONLY: u32 = 0x4;
pub(crate) const fn new(bits: u32) -> Self {
Self(bits)
}
#[must_use]
pub const fn bits(self) -> u32 {
self.0
}
#[must_use]
pub const fn contains(self, bit: u32) -> bool {
self.0 & bit == bit
}
#[must_use]
pub const fn is_secure(self) -> bool {
self.contains(Self::SECURE)
}
#[must_use]
pub const fn is_http_only(self) -> bool {
self.contains(Self::HTTP_ONLY)
}
}
impl fmt::Debug for Flags {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Flags")
.field("bits", &format_args!("{:#x}", self.0))
.field("secure", &self.is_secure())
.field("http_only", &self.is_http_only())
.finish()
}
}
#[cfg(test)]
mod tests {
use super::*;
fn assert_send_sync<T: Send + Sync>() {}
#[test]
fn public_types_are_send_and_sync() {
assert_send_sync::<BinaryCookies>();
assert_send_sync::<Page>();
assert_send_sync::<Cookie>();
assert_send_sync::<Flags>();
assert_send_sync::<crate::Error>();
assert_send_sync::<crate::Cookies<'static>>();
}
#[test]
fn flags_read_bits_via_bitmask() {
assert!(!Flags::new(0x0).is_secure() && !Flags::new(0x0).is_http_only());
assert!(Flags::new(0x1).is_secure() && !Flags::new(0x1).is_http_only());
assert!(!Flags::new(0x4).is_secure() && Flags::new(0x4).is_http_only());
assert!(Flags::new(0x5).is_secure() && Flags::new(0x5).is_http_only());
assert_eq!(Flags::new(0x2).bits(), 0x2);
}
}