diff --git a/src/cli.rs b/src/cli.rs index 40e5052..60ef5d0 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -1,16 +1,16 @@ +mod cmds; mod dates; +pub mod git; pub mod output; mod tables; -mod cmds; - -use crate::args::{Commands, TasksArgs}; +use crate::args::{Commands, GitExecute, TasksArgs}; use crate::args::{ - CompleteTask, CreateTask, DeleteTask, ModifyTask, ShowTask, StartTask, StopTask, + CompleteTask, CreateTask, DeleteTask, ModifyTask, ShowTask, StartTask, StopTask, SyncTasks, }; use crate::tasks::{Tasks, TasksError}; -pub fn execute(tasks: &mut Tasks, arguments: TasksArgs) -> Result<&mut Tasks, TasksError> { +pub fn execute(tasks: &mut Tasks, arguments: TasksArgs) -> Result<(), TasksError> { match arguments.command { Commands::Add(CreateTask { title, @@ -59,8 +59,17 @@ pub fn execute(tasks: &mut Tasks, arguments: TasksArgs) -> Result<&mut Tasks, Ta cmds::show(tasks, id)?; } + Commands::Git(GitExecute { command }) => match git::execute(&tasks.path, command) { + Ok(..) => (), + Err(..) => panic!("failed to execute git cmd"), + }, + + Commands::Sync(SyncTasks { remote }) => match git::sync(&tasks.path, remote) { + Ok(..) => (), + Err(..) => panic!("failed"), + }, + _ => todo!(), }; - - Ok(tasks) + Ok(()) } diff --git a/src/cli/cmds.rs b/src/cli/cmds.rs index 84d54a5..7d9b912 100644 --- a/src/cli/cmds.rs +++ b/src/cli/cmds.rs @@ -4,18 +4,13 @@ use crate::cli::tables; use crate::tasks::{Task, Tasks, TasksError}; fn parse_tags(tags: Option) -> Option> { - if let Some(..) = tags { - Some(tags.unwrap().split(',').map(str::to_string).collect()) - } else { - None - } + tags.map(|tags| tags.split(',').map(str::to_string).collect()) } pub fn show(tasks: &mut Tasks, id: Option) -> Result<(), TasksError> { // If no id is given, print out all tasks - if let Some(..) = id { + if let Some(id) = id { // Get the task the user wants to see - let id = id.unwrap(); let task = tasks.get_task(id)?; // Generate the table for the singular task diff --git a/src/cli/dates.rs b/src/cli/dates.rs index d23dd32..3e15655 100644 --- a/src/cli/dates.rs +++ b/src/cli/dates.rs @@ -2,8 +2,8 @@ use chrono::{Local, NaiveDateTime}; use colored::{ColoredString, Colorize}; pub fn parse_fuzzy_date(date_string: Option) -> Option { - if let Some(..) = date_string { - match fuzzydate::parse(date_string.unwrap()) { + if let Some(date_string) = date_string { + match fuzzydate::parse(date_string) { Ok(date) => Some(date), Err(err) => panic!("{} {:?}", "error:".red().bold(), err), } diff --git a/src/cli/git.rs b/src/cli/git.rs new file mode 100644 index 0000000..e796e48 --- /dev/null +++ b/src/cli/git.rs @@ -0,0 +1,30 @@ +use std::error::Error; +use std::process::Command; + +use crate::cli::output; + +pub fn execute(path: &str, command: String) -> Result<(), Box> { + let output = Command::new("git") + .args(["-C", path]) + .args(command.split(' ')) + .output()?; + + if !output.stdout.is_empty() { + output::git(String::from_utf8(output.stdout).unwrap()); + }; + if !output.stderr.is_empty() { + output::error(String::from_utf8(output.stderr).unwrap()); + }; + + Ok(()) +} + +pub fn sync(repo_path: &str, remote: String) -> Result<(), Box> { + execute( + repo_path, + format!("pull --ff --no-rebase --no-edit --commit {remote}"), + )?; + execute(repo_path, format!("push {remote}"))?; + + Ok(()) +} diff --git a/src/cli/output.rs b/src/cli/output.rs index 52551ec..41ee3c5 100644 --- a/src/cli/output.rs +++ b/src/cli/output.rs @@ -1,7 +1,11 @@ use crate::tasks::Task; use colored::Colorize; -pub fn warning(msg: &str) { +pub fn error(msg: String) { + println!("{} {}", "error".red().bold(), msg); +} + +pub fn warning(msg: String) { println!("{} {}", "warning:".yellow().bold(), msg); } @@ -9,6 +13,11 @@ pub fn info(msg: String) { println!("{} {}", "info:".blue().bold(), msg); } +pub fn git(msg: String) { + let msg = msg.strip_suffix('\n').unwrap_or(&msg); + println!("{} {}", "git:".blue().bold(), msg.bright_black().italic()); +} + pub fn success(msg: String) { println!("{} {}", "success:".green().bold(), msg); } diff --git a/src/data.rs b/src/data.rs index 8c9d13e..9833977 100644 --- a/src/data.rs +++ b/src/data.rs @@ -2,24 +2,30 @@ use dirs::home_dir; use std::error::Error; use std::fs; use std::path::Path; +use std::string::ToString; +use crate::cli::git; +use crate::cli::output; use crate::tasks::Tasks; -const TASKS_FILE_PATH: &str = "/.local/share/tasks"; - pub fn save_tasks>(path: P, tasks: &Tasks) -> Result<(), Box> { // Convert the tasks to TOML format let data = toml::to_string_pretty(&tasks)?; - // Write the JSON to the file + // Write the TOML to the file fs::write(path, data)?; Ok(()) } -pub fn load_tasks>(path: P) -> Result> { - // Read JSON from the file - let data = fs::read_to_string(path)?; +pub fn load_tasks + ToString>( + path: P, + tasks_file: &str, +) -> Result> { + let tasks_file_path = &format!("{}/{}", path.to_string(), tasks_file); + + // Read TOML from the file + let data = fs::read_to_string(tasks_file_path)?; // Load the tasks from TOML form let tasks: Tasks = toml::from_str(&data)?; @@ -27,11 +33,29 @@ pub fn load_tasks>(path: P) -> Result> { Ok(tasks) } -pub fn tasks_file_path() -> String { - // Generate the path for the location of tasks - format!( - "{}{}", - home_dir().unwrap().to_str().unwrap(), - TASKS_FILE_PATH - ) +pub fn ensure_repo(path: &str, tasks_file: &str) -> Result<(), Box> { + // Generate the path of the tasks file + let tasks_file_path = &format!("{}/{}", path, tasks_file); + + // Check if the path exists + if !Path::new(path).exists() { + output::warning(format!( + "tasks repository {path} does not exist. creating..." + )); + fs::create_dir_all(path).unwrap(); + let tasks = Tasks::new(path, tasks_file); + save_tasks(tasks_file_path, &tasks).unwrap(); + git::execute(path, String::from("init"))?; + git::execute(path, String::from("add ."))?; + output::success(format!("created tasks repo {path}")); + } + + Ok(()) +} + +pub fn tasks_repo_string() -> String { + // Generate the path for the location of tasks + let home_dir = home_dir().unwrap(); + let home_dir = home_dir.to_str().unwrap(); + format!("{home_dir}/.local/share/inertia") } diff --git a/src/main.rs b/src/main.rs index fa9391a..c47a677 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,36 +3,36 @@ mod cli; mod data; mod tasks; -use crate::args::TasksArgs; -use crate::tasks::Tasks; use clap::Parser; use colored::*; -use std::path::Path; + +use crate::args::TasksArgs; fn main() { - // Generate the file path for tasks - let tasks_file_path = data::tasks_file_path(); + // Generate the file paths for tasks + let repo_path = &data::tasks_repo_string(); + let tasks_file = "tasks"; // If the tasks file doesn't exist, create it first - if !Path::new(&tasks_file_path).exists() { - cli::output::warning("file '~/.local/share/tasks' does not exist. creating..."); - let tasks = Tasks::new(&tasks_file_path); - data::save_tasks(&tasks_file_path, &tasks).unwrap(); + match data::ensure_repo(repo_path, tasks_file) { + Ok(..) => (), + Err(error) => panic!("{} {:?}", "error:".red().bold(), error), }; // Load tasks and check for any errors when loading the tasks - let mut tasks = match data::load_tasks(&tasks_file_path) { + let mut tasks = match data::load_tasks(repo_path, tasks_file) { Ok(tasks) => tasks, Err(error) => panic!("{} {:?}", "error:".red().bold(), error), }; // Parse command line arguments let arguments = TasksArgs::parse(); - let tasks = match cli::execute(&mut tasks, arguments) { - Ok(tasks) => tasks, + match cli::execute(&mut tasks, arguments) { + Ok(..) => (), Err(error) => panic!("{} {:?}", "error:".red().bold(), error), }; // Save any changes - data::save_tasks(tasks_file_path, tasks).unwrap() + cli::git::execute(repo_path, String::from("add --all")).unwrap(); + data::save_tasks(&repo_path, &tasks).unwrap(); } diff --git a/src/tasks.rs b/src/tasks.rs index 8508b78..82cafa9 100644 --- a/src/tasks.rs +++ b/src/tasks.rs @@ -26,7 +26,8 @@ pub struct Task { #[derive(Serialize, Deserialize, Debug, Clone)] pub struct Tasks { - pub path: String, // Where the tasks are stored + pub path: String, // Path to the tasks repository + pub file: String, // Path to the tasks file in the repository pub tasks: Option>, // All the tasks in one vector } @@ -73,28 +74,28 @@ impl Task { deadline: Option, reminder: Option, ) { - if let Some(..) = title { - self.title = title.unwrap(); + if let Some(title) = title { + self.title = title; }; - if let Some(..) = notes { - self.notes = Some(notes.unwrap()); + if let Some(notes) = notes { + self.notes = Some(notes); }; - if let Some(..) = tags { - self.tags = Some(tags.unwrap()); + if let Some(tags) = tags { + self.tags = Some(tags); }; - if let Some(..) = when { - self.when = Some(when.unwrap()); + if let Some(when) = when { + self.when = Some(when); }; - if let Some(..) = deadline { - self.deadline = Some(deadline.unwrap()); + if let Some(deadline) = deadline { + self.deadline = Some(deadline); }; - if let Some(..) = reminder { - self.reminder = Some(reminder.unwrap()); + if let Some(reminder) = reminder { + self.reminder = Some(reminder); }; } @@ -116,9 +117,10 @@ impl Task { } impl Tasks { - pub fn new(tasks_path: &str) -> Self { + pub fn new(repo_path: &str, tasks_file: &str) -> Self { Self { - path: String::from(tasks_path), + path: String::from(repo_path), + file: String::from(tasks_file), tasks: None, } }