mod cli; mod config; mod core; mod misc; mod notes; mod projects; mod templates; mod timer; use clap::Parser; use strfmt::strfmt; use std::collections::HashMap; use std::{fs, process}; use cli::{CLI, Commands, NotesAction, PomodoroAction, ProjectsAction, TemplateAction}; use config::{Config, load_config, save_config}; use projects::{commits_info, repository_status}; use templates::{list_templates, load_template}; use timer::run_timer; fn parse_config() -> Config { let cfg: Config = match load_config() { Ok(cfg) => cfg, Err(err) => { eprintln!("Configuration error:\n{}", err); process::exit(1); } }; cfg } fn process_command(mut cfg: Config) { let cli = CLI::parse(); match cli.command { Some(Commands::Template(template_command)) => match template_command.action { TemplateAction::List => { let templates = match list_templates(&cfg.templates_dir) { Ok(temps) => temps, Err(err) => { eprintln!("Error scanning template list:\n{}", err); process::exit(1); } }; // TODO: Add grouping by language. for template in templates { eprintln!("{} >- {}", template.language, template.name); } } TemplateAction::Load { name, vars } => match load_template(&cfg.templates_dir, &name) { Ok(_) => { eprintln!("Template succesfully copied!"); eprintln!("Vars: {:?}", vars); } Err(err) => { eprintln!("Error while loading template:\n{}", err); process::exit(1) } }, }, Some(Commands::Timer(timer_command)) => match timer_command.action { PomodoroAction::Start => { match run_timer(cfg.timer_concentrate_mins, cfg.timer_rest_mins) { Ok(_) => {} Err(err) => { eprintln!("Error while running timer:\n{}", err); process::exit(1); } }; } PomodoroAction::Set { concentrate, rest } => { let mut concentrate_updated: bool = false; let mut rest_updated: bool = false; match concentrate { Some(mins) => { cfg.timer_concentrate_mins = mins; concentrate_updated = true; } _ => {} } match rest { Some(mins) => { cfg.timer_rest_mins = mins; rest_updated = true; } _ => {} } match save_config(&cfg) { Ok(_) => { if concentrate_updated { eprintln!( "Successfully update concentrate mins: {}", concentrate.unwrap() ); } if rest_updated { eprintln!("Successfully update rest mins: {}", rest.unwrap()); } if !concentrate_updated && !rest_updated { eprintln!("Error: you need to set 1 argument "); process::exit(1); } } Err(err) => { eprintln!("Get error while saving config:\n{}", err); process::exit(1); } }; } }, Some(Commands::Project(project_action)) => match project_action.action { ProjectsAction::Add { path } => { let path = match fs::canonicalize(path) { Ok(p) => p, Err(err) => { eprintln!("Error:\n{}", err); process::exit(1); } }; if let None = path.file_name() { eprintln!("Error:\nproject folder/file empty name."); process::exit(1); } if cfg.projects.contains(&path) { eprintln!("Project already added."); process::exit(1); } cfg.projects.push(path.clone()); match save_config(&cfg) { Ok(_) => { eprintln!("Successfully add path: {}", path.display()); } Err(err) => { eprintln!("Get error while saving config:\n{}", err); process::exit(1); } } } ProjectsAction::Remove { project_name } => { let mut is_deleted = false; for (idx, project) in cfg.projects.iter().enumerate() { match project.file_name() { Some(name) => { if String::from(name.to_str().unwrap()) == project_name { cfg.projects.remove(idx); is_deleted = true; break; } } None => eprintln!( "Error: project folder/file empty name '{}'", project.display() ), } } if !is_deleted { eprintln!("Can't find project with name '{}'", project_name); process::exit(1); } match save_config(&cfg) { Ok(_) => { eprintln!("Successfully remove project: {}", project_name); } Err(err) => { eprintln!("Get error while saving config:\n{}", err); process::exit(1); } } } ProjectsAction::List => { eprintln!("Projects:"); for project in cfg.projects { match project.file_name() { Some(name) => { if let Ok(commits_info) = commits_info(project.clone()) && let Ok(repo_status) = repository_status(project.clone()) { let mut template = String::new(); template.push_str("{project_name}: {last_change_info}\n\t"); template.push_str("commits: {commits_cnt}\n\t"); template.push_str("last update: {updated_date}\n\t"); template.push_str("current branch: {current_branch}\n\t"); template.push_str("uncommited changes: {is_dirty}\n\t"); template.push_str("upstream: {upstream_name}\n\t"); template.push_str("unpushed changes: {has_unpushed}\n\t"); template.push_str("\n"); let mut vars = HashMap::new(); vars.insert("project_name".to_string(), name.display().to_string()); vars.insert( "last_change_info".to_string(), commits_info.render_last_change_info(), ); vars.insert( "commits_cnt".to_string(), commits_info.cnt.to_string(), ); vars.insert( "updated_date".to_string(), commits_info.render_updated_date(), ); vars.insert("current_branch".to_string(), repo_status.branch_name); vars.insert( "is_dirty".to_string(), repo_status.is_dirty.to_string(), ); vars.insert( "upstream_name".to_string(), repo_status.upstream_name.unwrap_or("None".to_string()), ); vars.insert( "has_unpushed".to_string(), if let Some(st) = repo_status.has_unpushed { st.to_string() } else { "None".to_string() }, ); let rendered = strfmt(&template, &vars).unwrap(); eprint!("{}", rendered); } else { eprintln!("{}: can't get info", name.display()); }; } None => eprintln!( "Error: project folder/file empty name '{}'", project.display() ), } } } }, Some(Commands::Notes(notes_command)) => match notes_command.action { NotesAction::Add => { todo!("Add notes!"); } NotesAction::Remove => { todo!("Remove notes!"); } NotesAction::List => { todo!("List of notes!"); // let notes = cfg.notes; } }, None => { eprintln!("No command provided. Use --help for usage."); process::exit(1); } } } pub fn run() { let cfg = parse_config(); process_command(cfg); }