diff --git a/src/cli/add.rs b/src/cli/add.rs index 879d63e..475b277 100644 --- a/src/cli/add.rs +++ b/src/cli/add.rs @@ -1,6 +1,4 @@ -use std::path::PathBuf; - -use crate::Task; +use crate::repo::{self, Repository}; #[derive(clap::Args)] pub struct Args { @@ -10,18 +8,24 @@ pub struct Args { name: String, } -pub struct Request { - pub args: Args, - pub tasks: Vec, - pub tasks_file_path: PathBuf, -} +pub fn execute(repo: impl Repository, args: Args) { + let res = repo.insert_task(repo::InsertTaskData { + name: args.name, + link: args.link, + }); -pub fn execute(mut req: Request) { - tasks.push(Task { name, link }); - - let mut file = std::fs::File::create(&tasks_file_path).unwrap(); - file.write_all(&serde_json::to_vec(&tasks).unwrap()) - .unwrap(); + match res { + Ok(task) => { + println!("Task was added successfully"); + println!(" {}", task.name); + if let Some(link) = task.link { + println!(" link: {}", link); + } + } + Err(err) => { + eprintln!("Cannot insert data: {}", err); + } + } println!("added"); } diff --git a/src/domain.rs b/src/domain.rs new file mode 100644 index 0000000..05f8920 --- /dev/null +++ b/src/domain.rs @@ -0,0 +1,5 @@ +pub struct Task { + pub name: String, + pub link: Option, + // created_at +} diff --git a/src/main.rs b/src/main.rs index 168c45b..6c8734f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -27,23 +27,22 @@ //--------------------------------------------------------------------- use clap::Parser; +use repo::fs::FsRepo; use serde::{Deserialize, Serialize}; -use std::io::Write; use xdg::BaseDirectories; mod cli; +pub mod domain; +pub mod repo; fn main() { let args = cli::Args::parse(); let xdg_dirs = BaseDirectories::with_prefix(env!("CARGO_PKG_NAME")).unwrap(); - let finished_tasks_file_path = xdg_dirs.place_data_file("finished_data.json").unwrap(); + let repo = FsRepo::new(xdg_dirs); - let tasks_file_path = xdg_dirs.place_data_file("data.json").unwrap(); - let mut tasks: Vec = std::fs::File::open(&tasks_file_path) - .map(|file| serde_json::from_reader(file).unwrap()) - .unwrap_or_default(); + 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 = @@ -53,11 +52,7 @@ fn main() { match args.command { cli::SubCommand::Add(args) => { - cli::add::execute(cli::add::Request { - args, - tasks, - tasks_file_path, - }); + cli::add::execute(repo, args); } cli::SubCommand::Edit(args) => { cli::edit::execute(cli::edit::Request { @@ -118,13 +113,6 @@ fn main() { } } -#[derive(Deserialize, Serialize, Clone)] -pub struct Task { - name: String, - link: Option, - // created_at -} - #[derive(Deserialize, Serialize)] pub struct CurrentTaskInfo { task_idx: usize, diff --git a/src/repo.rs b/src/repo.rs new file mode 100755 index 0000000..f2e54c5 --- /dev/null +++ b/src/repo.rs @@ -0,0 +1,33 @@ +pub mod fs; + +use crate::domain; + +#[derive(Debug)] +pub enum Error { + NotFound, + InvalidData, + InsertData, +} + +impl std::fmt::Display for Error { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Error::NotFound => f.write_str("Cannot find data"), + Error::InvalidData => f.write_str("Invalid data format"), + Error::InsertData => f.write_str("Cannot insert data"), + } + } +} + +impl std::error::Error for Error {} + +pub struct InsertTaskData { + name: String, + link: Option, +} + +pub trait Repository { + fn get_tasks(&self) -> Result, Error>; + + fn insert_task(&self, insert_data: InsertTaskData) -> Result; +} diff --git a/src/repo/fs.rs b/src/repo/fs.rs new file mode 100644 index 0000000..750f85f --- /dev/null +++ b/src/repo/fs.rs @@ -0,0 +1,72 @@ +use std::fs::File; +use std::io::Write; + +use crate::domain; +use crate::repo::{Error, InsertTaskData, Repository}; + +use serde::{Deserialize, Serialize}; +use xdg::BaseDirectories; + +#[derive(Deserialize, Serialize, Clone)] +pub struct Task { + name: String, + link: Option, + // created_at +} + +impl From for domain::Task { + fn from(repo: Task) -> Self { + domain::Task { + name: repo.name, + link: repo.link, + } + } +} + +const DATA_FILE: &str = "data.json"; + +pub struct FsRepo { + xdg_dirs: BaseDirectories, +} + +impl FsRepo { + pub fn new(xdg_dirs: BaseDirectories) -> Self { + Self { xdg_dirs } + } +} + +impl Repository for FsRepo { + fn get_tasks(&self) -> Result, Error> { + self.get_tasks_impl() + .map(|tasks| tasks.into_iter().map(Task::into).collect()) + } + + fn insert_task(&self, insert_data: InsertTaskData) -> Result { + let new_task = Task { + name: insert_data.name, + link: insert_data.link, + }; + + let mut tasks = self.get_tasks_impl()?; + tasks.push(new_task.clone()); + + 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()) + } +} + +impl FsRepo { + fn get_tasks_impl(&self) -> Result, Error> { + let file_path = self.xdg_dirs.get_data_file(DATA_FILE); + File::open(&file_path) + .map_err(|_| Error::NotFound) + .and_then(|file| serde_json::from_reader(file).map_err(|_| Error::InvalidData)) + } +}