kickoff 0.5.0

Fast and minimal program launcher
use crate::color::Color;
use crate::keybinds::{KeyCombo, Modifiers};
use smithay_client_toolkit::seat::keyboard::{keysyms, ModifiersState};
use std::fmt::Debug;
use std::fs::{read_to_string, write};
use std::path::PathBuf;
use xdg::BaseDirectories;

use serde::Deserialize;

#[derive(Deserialize, Clone)]
#[serde(default)]
pub struct ColorConfig {
    pub background: Color,
    pub text: Color,
    pub text_query: Color,
    pub text_selected: Color,
    pub prompt: Color,
}

#[derive(Deserialize, Clone, Debug)]
#[serde(default)]
pub struct KeybindingsConfig {
    pub delete: Vec<KeyCombo>,
    pub delete_word: Vec<KeyCombo>,
    pub execute: Vec<KeyCombo>,
    pub paste: Vec<KeyCombo>,
    pub complete: Vec<KeyCombo>,
    pub nav_up: Vec<KeyCombo>,
    pub nav_down: Vec<KeyCombo>,
    pub exit: Vec<KeyCombo>,
}

#[derive(Deserialize, Clone)]
#[serde(default)]
pub struct HistoryConfig {
    pub decrease_interval: u64,
}

#[derive(Deserialize, Clone)]
#[serde(default)]
pub struct Config {
    pub prompt: String,
    pub padding: u32,
    pub font: String,
    pub font_size: f32,
    pub colors: ColorConfig,
    pub history: HistoryConfig,
    pub keybindings: KeybindingsConfig,
}

impl Default for KeybindingsConfig {
    fn default() -> Self {
        KeybindingsConfig {
            delete: vec![
                KeyCombo::new(Modifiers::default(), keysyms::XKB_KEY_BackSpace),
                KeyCombo::new(Modifiers::default(), keysyms::XKB_KEY_Delete),
                KeyCombo::new(Modifiers::default(), keysyms::XKB_KEY_KP_Delete),
            ],
            delete_word: vec![
                KeyCombo::new(
                    ModifiersState {
                        ctrl: true,
                        ..ModifiersState::default()
                    }
                    .into(),
                    keysyms::XKB_KEY_BackSpace,
                ),
                KeyCombo::new(
                    ModifiersState {
                        ctrl: true,
                        ..ModifiersState::default()
                    }
                    .into(),
                    keysyms::XKB_KEY_Delete,
                ),
                KeyCombo::new(
                    ModifiersState {
                        ctrl: true,
                        ..ModifiersState::default()
                    }
                    .into(),
                    keysyms::XKB_KEY_KP_Delete,
                ),
            ],
            execute: vec![
                KeyCombo::new(Modifiers::default(), keysyms::XKB_KEY_Return),
                KeyCombo::new(Modifiers::default(), keysyms::XKB_KEY_KP_Enter),
            ],
            paste: vec![KeyCombo::new(
                ModifiersState {
                    ctrl: true,
                    ..ModifiersState::default()
                }
                .into(),
                keysyms::XKB_KEY_v,
            )],
            complete: vec![KeyCombo::new(Modifiers::default(), keysyms::XKB_KEY_Tab)],
            nav_up: vec![
                KeyCombo::new(Modifiers::default(), keysyms::XKB_KEY_Up),
                KeyCombo::new(Modifiers::default(), keysyms::XKB_KEY_KP_Up),
            ],
            nav_down: vec![
                KeyCombo::new(Modifiers::default(), keysyms::XKB_KEY_Down),
                KeyCombo::new(Modifiers::default(), keysyms::XKB_KEY_KP_Down),
            ],
            exit: vec![KeyCombo::new(Modifiers::default(), keysyms::XKB_KEY_Escape)],
        }
    }
}
impl Default for ColorConfig {
    fn default() -> Self {
        ColorConfig {
            background: Color(40, 44, 52, 170),
            prompt: Color(171, 178, 191, 255),
            text: Color(255, 255, 255, 255),
            text_query: Color(229, 192, 123, 255),
            text_selected: Color(97, 175, 239, 255),
        }
    }
}
impl Default for Config {
    fn default() -> Self {
        Config {
            prompt: "".to_owned(),
            padding: 100,
            font: "".to_owned(),
            font_size: 32.,
            colors: ColorConfig::default(),
            history: HistoryConfig::default(),
            keybindings: KeybindingsConfig::default(),
        }
    }
}
impl Default for HistoryConfig {
    fn default() -> Self {
        HistoryConfig {
            decrease_interval: 48,
        }
    }
}

impl Config {
    pub fn load() -> Result<Self, Box<dyn std::error::Error>> {
        let xdg_dirs = BaseDirectories::with_prefix("kickoff")?;
        if let Some(config_file) = xdg_dirs.find_config_file("config.toml") {
            let content = read_to_string(config_file)?;
            Ok(toml::from_str(&content)?)
        } else {
            let config_file: PathBuf = xdg_dirs.place_config_file("config.toml")?;
            let default = include_bytes!("../assets/default_config.toml");
            write(config_file, default)?;
            Ok(toml::from_str(&String::from_utf8_lossy(default))?)
        }
    }
}