// 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 color_eyre::eyre::Result; use dirs::{config_dir, home_dir}; use lexopt::prelude::*; use owo_colors::OwoColorize; use owo_colors::colors::css::LightGreen; use owo_colors::colors::*; use std::env; use std::path::PathBuf; use walkdir::WalkDir; use crate::app; use crate::config::BibiConfig; // struct for CLIArgs #[derive(Debug, Default, Clone)] pub struct CLIArgs { pub pos_args: Vec, pub cfg_path: Option, pub light_theme: bool, pub pdf_path: Option, } impl CLIArgs { pub fn parse_args() -> color_eyre::Result<(CLIArgs, BibiConfig)> { let mut args = CLIArgs::default(); let mut parser = lexopt::Parser::from_env(); // Default config args.cfg_path = if config_dir().is_some() { Some(config_dir().unwrap().join("bibiman/bibiman.toml")) } else if home_dir().is_some() { Some(home_dir().unwrap().join(".config/bibiman/bibiman.toml")) } else { None }; // if parser // .raw_args() // .is_ok_and(|mut arg| arg.next_if(|a| a == "format-citekeys").is_some()) // { // todo!("Format citekeys options"); // } while let Some(arg) = parser.next()? { match arg { Short('h') | Long("help") => { println!("{}", help_func()); std::process::exit(0); } Short('v') | Long("version") => { println!("{}", version_func()); std::process::exit(0); } Short('c') | Long("config-file") => args.cfg_path = Some(parser.value()?.parse()?), Long("light-terminal") => args.light_theme = true, Long("pdf-path") => { args.pdf_path = Some(parser.value()?.parse()?); } // Value(pos_arg) => parse_files(&mut args, pos_arg), Value(pos_arg) => { if args.pos_args.is_empty() && pos_arg == "format-citekeys" { todo!("Write format citekeys function"); } else { args.pos_args.push(parser.value()?.into()); } } _ => return Err(arg.unexpected().into()), } } if args .cfg_path .as_ref() .is_some_and(|f| !f.try_exists().unwrap() || !f.is_file()) { BibiConfig::create_default_config(&args); } let mut cfg = if args.cfg_path.is_some() { BibiConfig::parse_config(&args)? } else { BibiConfig::new(&args) }; cfg.cli_overwrite(&args); Ok((args, cfg)) } } /// This function maps a vector containing paths to another vector containing paths. /// But it will walk all entries of the first vec which are directories /// and put only valid file paths with `.bib` ending to the resulting vec. pub fn parse_files(args: Vec) -> Vec { let mut files: Vec = Vec::new(); // If pos arg is file, just push it to path vec for i in args { // Expand tilde to /home/user let i = if i.starts_with("~") { app::expand_home(&i) } else { i }; if i.is_file() { files.push(i); // If pos arg is dir, walk dir and collect bibfiles } else if i.is_dir() { for file in WalkDir::new(i) { let f = file.unwrap().into_path(); if f.is_file() && f.extension().is_some() && f.extension().unwrap_or_default() == "bib" { files.push(f) } } } else { println!( "{}\n{}", "The positional argument is neither a valid file, nor a directory:" .red() .bold(), i.as_os_str().to_string_lossy().bright_red().italic() ); println!(); println!("{}", help_func()); std::process::exit(1) } } files } pub fn help_func() -> String { let help = vec![ format!( "{} {}\n", env!("CARGO_PKG_NAME").fg::().bold(), env!("CARGO_PKG_VERSION").fg::(), ), format!( "{}:\n\t{} [Flags] [files/dirs]\n", "USAGE".bold(), "bibiman".bold() ), format!( "{}:\n\t{}\t\tPath to {} file", "POSITIONAL ARGUMENTS".bold(), "".fg::().bold(), ".bib".fg::().bold() ), format!( "\t{}\tPath to directory containing {} files", "".fg::().bold(), ".bib".fg::().bold() ), format!("\n\t{}", "Both can be passed multiple times".italic()), format!("\n{}:", "FLAGS".bold()), format!("\t{}", "-h, --help".bold().fg::()), format!("\t\t{}", "Show this help and exit"), format!("\t{}", "-v, --version".bold().fg::()), format!("\t\t{}", "Show the version and exit"), format!("\t{}", "--light-terminal".bold().fg::()), format!( "\t\t{}", "Enable default colors for light terminal background" ), format!( "\t{}{}", "-c, --config-file=".bold().fg::(), "".bold().italic().fg::() ), format!("\t\t{}", "Path to config file used for current session."), format!("\t\t{}", "Takes precedence over standard config file."), format!( "\t{}{}", "--pdf-path=".bold().fg::(), "".bold().italic().fg::() ), format!("\t\t{}", "Path to directory containing PDF files."), format!( "\t\t{}", "If the pdf files basename matches an entrys citekey," ), format!( "\t\t{}", "its attached as connected PDF file for the current session." ), format!("\t\t{}", "Does not edit the bibfile itself!"), ]; let help = help.join("\n"); help } pub fn version_func() -> String { let version: Vec = vec![ format!( "{} {}", env!("CARGO_PKG_NAME").fg::().bold(), env!("CARGO_PKG_VERSION").fg::() ), format!("{}", env!("CARGO_PKG_AUTHORS").bold()), format!("{}", env!("CARGO_PKG_LICENSE")), format!("\n"), format!( "{} {}", "Target Triple:".bold(), env!("TARGET").fg::() ), ]; let version = version.join("\n"); version }