atros 0.1.0

Automation for the Rest of Us
Documentation
use colored::Colorize;
use serde::{Deserialize, Serialize};
use std::{
    collections::HashMap,
    fs::{self, File, OpenOptions},
    io::{self, BufReader, Read, Seek, Write},
    path::Path,
};

use crate::get_home_var;

#[derive(Serialize, Deserialize, Default, Debug)]
pub struct Cache {
    pub files: HashMap<String, CacheFile>,
}

#[derive(Serialize, Deserialize, Default, Debug)]
pub struct CacheFile {
    pub tasks: Vec<CachedTask>,
}

#[derive(Serialize, Deserialize, Default, Debug)]
pub struct CachedTask {
    pub failed: bool,
    pub command: String,
}

impl CachedTask {
    pub fn should_skip(&self, cmd: &str) -> anyhow::Result<bool> {
        if !self.failed && self.command == cmd {
            let mut input = String::new();

            print!("{}\n{}   {} You ran the same task before and it succeed. Still want to run it again? (y/N) ", "".bright_black(), "".bright_black(), "Warning:".yellow());
            io::stdout().flush()?;

            io::stdin()
                .read_line(&mut input)
                .expect("Failed to read line");

            let input = input.trim().to_lowercase();

            if input == "y" {
                println!(
                    "{}   Running again...\n{}",
                    "".bright_black(),
                    "".bright_black()
                );
                return Ok(false);
            } else {
                println!("{}   Skipping...", "".bright_black());
                return Ok(true);
            }
        }

        Ok(false)
    }
}

impl Cache {
    pub fn get() -> anyhow::Result<Self> {
        let cache_file = get_cache_file()?;
        let mut reader = BufReader::new(cache_file);
        let content_length = reader.seek(std::io::SeekFrom::End(0))? as usize;
        reader.seek(std::io::SeekFrom::Start(0))?;

        let mut content = String::with_capacity(content_length);

        reader.read_to_string(&mut content)?;

        let cache: anyhow::Result<Cache> =
            serde_json::from_str(&content).or_else(|_| Ok(Cache::default()));

        cache
    }

    pub fn save(&self) -> anyhow::Result<()> {
        let stringified = serde_json::to_string(self)?;

        let file_path = get_cache_path()?;
        let mut file = File::create(file_path)?;
        file.write_all(stringified.as_bytes())?;

        Ok(())
    }
}

fn get_cache_path() -> anyhow::Result<String> {
    let home = get_home_var()?;
    let system_cache = match std::env::consts::OS {
            "macos" => Ok("Library/Caches"),
            "linux" => Ok(".cache"),
            sys => Err(anyhow::anyhow!("Unsupported system is being used: {} Only Mac and Linux distributions are allowed for now", sys)),
        }?;
    let file_path = format!("{home}/{system_cache}/atros/cache.json");

    let path = Path::new(&file_path);
    if let Some(parent) = path.parent() {
        if !parent.exists() {
            fs::create_dir_all(parent)?;
        }
    }

    Ok(file_path)
}

fn get_cache_file() -> anyhow::Result<File> {
    let file_path = get_cache_path()?;

    Ok(OpenOptions::new()
        .create(true)
        .truncate(false)
        .read(true)
        .write(true)
        .open(file_path)?)
}