leyline/cookie/jar/
persist.rs1use 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;