Skip to main content

leyline/cookie/jar/
persist.rs

1use std::path::Path;
2
3use serde::{Deserialize, Deserializer, Serialize, Serializer};
4
5use crate::cookie::record::Cookie;
6use crate::core::device::FileVersion;
7use crate::core::{Error, Result};
8use crate::util::atomic::{FileMode, write_atomic};
9use crate::util::lock;
10
11use super::Jar;
12
13impl Jar {
14    pub fn save_to(&self, path: impl AsRef<Path>) -> Result<()> {
15        let file = JarFileOut {
16            version: FileVersion::default(),
17            cookies: self,
18        };
19        let bytes = serde_json::to_vec_pretty(&file).map_err(Error::from_json)?;
20        write_atomic(path.as_ref(), &bytes, FileMode::Private)
21    }
22
23    pub fn load_from(path: impl AsRef<Path>) -> Result<Jar> {
24        let bytes = std::fs::read(path)?;
25        let file: JarFileIn = serde_json::from_slice(&bytes).map_err(Error::from_json)?;
26        match file.version {
27            FileVersion::V1 => Ok(file.cookies),
28        }
29    }
30}
31
32#[derive(Serialize)]
33struct JarFileOut<'a> {
34    version: FileVersion,
35    cookies: &'a Jar,
36}
37
38#[derive(Deserialize)]
39struct JarFileIn {
40    version: FileVersion,
41    cookies: Jar,
42}
43
44impl Serialize for Jar {
45    fn serialize<S: Serializer>(&self, ser: S) -> std::result::Result<S::Ok, S::Error> {
46        let jar = lock(&self.inner);
47        let mut flat: Vec<&Cookie> = jar
48            .cookies
49            .values()
50            .flatten()
51            .filter(|c| !c.is_expired())
52            .collect();
53        flat.sort_by(|a, b| {
54            a.domain
55                .cmp(&b.domain)
56                .then_with(|| a.path.cmp(&b.path))
57                .then_with(|| a.name.cmp(&b.name))
58                .then_with(|| a.creation_time.cmp(&b.creation_time))
59        });
60        flat.serialize(ser)
61    }
62}
63
64impl<'de> Deserialize<'de> for Jar {
65    fn deserialize<D: Deserializer<'de>>(de: D) -> std::result::Result<Self, D::Error> {
66        let cookies: Vec<Cookie> = Vec::deserialize(de)?;
67        let jar = Jar::new();
68        {
69            let mut inner = lock(&jar.inner);
70            for cookie in cookies.into_iter().filter(|c| !c.is_expired()) {
71                inner.insert_loaded(cookie);
72            }
73        }
74        Ok(jar)
75    }
76}
77
78#[cfg(test)]
79mod tests;