mod dirs; mod tui; use std::fs; use std::io::Write; use std::path::PathBuf; use anyhow::{ensure, Result}; use clap::{Parser, Subcommand}; #[derive(Debug, Clone, clap::ValueEnum)] enum ShellType { Nu, } impl ShellType { pub fn integration(&self) -> String { let template = match self { Self::Nu => include_str!("./integrations/nushell.nu"), }; let file_path = dbg!(dirs::selected_project_file().to_str().unwrap()); let current_exe = std::env::current_exe().unwrap(); let current_exe = current_exe.to_str().unwrap(); template .replace("{%SELECTED_PROJECT_FILE%}", file_path) .replace("{%PROJ_EXE%}", current_exe) } } #[derive(Debug, Parser)] pub struct Args { #[command(subcommand)] cmd: Option, } #[derive(Debug, Subcommand)] enum Command { /// Add a project Add { path: PathBuf }, /// List existing projects List, /// Initialize shell integration Init { shell: ShellType }, } #[derive(Debug, Default, serde::Serialize, serde::Deserialize)] struct Projects { list: Vec, } fn main() -> Result<()> { let args = Args::parse(); let projects = read_projects_file()?; let result = match &args.cmd { Some(cmd) => handle_subcommands(projects, cmd), None => run_tui(projects), }; if let Err(err) = result { eprintln!("{err}"); std::process::exit(1); } Ok(()) } fn run_tui(projects: Projects) -> Result<()> { clear_selected_project_file()?; let selected_project = tui::run(projects)?; if let Some(selected_project) = selected_project { write_selected_project_file(selected_project)? } Ok(()) } fn handle_subcommands(mut projects: Projects, cmd: &Command) -> Result<()> { match cmd { Command::Add { path } => { add_project(&mut projects, path)?; } Command::List => { list_projects(&projects)?; } Command::Init { shell } => { print_shell_integration(shell); } } Ok(()) } fn print_shell_integration(shell: &ShellType) { println!("{}", shell.integration()); } fn add_project

(projects: &mut Projects, path: P) -> Result<()> where P: Into, { let path = path.into(); ensure!(path.is_dir(), "Project path does not exists"); ensure!( !projects.list.contains(&path.to_path_buf()), "Project path already registered" ); projects.list.push(path); write_projects_file(projects)?; Ok(()) } fn list_projects(projects: &Projects) -> Result<()> { for project in &projects.list { println!("{project:?}") } Ok(()) } fn clear_selected_project_file() -> Result<()> { let cache_file = dirs::selected_project_file(); if fs::exists(cache_file)? { fs::remove_file(cache_file)?; } Ok(()) } fn write_selected_project_file(path: PathBuf) -> Result<()> { let cache_file = dirs::selected_project_file(); let mut file = fs::OpenOptions::new() .write(true) .create(true) .truncate(true) .open(cache_file)?; write!(file, "{}", path.display())?; Ok(()) } fn write_projects_file(projects: &Projects) -> Result<()> { let projects_file = dirs::data_path().join("projects"); let file = fs::OpenOptions::new() .write(true) .create(true) .truncate(true) .open(projects_file)?; bincode::serialize_into(file, projects)?; Ok(()) } fn read_projects_file() -> Result { let projects_file = dirs::data_path().join("projects"); if !projects_file.exists() { return Ok(Projects::default()); } let file = std::fs::File::open(projects_file)?; let projects = bincode::deserialize_from(file)?; Ok(projects) }