decrypt_cookies/browser/
cookies.rs

1use std::fmt::Display;
2
3use chrono::{DateTime, Utc};
4
5#[derive(Default, Clone)]
6#[derive(Debug)]
7#[derive(PartialEq, Eq, PartialOrd, Ord)]
8#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
9pub struct LeetCodeCookies {
10    pub csrf: String,
11    pub session: String,
12    #[cfg_attr(feature = "serde", serde(skip))]
13    pub expiry: bool,
14}
15
16impl LeetCodeCookies {
17    pub fn is_completion(&self) -> bool {
18        !(self.expiry || self.csrf.is_empty() || self.session.is_empty())
19    }
20}
21
22impl Display for LeetCodeCookies {
23    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
24        f.write_fmt(format_args!(
25            "LEETCODE_SESSION={};csrftoken={};",
26            self.session, self.csrf
27        ))
28    }
29}
30
31pub trait CookiesInfo {
32    fn csv_header<D: Display>(sep: D) -> String {
33        format!("domain{sep}name{sep}path{sep}value{sep}creation{sep}expires{sep}is_secure{sep}is_http_only")
34    }
35
36    fn to_csv<D: Display>(&self, sep: D) -> String {
37        format!(
38            "{}{sep}{}{sep}{}{sep}{}{sep}{}{sep}{}{sep}{}{sep}{}",
39            self.domain(),
40            self.name(),
41            self.path(),
42            self.value(),
43            self.creation().unwrap_or_default(),
44            self.expires().unwrap_or_default(),
45            self.is_secure(),
46            self.is_http_only(),
47        )
48    }
49
50    /// <https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie>
51    fn set_cookie_header(&self) -> String {
52        let mut properties = vec![
53            format!("{}={}", self.name(), self.value()),
54            format!("Path={}", self.path()),
55        ];
56        if !self.name().starts_with("__Host-") {
57            properties.push(format!("Domain={}", self.domain()));
58        }
59        if let Some(expiry) = self.expiry() {
60            properties.push(format!("Expires={}", expiry));
61        }
62        if self.is_secure() {
63            properties.push("Secure".to_owned());
64        }
65        if self.is_http_only() {
66            properties.push("HttpOnly".to_owned());
67        }
68        properties.push(format!("SameSite={}", self.same_site()));
69
70        properties.join("; ")
71    }
72
73    // TODO: reanme to `url`
74    fn url(&self) -> String {
75        format!("https://{}{}", self.domain().trim_matches('.'), self.path())
76    }
77
78    fn name(&self) -> &str;
79    fn value(&self) -> &str;
80    fn path(&self) -> &str;
81    fn domain(&self) -> &str;
82    fn expiry(&self) -> Option<String>;
83    fn is_secure(&self) -> bool;
84    fn is_http_only(&self) -> bool;
85    fn same_site(&self) -> SameSite;
86    fn creation(&self) -> Option<DateTime<Utc>>;
87    fn expires(&self) -> Option<DateTime<Utc>>;
88}
89
90#[derive(Clone, Copy)]
91#[derive(Debug)]
92#[derive(Default)]
93#[derive(PartialEq, Eq, PartialOrd, Ord)]
94#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
95pub enum SameSite {
96    #[default]
97    None = 0,
98    Lax = 1,
99    Strict = 2,
100}
101
102impl From<i32> for SameSite {
103    fn from(value: i32) -> Self {
104        #[expect(clippy::wildcard_in_or_patterns, reason = "this is more clear")]
105        match value {
106            1 => Self::Lax,
107            2 => Self::Strict,
108            0 | _ => Self::None,
109        }
110    }
111}
112
113#[cfg(feature = "Safari")]
114impl From<binary_cookies::cookie::SameSite> for SameSite {
115    fn from(value: binary_cookies::cookie::SameSite) -> Self {
116        match value {
117            binary_cookies::cookie::SameSite::None => Self::None,
118            binary_cookies::cookie::SameSite::Lax => Self::Lax,
119            binary_cookies::cookie::SameSite::Strict => Self::Strict,
120        }
121    }
122}
123
124impl From<Option<i32>> for SameSite {
125    fn from(value: Option<i32>) -> Self {
126        value.unwrap_or_default().into()
127    }
128}
129
130impl Display for SameSite {
131    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
132        match self {
133            Self::None => "None",
134            Self::Lax => "Lax",
135            Self::Strict => "Strict",
136        }
137        .fmt(f)
138    }
139}