oc-session 0.1.0

Global OpenCode session browser and resume tool
use anyhow::Context;
use serde::Deserialize;
use std::{fs, path::PathBuf};

pub const DEFAULT_RESUME_COMMAND: &str = "opencode {directory} --session {session_id}";

#[derive(Clone, Debug, Deserialize)]
pub struct Config {
    #[serde(default = "default_command")]
    pub resume_command: String,
    #[serde(default = "default_limit")]
    pub limit: usize,
    #[serde(default)]
    pub paths: Paths,
    #[serde(default)]
    pub ui: Ui,
}

#[derive(Clone, Debug, Default, Deserialize)]
pub struct Paths {
    #[serde(default)]
    pub include: Vec<PathBuf>,
    #[serde(default)]
    pub exclude: Vec<PathBuf>,
}

#[derive(Clone, Debug, Deserialize)]
pub struct Ui {
    #[serde(default = "default_true")]
    pub show_directory: bool,
    #[serde(default = "default_true")]
    pub show_agent: bool,
    #[serde(default)]
    pub show_model: bool,
}

impl Default for Config {
    fn default() -> Self {
        Self {
            resume_command: default_command(),
            limit: default_limit(),
            paths: Paths::default(),
            ui: Ui::default(),
        }
    }
}

impl Default for Ui {
    fn default() -> Self {
        Self {
            show_directory: true,
            show_agent: true,
            show_model: false,
        }
    }
}

impl Config {
    pub fn load(path: Option<PathBuf>) -> anyhow::Result<Self> {
        let Some(path) = path.or_else(default_path) else {
            return Ok(Self::default());
        };
        if !path.exists() {
            return Ok(Self::default());
        }
        toml::from_str(
            &fs::read_to_string(&path).with_context(|| format!("read {}", path.display()))?,
        )
        .with_context(|| format!("parse {}", path.display()))
    }
}

fn default_path() -> Option<PathBuf> {
    dirs::config_dir().map(|dir| dir.join("oc-session").join("config.toml"))
}

fn default_command() -> String {
    DEFAULT_RESUME_COMMAND.to_string()
}

fn default_limit() -> usize {
    500
}

fn default_true() -> bool {
    true
}