termchan 0.2.0

ターミナルで動く軽量2chクライアント
Documentation
use anyhow::Context;
use cookie_store;
use reqwest_cookie_store::CookieStoreMutex;
use serde_json::{Error, Value};
use std::sync::Arc;
use std::{
    fs::{self, File, OpenOptions},
    io::{self, Read},
};

use crate::configs::config::Config;

pub struct CookieStore {
    arc: Arc<CookieStoreMutex>,
}

impl CookieStore {
    async fn path() -> anyhow::Result<String> {
        let conf = Config::load().await.context("failed to load config")?;
        let path = conf
            .cookie
            .as_ref()
            .context("cookie is not set")?
            .path
            .clone();
        Ok(path)
    }
    pub async fn load() -> anyhow::Result<Arc<CookieStoreMutex>> {
        let path = CookieStore::path().await?;
        let is_exist = std::path::Path::new(&path).exists();
        if !is_exist {
            fs::File::create(&path).context("failed to create cookie file")?;
        }
        let reader = File::open(&path)
            .map(io::BufReader::new)
            .context("failed to open cookie file")?;

        let cookie_store = cookie_store::CookieStore::load_json(reader).unwrap();
        let cookie_store = CookieStoreMutex::new(cookie_store);
        let cookie_store = std::sync::Arc::new(cookie_store);

        Ok(cookie_store)
    }

    pub async fn save(arc: Arc<CookieStoreMutex>) -> anyhow::Result<()> {
        let cookie_path = CookieStore::path().await?;
        let mut writer = File::create(&cookie_path)
            .map(io::BufWriter::new)
            .context("failed to create cookie file")?;
        let store = arc.lock().unwrap();
        store.save_json(&mut writer).unwrap();
        Ok(())
    }

    pub async fn clear() -> anyhow::Result<()> {
        let cookie_path = CookieStore::path().await?;
        let is_exist = std::path::Path::new(&cookie_path).exists();
        if is_exist {
            std::fs::remove_file(&cookie_path).context("failed to remove cookie file")?;
        };
        Ok(())
    }

    pub fn arc(&self) -> Arc<CookieStoreMutex> {
        Arc::clone(&self.arc)
    }

    pub async fn load_raw() -> Option<String> {
        let path = CookieStore::path().await.unwrap();
        let file = OpenOptions::new().read(true).open(&path);
        let mut file = match file {
            Ok(file) => file,
            Err(_) => return None,
        };
        let mut contents = String::new();
        file.read_to_string(&mut contents).unwrap();
        let v: Result<Value, Error> = serde_json::from_str(&contents);
        let v = match v {
            Ok(v) => v,
            Err(_) => return None,
        };

        let cookie = v.get("raw_cookie");
        match cookie {
            Some(c) => Some(c.as_str().unwrap().to_string()),
            None => None,
        }
    }
}

#[cfg(test)]
mod tests {
    use crate::login::Login;

    use super::*;

    #[tokio::test]
    async fn test_cookie_store() {
        let _ = Login::do_login().await;
        let cookie_store = CookieStore::load_raw().await;
        println!("{:?}", cookie_store);
    }
}