From bc602c4d34aac15fe6029d68171c0136ca341a5a Mon Sep 17 00:00:00 2001 From: aaron-jack-manning Date: Thu, 25 Aug 2022 10:44:22 +1000 Subject: [PATCH] cleaner representation of index --- Cargo.lock | 19 +++++++++++ Cargo.toml | 1 + src/edit.rs | 4 +-- src/index.rs | 94 ++++++++++++++++++++++++++++++++++++++++++++++++++++ src/main.rs | 15 +++++---- src/state.rs | 80 ++------------------------------------------ src/tasks.rs | 2 +- 7 files changed, 128 insertions(+), 87 deletions(-) create mode 100644 src/index.rs diff --git a/Cargo.lock b/Cargo.lock index a20f1cf..d1c384f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -234,6 +234,12 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "fixedbitset" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" + [[package]] name = "fnv" version = "1.0.7" @@ -496,6 +502,18 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" +[[package]] +name = "petgraph" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5014253a1331579ce62aa67443b4a658c5e7dd03d4bc6d302b94474888143" +dependencies = [ + "fixedbitset", + "indexmap", + "serde", + "serde_derive", +] + [[package]] name = "proc-macro-error" version = "1.0.4" @@ -832,6 +850,7 @@ dependencies = [ "colored", "comfy-table", "confy", + "petgraph", "serde", "serde_with", "termsize", diff --git a/Cargo.toml b/Cargo.toml index e64f90c..66a7524 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,6 +13,7 @@ clap = { version = "3.2.17", features = ["derive"] } colored = "2.0.0" comfy-table = "6.0.0" confy = "0.4.0" +petgraph = { version = "0.6.2", features = ["serde_derive", "serde", "serde-1"] } serde = { version = "1.0.143", features = ["derive"] } serde_with = "2.0.0" termsize = "0.1.6" diff --git a/src/edit.rs b/src/edit.rs index da2de41..56aae62 100644 --- a/src/edit.rs +++ b/src/edit.rs @@ -81,8 +81,8 @@ pub fn edit_raw(id : Id, vault_folder : path::PathBuf, editor : &str, state : &m } // Name change means index needs to be updated. if edited_task.data.name != task.data.name { - state.index_remove(task.data.name.clone(), id); - state.index_insert(edited_task.data.name.clone(), id); + state.data.index.remove(task.data.name.clone(), id); + state.data.index.insert(edited_task.data.name.clone(), id); } mem::swap(&mut edited_task.data, &mut task.data); diff --git a/src/index.rs b/src/index.rs new file mode 100644 index 0000000..f468b2f --- /dev/null +++ b/src/index.rs @@ -0,0 +1,94 @@ +use crate::tasks; +use crate::error; +use crate::colour; +use crate::tasks::Id; + +use std::collections::HashMap; +use serde_with::{serde_as, DisplayFromStr}; + +#[serde_as] +#[derive(serde::Serialize, serde::Deserialize)] +pub struct Index { + #[serde_as(as = "HashMap")] + map : HashMap> +} + +impl Index { + pub fn create(tasks : &Vec) -> Index { + let mut map : HashMap> = HashMap::with_capacity(tasks.len()); + for task in tasks { + match map.get_mut(&task.data.name) { + Some(ids) => { + ids.push(task.data.id); + }, + None => { + map.insert(task.data.name.clone(), vec![task.data.id]); + }, + } + } + + Self { + map + } + } + + pub fn insert(&mut self, name : String, id : Id) { + match self.map.get_mut(&name) { + Some(ids) => { + ids.push(id); + }, + None => { + self.map.insert(name, vec![id]); + } + } + } + + pub fn remove(&mut self, name : String, id : Id) { + if let Some(mut ids) = self.map.remove(&name) { + if let Some(index) = ids.iter().position(|i| i == &id) { + ids.swap_remove(index); + + if !ids.is_empty() { + self.map.insert(name, ids); + } + } + } + } + + pub fn lookup(&self, name_or_id : &String) -> Result { + match name_or_id.parse::() { + Ok(id) => Ok(id), + Err(_) => { + let name = name_or_id; + match self.map.get(name) { + Some(ids) => { + if ids.len() == 1 { + Ok(ids[0]) + } + else { + let coloured_ids : Vec<_> = + ids.into_iter() + .map(|i| colour::id(&i.to_string())) + .collect(); + + let mut display_ids = String::new(); + + for id in coloured_ids { + display_ids.push_str(&format!("{}, ", id)); + } + + if !display_ids.is_empty() { + display_ids.pop(); + display_ids.pop(); + } + + Err(error::Error::Generic(format!("Multiple notes (Ids: [{}]) by that name exist", display_ids))) + } + }, + None => Err(error::Error::Generic(format!("A note by the name {} does not exist", colour::task_name(&name)))), + } + } + } + + } +} diff --git a/src/main.rs b/src/main.rs index 8426673..de5dd15 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,7 @@ mod vcs; mod edit; mod vault; +mod index; mod error; mod tasks; mod state; @@ -252,21 +253,21 @@ fn program() -> Result<(), error::Error> { println!("Created task {} (ID: {})", colour::task_name(&task.data.name), colour::id(&task.data.id.to_string())); }, Delete { id_or_name } => { - let id = state.name_or_id_to_id(&id_or_name)?; + let id = state.data.index.lookup(&id_or_name)?; let task = tasks::Task::load(id, vault_folder, false)?; let name = task.data.name.clone(); - state.index_remove(task.data.name.clone(), task.data.id); + state.data.index.remove(task.data.name.clone(), task.data.id); task.delete()?; println!("Deleted task {} (ID: {})", colour::task_name(&name), colour::id(&id.to_string())); }, View { id_or_name } => { - let id = state.name_or_id_to_id(&id_or_name)?; + let id = state.data.index.lookup(&id_or_name)?; let task = tasks::Task::load(id, vault_folder, true)?; task.display()?; }, Edit { id_or_name, info } => { - let id = state.name_or_id_to_id(&id_or_name)?; + let id = state.data.index.lookup(&id_or_name)?; if info { edit::edit_info(id, vault_folder.clone(), &config.editor)?; } @@ -276,7 +277,7 @@ fn program() -> Result<(), error::Error> { println!("Updated task {}", colour::id(&id.to_string())); }, Track { id_or_name, hours, minutes } => { - let id = state.name_or_id_to_id(&id_or_name)?; + let id = state.data.index.lookup(&id_or_name)?; let mut task = tasks::Task::load(id, vault_folder, false)?; let entry = tasks::TimeEntry::new(hours, minutes); task.data.time_entries.push(entry); @@ -294,14 +295,14 @@ fn program() -> Result<(), error::Error> { } }, Discard { id_or_name } => { - let id = state.name_or_id_to_id(&id_or_name)?; + let id = state.data.index.lookup(&id_or_name)?; let mut task = tasks::Task::load(id, vault_folder, false)?; task.data.discarded = true; task.save()?; println!("Discarded task {}", colour::id(&id.to_string())); }, Complete { id_or_name } => { - let id = state.name_or_id_to_id(&id_or_name)?; + let id = state.data.index.lookup(&id_or_name)?; let mut task = tasks::Task::load(id, vault_folder, false)?; task.data.completed = Some(chrono::Local::now().naive_local()); task.save()?; diff --git a/src/state.rs b/src/state.rs index 3aa685b..b8ead06 100644 --- a/src/state.rs +++ b/src/state.rs @@ -1,29 +1,23 @@ use crate::error; use crate::tasks; -use crate::colour; +use crate::index; use crate::tasks::Id; use std::fs; use std::path; use std::io; use std::io::{Write, Seek}; -use std::collections::HashMap; -use serde_with::{serde_as, DisplayFromStr}; pub struct State { file : fs::File, pub data : InternalState, } -#[serde_as] #[derive(serde::Serialize, serde::Deserialize)] pub struct InternalState { pub next_id : Id, - #[serde_as(as = "HashMap")] - pub index : HashMap>, - //#[serde_as(as = "HashMap")] - //pub dependencies : HashMap>, + pub index : index::Index, } impl State { @@ -60,19 +54,8 @@ impl State { // Calculating out the index. let tasks = tasks::Task::load_all(vault_location, true)?; - let mut index : HashMap> = HashMap::with_capacity(tasks.len()); - for task in tasks { - match index.get_mut(&task.data.name) { - Some(ids) => { - ids.push(task.data.id); - }, - None => { - index.insert(task.data.name.clone(), vec![task.data.id]); - }, - } - } - // + let index = index::Index::create(&tasks); let data = InternalState { next_id : u64::try_from(max_id + 1).unwrap(), @@ -115,61 +98,4 @@ impl State { Ok(()) } - pub fn index_insert(&mut self, name : String, id : Id) { - match self.data.index.get_mut(&name) { - Some(ids) => { - ids.push(id); - }, - None => { - self.data.index.insert(name, vec![id]); - } - } - } - - pub fn index_remove(&mut self, name : String, id : Id) { - if let Some(mut ids) = self.data.index.remove(&name) { - if let Some(index) = ids.iter().position(|i| i == &id) { - ids.swap_remove(index); - - if !ids.is_empty() { - self.data.index.insert(name, ids); - } - } - } - } - - pub fn name_or_id_to_id(&self, name : &String) -> Result { - match name.parse::() { - Ok(id) => Ok(id), - Err(_) => { - match self.data.index.get(name) { - Some(ids) => { - if ids.len() == 1 { - Ok(ids[0]) - } - else { - let coloured_ids : Vec<_> = - ids.into_iter() - .map(|i| colour::id(&i.to_string())) - .collect(); - - let mut display_ids = String::new(); - - for id in coloured_ids { - display_ids.push_str(&format!("{}, ", id)); - } - - if !display_ids.is_empty() { - display_ids.pop(); - display_ids.pop(); - } - - Err(error::Error::Generic(format!("Multiple notes (Ids: [{}]) by that name exist", display_ids))) - } - }, - None => Err(error::Error::Generic(format!("A note by the name {} does not exist", colour::task_name(&name)))), - } - } - } - } } diff --git a/src/tasks.rs b/src/tasks.rs index 754d08d..c1cd33b 100644 --- a/src/tasks.rs +++ b/src/tasks.rs @@ -119,7 +119,7 @@ impl Task { file.seek(io::SeekFrom::Start(0))?; file.write_all(file_contents.as_bytes())?; - state.index_insert(data.name.clone(), id); + state.data.index.insert(data.name.clone(), id); Ok(Task { path,