Initial git support

This commit is contained in:
Maddie H 2023-02-25 22:42:17 +00:00
parent 584bcd4a88
commit b53ce1de5f
No known key found for this signature in database
GPG Key ID: 64FAA9959751687D
8 changed files with 127 additions and 58 deletions

View File

@ -1,16 +1,16 @@
mod cmds;
mod dates; mod dates;
pub mod git;
pub mod output; pub mod output;
mod tables; mod tables;
mod cmds; use crate::args::{Commands, GitExecute, TasksArgs};
use crate::args::{Commands, TasksArgs};
use crate::args::{ use crate::args::{
CompleteTask, CreateTask, DeleteTask, ModifyTask, ShowTask, StartTask, StopTask, CompleteTask, CreateTask, DeleteTask, ModifyTask, ShowTask, StartTask, StopTask, SyncTasks,
}; };
use crate::tasks::{Tasks, TasksError}; 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 { match arguments.command {
Commands::Add(CreateTask { Commands::Add(CreateTask {
title, title,
@ -59,8 +59,17 @@ pub fn execute(tasks: &mut Tasks, arguments: TasksArgs) -> Result<&mut Tasks, Ta
cmds::show(tasks, id)?; 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!(), _ => todo!(),
}; };
Ok(())
Ok(tasks)
} }

View File

@ -4,18 +4,13 @@ use crate::cli::tables;
use crate::tasks::{Task, Tasks, TasksError}; use crate::tasks::{Task, Tasks, TasksError};
fn parse_tags(tags: Option<String>) -> Option<Vec<String>> { fn parse_tags(tags: Option<String>) -> Option<Vec<String>> {
if let Some(..) = tags { tags.map(|tags| tags.split(',').map(str::to_string).collect())
Some(tags.unwrap().split(',').map(str::to_string).collect())
} else {
None
}
} }
pub fn show(tasks: &mut Tasks, id: Option<usize>) -> Result<(), TasksError> { pub fn show(tasks: &mut Tasks, id: Option<usize>) -> Result<(), TasksError> {
// If no id is given, print out all tasks // 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 // Get the task the user wants to see
let id = id.unwrap();
let task = tasks.get_task(id)?; let task = tasks.get_task(id)?;
// Generate the table for the singular task // Generate the table for the singular task

View File

@ -2,8 +2,8 @@ use chrono::{Local, NaiveDateTime};
use colored::{ColoredString, Colorize}; use colored::{ColoredString, Colorize};
pub fn parse_fuzzy_date(date_string: Option<String>) -> Option<NaiveDateTime> { pub fn parse_fuzzy_date(date_string: Option<String>) -> Option<NaiveDateTime> {
if let Some(..) = date_string { if let Some(date_string) = date_string {
match fuzzydate::parse(date_string.unwrap()) { match fuzzydate::parse(date_string) {
Ok(date) => Some(date), Ok(date) => Some(date),
Err(err) => panic!("{} {:?}", "error:".red().bold(), err), Err(err) => panic!("{} {:?}", "error:".red().bold(), err),
} }

30
src/cli/git.rs Normal file
View File

@ -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<dyn Error>> {
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<dyn Error>> {
execute(
repo_path,
format!("pull --ff --no-rebase --no-edit --commit {remote}"),
)?;
execute(repo_path, format!("push {remote}"))?;
Ok(())
}

View File

@ -1,7 +1,11 @@
use crate::tasks::Task; use crate::tasks::Task;
use colored::Colorize; 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); println!("{} {}", "warning:".yellow().bold(), msg);
} }
@ -9,6 +13,11 @@ pub fn info(msg: String) {
println!("{} {}", "info:".blue().bold(), msg); 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) { pub fn success(msg: String) {
println!("{} {}", "success:".green().bold(), msg); println!("{} {}", "success:".green().bold(), msg);
} }

View File

@ -2,24 +2,30 @@ use dirs::home_dir;
use std::error::Error; use std::error::Error;
use std::fs; use std::fs;
use std::path::Path; use std::path::Path;
use std::string::ToString;
use crate::cli::git;
use crate::cli::output;
use crate::tasks::Tasks; use crate::tasks::Tasks;
const TASKS_FILE_PATH: &str = "/.local/share/tasks";
pub fn save_tasks<P: AsRef<Path>>(path: P, tasks: &Tasks) -> Result<(), Box<dyn Error>> { pub fn save_tasks<P: AsRef<Path>>(path: P, tasks: &Tasks) -> Result<(), Box<dyn Error>> {
// Convert the tasks to TOML format // Convert the tasks to TOML format
let data = toml::to_string_pretty(&tasks)?; let data = toml::to_string_pretty(&tasks)?;
// Write the JSON to the file // Write the TOML to the file
fs::write(path, data)?; fs::write(path, data)?;
Ok(()) Ok(())
} }
pub fn load_tasks<P: AsRef<Path>>(path: P) -> Result<Tasks, Box<dyn Error>> { pub fn load_tasks<P: AsRef<Path> + ToString>(
// Read JSON from the file path: P,
let data = fs::read_to_string(path)?; tasks_file: &str,
) -> Result<Tasks, Box<dyn Error>> {
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 // Load the tasks from TOML form
let tasks: Tasks = toml::from_str(&data)?; let tasks: Tasks = toml::from_str(&data)?;
@ -27,11 +33,29 @@ pub fn load_tasks<P: AsRef<Path>>(path: P) -> Result<Tasks, Box<dyn Error>> {
Ok(tasks) Ok(tasks)
} }
pub fn tasks_file_path() -> String { pub fn ensure_repo(path: &str, tasks_file: &str) -> Result<(), Box<dyn Error>> {
// Generate the path for the location of tasks // Generate the path of the tasks file
format!( let tasks_file_path = &format!("{}/{}", path, tasks_file);
"{}{}",
home_dir().unwrap().to_str().unwrap(), // Check if the path exists
TASKS_FILE_PATH 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")
} }

View File

@ -3,36 +3,36 @@ mod cli;
mod data; mod data;
mod tasks; mod tasks;
use crate::args::TasksArgs;
use crate::tasks::Tasks;
use clap::Parser; use clap::Parser;
use colored::*; use colored::*;
use std::path::Path;
use crate::args::TasksArgs;
fn main() { fn main() {
// Generate the file path for tasks // Generate the file paths for tasks
let tasks_file_path = data::tasks_file_path(); let repo_path = &data::tasks_repo_string();
let tasks_file = "tasks";
// If the tasks file doesn't exist, create it first // If the tasks file doesn't exist, create it first
if !Path::new(&tasks_file_path).exists() { match data::ensure_repo(repo_path, tasks_file) {
cli::output::warning("file '~/.local/share/tasks' does not exist. creating..."); Ok(..) => (),
let tasks = Tasks::new(&tasks_file_path); Err(error) => panic!("{} {:?}", "error:".red().bold(), error),
data::save_tasks(&tasks_file_path, &tasks).unwrap();
}; };
// Load tasks and check for any errors when loading the tasks // 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, Ok(tasks) => tasks,
Err(error) => panic!("{} {:?}", "error:".red().bold(), error), Err(error) => panic!("{} {:?}", "error:".red().bold(), error),
}; };
// Parse command line arguments // Parse command line arguments
let arguments = TasksArgs::parse(); let arguments = TasksArgs::parse();
let tasks = match cli::execute(&mut tasks, arguments) { match cli::execute(&mut tasks, arguments) {
Ok(tasks) => tasks, Ok(..) => (),
Err(error) => panic!("{} {:?}", "error:".red().bold(), error), Err(error) => panic!("{} {:?}", "error:".red().bold(), error),
}; };
// Save any changes // 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();
} }

View File

@ -26,7 +26,8 @@ pub struct Task {
#[derive(Serialize, Deserialize, Debug, Clone)] #[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Tasks { 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<Vec<Task>>, // All the tasks in one vector pub tasks: Option<Vec<Task>>, // All the tasks in one vector
} }
@ -73,28 +74,28 @@ impl Task {
deadline: Option<NaiveDateTime>, deadline: Option<NaiveDateTime>,
reminder: Option<NaiveDateTime>, reminder: Option<NaiveDateTime>,
) { ) {
if let Some(..) = title { if let Some(title) = title {
self.title = title.unwrap(); self.title = title;
}; };
if let Some(..) = notes { if let Some(notes) = notes {
self.notes = Some(notes.unwrap()); self.notes = Some(notes);
}; };
if let Some(..) = tags { if let Some(tags) = tags {
self.tags = Some(tags.unwrap()); self.tags = Some(tags);
}; };
if let Some(..) = when { if let Some(when) = when {
self.when = Some(when.unwrap()); self.when = Some(when);
}; };
if let Some(..) = deadline { if let Some(deadline) = deadline {
self.deadline = Some(deadline.unwrap()); self.deadline = Some(deadline);
}; };
if let Some(..) = reminder { if let Some(reminder) = reminder {
self.reminder = Some(reminder.unwrap()); self.reminder = Some(reminder);
}; };
} }
@ -116,9 +117,10 @@ impl Task {
} }
impl Tasks { impl Tasks {
pub fn new(tasks_path: &str) -> Self { pub fn new(repo_path: &str, tasks_file: &str) -> Self {
Self { Self {
path: String::from(tasks_path), path: String::from(repo_path),
file: String::from(tasks_file),
tasks: None, tasks: None,
} }
} }