// bibiman - a TUI for managing BibLaTeX databases // Copyright (C) 2024 lukeflo // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . ///// use std::{ fs::{create_dir_all, File}, io::{stdin, Write}, path::PathBuf, str::FromStr, }; use color_eyre::{eyre::Result, owo_colors::OwoColorize}; use figment::{ providers::{Format, Serialized, Toml}, Figment, }; use ratatui::style::Color; use serde::{Deserialize, Serialize}; use crate::cliargs::CLIArgs; const DEFAULT_CONFIG: &str = r##" # [general] ## Default files/dirs which are loaded on startup ## Use absolute paths (~ for HOME works). Otherwise, loading might not work. # bibfiles = [ "/path/to/bibfile", "path/to/dir/with/bibfiles" ] ## Default editor to use when editing files. Arguments are possible # editor = "vim" # with args: "vim -y" ## Default app to open PDFs/Epubs # pdf_opener = "xdg-open" ## Default app to open URLs/DOIs # url_opener = "xdg-open" ## Prefix which is prepended to the filepath from the `file` field ## Use absolute paths (~ for HOME works). Otherwise, loading might not work. # file_prefix = "/some/path/prefix" # [colors] ## Default values for dark-themed terminal ## Possible values are: ## ANSI color names. E.g. "bright-black" ## 256-colors indices. E.g. "250" ## Hex color codes. E.g. "#3a3a3a" # main_text_color = "250" # highlight_text_color = "254" # entry_color = "36" # keyword_color = "101" # info_color = "99" # confirm_color = "47" # warn_color = "124" # bar_bg_color = "234" # popup_bg_color = "234" # selected_row_bg_color = "237" "##; /// Main struct of the config file. Contains substructs/headings in toml #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] pub struct BibiConfig { pub general: General, pub colors: Colors, } /// Substruct [general] in config.toml #[derive(Debug, Clone, Deserialize, Serialize, PartialEq)] pub struct General { pub bibfiles: Option>, pub editor: Option, pub pdf_opener: String, pub url_opener: String, pub file_prefix: Option, } /// Substruct [colors] in config.toml #[derive(Debug, Clone, Deserialize, Serialize, PartialEq)] pub struct Colors { pub main_text_color: Color, pub highlight_text_color: Color, pub entry_color: Color, pub keyword_color: Color, pub info_color: Color, pub confirm_color: Color, pub warn_color: Color, pub bar_bg_color: Color, pub popup_bg_color: Color, pub selected_row_bg_color: Color, } impl Default for BibiConfig { fn default() -> Self { Self { general: General { bibfiles: None, editor: None, pdf_opener: select_opener(), url_opener: select_opener(), file_prefix: None, }, colors: Colors { main_text_color: Color::Indexed(250), highlight_text_color: Color::Indexed(254), entry_color: Color::Indexed(36), keyword_color: Color::Indexed(101), info_color: Color::Indexed(99), confirm_color: Color::Indexed(47), warn_color: Color::Indexed(124), bar_bg_color: Color::Indexed(235), popup_bg_color: Color::Indexed(234), selected_row_bg_color: Color::Indexed(237), }, } } } impl BibiConfig { pub fn parse_config(args: &CLIArgs) -> Result { let cfg_file: BibiConfig = if args.cfg_path.as_ref().unwrap().is_file() { Figment::from(Serialized::defaults(BibiConfig::default())) .merge(Toml::file(&args.cfg_path.as_ref().unwrap())) .extract()? } else { BibiConfig::default() }; Ok(cfg_file) } /// Activates the default color scheme for light background terminals pub fn light_colors(&mut self) { self.colors.main_text_color = Color::Indexed(235); self.colors.highlight_text_color = Color::Indexed(232); self.colors.entry_color = Color::Indexed(23); self.colors.keyword_color = Color::Indexed(58); self.colors.info_color = Color::Indexed(57); self.colors.bar_bg_color = Color::Indexed(144); self.colors.popup_bg_color = Color::Indexed(187); self.colors.confirm_color = Color::Indexed(22); self.colors.selected_row_bg_color = Color::Indexed(107); } /// Function which offers the user to create a default config /// if no exists at the standard config path. pub fn create_default_config(args: &CLIArgs) { let path = args.cfg_path.as_ref().unwrap().to_str(); let mut input_str = String::new(); match path { Some(p) => { println!("It seems no config file {} exists.", p.bold()); } None => { println!( "Can't parse config file path. Running {} without any config file.", "bibiman".bold() ); return; } } loop { println!( "\nDo you want to create a default config? {}", "[Y|N]".bold() ); stdin() .read_line(&mut input_str) .expect("Couldn't read input"); match input_str.trim().to_lowercase().as_str() { "y" | "yes" => { break; } "n" | "no" => { println!("\nNo config file will be created."); return; } v => { println!("\nInvalid value {}.", v.red()); println!("Please type {} or {}.", "[Y]es".bold(), "[N]o".bold()); input_str.clear(); continue; } } } { // Ignore any errors of this function, if something goes wrong creating a file will fail too. let mut dirpath = PathBuf::from_str(path.unwrap()).unwrap_or_else(|_| PathBuf::new()); dirpath.pop(); let _ = create_dir_all(dirpath); } let cfg_file = File::create_new(path.unwrap()); match cfg_file { Ok(mut file) => { file.write_all(DEFAULT_CONFIG.as_bytes()).unwrap(); println!("\nCreated default config file {}", path.unwrap().bold()); println!( "Check {} for explanations how to configure it.", "https://codeberg.org/lukeflo/bibiman#configuration".bright_yellow() ) } Err(e) => { println!( "\nCouldn't create default config due to the following error:\n{}", e.red() ) } } } } fn select_opener() -> String { match std::env::consts::OS { "linux" => String::from("xdg-open"), "macos" | "apple" => String::from("open"), "windows" => String::from("start"), "freebsd" => String::from("xdg-open"), "openbsd" | "netbsd" | "solaris" | "redox" => String::from("open"), _ => panic!("Couldn't detect OS for setting correct opener"), } } #[cfg(test)] mod tests { use figment::{ providers::{Format, Toml}, Figment, }; use super::BibiConfig; #[test] fn parse_default_config() { figment::Jail::expect_with(|jail| { jail.create_file( "bibiman.toml", r#" [general] pdf_opener = "xdg-open" url_opener = "xdg-open" [colors] main_text_color = "250" highlight_text_color = "254" entry_color = "36" keyword_color = "101" info_color = "99" confirm_color = "47" warn_color = "124" bar_bg_color = "235" popup_bg_color = "234" selected_row_bg_color = "237" "#, )?; let config: BibiConfig = Figment::new().merge(Toml::file("bibiman.toml")).extract()?; let default_config: BibiConfig = BibiConfig::default(); assert_eq!(config, default_config); assert_eq!(config.general.bibfiles, None); assert_eq!(config.general.editor, None); Ok(()) }); } }