leyline-http 0.1.1

HTTP client that mimics browsers on the wire
Documentation
use std::path::Path;

use serde::{Deserialize, Deserializer, Serialize, Serializer};

use crate::cookie::record::Cookie;
use crate::core::device::FileVersion;
use crate::core::{Error, Result};
use crate::util::atomic::{FileMode, write_atomic};
use crate::util::lock;

use super::Jar;

impl Jar {
    pub fn save_to(&self, path: impl AsRef<Path>) -> Result<()> {
        let file = JarFileOut {
            version: FileVersion::default(),
            cookies: self,
        };
        let bytes = serde_json::to_vec_pretty(&file).map_err(Error::from_json)?;
        write_atomic(path.as_ref(), &bytes, FileMode::Private)
    }

    pub fn load_from(path: impl AsRef<Path>) -> Result<Jar> {
        let bytes = std::fs::read(path)?;
        let file: JarFileIn = serde_json::from_slice(&bytes).map_err(Error::from_json)?;
        match file.version {
            FileVersion::V1 => Ok(file.cookies),
        }
    }
}

#[derive(Serialize)]
struct JarFileOut<'a> {
    version: FileVersion,
    cookies: &'a Jar,
}

#[derive(Deserialize)]
struct JarFileIn {
    version: FileVersion,
    cookies: Jar,
}

impl Serialize for Jar {
    fn serialize<S: Serializer>(&self, ser: S) -> std::result::Result<S::Ok, S::Error> {
        let jar = lock(&self.inner);
        let mut flat: Vec<&Cookie> = jar
            .cookies
            .values()
            .flatten()
            .filter(|c| !c.is_expired())
            .collect();
        flat.sort_by(|a, b| {
            a.domain
                .cmp(&b.domain)
                .then_with(|| a.path.cmp(&b.path))
                .then_with(|| a.name.cmp(&b.name))
                .then_with(|| a.creation_time.cmp(&b.creation_time))
        });
        flat.serialize(ser)
    }
}

impl<'de> Deserialize<'de> for Jar {
    fn deserialize<D: Deserializer<'de>>(de: D) -> std::result::Result<Self, D::Error> {
        let cookies: Vec<Cookie> = Vec::deserialize(de)?;
        let jar = Jar::new();
        {
            let mut inner = lock(&jar.inner);
            for cookie in cookies.into_iter().filter(|c| !c.is_expired()) {
                inner.insert_loaded(cookie);
            }
        }
        Ok(jar)
    }
}

#[cfg(test)]
mod tests;