271 lines
10 KiB
Rust
271 lines
10 KiB
Rust
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 <concentrate/rest>");
|
|
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);
|
|
}
|