diff --git a/src/bookmark_reader/chromium.rs b/src/bookmark_reader/chromium.rs index f4837b7..a77b3f2 100644 --- a/src/bookmark_reader/chromium.rs +++ b/src/bookmark_reader/chromium.rs @@ -1,5 +1,8 @@ use super::{ReadBookmark, ReaderName}; -use crate::{bookmarks::Source, utils, SourceBookmark, SourceBookmarks, SourceType}; +use crate::{ + bookmarks::{Source, SourceBookmarkBuilder}, + utils, SourceBookmarks, SourceType, +}; use anyhow::anyhow; use log::{debug, trace}; use serde_json::{Map, Value}; @@ -19,8 +22,9 @@ impl Chromium { if type_value == "url" { if let Some(Value::String(url_value)) = obj.get("url") { if url_value.contains("http") { - let source_bookmark = - SourceBookmark::new(url_value.to_owned(), source.name.to_owned()); + let source_bookmark = SourceBookmarkBuilder::new(url_value) + .add_source(&source.name) + .build(); source_bookmarks.insert(source_bookmark); } } @@ -221,10 +225,7 @@ impl ReadBookmark for ChromiumNoExtensionBookmarkReader { #[cfg(test)] mod tests { use super::*; - use std::{ - collections::{HashMap, HashSet}, - path::Path, - }; + use std::{collections::HashMap, path::Path}; #[test] fn test_parse_all() { @@ -240,12 +241,41 @@ mod tests { let res = bookmark_reader.parse(&bookmarks, &source, &mut source_bookmarks); assert!(res.is_ok(), "{}", res.unwrap_err()); - assert_eq!(source_bookmarks.inner(), HashMap::from_iter([ - ("https://www.deepl.com/translator".to_owned(), HashSet::from_iter([SourceType::Chromium])), - ("https://www.quantamagazine.org/how-mathematical-curves-power-cryptography-20220919/".to_owned(), HashSet::from_iter([SourceType::Chromium])), - ("https://en.wikipedia.org/wiki/Design_Patterns".to_owned(), HashSet::from_iter([SourceType::Chromium])), - ("https://doc.rust-lang.org/book/title-page.html".to_owned(), HashSet::from_iter([SourceType::Chromium])), - ])); + let url1 = "https://www.deepl.com/translator"; + let url2 = + "https://www.quantamagazine.org/how-mathematical-curves-power-cryptography-20220919/"; + let url3 = "https://en.wikipedia.org/wiki/Design_Patterns"; + let url4 = "https://doc.rust-lang.org/book/title-page.html"; + + assert_eq!( + source_bookmarks.inner(), + HashMap::from_iter([ + ( + url1.to_owned(), + SourceBookmarkBuilder::new(url1) + .add_source(&SourceType::Chromium) + .build() + ), + ( + url2.to_owned(), + SourceBookmarkBuilder::new(url2) + .add_source(&SourceType::Chromium) + .build() + ), + ( + url3.to_owned(), + SourceBookmarkBuilder::new(url3) + .add_source(&SourceType::Chromium) + .build() + ), + ( + url4.to_owned(), + SourceBookmarkBuilder::new(url4) + .add_source(&SourceType::Chromium) + .build() + ) + ]) + ); } #[test] @@ -262,12 +292,41 @@ mod tests { let res = bookmark_reader.parse(&bookmarks, &source, &mut source_bookmarks); assert!(res.is_ok(), "{}", res.unwrap_err()); - assert_eq!(source_bookmarks.inner(), HashMap::from_iter([ - ("https://www.deepl.com/translator".to_owned(), HashSet::from_iter([SourceType::Chromium])), - ("https://www.quantamagazine.org/how-mathematical-curves-power-cryptography-20220919/".to_owned(), HashSet::from_iter([SourceType::Chromium])), - ("https://en.wikipedia.org/wiki/Design_Patterns".to_owned(), HashSet::from_iter([SourceType::Chromium])), - ("https://doc.rust-lang.org/book/title-page.html".to_owned(), HashSet::from_iter([SourceType::Chromium])), - ])); + let url1 = "https://www.deepl.com/translator"; + let url2 = + "https://www.quantamagazine.org/how-mathematical-curves-power-cryptography-20220919/"; + let url3 = "https://en.wikipedia.org/wiki/Design_Patterns"; + let url4 = "https://doc.rust-lang.org/book/title-page.html"; + + assert_eq!( + source_bookmarks.inner(), + HashMap::from_iter([ + ( + url1.to_owned(), + SourceBookmarkBuilder::new(url1) + .add_source(&SourceType::Chromium) + .build() + ), + ( + url2.to_owned(), + SourceBookmarkBuilder::new(url2) + .add_source(&SourceType::Chromium) + .build() + ), + ( + url3.to_owned(), + SourceBookmarkBuilder::new(url3) + .add_source(&SourceType::Chromium) + .build() + ), + ( + url4.to_owned(), + SourceBookmarkBuilder::new(url4) + .add_source(&SourceType::Chromium) + .build() + ) + ]) + ); } #[test] @@ -284,16 +343,23 @@ mod tests { let res = bookmark_reader.parse(&bookmarks, &source, &mut source_bookmarks); assert!(res.is_ok(), "{}", res.unwrap_err()); + let url1 = "https://en.wikipedia.org/wiki/Design_Patterns"; + let url2 = "https://doc.rust-lang.org/book/title-page.html"; + assert_eq!( source_bookmarks.inner(), HashMap::from_iter([ ( - "https://en.wikipedia.org/wiki/Design_Patterns".to_owned(), - HashSet::from_iter([SourceType::Chromium]) + url1.to_owned(), + SourceBookmarkBuilder::new(url1) + .add_source(&SourceType::Chromium) + .build() ), ( - "https://doc.rust-lang.org/book/title-page.html".to_owned(), - HashSet::from_iter([SourceType::Chromium]) + url2.to_owned(), + SourceBookmarkBuilder::new(url2) + .add_source(&SourceType::Chromium) + .build() ), ]) ); @@ -313,16 +379,23 @@ mod tests { let res = bookmark_reader.parse(&bookmarks, &source, &mut source_bookmarks); assert!(res.is_ok(), "{}", res.unwrap_err()); + let url1 = "https://en.wikipedia.org/wiki/Design_Patterns"; + let url2 = "https://doc.rust-lang.org/book/title-page.html"; + assert_eq!( source_bookmarks.inner(), HashMap::from_iter([ ( - "https://en.wikipedia.org/wiki/Design_Patterns".to_owned(), - HashSet::from_iter([SourceType::Chromium]) + url1.to_owned(), + SourceBookmarkBuilder::new(url1) + .add_source(&SourceType::Chromium) + .build() ), ( - "https://doc.rust-lang.org/book/title-page.html".to_owned(), - HashSet::from_iter([SourceType::Chromium]) + url2.to_owned(), + SourceBookmarkBuilder::new(url2) + .add_source(&SourceType::Chromium) + .build() ), ]) ); diff --git a/src/bookmark_reader/firefox.rs b/src/bookmark_reader/firefox.rs index 69d36ab..75ccdbf 100644 --- a/src/bookmark_reader/firefox.rs +++ b/src/bookmark_reader/firefox.rs @@ -1,5 +1,5 @@ use super::{ReadBookmark, ReaderName}; -use crate::{utils, Source, SourceBookmark, SourceBookmarks, SourceType}; +use crate::{bookmarks::SourceBookmarkBuilder, utils, Source, SourceBookmarks, SourceType}; use anyhow::anyhow; use log::{debug, trace}; use lz4::block; @@ -25,8 +25,9 @@ impl Firefox { if type_value == "text/x-moz-place" { if let Some(Value::String(uri_value)) = obj.get("uri") { if uri_value.contains("http") { - let source_bookmark = - SourceBookmark::new(uri_value.to_owned(), source.name.clone()); + let source_bookmark = SourceBookmarkBuilder::new(uri_value) + .add_source(&source.name) + .build(); source_bookmarks.insert(source_bookmark); } } @@ -316,10 +317,7 @@ impl ReadBookmark for FirefoxCompressedBookmarkReader { mod tests { use super::*; use crate::{test_utils, utils}; - use std::{ - collections::{HashMap, HashSet}, - io::Cursor, - }; + use std::{collections::HashMap, io::Cursor}; #[test] fn test_read() { @@ -370,12 +368,41 @@ mod tests { let res = bookmark_reader.parse(&raw_bookmarks, &source, &mut source_bookmarks); assert!(res.is_ok(), "{}", res.unwrap_err()); - assert_eq!(source_bookmarks.inner(), HashMap::from_iter([ - ("https://www.mozilla.org/en-US/firefox/central/".to_owned(), HashSet::from_iter([SourceType::Firefox])), - ("https://www.quantamagazine.org/how-mathematical-curves-power-cryptography-20220919/".to_owned(), HashSet::from_iter([SourceType::Firefox])), - ("https://en.wikipedia.org/wiki/Design_Patterns".to_owned(), HashSet::from_iter([SourceType::Firefox])), - ("https://doc.rust-lang.org/book/title-page.html".to_owned(), HashSet::from_iter([SourceType::Firefox])), - ])); + let url1 = "https://www.mozilla.org/en-US/firefox/central/"; + let url2 = + "https://www.quantamagazine.org/how-mathematical-curves-power-cryptography-20220919/"; + let url3 = "https://en.wikipedia.org/wiki/Design_Patterns"; + let url4 = "https://doc.rust-lang.org/book/title-page.html"; + + assert_eq!( + source_bookmarks.inner(), + HashMap::from_iter([ + ( + url1.to_owned(), + SourceBookmarkBuilder::new(url1) + .add_source(&SourceType::Firefox) + .build() + ), + ( + url2.to_owned(), + SourceBookmarkBuilder::new(url2) + .add_source(&SourceType::Firefox) + .build() + ), + ( + url3.to_owned(), + SourceBookmarkBuilder::new(url3) + .add_source(&SourceType::Firefox) + .build() + ), + ( + url4.to_owned(), + SourceBookmarkBuilder::new(url4) + .add_source(&SourceType::Firefox) + .build() + ) + ]) + ); } #[test] @@ -393,12 +420,41 @@ mod tests { let res = bookmark_reader.parse(&raw_bookmarks, &source, &mut source_bookmarks); assert!(res.is_ok(), "{}", res.unwrap_err()); - assert_eq!(source_bookmarks.inner(), HashMap::from_iter([ - ("https://www.mozilla.org/en-US/firefox/central/".to_owned(), HashSet::from_iter([SourceType::Firefox])), - ("https://www.quantamagazine.org/how-mathematical-curves-power-cryptography-20220919/".to_owned(), HashSet::from_iter([SourceType::Firefox])), - ("https://en.wikipedia.org/wiki/Design_Patterns".to_owned(), HashSet::from_iter([SourceType::Firefox])), - ("https://doc.rust-lang.org/book/title-page.html".to_owned(), HashSet::from_iter([SourceType::Firefox])), - ])); + let url1 = "https://www.mozilla.org/en-US/firefox/central/"; + let url2 = + "https://www.quantamagazine.org/how-mathematical-curves-power-cryptography-20220919/"; + let url3 = "https://en.wikipedia.org/wiki/Design_Patterns"; + let url4 = "https://doc.rust-lang.org/book/title-page.html"; + + assert_eq!( + source_bookmarks.inner(), + HashMap::from_iter([ + ( + url1.to_owned(), + SourceBookmarkBuilder::new(url1) + .add_source(&SourceType::Firefox) + .build() + ), + ( + url2.to_owned(), + SourceBookmarkBuilder::new(url2) + .add_source(&SourceType::Firefox) + .build() + ), + ( + url3.to_owned(), + SourceBookmarkBuilder::new(url3) + .add_source(&SourceType::Firefox) + .build() + ), + ( + url4.to_owned(), + SourceBookmarkBuilder::new(url4) + .add_source(&SourceType::Firefox) + .build() + ) + ]) + ); } #[test] @@ -419,16 +475,23 @@ mod tests { let res = bookmark_reader.parse(&raw_bookmarks, &source, &mut source_bookmarks); assert!(res.is_ok(), "{}", res.unwrap_err()); + let url1 = "https://en.wikipedia.org/wiki/Design_Patterns"; + let url2 = "https://doc.rust-lang.org/book/title-page.html"; + assert_eq!( source_bookmarks.inner(), HashMap::from_iter([ ( - "https://en.wikipedia.org/wiki/Design_Patterns".to_owned(), - HashSet::from_iter([SourceType::Firefox]) + url1.to_owned(), + SourceBookmarkBuilder::new(url1) + .add_source(&SourceType::Firefox) + .build() ), ( - "https://doc.rust-lang.org/book/title-page.html".to_owned(), - HashSet::from_iter([SourceType::Firefox]) + url2.to_owned(), + SourceBookmarkBuilder::new(url2) + .add_source(&SourceType::Firefox) + .build() ), ]) ); @@ -453,16 +516,23 @@ mod tests { let res = bookmark_reader.parse(&raw_bookmarks, &source, &mut source_bookmarks); assert!(res.is_ok(), "{}", res.unwrap_err()); + let url1 = "https://en.wikipedia.org/wiki/Design_Patterns"; + let url2 = "https://doc.rust-lang.org/book/title-page.html"; + assert_eq!( source_bookmarks.inner(), HashMap::from_iter([ ( - "https://en.wikipedia.org/wiki/Design_Patterns".to_owned(), - HashSet::from_iter([SourceType::Firefox]) + url1.to_owned(), + SourceBookmarkBuilder::new(url1) + .add_source(&SourceType::Firefox) + .build() ), ( - "https://doc.rust-lang.org/book/title-page.html".to_owned(), - HashSet::from_iter([SourceType::Firefox]) + url2.to_owned(), + SourceBookmarkBuilder::new(url2) + .add_source(&SourceType::Firefox) + .build() ), ]) ); diff --git a/src/bookmark_reader/simple.rs b/src/bookmark_reader/simple.rs index 7dbe883..134b9e8 100644 --- a/src/bookmark_reader/simple.rs +++ b/src/bookmark_reader/simple.rs @@ -1,5 +1,8 @@ use super::{ReadBookmark, ReaderName}; -use crate::{bookmarks::Source, SourceBookmark, SourceBookmarks, SourceType}; +use crate::{ + bookmarks::{Source, SourceBookmarkBuilder}, + SourceBookmarks, SourceType, +}; use std::{ io::{BufRead, BufReader, Read}, path::Path, @@ -35,7 +38,9 @@ impl ReadBookmark for SimpleBookmarkReader { let url = line?; if !url.is_empty() { - let source_bookmark = SourceBookmark::new(url, source.name.clone()); + let source_bookmark = SourceBookmarkBuilder::new(&url) + .add_source(&source.name) + .build(); source_bookmarks.insert(source_bookmark); } } @@ -47,11 +52,8 @@ impl ReadBookmark for SimpleBookmarkReader { #[cfg(test)] mod tests { use super::*; - use crate::utils; - use std::{ - collections::{HashMap, HashSet}, - path::Path, - }; + use crate::{bookmarks::SourceBookmarkBuilder, utils}; + use std::{collections::HashMap, path::Path}; #[test] fn test_read_txt() { @@ -70,11 +72,40 @@ mod tests { ); assert!(res.is_ok(), "{}", res.unwrap_err()); - assert_eq!(source_bookmarks.inner(), HashMap::from_iter([ - ("https://www.deepl.com/translator".to_owned(), HashSet::from_iter([SourceType::Simple])), - ("https://www.quantamagazine.org/how-mathematical-curves-power-cryptography-20220919/".to_owned(), HashSet::from_iter([SourceType::Simple])), - ("https://en.wikipedia.org/wiki/Design_Patterns".to_owned(), HashSet::from_iter([SourceType::Simple])), - ("https://doc.rust-lang.org/book/title-page.html".to_owned(), HashSet::from_iter([SourceType::Simple])), - ])) + let url1 = "https://www.deepl.com/translator"; + let url2 = + "https://www.quantamagazine.org/how-mathematical-curves-power-cryptography-20220919/"; + let url3 = "https://en.wikipedia.org/wiki/Design_Patterns"; + let url4 = "https://doc.rust-lang.org/book/title-page.html"; + + assert_eq!( + source_bookmarks.inner(), + HashMap::from_iter([ + ( + url1.to_owned(), + SourceBookmarkBuilder::new(url1) + .add_source(&SourceType::Simple) + .build() + ), + ( + url2.to_owned(), + SourceBookmarkBuilder::new(url2) + .add_source(&SourceType::Simple) + .build() + ), + ( + url3.to_owned(), + SourceBookmarkBuilder::new(url3) + .add_source(&SourceType::Simple) + .build() + ), + ( + url4.to_owned(), + SourceBookmarkBuilder::new(url4) + .add_source(&SourceType::Simple) + .build() + ) + ]) + ); } } diff --git a/src/bookmarks/mod.rs b/src/bookmarks/mod.rs index be06434..277033e 100644 --- a/src/bookmarks/mod.rs +++ b/src/bookmarks/mod.rs @@ -2,7 +2,7 @@ mod source_bookmarks; mod target_bookmarks; use serde::{Deserialize, Serialize}; -pub use source_bookmarks::{SourceBookmark, SourceBookmarks}; +pub use source_bookmarks::{SourceBookmark, SourceBookmarkBuilder, SourceBookmarks}; use std::{ cmp::Ordering, path::{Path, PathBuf}, @@ -10,6 +10,20 @@ use std::{ }; pub use target_bookmarks::{TargetBookmark, TargetBookmarks}; +/// The action to be performed on the bookmark. +#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)] +pub enum Action { + /// Fetch and cache the bookmark, even if it is cached already. The cached + /// content will be updated with the most recent version of the website. + Fetch, + /// Fetch and cache bookmark if it is not cached yet. + Add, + /// Remove bookmark from cache. + Remove, + /// No actions to be performed. + None, +} + /// The type used to identify a source. #[derive(Debug, Clone, Default, PartialEq, Eq, Hash, Serialize, Deserialize)] pub enum SourceType { diff --git a/src/bookmarks/source_bookmarks.rs b/src/bookmarks/source_bookmarks.rs index 220b0f6..05da400 100644 --- a/src/bookmarks/source_bookmarks.rs +++ b/src/bookmarks/source_bookmarks.rs @@ -1,41 +1,74 @@ use crate::SourceType; use log::debug; use std::collections::{ - hash_map::{Entry, IntoIter, Iter, Keys}, + hash_map::{Entry, IntoIter, Iter, IterMut, Keys}, HashMap, HashSet, }; /// A bookmark from a specific source, like Firefox or Chrome. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq, Eq)] pub struct SourceBookmark { pub url: String, - pub source_type: SourceType, + pub sources: HashSet, } impl SourceBookmark { - pub fn new(url: String, source_type: SourceType) -> Self { - Self { url, source_type } + pub fn builder(url: &str) -> SourceBookmarkBuilder { + SourceBookmarkBuilder { + url: url.to_owned(), + sources: HashSet::new(), + } + } + + pub fn add_source(&mut self, source: SourceType) { + self.sources.insert(source); + } +} + +pub struct SourceBookmarkBuilder { + url: String, + sources: HashSet, +} + +impl SourceBookmarkBuilder { + pub fn new(url: &str) -> Self { + Self { + url: url.to_owned(), + sources: HashSet::new(), + } + } + + pub fn add_source(mut self, source: &SourceType) -> Self { + self.sources.insert(source.to_owned()); + self + } + + pub fn build(self) -> SourceBookmark { + SourceBookmark { + url: self.url, + sources: self.sources, + } } } /// Describes the bookmark url which originates from one or more sources. #[derive(Debug, Clone, Default)] -pub struct SourceBookmarks(HashMap>); +pub struct SourceBookmarks(HashMap); impl SourceBookmarks { - pub fn new(bookmarks: HashMap>) -> Self { + pub fn new(bookmarks: HashMap) -> Self { Self(bookmarks) } - pub fn inner(self) -> HashMap> { + pub fn inner(self) -> HashMap { self.0 } - pub fn get(&self, url: &str) -> Option<&HashSet> { + pub fn get(&self, url: &str) -> Option<&SourceBookmark> { self.0.get(url) } - pub fn keys(&self) -> Keys> { + pub fn keys(&self) -> Keys { self.0.keys() } @@ -43,42 +76,51 @@ impl SourceBookmarks { self.0.contains_key(url) } - pub fn iter(&self) -> Iter> { + pub fn iter(&self) -> Iter { self.0.iter() } + pub fn iter_mut(&mut self) -> IterMut { + self.0.iter_mut() + } + pub fn insert(&mut self, bookmark: SourceBookmark) { - let url = bookmark.url; - let source_type = bookmark.source_type; - let entry = self.0.entry(url); + let url = &bookmark.url; + let sources = bookmark.sources; + let entry = self.0.entry(url.to_owned()); match entry { Entry::Occupied(entry) => { let url = entry.key().clone(); - let source_types = entry.into_mut(); debug!("Overwrite duplicate source bookmark: {}", url); - source_types.insert(source_type); + let source_bookmark = entry.into_mut(); + + for source in sources { + source_bookmark.add_source(source); + } } Entry::Vacant(entry) => { - let mut source_types = HashSet::new(); - source_types.insert(source_type); - entry.insert(source_types); + let source_bookmark = SourceBookmark { + url: url.to_owned(), + sources, + }; + entry.insert(source_bookmark); } } } } impl IntoIterator for SourceBookmarks { - type Item = (String, HashSet); - type IntoIter = IntoIter>; + type Item = (String, SourceBookmark); + type IntoIter = IntoIter; fn into_iter(self) -> Self::IntoIter { self.0.into_iter() } } -impl AsRef>> for SourceBookmarks { - fn as_ref(&self) -> &HashMap> { +impl AsRef> for SourceBookmarks { + fn as_ref(&self) -> &HashMap { &self.0 } } diff --git a/src/bookmarks/target_bookmarks.rs b/src/bookmarks/target_bookmarks.rs index f46e656..2bb48bd 100644 --- a/src/bookmarks/target_bookmarks.rs +++ b/src/bookmarks/target_bookmarks.rs @@ -1,4 +1,5 @@ -use crate::{cache::CacheMode, SourceBookmarks, SourceType}; +use super::Action; +use crate::{cache::CacheMode, SourceBookmark, SourceBookmarks, SourceType}; use chrono::{DateTime, Utc}; use log::{debug, trace}; use serde::{Deserialize, Serialize}; @@ -18,6 +19,7 @@ pub struct TargetBookmark { pub last_cached: Option, pub sources: HashSet, pub cache_modes: HashSet, + pub action: Action, } impl TargetBookmark { @@ -27,6 +29,7 @@ impl TargetBookmark { last_cached: Option>, sources: HashSet, cache_modes: HashSet, + action: Action, ) -> Self { Self { id: Uuid::new_v4().to_string(), @@ -35,6 +38,7 @@ impl TargetBookmark { last_cached: last_cached.map(|timestamp| timestamp.timestamp_millis()), sources, cache_modes, + action, } } } @@ -97,6 +101,24 @@ impl TargetBookmarks { self.0.len() } + /// If the cache was removed, reset the cache values in the target + /// bookmarks. + pub fn reset_cache_status(&mut self) { + debug!("Reset cache status"); + for bookmark in self.values_mut() { + bookmark.last_cached = None; + bookmark.cache_modes.clear(); + } + } + + pub fn set_action(&mut self, action: &Action) { + debug!("Set action to {action:#?}"); + + for bookmark in self.values_mut() { + bookmark.action = action.clone() + } + } + pub fn insert(&mut self, bookmark: TargetBookmark) { let url = &bookmark.url; let entry = self.0.entry(url.clone()); @@ -118,72 +140,96 @@ impl TargetBookmarks { self.0.remove(url) } - pub fn filter_to_add<'a>(&self, source_bookmarks: &'a SourceBookmarks) -> Vec<&'a str> { + /// Clean up bookmarks which are marked by [`Action::Remove`]. + pub fn clean_up(&mut self) { + let urls_to_remove = self + .values() + .filter_map(|bookmark| { + if bookmark.action == Action::Remove { + Some(bookmark.url.to_owned()) + } else { + None + } + }) + .collect::>(); + + for url in urls_to_remove { + self.remove(&url); + } + } + + pub fn filter_to_add<'a>( + &self, + source_bookmarks: &'a SourceBookmarks, + ) -> Vec<&'a SourceBookmark> { source_bookmarks - .keys() - .filter(|url| !self.0.contains_key(*url)) - .map(|url| url.as_str()) + .iter() + .filter(|(url, _)| !self.0.contains_key(*url)) + .map(|(_, bookmark)| bookmark) .collect() } - pub fn filter_to_remove(&self, source_bookmarks: &SourceBookmarks) -> Vec { + pub fn filter_to_remove<'a>( + &'a mut self, + source_bookmarks: &SourceBookmarks, + ) -> Vec<&'a mut TargetBookmark> { self.0 - .iter() + .iter_mut() .filter(|(url, _)| !source_bookmarks.contains_key(url)) .map(|(_, target_bookmark)| target_bookmark) - .cloned() .collect() } /// Update target bookmarks. /// /// Determine the difference between source and target bookmarks and update - /// the target bookmarks. - pub fn update( - &mut self, - source_bookmarks: &SourceBookmarks, - ) -> Result<(Vec, Vec), anyhow::Error> { + /// the `action` of the target bookmarks. + pub fn update(&mut self, source_bookmarks: &SourceBookmarks) -> Result<(), anyhow::Error> { let now = Utc::now(); - let bookmarks_to_remove = self.filter_to_remove(source_bookmarks); - let urls_to_add = self.filter_to_add(source_bookmarks); - let mut bookmarks_to_add = vec![]; - for bookmark in &bookmarks_to_remove { - self.remove(&bookmark.url); + let bookmarks_to_add = self.filter_to_add(source_bookmarks); + let urls_to_add = bookmarks_to_add + .iter() + .map(|bookmark| bookmark.url.to_owned()) + .collect::>(); + + for bookmark in bookmarks_to_add { + let target_bookmark = TargetBookmark::new( + bookmark.url.to_owned(), + now, + None, + bookmark.sources.to_owned(), + HashSet::new(), + Action::Add, + ); + self.insert(target_bookmark); } - for url in urls_to_add { - if let Some(sources) = source_bookmarks.get(url) { - let target_bookmark = TargetBookmark::new( - url.to_owned(), - now, - None, - sources.to_owned(), - HashSet::new(), - ); - bookmarks_to_add.push(target_bookmark.clone()); - self.insert(target_bookmark); - } + let bookmarks_to_remove = self.filter_to_remove(source_bookmarks); + let urls_to_remove = bookmarks_to_remove + .iter() + .map(|bookmark| bookmark.url.to_owned()) + .collect::>(); + + for bookmark in bookmarks_to_remove { + bookmark.action = Action::Remove; } - if !bookmarks_to_add.is_empty() { - println!("Added {} new bookmarks", bookmarks_to_add.len()); - trace!( - "Added new bookmarks: {:#?}", - bookmarks_to_add.iter().map(|bookmark| &bookmark.url) - ); + if !urls_to_add.is_empty() { + println!("Added {} new bookmarks", urls_to_add.len()); + trace!("Added new bookmarks: {urls_to_add:#?}",); } - if !bookmarks_to_remove.is_empty() { - println!("Removed {} bookmarks", bookmarks_to_remove.len()); - trace!("Removed bookmarks: {bookmarks_to_remove:#?}"); + if !urls_to_remove.is_empty() { + println!("Removed {} bookmarks", urls_to_remove.len()); + trace!("Removed bookmarks: {urls_to_remove:#?}",); } - if bookmarks_to_add.is_empty() && bookmarks_to_remove.is_empty() { + if urls_to_add.is_empty() && urls_to_remove.is_empty() { println!("Bookmarks are already up to date"); } - Ok((bookmarks_to_add, bookmarks_to_remove)) + Ok(()) } } @@ -206,8 +252,9 @@ impl From for TargetBookmarks { source_bookmark.0, now, None, - source_bookmark.1, + source_bookmark.1.sources, HashSet::new(), + Action::None, ); target_bookmarks.insert(target_bookmark) } @@ -219,7 +266,10 @@ impl From for TargetBookmarks { #[cfg(test)] mod tests { use super::*; - use crate::bookmark_reader::{ReadTarget, WriteTarget}; + use crate::{ + bookmark_reader::{ReadTarget, WriteTarget}, + bookmarks::SourceBookmarkBuilder, + }; use std::{ collections::{HashMap, HashSet}, io::Cursor, @@ -233,7 +283,8 @@ mod tests { "last_imported": 1694989714351, "last_cached": null, "sources": [], - "cache_modes": [] + "cache_modes": [], + "action": "None" }, { "id": "511b1590-e6de-4989-bca4-96dc61730508", @@ -241,7 +292,8 @@ mod tests { "last_imported": 1694989714351, "last_cached": null, "sources": [], - "cache_modes": [] + "cache_modes": [], + "action": "None" } ] }"#; @@ -253,24 +305,43 @@ mod tests { #[test] fn test_update() { let now = Utc::now(); + + let url1 = "https://www.deepl.com/translator"; + let url2 = + "https://www.quantamagazine.org/how-mathematical-curves-power-cryptography-20220919/"; + let url3 = "https://en.wikipedia.org/wiki/Design_Patterns"; + let url4 = "https://doc.rust-lang.org/book/title-page.html"; + let expected_bookmarks = HashMap::from_iter([ - ("https://www.deepl.com/translator".to_owned(), HashSet::new()), - ("https://www.quantamagazine.org/how-mathematical-curves-power-cryptography-20220919/".to_owned(), HashSet::new()), - ("https://en.wikipedia.org/wiki/Design_Patterns".to_owned(), HashSet::new()), - ("https://doc.rust-lang.org/book/title-page.html".to_owned(), HashSet::new()), + (url1.to_owned(), SourceBookmarkBuilder::new(url1).build()), + (url2.to_owned(), SourceBookmarkBuilder::new(url2).build()), + (url3.to_owned(), SourceBookmarkBuilder::new(url3).build()), + (url4.to_owned(), SourceBookmarkBuilder::new(url4).build()), ]); let source_bookmarks = SourceBookmarks::new(expected_bookmarks.clone()); - let mut target_bookmarks = TargetBookmarks::new(HashMap::from_iter([("https://www.deepl.com/translator".to_owned(), TargetBookmark::new( - "https://www.deepl.com/translator", - now, - None, - HashSet::new(), - HashSet::new() - )), ("https://www.quantamagazine.org/how-mathematical-curves-power-cryptography-20220919/".to_owned(), TargetBookmark::new( - "https://www.quantamagazine.org/how-mathematical-curves-power-cryptography-20220919/", - now, - None, - HashSet::new(),HashSet::new())), + let mut target_bookmarks = TargetBookmarks::new(HashMap::from_iter([ + ( + url1.to_owned(), + TargetBookmark::new( + url1, + now, + None, + HashSet::new(), + HashSet::new(), + Action::None, + ), + ), + ( + url2.to_owned(), + TargetBookmark::new( + url2, + now, + None, + HashSet::new(), + HashSet::new(), + Action::None, + ), + ), ])); let res = target_bookmarks.update(&source_bookmarks); assert!(res.is_ok()); @@ -299,7 +370,8 @@ mod tests { last_imported: 1694989714351, last_cached: None, sources: HashSet::new(), - cache_modes: HashSet::new() + cache_modes: HashSet::new(), + action: Action::None, } ), ( @@ -310,7 +382,8 @@ mod tests { last_imported: 1694989714351, last_cached: None, sources: HashSet::new(), - cache_modes: HashSet::new() + cache_modes: HashSet::new(), + action: Action::None, } ) ])) @@ -340,6 +413,7 @@ mod tests { last_cached: None, sources: HashSet::new(), cache_modes: HashSet::new(), + action: Action::None, }, ), ( @@ -351,6 +425,7 @@ mod tests { last_cached: None, sources: HashSet::new(), cache_modes: HashSet::new(), + action: Action::None, }, ), ])); diff --git a/src/cache.rs b/src/cache.rs index efd97d1..5c367ad 100644 --- a/src/cache.rs +++ b/src/cache.rs @@ -409,6 +409,7 @@ impl Caching for MockCache { #[cfg(test)] mod tests { use super::*; + use crate::bookmarks::Action; use chrono::Utc; use std::collections::HashSet; @@ -422,6 +423,7 @@ mod tests { None, HashSet::new(), HashSet::new(), + Action::None, ); let content = "

Test content

"; let cached_content = cache.add(content.to_owned(), &mut bookmark).await.unwrap(); @@ -445,6 +447,7 @@ mod tests { None, HashSet::new(), HashSet::new(), + Action::None, ); let content = "

Test content

"; let cached_content = cache.add(content.to_owned(), &mut bookmark).await.unwrap(); @@ -465,6 +468,7 @@ mod tests { None, HashSet::new(), HashSet::new(), + Action::None, ); let content1 = "

Test content 1

"; cache.add(content1.to_owned(), &mut bookmark).await.unwrap(); @@ -491,6 +495,7 @@ mod tests { None, HashSet::new(), HashSet::new(), + Action::None, ); let content1 = "

Test content 1

"; cache.add(content1.to_owned(), &mut bookmark).await.unwrap(); @@ -514,6 +519,7 @@ mod tests { None, HashSet::new(), HashSet::new(), + Action::None, ); let content = "

Test content

"; @@ -541,6 +547,7 @@ mod tests { None, HashSet::new(), HashSet::new(), + Action::None, ), ), ( @@ -551,6 +558,7 @@ mod tests { None, HashSet::new(), HashSet::new(), + Action::None, ), ), ])); @@ -582,6 +590,7 @@ mod tests { None, HashSet::new(), HashSet::new(), + Action::None, ), )])); diff --git a/src/client.rs b/src/client.rs index 5474844..4f90aa6 100644 --- a/src/client.rs +++ b/src/client.rs @@ -201,6 +201,7 @@ impl Fetch for MockClient { #[cfg(test)] mod tests { use super::*; + use crate::bookmarks::Action; use std::collections::HashSet; use tokio::time::Instant; @@ -216,6 +217,7 @@ mod tests { None, HashSet::new(), HashSet::new(), + Action::None, ); let bookmark2 = TargetBookmark::new( "https://en.wikipedia.org/wiki/Monad_(functional_programming)", @@ -223,6 +225,7 @@ mod tests { None, HashSet::new(), HashSet::new(), + Action::None, ); let start_instant = Instant::now(); @@ -248,6 +251,7 @@ mod tests { None, HashSet::new(), HashSet::new(), + Action::None, ); let bookmark2 = TargetBookmark::new( "https://en.wikipedia.org/wiki/Monad_(functional_programming)", @@ -255,6 +259,7 @@ mod tests { None, HashSet::new(), HashSet::new(), + Action::None, ); let last_fetched = throttler.last_fetched(&bookmark1, now).unwrap(); diff --git a/src/cmd/add.rs b/src/cmd/add.rs index 9b6d224..4c691b4 100644 --- a/src/cmd/add.rs +++ b/src/cmd/add.rs @@ -1,6 +1,7 @@ use crate::{ args::AddArgs, bookmark_reader::{ReadTarget, WriteTarget}, + bookmarks::Action, utils, Config, SourceType, TargetBookmark, TargetBookmarks, }; use anyhow::anyhow; @@ -42,7 +43,14 @@ fn add_urls( target_reader.read(&mut target_bookmarks)?; for url in urls { - let bookmark = TargetBookmark::new(url, now, None, sources.clone(), cache_modes.clone()); + let bookmark = TargetBookmark::new( + url, + now, + None, + sources.clone(), + cache_modes.clone(), + Action::None, + ); target_bookmarks.insert(bookmark); } diff --git a/src/cmd/fetch.rs b/src/cmd/fetch.rs index ee4b910..eaf72f7 100644 --- a/src/cmd/fetch.rs +++ b/src/cmd/fetch.rs @@ -1,5 +1,6 @@ use crate::{ bookmark_reader::{ReadTarget, WriteTarget}, + bookmarks::Action, cache::CacheMode, errors::BogrepError, html, utils, Cache, Caching, Client, Config, Fetch, FetchArgs, TargetBookmark, TargetBookmarks, @@ -62,8 +63,15 @@ pub async fn fetch_urls( target_reader.read(&mut target_bookmarks)?; for url in urls { - let mut bookmark = TargetBookmark::new(url, now, None, HashSet::new(), HashSet::new()); - fetch_and_cache_bookmark(client, cache, &mut bookmark, true).await?; + let mut bookmark = TargetBookmark::new( + url, + now, + None, + HashSet::new(), + HashSet::new(), + Action::Fetch, + ); + fetch_and_cache_bookmark(client, cache, &mut bookmark).await?; println!("Fetched website for {url}"); target_bookmarks.insert(bookmark); } @@ -86,12 +94,13 @@ pub async fn fetch_bookmarks( if cache.is_empty() { debug!("Cache is empty"); - // If the cache was removed, reset the cache values in - // the target bookmarks - for bookmark in target_bookmarks.values_mut() { - bookmark.last_cached = None; - bookmark.cache_modes.clear(); - } + target_bookmarks.reset_cache_status(); + } + + if fetch_all { + target_bookmarks.set_action(&Action::Fetch); + } else { + target_bookmarks.set_action(&Action::Add); } fetch_and_cache_bookmarks( @@ -99,7 +108,6 @@ pub async fn fetch_bookmarks( cache, target_bookmarks.values_mut().collect(), max_concurrent_requests, - fetch_all, ) .await?; @@ -117,7 +125,6 @@ pub async fn fetch_and_cache_bookmarks( cache: &impl Caching, bookmarks: Vec<&mut TargetBookmark>, max_concurrent_requests: usize, - fetch_all: bool, ) -> Result<(), BogrepError> { let mut processed = 0; let mut cached = 0; @@ -127,7 +134,7 @@ pub async fn fetch_and_cache_bookmarks( let total = bookmarks.len(); let mut stream = stream::iter(bookmarks) - .map(|bookmark| fetch_and_cache_bookmark(client, cache, bookmark, fetch_all)) + .map(|bookmark| fetch_and_cache_bookmark(client, cache, bookmark)) .buffer_unordered(max_concurrent_requests); while let Some(item) = stream.next().await { @@ -206,20 +213,30 @@ async fn fetch_and_cache_bookmark( client: &impl Fetch, cache: &impl Caching, bookmark: &mut TargetBookmark, - fetch_all: bool, ) -> Result<(), BogrepError> { - if fetch_all { - let website = client.fetch(bookmark).await?; - trace!("Fetched website: {website}"); - let html = html::filter_html(&website)?; - cache.replace(html, bookmark).await?; - } else if !cache.exists(bookmark) { - let website = client.fetch(bookmark).await?; - trace!("Fetched website: {website}"); - let html = html::filter_html(&website)?; - cache.add(html, bookmark).await?; + match bookmark.action { + Action::Fetch => { + let website = client.fetch(bookmark).await?; + trace!("Fetched website: {website}"); + let html = html::filter_html(&website)?; + cache.replace(html, bookmark).await?; + } + Action::Add => { + if !cache.exists(bookmark) { + let website = client.fetch(bookmark).await?; + trace!("Fetched website: {website}"); + let html = html::filter_html(&website)?; + cache.add(html, bookmark).await?; + } + } + Action::Remove => { + cache.remove(bookmark).await?; + } + Action::None => (), } + bookmark.action = Action::None; + Ok(()) } @@ -293,6 +310,7 @@ mod tests { last_cached: None, sources: HashSet::new(), cache_modes: HashSet::new(), + action: Action::Fetch, }, ), ( @@ -304,6 +322,7 @@ mod tests { last_cached: None, sources: HashSet::new(), cache_modes: HashSet::new(), + action: Action::Fetch, }, ), ])); @@ -322,7 +341,6 @@ mod tests { &cache, target_bookmarks.values_mut().collect(), 100, - true, ) .await; assert!(res.is_ok()); @@ -342,7 +360,7 @@ mod tests { } #[tokio::test] - async fn test_fetch_and_add_all_mode_text() { + async fn test_fetch_and_cache_mode_text() { let now = Utc::now(); let client = MockClient::new(); let cache = MockCache::new(CacheMode::Text); @@ -356,6 +374,7 @@ mod tests { last_cached: None, sources: HashSet::new(), cache_modes: HashSet::new(), + action: Action::Fetch, }, ), ( @@ -367,6 +386,7 @@ mod tests { last_cached: None, sources: HashSet::new(), cache_modes: HashSet::new(), + action: Action::Fetch, }, ), ])); @@ -385,7 +405,6 @@ mod tests { &cache, target_bookmarks.values_mut().collect(), 100, - true, ) .await; assert!(res.is_ok()); @@ -405,7 +424,7 @@ mod tests { } #[tokio::test] - async fn test_fetch_and_add_all_if_not_exists_mode_html() { + async fn test_fetch_and_cache_if_not_exists_mode_html() { let now = Utc::now().timestamp_millis(); let client = MockClient::new(); let cache = MockCache::new(CacheMode::Html); @@ -419,6 +438,7 @@ mod tests { last_cached: Some(now), sources: HashSet::new(), cache_modes: HashSet::new(), + action: Action::Add, }, ), ( @@ -430,6 +450,7 @@ mod tests { last_cached: None, sources: HashSet::new(), cache_modes: HashSet::new(), + action: Action::Add, }, ), ])); @@ -456,7 +477,6 @@ mod tests { &cache, target_bookmarks.values_mut().collect(), 100, - false, ) .await; assert!(res.is_ok()); @@ -478,7 +498,7 @@ mod tests { } #[tokio::test] - async fn test_fetch_and_add_all_if_not_exists_mode_text() { + async fn test_fetch_and_cache_if_not_exists_mode_text() { let now = Utc::now().timestamp_millis(); let client = MockClient::new(); let cache = MockCache::new(CacheMode::Text); @@ -492,6 +512,7 @@ mod tests { last_cached: Some(now), sources: HashSet::new(), cache_modes: HashSet::new(), + action: Action::Add, }, ), ( @@ -503,6 +524,7 @@ mod tests { last_cached: None, sources: HashSet::new(), cache_modes: HashSet::new(), + action: Action::Add, }, ), ])); @@ -529,7 +551,6 @@ mod tests { &cache, target_bookmarks.values_mut().collect(), 100, - false, ) .await; assert!(res.is_ok()); diff --git a/src/cmd/import.rs b/src/cmd/import.rs index 20a019c..4a7d0f1 100644 --- a/src/cmd/import.rs +++ b/src/cmd/import.rs @@ -1,7 +1,7 @@ use crate::{ args::ImportArgs, bookmark_reader::{ReadTarget, SourceReader, WriteTarget}, - utils, Config, SourceBookmarks, TargetBookmarks, + utils, Action, Config, SourceBookmarks, TargetBookmarks, }; use log::{debug, trace}; @@ -44,6 +44,7 @@ fn import_source( target_reader.read(&mut target_bookmarks)?; target_bookmarks.update(&source_bookmarks)?; + target_bookmarks.clean_up(); target_writer.write(&target_bookmarks)?; log_import(&source_reader, &target_bookmarks); @@ -60,7 +61,11 @@ fn log_import(source_reader: &[SourceReader], target_bookmarks: &TargetBookmarks println!( "Imported {} bookmarks from {} {source}: {}", - target_bookmarks.len(), + target_bookmarks + .values() + .filter(|bookmark| bookmark.action == Action::Fetch || bookmark.action == Action::Add) + .collect::>() + .len(), source_reader.len(), source_reader .iter() diff --git a/src/cmd/init.rs b/src/cmd/init.rs index d66160e..2a311c1 100644 --- a/src/cmd/init.rs +++ b/src/cmd/init.rs @@ -2,7 +2,8 @@ use crate::{ bookmark_reader::{ReadTarget, SourceReader, WriteTarget}, cache::CacheMode, cmd::fetch_and_cache_bookmarks, - utils, Cache, Caching, Client, Config, Fetch, InitArgs, SourceBookmarks, TargetBookmarks, + utils, Action, Cache, Caching, Client, Config, Fetch, InitArgs, SourceBookmarks, + TargetBookmarks, }; use log::debug; @@ -61,6 +62,8 @@ async fn init_bookmarks( let mut target_bookmarks = TargetBookmarks::from(source_bookmarks); + target_bookmarks.set_action(&Action::Add); + println!( "Imported {} bookmarks from {} sources: {}", target_bookmarks.len(), @@ -77,7 +80,6 @@ async fn init_bookmarks( cache, target_bookmarks.values_mut().collect(), max_concurrent_requests, - false, ) .await?; diff --git a/src/cmd/remove.rs b/src/cmd/remove.rs index 2ff5161..ff173a6 100644 --- a/src/cmd/remove.rs +++ b/src/cmd/remove.rs @@ -52,7 +52,7 @@ fn remove_urls( #[cfg(test)] mod tests { use super::*; - use crate::{json, BookmarksJson, SourceType, TargetBookmark}; + use crate::{bookmarks::Action, json, BookmarksJson, SourceType, TargetBookmark}; use chrono::{DateTime, Utc}; use std::{ collections::HashSet, @@ -62,7 +62,7 @@ mod tests { fn create_target_bookmark(url: &str, now: DateTime) -> TargetBookmark { let mut sources = HashSet::new(); sources.insert(SourceType::Internal); - TargetBookmark::new(url, now, None, sources, HashSet::new()) + TargetBookmark::new(url, now, None, sources, HashSet::new(), Action::None) } #[test] diff --git a/src/cmd/update.rs b/src/cmd/update.rs index cdc1007..ad435bd 100644 --- a/src/cmd/update.rs +++ b/src/cmd/update.rs @@ -58,30 +58,17 @@ async fn update_bookmarks( reader.read_and_parse(&mut source_bookmarks)?; } - let (mut bookmarks_to_add, mut bookmarks_to_remove) = - target_bookmarks.update(&source_bookmarks)?; + target_bookmarks.update(&source_bookmarks)?; - if !bookmarks_to_add.is_empty() { - // Fetch and cache new bookmarks. - cmd::fetch_and_cache_bookmarks( - client, - cache, - bookmarks_to_add.iter_mut().collect(), - max_concurrent_requests, - false, - ) - .await?; - } - - // Clean up cache for missing bookmarks. - for bookmark in bookmarks_to_remove.iter_mut() { - cache.remove(bookmark).await?; - } + cmd::fetch_and_cache_bookmarks( + client, + cache, + target_bookmarks.values_mut().collect(), + max_concurrent_requests, + ) + .await?; - // Update the `last_cached` timestamp. - for bookmark in bookmarks_to_add { - target_bookmarks.insert(bookmark) - } + target_bookmarks.clean_up(); Ok(()) } @@ -89,7 +76,7 @@ async fn update_bookmarks( #[cfg(test)] mod tests { use super::*; - use crate::{bookmarks::RawSource, MockCache, MockClient, TargetBookmark}; + use crate::{bookmarks::RawSource, Action, MockCache, MockClient, TargetBookmark}; use chrono::Utc; use std::{ collections::{HashMap, HashSet}, @@ -120,7 +107,8 @@ mod tests { last_imported: now.timestamp_millis(), last_cached: Some(now.timestamp_millis()), sources: HashSet::new(), - cache_modes: HashSet::new() + cache_modes: HashSet::from_iter([CacheMode::Html]), + action: Action::Add, }),("https://www.quantamagazine.org/how-mathematical-curves-power-cryptography-20220919/".to_owned(), TargetBookmark { id: "25b6357e-6eda-4367-8212-84376c6efe05".to_owned(), @@ -128,7 +116,8 @@ mod tests { last_imported: now.timestamp_millis(), last_cached: Some(now.timestamp_millis()), sources: HashSet::new(), - cache_modes: HashSet::new() + cache_modes: HashSet::from_iter([CacheMode::Html]), + action: Action::Add, }), ]), ); @@ -242,14 +231,16 @@ mod tests { last_imported: now.timestamp_millis(), last_cached: Some(now.timestamp_millis()), sources: HashSet::new(), - cache_modes: HashSet::new() + cache_modes: HashSet::from_iter([CacheMode::Text]), + action: Action::Add, }), ("https://www.quantamagazine.org/how-mathematical-curves-power-cryptography-20220919/".to_owned(), TargetBookmark { id: "25b6357e-6eda-4367-8212-84376c6efe05".to_owned(), url: "https://www.quantamagazine.org/how-mathematical-curves-power-cryptography-20220919/".to_owned(), last_imported: now.timestamp_millis(), last_cached: Some(now.timestamp_millis()), sources: HashSet::new(), - cache_modes: HashSet::new() + cache_modes: HashSet::from_iter([CacheMode::Text]), + action: Action::Add, })])); for url in &expected_bookmarks { client diff --git a/src/lib.rs b/src/lib.rs index 48e6143..35ec20b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -53,7 +53,7 @@ pub use bookmark_reader::{ ChromiumBookmarkReader, FirefoxBookmarkReader, ReadBookmark, SimpleBookmarkReader, }; pub use bookmarks::{ - BookmarksJson, Source, SourceBookmark, SourceBookmarks, SourceType, TargetBookmark, + Action, BookmarksJson, Source, SourceBookmark, SourceBookmarks, SourceType, TargetBookmark, TargetBookmarks, }; pub use cache::{Cache, Caching, MockCache}; diff --git a/tests/test_rename.rs b/tests/test_rename.rs index c90526b..aca6153 100644 --- a/tests/test_rename.rs +++ b/tests/test_rename.rs @@ -1,6 +1,6 @@ mod common; -use bogrep::{json, utils, BookmarksJson, TargetBookmark}; +use bogrep::{json, utils, Action, BookmarksJson, TargetBookmark}; use chrono::Utc; use std::{collections::HashSet, io::Write}; use tempfile::tempdir; @@ -27,6 +27,7 @@ fn test_rename() { None, HashSet::new(), HashSet::new(), + Action::None, )); let buf = json::serialize(&bookmarks_json).unwrap(); let mut bookmarks_lock_file = utils::open_and_truncate_file(&bookmarks_lock_path).unwrap();