Radish alpha
r
rad:z39mP9rQAaGmERfUMPULfPUi473tY
Radicle terminal user interface
Radicle
Git
radicle-tui bin settings.rs
use std::collections::HashMap;
use std::env;
use std::path::PathBuf;

use lazy_static::lazy_static;

use thiserror::Error;

use homedir::my_home;

use radicle_tui as tui;

use tui::ui::theme::Theme;

static THEME_RADICLE: &str = "Radicle";

#[derive(Clone, Debug)]
pub struct TerminalInfo {
    pub luma: Option<f32>,
}

impl TerminalInfo {
    pub fn is_dark(&self) -> bool {
        self.luma.unwrap_or_default() <= 0.6
    }
}

lazy_static! {
    static ref TERMINAL_INFO: TerminalInfo = TerminalInfo {
        luma: Some(terminal_light::luma().unwrap_or_default())
    };
}

pub type ThemeBundleId = String;

#[derive(Error, Debug)]
pub enum Error {
    #[error(
        "could not resolve home directory ($HOME is not set and `/etc/passwd` does not resolve)"
    )]
    Home,
}

/// `ThemeMode` defines which theme is selected from a `ThemeBundle`. It can
/// be either `light``, `dark`` or `auto``, which sets the mode depending on
/// the terminal background luma.
#[allow(dead_code)]
#[derive(Debug, PartialEq, Eq, Hash)]
pub enum ThemeMode {
    Auto,
    Light,
    Dark,
}

/// A `ThemeBundle` defines a tuple of themes, that should be adapted to light or
/// dark terminal colors.
#[derive(Debug)]
pub struct ThemeBundle {
    pub light: Theme,
    pub dark: Theme,
}

impl Default for ThemeBundle {
    fn default() -> Self {
        Self {
            light: Theme::default_light(),
            dark: Theme::default_dark(),
        }
    }
}

#[derive(Debug)]
pub struct ThemeSettings {
    /// Set light or dark mode, or detect terminal background luma and
    /// switch automatically.
    mode: ThemeMode,
    /// Theme bundle identifier.
    active_bundle: ThemeBundleId,
    /// All theme bundles.
    bundles: HashMap<ThemeBundleId, ThemeBundle>,
}

impl ThemeSettings {
    pub fn mode(&self) -> &ThemeMode {
        &self.mode
    }

    pub fn active_bundle(&self) -> Option<&ThemeBundle> {
        self.bundles.get(&self.active_bundle)
    }
}

#[derive(Debug)]
pub struct Settings {
    pub theme: ThemeSettings,
}

impl Default for Settings {
    fn default() -> Self {
        Self {
            theme: ThemeSettings {
                mode: ThemeMode::Auto,
                active_bundle: THEME_RADICLE.into(),
                bundles: HashMap::from([(THEME_RADICLE.to_string(), ThemeBundle::default())]),
            },
        }
    }
}

pub fn configure_theme(settings: &Settings) -> Theme {
    let default_bundle = ThemeBundle::default();
    let theme_bundle = settings.theme.active_bundle().unwrap_or(&default_bundle);

    match settings.theme.mode() {
        ThemeMode::Auto => {
            if TERMINAL_INFO.is_dark() {
                theme_bundle.dark.clone()
            } else {
                theme_bundle.light.clone()
            }
        }
        ThemeMode::Light => theme_bundle.light.clone(),
        ThemeMode::Dark => theme_bundle.dark.clone(),
    }
}

pub fn get_state_path() -> Result<PathBuf, Error> {
    let base = match env::var("XDG_STATE_HOME") {
        Ok(path) => PathBuf::from(path),
        Err(err) => {
            log::debug!("Could not read `XDG_STATE_HOME`: {err}");
            my_home()
                .ok()
                .flatten()
                .ok_or(Error::Home)?
                .join(".local/state")
        }
    };

    Ok(base.join("radicle-tui"))
}