Repo: move all methods to repository #14
13 changed files with 385 additions and 251 deletions
|
@ -12,18 +12,19 @@ pub fn execute(repo: impl Repository, args: Args) {
|
||||||
let res = repo.insert_task(repo::InsertTaskData {
|
let res = repo.insert_task(repo::InsertTaskData {
|
||||||
name: args.name,
|
name: args.name,
|
||||||
link: args.link,
|
link: args.link,
|
||||||
|
index: None,
|
||||||
});
|
});
|
||||||
|
|
||||||
match res {
|
match res {
|
||||||
Ok(task) => {
|
Ok(task) => {
|
||||||
println!("Task was added successfully");
|
println!("The task was added successfully");
|
||||||
println!(" {}", task.name);
|
println!(" {}", task.name);
|
||||||
if let Some(link) = task.link {
|
if let Some(link) = task.link {
|
||||||
println!(" link: {}", link);
|
println!(" link: {}", link);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
eprintln!("Cannot insert data: {}", err);
|
eprintln!("Cannot insert a new task: {}", err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,4 @@
|
||||||
use std::io::Write;
|
use crate::repo::{self, Repository};
|
||||||
use std::path::PathBuf;
|
|
||||||
|
|
||||||
use crate::{CurrentTaskInfo, Task};
|
|
||||||
|
|
||||||
#[derive(clap::Args)]
|
#[derive(clap::Args)]
|
||||||
pub struct Args {
|
pub struct Args {
|
||||||
|
@ -16,36 +13,34 @@ pub struct Args {
|
||||||
idx: usize,
|
idx: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Request {
|
pub fn execute(repo: impl Repository, args: Args) {
|
||||||
pub args: Args,
|
match repo.get_current_task_opt() {
|
||||||
pub current_task_info: Option<CurrentTaskInfo>,
|
Ok(Some(_)) => {
|
||||||
pub tasks: Vec<Task>,
|
return eprintln!("You can edit task only when you don't have an active task, yet")
|
||||||
pub tasks_file_path: PathBuf,
|
}
|
||||||
}
|
Err(err) => {
|
||||||
|
return eprintln!("Cannot read current task: {}", err);
|
||||||
pub fn execute(mut req: Request) {
|
}
|
||||||
if req.current_task_info.is_some() {
|
_ => {}
|
||||||
panic!("You can edit task only when you don't have an active task, yet");
|
}
|
||||||
}
|
|
||||||
|
let res = repo.update_task(
|
||||||
let idx = req.args.idx;
|
args.idx,
|
||||||
if idx == 0 || idx > req.tasks.len() {
|
repo::UpdateTaskData {
|
||||||
panic!("invalid index");
|
name: args.name,
|
||||||
}
|
link: args.no_link.then(|| None).or_else(|| args.link.map(Some)),
|
||||||
|
},
|
||||||
let mut task = &mut req.tasks[idx - 1];
|
);
|
||||||
if let Some(name) = req.args.name {
|
match res {
|
||||||
task.name = name;
|
Ok(task) => {
|
||||||
}
|
println!("The task was changed successfully");
|
||||||
if let Some(link) = req.args.link {
|
println!(" {}", task.name);
|
||||||
task.link = Some(link);
|
if let Some(link) = task.link {
|
||||||
} else if req.args.no_link {
|
println!(" link: {}", link);
|
||||||
task.link = None;
|
}
|
||||||
}
|
}
|
||||||
|
Err(err) => {
|
||||||
let mut file = std::fs::File::create(&req.tasks_file_path).unwrap();
|
eprintln!("Cannot update the task: {}", err);
|
||||||
file.write_all(&serde_json::to_vec(&req.tasks).unwrap())
|
}
|
||||||
.unwrap();
|
}
|
||||||
|
|
||||||
println!("changed");
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,41 +1,17 @@
|
||||||
use crate::{CurrentTaskInfo, Task};
|
use crate::repo::{self, Repository};
|
||||||
use std::io::Write;
|
|
||||||
use std::path::PathBuf;
|
|
||||||
|
|
||||||
pub struct Request {
|
pub fn execute(repo: impl Repository) {
|
||||||
pub current_task_info: Option<CurrentTaskInfo>,
|
match repo.finish_task() {
|
||||||
pub tasks: Vec<Task>,
|
Err(repo::Error::NotFound) => {
|
||||||
pub finished_tasks_file_path: PathBuf,
|
eprintln!("You can use the finish subcommand only when you have an active task")
|
||||||
pub tasks_file_path: PathBuf,
|
}
|
||||||
pub current_task_info_file_path: PathBuf,
|
Err(err) => eprintln!("Cannot finish the task: {}", err),
|
||||||
}
|
Ok(task) => {
|
||||||
|
println!("The task was finished successfully");
|
||||||
pub fn execute(mut req: Request) {
|
println!(" {}", task.name);
|
||||||
match req.current_task_info.take() {
|
if let Some(link) = task.link {
|
||||||
None => {
|
println!(" link: {}", link);
|
||||||
panic!("You can use the finish subcommand only when you have an active task")
|
|
||||||
}
|
}
|
||||||
Some(info) => {
|
|
||||||
let mut finished_tasks: Vec<Task> = std::fs::File::open(&req.finished_tasks_file_path)
|
|
||||||
.map(|file| serde_json::from_reader(file).unwrap())
|
|
||||||
.unwrap_or_default();
|
|
||||||
|
|
||||||
let task = req.tasks.remove(info.task_idx - 1);
|
|
||||||
finished_tasks.push(task);
|
|
||||||
|
|
||||||
let mut file = std::fs::File::create(req.tasks_file_path).unwrap();
|
|
||||||
file.write_all(&serde_json::to_vec(&req.tasks).unwrap())
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let mut file = std::fs::File::create(req.finished_tasks_file_path).unwrap();
|
|
||||||
file.write_all(&serde_json::to_vec(&finished_tasks).unwrap())
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let mut file = std::fs::File::create(req.current_task_info_file_path).unwrap();
|
|
||||||
file.write_all(&serde_json::to_vec(&req.current_task_info).unwrap())
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
println!("finished");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,15 +1,23 @@
|
||||||
use crate::{CurrentTaskInfo, Task};
|
use crate::domain::CurrentTaskInfo;
|
||||||
|
use crate::repo::Repository;
|
||||||
|
|
||||||
pub struct Request {
|
pub fn execute(repo: impl Repository) {
|
||||||
pub tasks: Vec<Task>,
|
let tasks = match repo.get_tasks() {
|
||||||
pub current_task_info: Option<CurrentTaskInfo>,
|
Ok(tasks) => tasks,
|
||||||
}
|
Err(err) => return eprintln!("Cannot read tasks: {}", err),
|
||||||
|
};
|
||||||
|
|
||||||
pub fn execute(req: Request) {
|
let cur_task = match repo.get_current_task_opt() {
|
||||||
for (i, task) in req.tasks.iter().enumerate() {
|
Ok(cur_task) => cur_task,
|
||||||
|
Err(err) => {
|
||||||
|
return eprintln!("Cannot read current task: {}", err);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
for (i, task) in tasks.iter().enumerate() {
|
||||||
let idx = i + 1;
|
let idx = i + 1;
|
||||||
|
|
||||||
match req.current_task_info {
|
match cur_task {
|
||||||
Some(CurrentTaskInfo { task_idx, .. }) if task_idx == idx => print!("> "),
|
Some(CurrentTaskInfo { task_idx, .. }) if task_idx == idx => print!("> "),
|
||||||
_ => print!(" "),
|
_ => print!(" "),
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,20 +1,26 @@
|
||||||
use std::io::Write;
|
use crate::repo::{self, Repository};
|
||||||
use std::path::PathBuf;
|
|
||||||
|
|
||||||
use crate::CurrentTaskInfo;
|
pub fn execute(repo: impl Repository) {
|
||||||
|
let task = match repo.stop_task() {
|
||||||
pub struct Request {
|
Err(repo::Error::NotFound) => {
|
||||||
pub current_task_info: Option<CurrentTaskInfo>,
|
return eprintln!("You can use the pause subcommand only when you have an active task")
|
||||||
pub current_task_info_file_path: PathBuf,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn execute(mut req: Request) {
|
|
||||||
if req.current_task_info.take().is_none() {
|
|
||||||
panic!("You can use the pause subcommand only when you have an active task");
|
|
||||||
}
|
}
|
||||||
|
Err(err) => {
|
||||||
|
return eprintln!("Cannot read current task: {}", err);
|
||||||
|
}
|
||||||
|
Ok(task) => task,
|
||||||
|
};
|
||||||
|
|
||||||
let mut file = std::fs::File::create(&req.current_task_info_file_path).unwrap();
|
match repo.stop_task() {
|
||||||
file.write_all(&serde_json::to_vec(&req.current_task_info).unwrap())
|
Ok(_) => {
|
||||||
.unwrap();
|
println!("The task was paused successfully");
|
||||||
println!("paused");
|
println!(" {}", task.name);
|
||||||
|
if let Some(link) = task.link {
|
||||||
|
println!(" link: {}", link);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
eprintln!("Cannot pause the task: {}", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
use crate::{CurrentTaskInfo, Task};
|
use crate::repo::{self, Repository};
|
||||||
|
|
||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
use std::io::Write;
|
|
||||||
use std::path::PathBuf;
|
|
||||||
|
|
||||||
#[derive(clap::Args)]
|
#[derive(clap::Args)]
|
||||||
pub struct Args {
|
pub struct Args {
|
||||||
|
@ -17,46 +15,61 @@ pub enum Priority {
|
||||||
After { idx: usize },
|
After { idx: usize },
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Request {
|
pub fn execute(repo: impl Repository, args: Args) {
|
||||||
pub args: Args,
|
match repo.get_current_task_opt() {
|
||||||
pub tasks: Vec<Task>,
|
Ok(Some(_)) => {
|
||||||
pub current_task_info: Option<CurrentTaskInfo>,
|
return eprintln!(
|
||||||
pub tasks_file_path: PathBuf,
|
"You can change priority only when you don't have an active task, yet"
|
||||||
}
|
)
|
||||||
|
}
|
||||||
pub fn execute(mut req: Request) {
|
Err(err) => {
|
||||||
if req.current_task_info.is_some() {
|
return eprintln!("Cannot read current task: {}", err);
|
||||||
panic!("You can change priority only when you don't have an active task, yet");
|
}
|
||||||
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
let target_idx = req.args.idx;
|
let target_idx = args.idx;
|
||||||
if target_idx == 0 || target_idx > req.tasks.len() {
|
|
||||||
panic!("invalid index");
|
if let Err(err) = repo.get_task_opt(target_idx) {
|
||||||
|
return eprintln!("Task not found: {}", err);
|
||||||
}
|
}
|
||||||
|
|
||||||
let idx = match req.args.priority {
|
let idx = match args.priority {
|
||||||
Priority::Before { idx } | Priority::After { idx } => match target_idx.cmp(&idx) {
|
Priority::Before { idx } | Priority::After { idx } => match target_idx.cmp(&idx) {
|
||||||
Ordering::Equal => return,
|
Ordering::Equal => return,
|
||||||
Ordering::Less => idx - 1,
|
Ordering::Less => idx - 1,
|
||||||
Ordering::Greater => idx,
|
Ordering::Greater => idx,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
if idx == 0 || idx > req.tasks.len() {
|
if let Err(err) = repo.get_task_opt(idx) {
|
||||||
panic!("invalid index");
|
return eprintln!("Task not found: {}", err);
|
||||||
}
|
}
|
||||||
|
|
||||||
let target = req.tasks.remove(target_idx - 1);
|
let target = match repo.remove_task(target_idx) {
|
||||||
|
Ok(removed) => removed,
|
||||||
|
Err(err) => return eprintln!("Cannot remove the task: {}", err),
|
||||||
|
};
|
||||||
|
|
||||||
match req.args.priority {
|
let new_idx = match args.priority {
|
||||||
Priority::Before { .. } => {
|
Priority::Before { .. } => idx - 1,
|
||||||
req.tasks.insert(idx - 1, target);
|
Priority::After { .. } => idx,
|
||||||
}
|
};
|
||||||
Priority::After { .. } => {
|
|
||||||
req.tasks.insert(idx, target);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut file = std::fs::File::create(&req.tasks_file_path).unwrap();
|
let res = repo.insert_task(repo::InsertTaskData {
|
||||||
file.write_all(&serde_json::to_vec(&req.tasks).unwrap())
|
index: Some(new_idx),
|
||||||
.unwrap();
|
name: target.name,
|
||||||
|
link: target.link,
|
||||||
|
});
|
||||||
|
match res {
|
||||||
|
Ok(task) => {
|
||||||
|
println!("The task was reordered successfully");
|
||||||
|
println!(" {}", task.name);
|
||||||
|
if let Some(link) = task.link {
|
||||||
|
println!(" link: {}", link);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
eprintln!("Cannot reorder the task: {}", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,31 +1,28 @@
|
||||||
|
use crate::repo::{self, Repository};
|
||||||
use std::io::{BufRead, Write};
|
use std::io::{BufRead, Write};
|
||||||
use std::path::PathBuf;
|
|
||||||
|
|
||||||
use crate::{CurrentTaskInfo, Task};
|
|
||||||
|
|
||||||
#[derive(clap::Args)]
|
#[derive(clap::Args)]
|
||||||
pub struct Args {
|
pub struct Args {
|
||||||
idx: usize,
|
idx: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Request {
|
pub fn execute(repo: impl Repository, args: Args) {
|
||||||
pub args: Args,
|
match repo.get_current_task_opt() {
|
||||||
pub current_task_info: Option<CurrentTaskInfo>,
|
Ok(Some(_)) => {
|
||||||
pub tasks: Vec<Task>,
|
return eprintln!("You can remove task only when you don't have an active task, yet");
|
||||||
pub tasks_file_path: PathBuf,
|
}
|
||||||
}
|
Err(err) => {
|
||||||
|
return eprintln!("Cannot read current task: {}", err);
|
||||||
pub fn execute(mut req: Request) {
|
}
|
||||||
if req.current_task_info.is_some() {
|
_ => {}
|
||||||
panic!("You can remove task only when you don't have an active task, yet");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let idx = req.args.idx;
|
let task = match repo.get_task_opt(args.idx) {
|
||||||
if idx == 0 || idx > req.tasks.len() {
|
Ok(Some(task)) => task,
|
||||||
panic!("invalid index");
|
Ok(None) | Err(repo::Error::NotFound) => return eprintln!("Task not found"),
|
||||||
}
|
Err(err) => return eprintln!("Cannot get task: {}", err),
|
||||||
|
};
|
||||||
|
|
||||||
let task = &req.tasks[idx - 1];
|
|
||||||
println!("You are deleting task:");
|
println!("You are deleting task:");
|
||||||
println!(" {}", task.name);
|
println!(" {}", task.name);
|
||||||
if let Some(ref link) = task.link {
|
if let Some(ref link) = task.link {
|
||||||
|
@ -46,11 +43,16 @@ pub fn execute(mut req: Request) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
req.tasks.remove(idx - 1);
|
match repo.remove_task(args.idx) {
|
||||||
|
Ok(_) => {
|
||||||
let mut file = std::fs::File::create(&req.tasks_file_path).unwrap();
|
println!("The task was removed successfully");
|
||||||
file.write_all(&serde_json::to_vec(&req.tasks).unwrap())
|
println!(" {}", task.name);
|
||||||
.unwrap();
|
if let Some(link) = task.link {
|
||||||
|
println!(" link: {}", link);
|
||||||
println!("removed");
|
}
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
eprintln!("Cannot remove the task: {}", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
use crate::{CurrentTaskInfo, Task};
|
use crate::repo::{self, Repository};
|
||||||
use std::{io::Write, path::PathBuf};
|
|
||||||
|
|
||||||
#[derive(clap::Args)]
|
#[derive(clap::Args)]
|
||||||
pub struct Args {
|
pub struct Args {
|
||||||
|
@ -9,31 +8,20 @@ pub struct Args {
|
||||||
idx: Option<usize>,
|
idx: Option<usize>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Request {
|
pub fn execute(repo: impl Repository, args: Args) {
|
||||||
pub args: Args,
|
let task = match repo.start_task(args.idx.unwrap_or(1)) {
|
||||||
pub tasks: Vec<Task>,
|
Ok(task) => task,
|
||||||
pub current_task_info: Option<CurrentTaskInfo>,
|
Err(repo::Error::NotFound) => return eprintln!("Task not found"),
|
||||||
pub current_task_info_file_path: PathBuf,
|
Err(err) => return eprintln!("Cannot start task: {}", err),
|
||||||
}
|
};
|
||||||
|
|
||||||
pub fn execute(mut req: Request) {
|
println!("The task was started successfully");
|
||||||
let idx = req.args.idx.unwrap_or(1);
|
println!(" {}", task.name);
|
||||||
if idx == 0 || idx > req.tasks.len() {
|
if let Some(link) = task.link.as_ref() {
|
||||||
panic!("invalid index");
|
println!(" link: {}", link);
|
||||||
}
|
}
|
||||||
let task = &req.tasks[idx - 1];
|
|
||||||
|
|
||||||
req.current_task_info.replace(CurrentTaskInfo {
|
if let (Some(link), true) = (task.link.as_ref(), args.open) {
|
||||||
task_idx: idx,
|
|
||||||
task: task.clone(),
|
|
||||||
});
|
|
||||||
|
|
||||||
let mut file = std::fs::File::create(&req.current_task_info_file_path).unwrap();
|
|
||||||
file.write_all(&serde_json::to_vec(&req.current_task_info).unwrap())
|
|
||||||
.unwrap();
|
|
||||||
println!("started");
|
|
||||||
|
|
||||||
if let (Some(link), true) = (task.link.as_ref(), req.args.open) {
|
|
||||||
log::debug!("opening link...");
|
log::debug!("opening link...");
|
||||||
std::process::Command::new("xdg-open")
|
std::process::Command::new("xdg-open")
|
||||||
.arg(link)
|
.arg(link)
|
||||||
|
|
|
@ -1,16 +1,19 @@
|
||||||
use crate::CurrentTaskInfo;
|
use crate::repo::Repository;
|
||||||
|
|
||||||
pub fn execute(current_task_info: Option<CurrentTaskInfo>) {
|
pub fn execute(repo: impl Repository) {
|
||||||
match current_task_info {
|
match repo.get_current_task_opt() {
|
||||||
None => {
|
Ok(None) => {
|
||||||
eprintln!("You don't have an active task.");
|
eprintln!("You don't have an active task.");
|
||||||
}
|
}
|
||||||
Some(info) => {
|
Ok(Some(info)) => {
|
||||||
println!("Information about your current task:");
|
println!("Information about your current task:");
|
||||||
println!(" {}", info.task.name);
|
println!(" {}", info.task.name);
|
||||||
if let Some(link) = info.task.link {
|
if let Some(link) = info.task.link {
|
||||||
println!(" link: {}", link);
|
println!(" link: {}", link);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Err(err) => {
|
||||||
|
eprintln!("Cannot read current task: {}", err);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,13 @@
|
||||||
|
pub type TaskId = usize;
|
||||||
|
|
||||||
pub struct Task {
|
pub struct Task {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub link: Option<String>,
|
pub link: Option<String>,
|
||||||
// created_at
|
// created_at
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct CurrentTaskInfo {
|
||||||
|
pub task_idx: usize,
|
||||||
|
pub task: Task,
|
||||||
|
// started_at
|
||||||
|
}
|
||||||
|
|
65
src/main.rs
65
src/main.rs
|
@ -28,7 +28,6 @@
|
||||||
|
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use repo::fs::FsRepo;
|
use repo::fs::FsRepo;
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use xdg::BaseDirectories;
|
use xdg::BaseDirectories;
|
||||||
|
|
||||||
mod cli;
|
mod cli;
|
||||||
|
@ -39,83 +38,35 @@ fn main() {
|
||||||
let args = cli::Args::parse();
|
let args = cli::Args::parse();
|
||||||
|
|
||||||
let xdg_dirs = BaseDirectories::with_prefix(env!("CARGO_PKG_NAME")).unwrap();
|
let xdg_dirs = BaseDirectories::with_prefix(env!("CARGO_PKG_NAME")).unwrap();
|
||||||
|
|
||||||
let repo = FsRepo::new(xdg_dirs);
|
let repo = FsRepo::new(xdg_dirs);
|
||||||
|
|
||||||
let finished_tasks_file_path = xdg_dirs.place_data_file("finished_data.json").unwrap();
|
|
||||||
|
|
||||||
let current_task_info_file_path = xdg_dirs.place_data_file("current.json").unwrap();
|
|
||||||
let current_task_info: Option<CurrentTaskInfo> =
|
|
||||||
std::fs::File::open(¤t_task_info_file_path)
|
|
||||||
.map(|file| serde_json::from_reader(file).unwrap())
|
|
||||||
.unwrap_or_default();
|
|
||||||
|
|
||||||
match args.command {
|
match args.command {
|
||||||
cli::SubCommand::Add(args) => {
|
cli::SubCommand::Add(args) => {
|
||||||
cli::add::execute(repo, args);
|
cli::add::execute(repo, args);
|
||||||
}
|
}
|
||||||
cli::SubCommand::Edit(args) => {
|
cli::SubCommand::Edit(args) => {
|
||||||
cli::edit::execute(cli::edit::Request {
|
cli::edit::execute(repo, args);
|
||||||
args,
|
|
||||||
current_task_info,
|
|
||||||
tasks,
|
|
||||||
tasks_file_path,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
cli::SubCommand::Remove(args) => {
|
cli::SubCommand::Remove(args) => {
|
||||||
cli::remove::execute(cli::remove::Request {
|
cli::remove::execute(repo, args);
|
||||||
args,
|
|
||||||
current_task_info,
|
|
||||||
tasks,
|
|
||||||
tasks_file_path,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
cli::SubCommand::List => {
|
cli::SubCommand::List => {
|
||||||
cli::list::execute(cli::list::Request {
|
cli::list::execute(repo);
|
||||||
tasks,
|
|
||||||
current_task_info,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
cli::SubCommand::Priority(args) => {
|
cli::SubCommand::Priority(args) => {
|
||||||
cli::priority::execute(cli::priority::Request {
|
cli::priority::execute(repo, args);
|
||||||
args,
|
|
||||||
tasks,
|
|
||||||
tasks_file_path,
|
|
||||||
current_task_info,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
cli::SubCommand::Start(args) => {
|
cli::SubCommand::Start(args) => {
|
||||||
cli::start::execute(cli::start::Request {
|
cli::start::execute(repo, args);
|
||||||
args,
|
|
||||||
tasks,
|
|
||||||
current_task_info,
|
|
||||||
current_task_info_file_path,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
cli::SubCommand::Pause => {
|
cli::SubCommand::Pause => {
|
||||||
cli::pause::execute(cli::pause::Request {
|
cli::pause::execute(repo);
|
||||||
current_task_info,
|
|
||||||
current_task_info_file_path,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
cli::SubCommand::Finish => {
|
cli::SubCommand::Finish => {
|
||||||
cli::finish::execute(cli::finish::Request {
|
cli::finish::execute(repo);
|
||||||
tasks,
|
|
||||||
current_task_info,
|
|
||||||
current_task_info_file_path,
|
|
||||||
tasks_file_path,
|
|
||||||
finished_tasks_file_path,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
cli::SubCommand::Status => {
|
cli::SubCommand::Status => {
|
||||||
cli::status::execute(current_task_info);
|
cli::status::execute(repo);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize)]
|
|
||||||
pub struct CurrentTaskInfo {
|
|
||||||
task_idx: usize,
|
|
||||||
task: Task,
|
|
||||||
// started_at
|
|
||||||
}
|
|
||||||
|
|
28
src/repo.rs
28
src/repo.rs
|
@ -22,12 +22,36 @@ impl std::fmt::Display for Error {
|
||||||
impl std::error::Error for Error {}
|
impl std::error::Error for Error {}
|
||||||
|
|
||||||
pub struct InsertTaskData {
|
pub struct InsertTaskData {
|
||||||
name: String,
|
pub name: String,
|
||||||
link: Option<String>,
|
pub link: Option<String>,
|
||||||
|
pub index: Option<usize>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct UpdateTaskData {
|
||||||
|
pub name: Option<String>,
|
||||||
|
pub link: Option<Option<String>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait Repository {
|
pub trait Repository {
|
||||||
|
fn get_current_task_opt(&self) -> Result<Option<domain::CurrentTaskInfo>, Error>;
|
||||||
|
|
||||||
|
fn get_task_opt(&self, id: domain::TaskId) -> Result<Option<domain::Task>, Error>;
|
||||||
|
|
||||||
fn get_tasks(&self) -> Result<Vec<domain::Task>, Error>;
|
fn get_tasks(&self) -> Result<Vec<domain::Task>, Error>;
|
||||||
|
|
||||||
|
fn remove_task(&self, id: domain::TaskId) -> Result<domain::Task, Error>;
|
||||||
|
|
||||||
|
fn update_task(
|
||||||
|
&self,
|
||||||
|
id: domain::TaskId,
|
||||||
|
update_data: UpdateTaskData,
|
||||||
|
) -> Result<domain::Task, Error>;
|
||||||
|
|
||||||
fn insert_task(&self, insert_data: InsertTaskData) -> Result<domain::Task, Error>;
|
fn insert_task(&self, insert_data: InsertTaskData) -> Result<domain::Task, Error>;
|
||||||
|
|
||||||
|
fn start_task(&self, id: domain::TaskId) -> Result<domain::Task, Error>;
|
||||||
|
|
||||||
|
fn stop_task(&self) -> Result<domain::Task, Error>;
|
||||||
|
|
||||||
|
fn finish_task(&self) -> Result<domain::Task, Error>;
|
||||||
}
|
}
|
||||||
|
|
181
src/repo/fs.rs
181
src/repo/fs.rs
|
@ -2,7 +2,7 @@ use std::fs::File;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
|
|
||||||
use crate::domain;
|
use crate::domain;
|
||||||
use crate::repo::{Error, InsertTaskData, Repository};
|
use crate::repo::{Error, InsertTaskData, Repository, UpdateTaskData};
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use xdg::BaseDirectories;
|
use xdg::BaseDirectories;
|
||||||
|
@ -23,7 +23,25 @@ impl From<Task> for domain::Task {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Serialize, Clone)]
|
||||||
|
pub struct CurrentTaskInfo {
|
||||||
|
task_idx: usize,
|
||||||
|
task: Task,
|
||||||
|
// started_at
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<CurrentTaskInfo> for domain::CurrentTaskInfo {
|
||||||
|
fn from(repo: CurrentTaskInfo) -> Self {
|
||||||
|
domain::CurrentTaskInfo {
|
||||||
|
task_idx: repo.task_idx,
|
||||||
|
task: repo.task.into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const CURRENT_TASK_FILE: &str = "current.json";
|
||||||
const DATA_FILE: &str = "data.json";
|
const DATA_FILE: &str = "data.json";
|
||||||
|
const FINISHED_DATA_FILE: &str = "finished_data.json";
|
||||||
|
|
||||||
pub struct FsRepo {
|
pub struct FsRepo {
|
||||||
xdg_dirs: BaseDirectories,
|
xdg_dirs: BaseDirectories,
|
||||||
|
@ -36,11 +54,63 @@ impl FsRepo {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Repository for FsRepo {
|
impl Repository for FsRepo {
|
||||||
|
fn get_current_task_opt(&self) -> Result<Option<domain::CurrentTaskInfo>, Error> {
|
||||||
|
self.get_current_task_impl()
|
||||||
|
.map(|cur_task| cur_task.map(From::from))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_task_opt(&self, id: domain::TaskId) -> Result<Option<domain::Task>, Error> {
|
||||||
|
let tasks = self.get_tasks_impl()?;
|
||||||
|
if id == 0 || id > tasks.len() {
|
||||||
|
return Err(Error::NotFound);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Some(tasks[id - 1].clone().into()))
|
||||||
|
}
|
||||||
|
|
||||||
fn get_tasks(&self) -> Result<Vec<domain::Task>, Error> {
|
fn get_tasks(&self) -> Result<Vec<domain::Task>, Error> {
|
||||||
self.get_tasks_impl()
|
self.get_tasks_impl()
|
||||||
.map(|tasks| tasks.into_iter().map(Task::into).collect())
|
.map(|tasks| tasks.into_iter().map(Task::into).collect())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn remove_task(&self, id: domain::TaskId) -> Result<domain::Task, Error> {
|
||||||
|
let mut tasks = self.get_tasks_impl()?;
|
||||||
|
if id == 0 || id > tasks.len() {
|
||||||
|
return Err(Error::NotFound);
|
||||||
|
}
|
||||||
|
|
||||||
|
let removed = tasks.remove(id - 1);
|
||||||
|
self.save_tasks_impl(tasks)?;
|
||||||
|
|
||||||
|
Ok(removed.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update_task(
|
||||||
|
&self,
|
||||||
|
id: domain::TaskId,
|
||||||
|
update_data: UpdateTaskData,
|
||||||
|
) -> Result<domain::Task, Error> {
|
||||||
|
let mut tasks = self.get_tasks_impl()?;
|
||||||
|
if id == 0 || id > tasks.len() {
|
||||||
|
return Err(Error::NotFound);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut task = &mut tasks[id];
|
||||||
|
if let Some(name) = update_data.name {
|
||||||
|
task.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(link) = update_data.link {
|
||||||
|
task.link = link;
|
||||||
|
}
|
||||||
|
|
||||||
|
let new_task = task.clone();
|
||||||
|
|
||||||
|
self.save_tasks_impl(tasks)?;
|
||||||
|
|
||||||
|
Ok(new_task.into())
|
||||||
|
}
|
||||||
|
|
||||||
fn insert_task(&self, insert_data: InsertTaskData) -> Result<domain::Task, Error> {
|
fn insert_task(&self, insert_data: InsertTaskData) -> Result<domain::Task, Error> {
|
||||||
let new_task = Task {
|
let new_task = Task {
|
||||||
name: insert_data.name,
|
name: insert_data.name,
|
||||||
|
@ -48,25 +118,114 @@ impl Repository for FsRepo {
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut tasks = self.get_tasks_impl()?;
|
let mut tasks = self.get_tasks_impl()?;
|
||||||
tasks.push(new_task.clone());
|
|
||||||
|
|
||||||
|
match insert_data.index {
|
||||||
|
None => tasks.push(new_task.clone()),
|
||||||
|
Some(idx) => tasks.insert(idx, new_task.clone()),
|
||||||
|
}
|
||||||
|
|
||||||
|
self.save_tasks_impl(tasks)?;
|
||||||
|
|
||||||
|
Ok(new_task.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn start_task(&self, id: domain::TaskId) -> Result<domain::Task, Error> {
|
||||||
|
let tasks = self.get_tasks_impl()?;
|
||||||
|
if id == 0 || id > tasks.len() {
|
||||||
|
return Err(Error::NotFound);
|
||||||
|
}
|
||||||
|
|
||||||
|
let task = &tasks[id - 1];
|
||||||
|
let mut cur_task = self.get_current_task_impl()?;
|
||||||
|
|
||||||
|
cur_task.replace(CurrentTaskInfo {
|
||||||
|
task_idx: id,
|
||||||
|
task: task.clone(),
|
||||||
|
});
|
||||||
|
|
||||||
|
self.save_current_task_impl(cur_task)?;
|
||||||
|
Ok(task.clone().into())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn stop_task(&self) -> Result<domain::Task, Error> {
|
||||||
|
let mut cur_task = self.get_current_task_impl()?;
|
||||||
|
let old = cur_task.take().ok_or(Error::NotFound)?;
|
||||||
|
self.save_current_task_impl(cur_task)?;
|
||||||
|
Ok(old.task.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn finish_task(&self) -> Result<domain::Task, Error> {
|
||||||
|
let mut cur_task = self.get_current_task_impl()?;
|
||||||
|
let old = cur_task.take().ok_or(Error::NotFound)?;
|
||||||
|
|
||||||
|
let mut finished_tasks = self.get_finished_tasks_impl()?;
|
||||||
|
let mut tasks = self.get_tasks_impl()?;
|
||||||
|
|
||||||
|
let task = tasks.remove(old.task_idx - 1);
|
||||||
|
finished_tasks.push(task.clone());
|
||||||
|
|
||||||
|
self.save_current_task_impl(cur_task)?;
|
||||||
|
self.save_tasks_impl(tasks)?;
|
||||||
|
self.save_finished_tasks_impl(finished_tasks)?;
|
||||||
|
|
||||||
|
Ok(task.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FsRepo {
|
||||||
|
fn get_current_task_impl(&self) -> Result<Option<CurrentTaskInfo>, Error> {
|
||||||
|
let file_path = self.xdg_dirs.get_data_file(CURRENT_TASK_FILE);
|
||||||
|
File::open(&file_path)
|
||||||
|
.ok()
|
||||||
|
.map(|file| serde_json::from_reader(file).map_err(|_| Error::InvalidData))
|
||||||
|
.transpose()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn save_current_task_impl(&self, cur_task: Option<CurrentTaskInfo>) -> Result<(), Error> {
|
||||||
|
let file_path = self
|
||||||
|
.xdg_dirs
|
||||||
|
.place_data_file(CURRENT_TASK_FILE)
|
||||||
|
.map_err(|_| Error::InsertData)?;
|
||||||
|
let mut file = File::create(&file_path).map_err(|_| Error::InsertData)?;
|
||||||
|
let new_data = serde_json::to_vec(&cur_task).map_err(|_| Error::InvalidData)?;
|
||||||
|
file.write_all(&new_data).map_err(|_| Error::InsertData)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_tasks_impl(&self) -> Result<Vec<Task>, Error> {
|
||||||
|
let file_path = self.xdg_dirs.get_data_file(DATA_FILE);
|
||||||
|
File::open(&file_path)
|
||||||
|
.ok()
|
||||||
|
.map(|file| serde_json::from_reader(file).map_err(|_| Error::InvalidData))
|
||||||
|
.transpose()
|
||||||
|
.map(|tasks| tasks.unwrap_or_default())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn save_tasks_impl(&self, tasks: Vec<Task>) -> Result<(), Error> {
|
||||||
let file_path = self
|
let file_path = self
|
||||||
.xdg_dirs
|
.xdg_dirs
|
||||||
.place_data_file(DATA_FILE)
|
.place_data_file(DATA_FILE)
|
||||||
.map_err(|_| Error::InsertData)?;
|
.map_err(|_| Error::InsertData)?;
|
||||||
let mut file = File::create(&file_path).map_err(|_| Error::InsertData)?;
|
let mut file = File::create(&file_path).map_err(|_| Error::InsertData)?;
|
||||||
let new_data = serde_json::to_vec(&tasks).map_err(|_| Error::InvalidData)?;
|
let new_data = serde_json::to_vec(&tasks).map_err(|_| Error::InvalidData)?;
|
||||||
file.write_all(&new_data).map_err(|_| Error::InsertData);
|
file.write_all(&new_data).map_err(|_| Error::InsertData)
|
||||||
|
|
||||||
Ok(new_task.into())
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl FsRepo {
|
fn get_finished_tasks_impl(&self) -> Result<Vec<Task>, Error> {
|
||||||
fn get_tasks_impl(&self) -> Result<Vec<Task>, Error> {
|
let file_path = self.xdg_dirs.get_data_file(FINISHED_DATA_FILE);
|
||||||
let file_path = self.xdg_dirs.get_data_file(DATA_FILE);
|
|
||||||
File::open(&file_path)
|
File::open(&file_path)
|
||||||
.map_err(|_| Error::NotFound)
|
.ok()
|
||||||
.and_then(|file| serde_json::from_reader(file).map_err(|_| Error::InvalidData))
|
.map(|file| serde_json::from_reader(file).map_err(|_| Error::InvalidData))
|
||||||
|
.transpose()
|
||||||
|
.map(|tasks| tasks.unwrap_or_default())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn save_finished_tasks_impl(&self, tasks: Vec<Task>) -> Result<(), Error> {
|
||||||
|
let file_path = self
|
||||||
|
.xdg_dirs
|
||||||
|
.place_data_file(FINISHED_DATA_FILE)
|
||||||
|
.map_err(|_| Error::InsertData)?;
|
||||||
|
let mut file = File::create(&file_path).map_err(|_| Error::InsertData)?;
|
||||||
|
let new_data = serde_json::to_vec(&tasks).map_err(|_| Error::InvalidData)?;
|
||||||
|
file.write_all(&new_data).map_err(|_| Error::InsertData)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue