Merge pull request 'Repo: move all methods to repository' (#14) from repo into main
Reviewed-on: #14
This commit is contained in:
commit
512514f721
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 {
|
||||
name: args.name,
|
||||
link: args.link,
|
||||
index: None,
|
||||
});
|
||||
|
||||
match res {
|
||||
Ok(task) => {
|
||||
println!("Task was added successfully");
|
||||
println!("The task was added successfully");
|
||||
println!(" {}", task.name);
|
||||
if let Some(link) = task.link {
|
||||
println!(" link: {}", link);
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
eprintln!("Cannot insert data: {}", err);
|
||||
eprintln!("Cannot insert a new task: {}", err);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,4 @@
|
|||
use std::io::Write;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use crate::{CurrentTaskInfo, Task};
|
||||
use crate::repo::{self, Repository};
|
||||
|
||||
#[derive(clap::Args)]
|
||||
pub struct Args {
|
||||
|
@ -16,36 +13,34 @@ pub struct Args {
|
|||
idx: usize,
|
||||
}
|
||||
|
||||
pub struct Request {
|
||||
pub args: Args,
|
||||
pub current_task_info: Option<CurrentTaskInfo>,
|
||||
pub tasks: Vec<Task>,
|
||||
pub tasks_file_path: PathBuf,
|
||||
}
|
||||
|
||||
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 idx = req.args.idx;
|
||||
if idx == 0 || idx > req.tasks.len() {
|
||||
panic!("invalid index");
|
||||
}
|
||||
|
||||
let mut task = &mut req.tasks[idx - 1];
|
||||
if let Some(name) = req.args.name {
|
||||
task.name = name;
|
||||
}
|
||||
if let Some(link) = req.args.link {
|
||||
task.link = Some(link);
|
||||
} else if req.args.no_link {
|
||||
task.link = None;
|
||||
}
|
||||
|
||||
let mut file = std::fs::File::create(&req.tasks_file_path).unwrap();
|
||||
file.write_all(&serde_json::to_vec(&req.tasks).unwrap())
|
||||
.unwrap();
|
||||
|
||||
println!("changed");
|
||||
pub fn execute(repo: impl Repository, args: Args) {
|
||||
match repo.get_current_task_opt() {
|
||||
Ok(Some(_)) => {
|
||||
return eprintln!("You can edit task only when you don't have an active task, yet")
|
||||
}
|
||||
Err(err) => {
|
||||
return eprintln!("Cannot read current task: {}", err);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
let res = repo.update_task(
|
||||
args.idx,
|
||||
repo::UpdateTaskData {
|
||||
name: args.name,
|
||||
link: args.no_link.then(|| None).or_else(|| args.link.map(Some)),
|
||||
},
|
||||
);
|
||||
match res {
|
||||
Ok(task) => {
|
||||
println!("The task was changed successfully");
|
||||
println!(" {}", task.name);
|
||||
if let Some(link) = task.link {
|
||||
println!(" link: {}", link);
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
eprintln!("Cannot update the task: {}", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,41 +1,17 @@
|
|||
use crate::{CurrentTaskInfo, Task};
|
||||
use std::io::Write;
|
||||
use std::path::PathBuf;
|
||||
use crate::repo::{self, Repository};
|
||||
|
||||
pub struct Request {
|
||||
pub current_task_info: Option<CurrentTaskInfo>,
|
||||
pub tasks: Vec<Task>,
|
||||
pub finished_tasks_file_path: PathBuf,
|
||||
pub tasks_file_path: PathBuf,
|
||||
pub current_task_info_file_path: PathBuf,
|
||||
}
|
||||
|
||||
pub fn execute(mut req: Request) {
|
||||
match req.current_task_info.take() {
|
||||
None => {
|
||||
panic!("You can use the finish subcommand only when you have an active task")
|
||||
pub fn execute(repo: impl Repository) {
|
||||
match repo.finish_task() {
|
||||
Err(repo::Error::NotFound) => {
|
||||
eprintln!("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");
|
||||
Err(err) => eprintln!("Cannot finish the task: {}", err),
|
||||
Ok(task) => {
|
||||
println!("The task was finished successfully");
|
||||
println!(" {}", task.name);
|
||||
if let Some(link) = task.link {
|
||||
println!(" link: {}", link);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,15 +1,23 @@
|
|||
use crate::{CurrentTaskInfo, Task};
|
||||
use crate::domain::CurrentTaskInfo;
|
||||
use crate::repo::Repository;
|
||||
|
||||
pub struct Request {
|
||||
pub tasks: Vec<Task>,
|
||||
pub current_task_info: Option<CurrentTaskInfo>,
|
||||
}
|
||||
pub fn execute(repo: impl Repository) {
|
||||
let tasks = match repo.get_tasks() {
|
||||
Ok(tasks) => tasks,
|
||||
Err(err) => return eprintln!("Cannot read tasks: {}", err),
|
||||
};
|
||||
|
||||
pub fn execute(req: Request) {
|
||||
for (i, task) in req.tasks.iter().enumerate() {
|
||||
let cur_task = match repo.get_current_task_opt() {
|
||||
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;
|
||||
|
||||
match req.current_task_info {
|
||||
match cur_task {
|
||||
Some(CurrentTaskInfo { task_idx, .. }) if task_idx == idx => print!("> "),
|
||||
_ => print!(" "),
|
||||
}
|
||||
|
|
|
@ -1,20 +1,26 @@
|
|||
use std::io::Write;
|
||||
use std::path::PathBuf;
|
||||
use crate::repo::{self, Repository};
|
||||
|
||||
use crate::CurrentTaskInfo;
|
||||
pub fn execute(repo: impl Repository) {
|
||||
let task = match repo.stop_task() {
|
||||
Err(repo::Error::NotFound) => {
|
||||
return eprintln!("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,
|
||||
};
|
||||
|
||||
pub struct Request {
|
||||
pub current_task_info: Option<CurrentTaskInfo>,
|
||||
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");
|
||||
match repo.stop_task() {
|
||||
Ok(_) => {
|
||||
println!("The task was paused successfully");
|
||||
println!(" {}", task.name);
|
||||
if let Some(link) = task.link {
|
||||
println!(" link: {}", link);
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
eprintln!("Cannot pause the task: {}", err);
|
||||
}
|
||||
}
|
||||
|
||||
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!("paused");
|
||||
}
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
use crate::{CurrentTaskInfo, Task};
|
||||
use crate::repo::{self, Repository};
|
||||
|
||||
use std::cmp::Ordering;
|
||||
use std::io::Write;
|
||||
use std::path::PathBuf;
|
||||
|
||||
#[derive(clap::Args)]
|
||||
pub struct Args {
|
||||
|
@ -17,46 +15,61 @@ pub enum Priority {
|
|||
After { idx: usize },
|
||||
}
|
||||
|
||||
pub struct Request {
|
||||
pub args: Args,
|
||||
pub tasks: Vec<Task>,
|
||||
pub current_task_info: Option<CurrentTaskInfo>,
|
||||
pub tasks_file_path: PathBuf,
|
||||
}
|
||||
|
||||
pub fn execute(mut req: Request) {
|
||||
if req.current_task_info.is_some() {
|
||||
panic!("You can change priority only when you don't have an active task, yet");
|
||||
pub fn execute(repo: impl Repository, args: Args) {
|
||||
match repo.get_current_task_opt() {
|
||||
Ok(Some(_)) => {
|
||||
return eprintln!(
|
||||
"You can change priority only when you don't have an active task, yet"
|
||||
)
|
||||
}
|
||||
Err(err) => {
|
||||
return eprintln!("Cannot read current task: {}", err);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
let target_idx = req.args.idx;
|
||||
if target_idx == 0 || target_idx > req.tasks.len() {
|
||||
panic!("invalid index");
|
||||
let target_idx = args.idx;
|
||||
|
||||
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) {
|
||||
Ordering::Equal => return,
|
||||
Ordering::Less => idx - 1,
|
||||
Ordering::Greater => idx,
|
||||
},
|
||||
};
|
||||
if idx == 0 || idx > req.tasks.len() {
|
||||
panic!("invalid index");
|
||||
if let Err(err) = repo.get_task_opt(idx) {
|
||||
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 {
|
||||
Priority::Before { .. } => {
|
||||
req.tasks.insert(idx - 1, target);
|
||||
let new_idx = match args.priority {
|
||||
Priority::Before { .. } => idx - 1,
|
||||
Priority::After { .. } => idx,
|
||||
};
|
||||
|
||||
let res = repo.insert_task(repo::InsertTaskData {
|
||||
index: Some(new_idx),
|
||||
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);
|
||||
}
|
||||
}
|
||||
Priority::After { .. } => {
|
||||
req.tasks.insert(idx, target);
|
||||
Err(err) => {
|
||||
eprintln!("Cannot reorder the task: {}", err);
|
||||
}
|
||||
}
|
||||
|
||||
let mut file = std::fs::File::create(&req.tasks_file_path).unwrap();
|
||||
file.write_all(&serde_json::to_vec(&req.tasks).unwrap())
|
||||
.unwrap();
|
||||
}
|
||||
|
|
|
@ -1,31 +1,28 @@
|
|||
use crate::repo::{self, Repository};
|
||||
use std::io::{BufRead, Write};
|
||||
use std::path::PathBuf;
|
||||
|
||||
use crate::{CurrentTaskInfo, Task};
|
||||
|
||||
#[derive(clap::Args)]
|
||||
pub struct Args {
|
||||
idx: usize,
|
||||
}
|
||||
|
||||
pub struct Request {
|
||||
pub args: Args,
|
||||
pub current_task_info: Option<CurrentTaskInfo>,
|
||||
pub tasks: Vec<Task>,
|
||||
pub tasks_file_path: PathBuf,
|
||||
}
|
||||
|
||||
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");
|
||||
pub fn execute(repo: impl Repository, args: Args) {
|
||||
match repo.get_current_task_opt() {
|
||||
Ok(Some(_)) => {
|
||||
return eprintln!("You can remove task only when you don't have an active task, yet");
|
||||
}
|
||||
Err(err) => {
|
||||
return eprintln!("Cannot read current task: {}", err);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
let idx = req.args.idx;
|
||||
if idx == 0 || idx > req.tasks.len() {
|
||||
panic!("invalid index");
|
||||
}
|
||||
let task = match repo.get_task_opt(args.idx) {
|
||||
Ok(Some(task)) => task,
|
||||
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!(" {}", task.name);
|
||||
if let Some(ref link) = task.link {
|
||||
|
@ -46,11 +43,16 @@ pub fn execute(mut req: Request) {
|
|||
}
|
||||
}
|
||||
|
||||
req.tasks.remove(idx - 1);
|
||||
|
||||
let mut file = std::fs::File::create(&req.tasks_file_path).unwrap();
|
||||
file.write_all(&serde_json::to_vec(&req.tasks).unwrap())
|
||||
.unwrap();
|
||||
|
||||
println!("removed");
|
||||
match repo.remove_task(args.idx) {
|
||||
Ok(_) => {
|
||||
println!("The task was removed successfully");
|
||||
println!(" {}", task.name);
|
||||
if let Some(link) = task.link {
|
||||
println!(" link: {}", link);
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
eprintln!("Cannot remove the task: {}", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
use crate::{CurrentTaskInfo, Task};
|
||||
use std::{io::Write, path::PathBuf};
|
||||
use crate::repo::{self, Repository};
|
||||
|
||||
#[derive(clap::Args)]
|
||||
pub struct Args {
|
||||
|
@ -9,31 +8,20 @@ pub struct Args {
|
|||
idx: Option<usize>,
|
||||
}
|
||||
|
||||
pub struct Request {
|
||||
pub args: Args,
|
||||
pub tasks: Vec<Task>,
|
||||
pub current_task_info: Option<CurrentTaskInfo>,
|
||||
pub current_task_info_file_path: PathBuf,
|
||||
}
|
||||
pub fn execute(repo: impl Repository, args: Args) {
|
||||
let task = match repo.start_task(args.idx.unwrap_or(1)) {
|
||||
Ok(task) => task,
|
||||
Err(repo::Error::NotFound) => return eprintln!("Task not found"),
|
||||
Err(err) => return eprintln!("Cannot start task: {}", err),
|
||||
};
|
||||
|
||||
pub fn execute(mut req: Request) {
|
||||
let idx = req.args.idx.unwrap_or(1);
|
||||
if idx == 0 || idx > req.tasks.len() {
|
||||
panic!("invalid index");
|
||||
println!("The task was started successfully");
|
||||
println!(" {}", task.name);
|
||||
if let Some(link) = task.link.as_ref() {
|
||||
println!(" link: {}", link);
|
||||
}
|
||||
let task = &req.tasks[idx - 1];
|
||||
|
||||
req.current_task_info.replace(CurrentTaskInfo {
|
||||
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) {
|
||||
if let (Some(link), true) = (task.link.as_ref(), args.open) {
|
||||
log::debug!("opening link...");
|
||||
std::process::Command::new("xdg-open")
|
||||
.arg(link)
|
||||
|
|
|
@ -1,16 +1,19 @@
|
|||
use crate::CurrentTaskInfo;
|
||||
use crate::repo::Repository;
|
||||
|
||||
pub fn execute(current_task_info: Option<CurrentTaskInfo>) {
|
||||
match current_task_info {
|
||||
None => {
|
||||
pub fn execute(repo: impl Repository) {
|
||||
match repo.get_current_task_opt() {
|
||||
Ok(None) => {
|
||||
eprintln!("You don't have an active task.");
|
||||
}
|
||||
Some(info) => {
|
||||
Ok(Some(info)) => {
|
||||
println!("Information about your current task:");
|
||||
println!(" {}", info.task.name);
|
||||
if let Some(link) = info.task.link {
|
||||
println!(" link: {}", link);
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
eprintln!("Cannot read current task: {}", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,13 @@
|
|||
pub type TaskId = usize;
|
||||
|
||||
pub struct Task {
|
||||
pub name: String,
|
||||
pub link: Option<String>,
|
||||
// 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 repo::fs::FsRepo;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use xdg::BaseDirectories;
|
||||
|
||||
mod cli;
|
||||
|
@ -39,83 +38,35 @@ fn main() {
|
|||
let args = cli::Args::parse();
|
||||
|
||||
let xdg_dirs = BaseDirectories::with_prefix(env!("CARGO_PKG_NAME")).unwrap();
|
||||
|
||||
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 {
|
||||
cli::SubCommand::Add(args) => {
|
||||
cli::add::execute(repo, args);
|
||||
}
|
||||
cli::SubCommand::Edit(args) => {
|
||||
cli::edit::execute(cli::edit::Request {
|
||||
args,
|
||||
current_task_info,
|
||||
tasks,
|
||||
tasks_file_path,
|
||||
});
|
||||
cli::edit::execute(repo, args);
|
||||
}
|
||||
cli::SubCommand::Remove(args) => {
|
||||
cli::remove::execute(cli::remove::Request {
|
||||
args,
|
||||
current_task_info,
|
||||
tasks,
|
||||
tasks_file_path,
|
||||
});
|
||||
cli::remove::execute(repo, args);
|
||||
}
|
||||
cli::SubCommand::List => {
|
||||
cli::list::execute(cli::list::Request {
|
||||
tasks,
|
||||
current_task_info,
|
||||
});
|
||||
cli::list::execute(repo);
|
||||
}
|
||||
cli::SubCommand::Priority(args) => {
|
||||
cli::priority::execute(cli::priority::Request {
|
||||
args,
|
||||
tasks,
|
||||
tasks_file_path,
|
||||
current_task_info,
|
||||
});
|
||||
cli::priority::execute(repo, args);
|
||||
}
|
||||
cli::SubCommand::Start(args) => {
|
||||
cli::start::execute(cli::start::Request {
|
||||
args,
|
||||
tasks,
|
||||
current_task_info,
|
||||
current_task_info_file_path,
|
||||
});
|
||||
cli::start::execute(repo, args);
|
||||
}
|
||||
cli::SubCommand::Pause => {
|
||||
cli::pause::execute(cli::pause::Request {
|
||||
current_task_info,
|
||||
current_task_info_file_path,
|
||||
});
|
||||
cli::pause::execute(repo);
|
||||
}
|
||||
cli::SubCommand::Finish => {
|
||||
cli::finish::execute(cli::finish::Request {
|
||||
tasks,
|
||||
current_task_info,
|
||||
current_task_info_file_path,
|
||||
tasks_file_path,
|
||||
finished_tasks_file_path,
|
||||
});
|
||||
cli::finish::execute(repo);
|
||||
}
|
||||
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 {}
|
||||
|
||||
pub struct InsertTaskData {
|
||||
name: String,
|
||||
link: Option<String>,
|
||||
pub name: String,
|
||||
pub link: Option<String>,
|
||||
pub index: Option<usize>,
|
||||
}
|
||||
|
||||
pub struct UpdateTaskData {
|
||||
pub name: Option<String>,
|
||||
pub link: Option<Option<String>>,
|
||||
}
|
||||
|
||||
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 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 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 crate::domain;
|
||||
use crate::repo::{Error, InsertTaskData, Repository};
|
||||
use crate::repo::{Error, InsertTaskData, Repository, UpdateTaskData};
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
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 FINISHED_DATA_FILE: &str = "finished_data.json";
|
||||
|
||||
pub struct FsRepo {
|
||||
xdg_dirs: BaseDirectories,
|
||||
|
@ -36,11 +54,63 @@ impl 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> {
|
||||
self.get_tasks_impl()
|
||||
.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> {
|
||||
let new_task = Task {
|
||||
name: insert_data.name,
|
||||
|
@ -48,25 +118,114 @@ impl Repository for FsRepo {
|
|||
};
|
||||
|
||||
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
|
||||
.xdg_dirs
|
||||
.place_data_file(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);
|
||||
|
||||
Ok(new_task.into())
|
||||
file.write_all(&new_data).map_err(|_| Error::InsertData)
|
||||
}
|
||||
}
|
||||
|
||||
impl FsRepo {
|
||||
fn get_tasks_impl(&self) -> Result<Vec<Task>, Error> {
|
||||
let file_path = self.xdg_dirs.get_data_file(DATA_FILE);
|
||||
fn get_finished_tasks_impl(&self) -> Result<Vec<Task>, Error> {
|
||||
let file_path = self.xdg_dirs.get_data_file(FINISHED_DATA_FILE);
|
||||
File::open(&file_path)
|
||||
.map_err(|_| Error::NotFound)
|
||||
.and_then(|file| serde_json::from_reader(file).map_err(|_| Error::InvalidData))
|
||||
.ok()
|
||||
.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