lla 0.2.6

A lightweight ls replacement
use serde::{Deserialize, Serialize};
use std::fs;
use std::io;
use std::path::{Path, PathBuf};

use crate::error::LlaError;

#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct Config {
    pub default_sort: String,
    pub default_format: String,
    pub enabled_plugins: Vec<String>,
    pub plugins_dir: PathBuf,
    pub default_depth: Option<usize>,
}

impl Config {
    pub fn new() -> Self {
        Config::default()
    }

    pub fn load(path: &Path) -> io::Result<Self> {
        if let Some(parent) = path.parent() {
            fs::create_dir_all(parent)?;
        }
        
        if path.exists() {
            let contents = fs::read_to_string(path)?;
            toml::from_str(&contents).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))
        } else {
            let config = Config::default();
            config.ensure_plugins_dir()?;
            config.save(path)?;
            Ok(config)
        }
    }

    pub fn save(&self, path: &Path) -> io::Result<()> {
        if let Some(parent) = path.parent() {
            fs::create_dir_all(parent)?;
        }
        self.ensure_plugins_dir()?;
        
        let contents = toml::to_string_pretty(self)
            .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
        fs::write(path, contents)
    }

    pub fn get_config_path() -> PathBuf {
        let home = dirs::home_dir().unwrap_or_else(|| PathBuf::from("."));
        home.join(".config").join("lla").join("config.toml")
    }

    pub fn ensure_plugins_dir(&self) -> io::Result<()> {
        fs::create_dir_all(&self.plugins_dir)
    }

    pub fn enable_plugin(&mut self, plugin_name: &str) -> Result<(), LlaError> {
        self.ensure_plugins_dir().map_err(LlaError::Io)?;
        if !self.enabled_plugins.contains(&plugin_name.to_string()) {
            self.enabled_plugins.push(plugin_name.to_string());
            self.save(&Self::get_config_path())?;
        }
        Ok(())
    }

    pub fn disable_plugin(&mut self, plugin_name: &str) -> Result<(), LlaError> {
        self.enabled_plugins.retain(|name| name != plugin_name);
        self.save(&Self::get_config_path())?;
        Ok(())
    }
}

impl Default for Config {
    fn default() -> Self {
        let home = dirs::home_dir().unwrap_or_else(|| PathBuf::from("."));
        let default_plugins_dir = home.join(".config").join("lla").join("plugins");

        Config {
            default_sort: String::from("name"),
            default_format: String::from("default"),
            enabled_plugins: vec![],
            plugins_dir: default_plugins_dir,
            default_depth: Some(3),
        }
    }
}

pub fn initialize_config() -> Result<(), LlaError> {
    let config_path = Config::get_config_path();

    if config_path.exists() {
        println!("Config file already exists at {:?}", config_path);
        println!("Use `lla config` to view or modify the configuration.");
        return Ok(());
    }

    let config = Config::default();
    config.ensure_plugins_dir().map_err(LlaError::Io)?;
    config.save(&config_path).map_err(LlaError::Io)?;

    println!("Config file initialized at {:?}", config_path);
    println!("Plugins directory created at {:?}", config.plugins_dir);
    println!("Default configuration:");
    println!("{:#?}", config);

    Ok(())
}

pub fn view_config() -> Result<(), LlaError> {
    let config_path = Config::get_config_path();
    let config = Config::load(&config_path).map_err(LlaError::Io)?;
    println!("Current configuration at {:?}:", config_path);
    println!("{:#?}", config);
    Ok(())
}