autosway 0.5.0

Automation program
Documentation
use directories;
pub use rayon::prelude::*;
use std::env::current_dir;
use std::fs;
use std::path::PathBuf;

pub mod log {
    pub use urs::log;
    pub use urs::log::LogType::*;
}

pub enum ConfigDir {
    RootDir,
    KnifeDir,
    LocalDir,
}

pub fn get_config_dir(d: ConfigDir) -> Option<PathBuf> {
    let base_dir = directories::BaseDirs::new()?;
    let root = base_dir.config_dir().join("autosway");
    match d {
        ConfigDir::RootDir => Some(root),
        ConfigDir::KnifeDir => Some(root.join("knifes")),
        ConfigDir::LocalDir => Some(if let Ok(p) = current_dir() {
            p.join(".autosway")
        } else {
            log::log!([fatal] "Failed to get local config directory");
        }),
    }
}

pub struct AutoswayLocalConfig {
    pub knife_dir: PathBuf,
}

impl Default for AutoswayLocalConfig {
    fn default() -> Self {
        let local_dir = if let Some(p) = get_config_dir(ConfigDir::LocalDir) {
            p
        } else {
            log::log!([fatal] "Failed to get local autosway directory");
        };

        let mut knife_dir = local_dir.join("knifes");

        if let Ok(mut f) = std::fs::File::open(local_dir.join("config")) {
            if let Ok(c) = urs::misc::config::parse_config_file(&mut f) {
                if let Some(dir) = c.get(&"knife_dir".to_string()) {
                    knife_dir = if let Ok(p) = current_dir() {
                        p.join(PathBuf::from(dir))
                    } else {
                        log::log!([fatal] "Failed to get `knife_dir` option");
                    }
                }
            }
        }

        Self { knife_dir }
    }
}

#[derive(Debug, Clone)]
pub struct Knife {
    pub path: PathBuf,
    pub name: String,
    pub init: String,
}

pub fn get_knifes() -> Vec<Knife> {
    use std::sync::{Arc, RwLock};

    let knifes = Arc::new(RwLock::new(vec![]));

    let push_knife = |path: PathBuf, knifes: &mut Vec<Knife>| {
        let name = if let Some(name) = path.file_name() {
            match name.to_owned().to_str() {
                Some(s) => s.to_owned(),
                None => return,
            }
        } else {
            return;
        };

        let init = path.join("init.lua");

        if init.exists() {
            let init_str = match init.to_str() {
                Some(s) => s.to_owned(),
                None => return,
            };

            knifes.push(Knife {
                path,
                name,
                init: init_str,
            });
        }
    };

    if let Some(knife_dir) = get_config_dir(ConfigDir::KnifeDir) {
        match fs::read_dir(&knife_dir) {
            Ok(read) => {
                read.par_bridge().for_each(|e| {
                    if let Ok(e) = e {
                        if e.path().is_dir() {
                            let mut lock = knifes.write().unwrap();
                            push_knife(e.path(), &mut lock);
                        }
                    }
                });
            }
            Err(e) => {
                urs::log!([warn] "Global knife directory not found: {e}");
            }
        };
    }

    let local_dir = AutoswayLocalConfig::default().knife_dir;

    let read = match fs::read_dir(local_dir) {
        Ok(read_dir) => read_dir,
        Err(_) => {
            let lock = knifes.read().unwrap();
            return (*lock).clone();
        }
    };

    read.par_bridge().for_each(|e| {
        if let Ok(e) = e {
            if e.path().is_dir() {
                let mut lock = knifes.write().unwrap();
                push_knife(e.path(), &mut lock);
            }
        }
    });

    let lock = knifes.read().unwrap();
    (*lock).clone()
}

#[derive(Default)]
pub struct StateOptions {
    pub verbose: bool,
}

pub struct State {
    pub lua: mlua::Lua,
    pub knifes: Vec<Knife>,
    pub options: StateOptions,
}

impl Default for State {
    fn default() -> Self {
        Self::new()
    }
}

impl State {
    pub fn new() -> Self {
        let mut state = Self {
            lua: mlua::Lua::new(),
            knifes: get_knifes(),
            options: StateOptions::default(),
        };

        match state.setup_lua() {
            Ok(()) => {}
            Err(e) => {
                log::log!([fatal] "{e}");
            }
        }

        state
    }
}