use std::fs;
use std::path::PathBuf;
use serde::Deserialize;
#[derive(Debug, Deserialize, Default)]
pub struct Config {
#[serde(default)]
pub recall: RecallConfig,
#[serde(default)]
pub shell: ShellConfig,
}
#[derive(Debug, Deserialize, Default)]
#[serde(default)]
pub struct ShellConfig {
pub disable_ctrl_r: bool,
}
#[derive(Debug, Deserialize)]
#[serde(default)]
pub struct RecallConfig {
pub keymap: String,
pub show_preview: bool,
pub result_limit: usize,
pub preview: PreviewConfig,
}
impl Default for RecallConfig {
fn default() -> Self {
RecallConfig {
keymap: "emacs".to_string(),
show_preview: true,
result_limit: 5000,
preview: PreviewConfig::default(),
}
}
}
#[derive(Debug, Clone, Deserialize)]
#[serde(default)]
pub struct PreviewConfig {
pub show_directory: bool,
pub show_timestamp: bool,
pub show_exit_status: bool,
pub show_hostname: bool,
pub show_duration: bool,
}
impl Default for PreviewConfig {
fn default() -> Self {
PreviewConfig {
show_directory: true,
show_timestamp: true,
show_exit_status: true,
show_hostname: false,
show_duration: true,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum KeymapMode {
#[default]
Emacs,
VimInsert,
VimNormal,
}
impl RecallConfig {
pub fn initial_keymap_mode(&self) -> KeymapMode {
match self.keymap.to_lowercase().as_str() {
"vim" => KeymapMode::VimInsert,
_ => KeymapMode::Emacs,
}
}
}
impl Config {
pub fn load() -> Self {
Self::load_from_default_path().unwrap_or_default()
}
fn load_from_default_path() -> Option<Self> {
let config_path = Self::default_config_path()?;
Self::load_from_path(&config_path)
}
fn default_config_path() -> Option<PathBuf> {
let home = home::home_dir()?;
Some(home.join(".pxh").join("config.toml"))
}
fn load_from_path(path: &PathBuf) -> Option<Self> {
let content = fs::read_to_string(path).ok()?;
toml::from_str(&content).ok()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_default_config() {
let config = Config::default();
assert_eq!(config.recall.keymap, "emacs");
assert!(config.recall.show_preview);
assert_eq!(config.recall.result_limit, 5000);
assert!(config.recall.preview.show_directory);
assert!(config.recall.preview.show_timestamp);
assert!(config.recall.preview.show_exit_status);
assert!(!config.recall.preview.show_hostname);
assert!(config.recall.preview.show_duration);
}
#[test]
fn test_parse_config() {
let toml = r#"
[recall]
keymap = "vim"
show_preview = false
result_limit = 1000
[recall.preview]
show_directory = false
show_hostname = true
"#;
let config: Config = toml::from_str(toml).unwrap();
assert_eq!(config.recall.keymap, "vim");
assert!(!config.recall.show_preview);
assert_eq!(config.recall.result_limit, 1000);
assert!(!config.recall.preview.show_directory);
assert!(config.recall.preview.show_hostname);
assert!(config.recall.preview.show_timestamp);
assert!(config.recall.preview.show_exit_status);
}
#[test]
fn test_initial_keymap_mode() {
let mut config = RecallConfig::default();
assert_eq!(config.initial_keymap_mode(), KeymapMode::Emacs);
config.keymap = "vim".to_string();
assert_eq!(config.initial_keymap_mode(), KeymapMode::VimInsert);
config.keymap = "VIM".to_string();
assert_eq!(config.initial_keymap_mode(), KeymapMode::VimInsert);
config.keymap = "unknown".to_string();
assert_eq!(config.initial_keymap_mode(), KeymapMode::Emacs);
}
}