✨ Initial git support
This commit is contained in:
parent
584bcd4a88
commit
b53ce1de5f
23
src/cli.rs
23
src/cli.rs
@ -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)
|
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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
30
src/cli/git.rs
Normal 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(())
|
||||||
|
}
|
@ -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);
|
||||||
}
|
}
|
||||||
|
50
src/data.rs
50
src/data.rs
@ -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")
|
||||||
}
|
}
|
||||||
|
26
src/main.rs
26
src/main.rs
@ -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();
|
||||||
}
|
}
|
||||||
|
32
src/tasks.rs
32
src/tasks.rs
@ -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,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user