diff --git a/Cargo.toml b/Cargo.toml index 6cd9239..7d55f07 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "bibliographix" description = "A bibliography management crate." -version = "0.4.0" +version = "0.5.0" authors = ["trivernis "] edition = "2018" license = "Apache-2.0" diff --git a/src/bib_manager.rs b/src/bib_manager.rs index 5cda345..3e0096a 100644 --- a/src/bib_manager.rs +++ b/src/bib_manager.rs @@ -99,7 +99,7 @@ impl BibManager { entry_map.insert(K_KEY.to_string(), k.clone()); - Some(*BibliographyEntry::from_hash_map(&entry_map)?) + Some(*BibliographyEntry::from_hash_map(&entry_map).ok()?) }) .collect::>(); diff --git a/src/bibliography/bib_types/article.rs b/src/bibliography/bib_types/article.rs index f9f0b83..9eb4d91 100644 --- a/src/bibliography/bib_types/article.rs +++ b/src/bibliography/bib_types/article.rs @@ -36,11 +36,11 @@ impl Article { } impl FromHashMap for Article { - fn from_hash_map(map: &HashMap) -> Option> { - let author = map.get(K_AUTHOR)?; - let title = map.get(K_TITLE)?; - let journal = map.get(K_JOURNAL)?; - let date = parse_date(map.get(K_DATE)?)?; + fn from_hash_map(map: &HashMap) -> Result, String> { + let author = map.get(K_AUTHOR).ok_or(missing_field!(K_AUTHOR))?; + let title = map.get(K_TITLE).ok_or(missing_field!(K_TITLE))?; + let journal = map.get(K_JOURNAL).ok_or(missing_field!(K_JOURNAL))?; + let date = parse_date(map.get(K_DATE).ok_or(missing_field!(K_DATE))?)?; let mut article = Self::new(author.clone(), title.clone(), journal.clone(), date); article.volume = map.get(K_VOLUME).cloned(); @@ -49,6 +49,6 @@ impl FromHashMap for Article { article.pages = map.get(K_PAGES).cloned(); article.url = map.get(K_URL).cloned(); - Some(Box::new(article)) + Ok(Box::new(article)) } } diff --git a/src/bibliography/bib_types/book.rs b/src/bibliography/bib_types/book.rs index 5eb9f4d..65507af 100644 --- a/src/bibliography/bib_types/book.rs +++ b/src/bibliography/bib_types/book.rs @@ -37,11 +37,11 @@ impl Book { } impl FromHashMap for Book { - fn from_hash_map(map: &HashMap) -> Option> { - let author = map.get(K_AUTHOR)?; - let title = map.get(K_TITLE)?; - let publisher = map.get(K_PUBLISHER)?; - let date = parse_date(map.get(K_DATE)?)?; + fn from_hash_map(map: &HashMap) -> Result, String> { + let author = map.get(K_AUTHOR).ok_or(missing_field!(K_AUTHOR))?; + let title = map.get(K_TITLE).ok_or(missing_field!(K_TITLE))?; + let publisher = map.get(K_PUBLISHER).ok_or(missing_field!(K_PUBLISHER))?; + let date = parse_date(map.get(K_DATE).ok_or(missing_field!(K_DATE))?)?; let mut book = Book::new(author.clone(), title.clone(), publisher.clone(), date); book.volume = map.get(K_VOLUME).cloned(); @@ -50,6 +50,6 @@ impl FromHashMap for Book { book.edition = map.get(K_EDITION).cloned(); book.url = map.get(K_URL).cloned(); - Some(Box::new(book)) + Ok(Box::new(book)) } } diff --git a/src/bibliography/bib_types/booklet.rs b/src/bibliography/bib_types/booklet.rs index 0db547e..f6c8068 100644 --- a/src/bibliography/bib_types/booklet.rs +++ b/src/bibliography/bib_types/booklet.rs @@ -28,15 +28,19 @@ impl Booklet { } impl FromHashMap for Booklet { - fn from_hash_map(map: &HashMap) -> Option> { - let title = map.get(K_TITLE)?; + fn from_hash_map(map: &HashMap) -> Result, String> { + let title = map.get(K_TITLE).ok_or(missing_field!(K_TITLE))?; let mut booklet = Booklet::new(title.clone()); booklet.author = map.get(K_AUTHOR).cloned(); booklet.how_published = map.get(K_HOW_PUBLISHED).cloned(); booklet.address = map.get(K_ADDRESS).cloned(); - booklet.date = map.get(K_DATE).and_then(|d| parse_date(d)); + booklet.date = map + .get(K_DATE) + .ok_or(missing_field!(K_DATE)) + .and_then(|d| parse_date(d)) + .ok(); - Some(Box::new(booklet)) + Ok(Box::new(booklet)) } } diff --git a/src/bibliography/bib_types/in_book.rs b/src/bibliography/bib_types/in_book.rs index 1bf2614..75ea3de 100644 --- a/src/bibliography/bib_types/in_book.rs +++ b/src/bibliography/bib_types/in_book.rs @@ -1,5 +1,5 @@ use crate::bibliography::keys::{ - K_ADDRESS, K_AUTHOR, K_DATE, K_EDITION, K_PUBLISHER, K_SERIES, K_TITLE, K_VOLUME, + K_ADDRESS, K_AUTHOR, K_DATE, K_EDITION, K_POSITION, K_PUBLISHER, K_SERIES, K_TITLE, K_VOLUME, }; use crate::bibliography::FromHashMap; use crate::utils::date::{parse_date, LocalDate}; @@ -44,12 +44,12 @@ impl InBook { } impl FromHashMap for InBook { - fn from_hash_map(map: &HashMap) -> Option> { - let author = map.get(K_AUTHOR)?; - let title = map.get(K_TITLE)?; - let position = map.get(K_TITLE)?; - let publisher = map.get(K_PUBLISHER)?; - let date = parse_date(map.get(K_DATE)?)?; + fn from_hash_map(map: &HashMap) -> Result, String> { + let author = map.get(K_AUTHOR).ok_or(missing_field!(K_AUTHOR))?; + let title = map.get(K_TITLE).ok_or(missing_field!(K_TITLE))?; + let position = map.get(K_POSITION).ok_or(missing_field!(K_POSITION))?; + let publisher = map.get(K_PUBLISHER).ok_or(missing_field!(K_PUBLISHER))?; + let date = parse_date(map.get(K_DATE).ok_or(missing_field!(K_DATE))?)?; let mut in_book = InBook::new( author.clone(), title.clone(), @@ -63,6 +63,6 @@ impl FromHashMap for InBook { in_book.address = map.get(K_ADDRESS).cloned(); in_book.edition = map.get(K_EDITION).cloned(); - Some(Box::new(in_book)) + Ok(Box::new(in_book)) } } diff --git a/src/bibliography/bib_types/in_collection.rs b/src/bibliography/bib_types/in_collection.rs index 940f2d3..1b6d215 100644 --- a/src/bibliography/bib_types/in_collection.rs +++ b/src/bibliography/bib_types/in_collection.rs @@ -41,28 +41,20 @@ impl InCollection { } impl FromHashMap for InCollection { - fn from_hash_map(map: &HashMap) -> Option> { - let author = map.get(K_AUTHOR)?; - let title = map.get(K_TITLE)?; - let publisher = map.get(K_PUBLISHER)?; - let date = parse_date(map.get(K_DATE)?)?; + fn from_hash_map(map: &HashMap) -> Result, String> { + let author = map.get(K_AUTHOR).ok_or(missing_field!(K_AUTHOR))?; + let title = map.get(K_TITLE).ok_or(missing_field!(K_TITLE))?; + let publisher = map.get(K_PUBLISHER).ok_or(missing_field!(K_PUBLISHER))?; + let date = parse_date(map.get(K_DATE).ok_or(missing_field!(K_DATE))?)?; let mut in_col = InCollection::new(author.clone(), title.clone(), publisher.clone(), date); in_col.editor = map.get(K_EDITOR).cloned(); in_col.volume = map.get(K_VOLUME).cloned(); - if let Some(series) = map.get(K_SERIES) { - Some(series.clone()); - } - if let Some(position) = map.get(K_POSITION) { - in_col.position = Some(position.clone()); - } - if let Some(address) = map.get(K_ADDRESS) { - in_col.address = Some(address.clone()); - } - if let Some(edition) = map.get(K_EDITION) { - in_col.edition = Some(edition.clone()); - } + in_col.series = map.get(K_SERIES).cloned(); + in_col.position = map.get(K_POSITION).cloned(); + in_col.address = map.get(K_ADDRESS).cloned(); + in_col.edition = map.get(K_EDITION).cloned(); - Some(Box::new(in_col)) + Ok(Box::new(in_col)) } } diff --git a/src/bibliography/bib_types/manual.rs b/src/bibliography/bib_types/manual.rs index b923314..7061108 100644 --- a/src/bibliography/bib_types/manual.rs +++ b/src/bibliography/bib_types/manual.rs @@ -30,16 +30,20 @@ impl Manual { } impl FromHashMap for Manual { - fn from_hash_map(map: &HashMap) -> Option> { - let title = map.get(K_TITLE)?; + fn from_hash_map(map: &HashMap) -> Result, String> { + let title = map.get(K_TITLE).ok_or(missing_field!(K_TITLE))?; let mut manual = Manual::new(title.clone()); manual.author = map.get(K_AUTHOR).cloned(); manual.organization = map.get(K_ORGANIZATION).cloned(); manual.address = map.get(K_ADDRESS).cloned(); manual.edition = map.get(K_EDITION).cloned(); - manual.date = map.get(K_DATE).and_then(|d| parse_date(d)); + manual.date = map + .get(K_DATE) + .ok_or(missing_field!(K_DATE)) + .and_then(|d| parse_date(d)) + .ok(); - Some(Box::new(manual)) + Ok(Box::new(manual)) } } diff --git a/src/bibliography/bib_types/misc.rs b/src/bibliography/bib_types/misc.rs index 53768b9..bb810e0 100644 --- a/src/bibliography/bib_types/misc.rs +++ b/src/bibliography/bib_types/misc.rs @@ -28,15 +28,19 @@ impl Misc { } impl FromHashMap for Misc { - fn from_hash_map(map: &HashMap) -> Option> { + fn from_hash_map(map: &HashMap) -> Result, String> { let mut misc = Misc::new(); misc.author = map.get(K_AUTHOR).cloned(); misc.title = map.get(K_TITLE).cloned(); misc.url = map.get(K_URL).cloned(); misc.how_published = map.get(K_HOW_PUBLISHED).cloned(); - misc.date = map.get(K_DATE).and_then(|d| parse_date(d)); + misc.date = map + .get(K_DATE) + .ok_or(missing_field!(K_DATE)) + .and_then(|d| parse_date(d)) + .ok(); - Some(Box::new(misc)) + Ok(Box::new(misc)) } } diff --git a/src/bibliography/bib_types/mod.rs b/src/bibliography/bib_types/mod.rs index c3458e2..91f2101 100644 --- a/src/bibliography/bib_types/mod.rs +++ b/src/bibliography/bib_types/mod.rs @@ -68,25 +68,25 @@ impl BibliographyType { } impl FromHashMap for BibliographyType { - fn from_hash_map(map: &HashMap) -> Option> { - match map.get(K_TYPE)?.as_str() { - T_ARTICLE => Some(Box::new(Self::Article(*Article::from_hash_map(map)?))), - T_BOOK => Some(Box::new(Self::Book(*Book::from_hash_map(map)?))), - T_BOOKLET => Some(Box::new(Self::Booklet(*Booklet::from_hash_map(map)?))), - T_IN_BOOK => Some(Box::new(Self::InBook(*InBook::from_hash_map(map)?))), - T_IN_COLLECTION => Some(Box::new(Self::InCollection(*InCollection::from_hash_map( + fn from_hash_map(map: &HashMap) -> Result, String> { + match map.get(K_TYPE).ok_or(missing_field!(K_TYPE))?.as_str() { + T_ARTICLE => Ok(Box::new(Self::Article(*Article::from_hash_map(map)?))), + T_BOOK => Ok(Box::new(Self::Book(*Book::from_hash_map(map)?))), + T_BOOKLET => Ok(Box::new(Self::Booklet(*Booklet::from_hash_map(map)?))), + T_IN_BOOK => Ok(Box::new(Self::InBook(*InBook::from_hash_map(map)?))), + T_IN_COLLECTION => Ok(Box::new(Self::InCollection(*InCollection::from_hash_map( map, )?))), - T_MANUAL => Some(Box::new(Self::Manual(*Manual::from_hash_map(map)?))), - T_MISC => Some(Box::new(Self::Misc(*Misc::from_hash_map(map)?))), - T_REPOSITORY => Some(Box::new(Self::Repository(*Repository::from_hash_map(map)?))), - T_TECH_REPORT => Some(Box::new(Self::TechReport(*TechReport::from_hash_map(map)?))), - T_THESIS => Some(Box::new(Self::Thesis(*Thesis::from_hash_map(map)?))), - T_UNPUBLISHED => Some(Box::new(Self::Unpublished(*Unpublished::from_hash_map( + T_MANUAL => Ok(Box::new(Self::Manual(*Manual::from_hash_map(map)?))), + T_MISC => Ok(Box::new(Self::Misc(*Misc::from_hash_map(map)?))), + T_REPOSITORY => Ok(Box::new(Self::Repository(*Repository::from_hash_map(map)?))), + T_TECH_REPORT => Ok(Box::new(Self::TechReport(*TechReport::from_hash_map(map)?))), + T_THESIS => Ok(Box::new(Self::Thesis(*Thesis::from_hash_map(map)?))), + T_UNPUBLISHED => Ok(Box::new(Self::Unpublished(*Unpublished::from_hash_map( map, )?))), - T_WEBSITE => Some(Box::new(Self::Website(*Website::from_hash_map(map)?))), - _ => None, + T_WEBSITE => Ok(Box::new(Self::Website(*Website::from_hash_map(map)?))), + _ => Err(format!("Unknown type")), } } } diff --git a/src/bibliography/bib_types/repository.rs b/src/bibliography/bib_types/repository.rs index 91d72b8..29c0503 100644 --- a/src/bibliography/bib_types/repository.rs +++ b/src/bibliography/bib_types/repository.rs @@ -31,16 +31,20 @@ impl Repository { } impl FromHashMap for Repository { - fn from_hash_map(map: &HashMap) -> Option> { - let author = map.get(K_AUTHOR)?; - let title = map.get(K_TITLE)?; + fn from_hash_map(map: &HashMap) -> Result, String> { + let author = map.get(K_AUTHOR).ok_or(missing_field!(K_AUTHOR))?; + let title = map.get(K_TITLE).ok_or(missing_field!(K_TITLE))?; let mut repo = Repository::new(author.clone(), title.clone()); repo.url = map.get(K_URL).cloned(); repo.license = map.get(K_LICENSE).cloned(); repo.cms = map.get(K_CMS).cloned(); - repo.accessed_at = map.get(K_ACCESSED_AT).and_then(|d| parse_date(d)); + repo.accessed_at = map + .get(K_ACCESSED_AT) + .ok_or(missing_field!(K_ACCESSED_AT)) + .and_then(|d| parse_date(d)) + .ok(); - Some(Box::new(repo)) + Ok(Box::new(repo)) } } diff --git a/src/bibliography/bib_types/tech_report.rs b/src/bibliography/bib_types/tech_report.rs index 77bd6a0..c0fbcbf 100644 --- a/src/bibliography/bib_types/tech_report.rs +++ b/src/bibliography/bib_types/tech_report.rs @@ -30,17 +30,22 @@ impl TechReport { } impl FromHashMap for TechReport { - fn from_hash_map(map: &HashMap) -> Option> { - let author = map.get(K_AUTHOR)?; - let title = map.get(K_TITLE)?; - let institution = map.get(K_INSTITUTION)?; - let date = map.get(K_DATE).and_then(|d| parse_date(d))?; + fn from_hash_map(map: &HashMap) -> Result, String> { + let author = map.get(K_AUTHOR).ok_or(missing_field!(K_AUTHOR))?; + let title = map.get(K_TITLE).ok_or(missing_field!(K_TITLE))?; + let institution = map + .get(K_INSTITUTION) + .ok_or(missing_field!(K_INSTITUTION))?; + let date = map + .get(K_DATE) + .ok_or(missing_field!(K_DATE)) + .and_then(|d| parse_date(d))?; let mut tech_report = TechReport::new(author.clone(), title.clone(), institution.clone(), date); tech_report.number = map.get(K_NUMBER).cloned(); tech_report.address = map.get(K_ADDRESS).cloned(); - Some(Box::new(tech_report)) + Ok(Box::new(tech_report)) } } diff --git a/src/bibliography/bib_types/thesis.rs b/src/bibliography/bib_types/thesis.rs index 8b82568..bc7a758 100644 --- a/src/bibliography/bib_types/thesis.rs +++ b/src/bibliography/bib_types/thesis.rs @@ -28,15 +28,18 @@ impl Thesis { } impl FromHashMap for Thesis { - fn from_hash_map(map: &HashMap) -> Option> { - let author = map.get(K_AUTHOR)?; - let title = map.get(K_TITLE)?; - let school = map.get(K_SCHOOL)?; - let date = map.get(K_DATE).and_then(|d| parse_date(d))?; + fn from_hash_map(map: &HashMap) -> Result, String> { + let author = map.get(K_AUTHOR).ok_or(missing_field!(K_AUTHOR))?; + let title = map.get(K_TITLE).ok_or(missing_field!(K_TITLE))?; + let school = map.get(K_SCHOOL).ok_or(missing_field!(K_SCHOOL))?; + let date = map + .get(K_DATE) + .ok_or(missing_field!(K_DATE)) + .and_then(|d| parse_date(d))?; let mut thesis = Thesis::new(author.clone(), title.clone(), school.clone(), date.clone()); thesis.address = map.get(K_ADDRESS).cloned(); - Some(Box::new(thesis)) + Ok(Box::new(thesis)) } } diff --git a/src/bibliography/bib_types/unpublished.rs b/src/bibliography/bib_types/unpublished.rs index 2b5691c..aeecd83 100644 --- a/src/bibliography/bib_types/unpublished.rs +++ b/src/bibliography/bib_types/unpublished.rs @@ -24,13 +24,13 @@ impl Unpublished { } impl FromHashMap for Unpublished { - fn from_hash_map(map: &HashMap) -> Option> { - let author = map.get(K_AUTHOR)?; - let title = map.get(K_TITLE)?; + fn from_hash_map(map: &HashMap) -> Result, String> { + let author = map.get(K_AUTHOR).ok_or(missing_field!(K_AUTHOR))?; + let title = map.get(K_TITLE).ok_or(missing_field!(K_TITLE))?; let mut unpub = Unpublished::new(author.clone(), title.clone()); - unpub.date = map.get(K_DATE).and_then(|d| parse_date(d)); + unpub.date = map.get(K_DATE).and_then(|d| parse_date(d).ok()); - Some(Box::new(unpub)) + Ok(Box::new(unpub)) } } diff --git a/src/bibliography/bib_types/website.rs b/src/bibliography/bib_types/website.rs index 1c7f5ea..604900f 100644 --- a/src/bibliography/bib_types/website.rs +++ b/src/bibliography/bib_types/website.rs @@ -28,15 +28,15 @@ impl Website { } impl FromHashMap for Website { - fn from_hash_map(map: &HashMap) -> Option> { - let url = map.get(K_URL)?; + fn from_hash_map(map: &HashMap) -> Result, String> { + let url = map.get(K_URL).ok_or(missing_field!(K_URL))?; let mut website = Website::new(url.clone()); website.title = map.get(K_TITLE).cloned(); website.author = map.get(K_AUTHOR).cloned(); - website.accessed_at = map.get(K_ACCESSED_AT).and_then(|d| parse_date(d)); - website.date = map.get(K_DATE).and_then(|d| parse_date(d)); + website.accessed_at = map.get(K_ACCESSED_AT).and_then(|d| parse_date(d).ok()); + website.date = map.get(K_DATE).and_then(|d| parse_date(d).ok()); - Some(Box::new(website)) + Ok(Box::new(website)) } } diff --git a/src/bibliography/bibliography_dict.rs b/src/bibliography/bibliography_dict.rs index e0d789f..70c57c9 100644 --- a/src/bibliography/bibliography_dict.rs +++ b/src/bibliography/bibliography_dict.rs @@ -25,13 +25,13 @@ impl BibliographyDictionary { } /// Inserts a bibliography entry represented as a HashMap - pub fn insert_map(&mut self, map: &HashMap) -> Option<()> { - let key = map.get(K_KEY)?; + pub fn insert_map(&mut self, map: &HashMap) -> Result<(), String> { + let key = map.get(K_KEY).ok_or(missing_field!(K_KEY))?; let entry = *BibliographyEntry::from_hash_map(map)?; self.entries .insert(key.clone(), Arc::new(Mutex::new(entry))); - Some(()) + Ok(()) } /// Returns the reference to the bibliography entry with the given key diff --git a/src/bibliography/bibliography_entry.rs b/src/bibliography/bibliography_entry.rs index 39d9bab..53d807c 100644 --- a/src/bibliography/bibliography_entry.rs +++ b/src/bibliography/bibliography_entry.rs @@ -35,8 +35,8 @@ impl BibliographyEntry { } impl FromHashMap for BibliographyEntry { - fn from_hash_map(map: &HashMap) -> Option> { - let key = map.get(K_KEY)?; + fn from_hash_map(map: &HashMap) -> Result, String> { + let key = map.get(K_KEY).ok_or(missing_field!(K_KEY))?; let bib_type = BibliographyType::from_hash_map(map)?; let mut entry = Self::new(key.clone()); @@ -45,6 +45,6 @@ impl FromHashMap for BibliographyEntry { entry.bib_type = *bib_type; entry.raw_fields = map.clone(); - Some(Box::new(entry)) + Ok(Box::new(entry)) } } diff --git a/src/bibliography/mod.rs b/src/bibliography/mod.rs index f343527..917eda9 100644 --- a/src/bibliography/mod.rs +++ b/src/bibliography/mod.rs @@ -1,5 +1,11 @@ use std::collections::HashMap; +macro_rules! missing_field { + ($e:expr) => { + format!("Missing field '{}'", $e) + }; +} + pub mod bib_types; pub mod bibliography_dict; pub mod bibliography_entry; @@ -8,5 +14,5 @@ pub mod keys; /// A trait that provides the from_has_map function that can be used /// to create a bibliography source type from a hashmap pub trait FromHashMap { - fn from_hash_map(map: &HashMap) -> Option>; + fn from_hash_map(map: &HashMap) -> Result, String>; } diff --git a/src/lib.rs b/src/lib.rs index 25dd4e5..182b758 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -50,7 +50,12 @@ mod tests { map.insert("title".to_string(), "test_title".to_string()); map.insert("journal".to_string(), "test_journal".to_string()); map.insert("date".to_string(), "01.09.2020".to_string()); - manager.entry_dictionary().lock().unwrap().insert_map(&map); + manager + .entry_dictionary() + .lock() + .unwrap() + .insert_map(&map) + .unwrap(); manager.assign_entries_to_references(); assert!(anchor1.lock().unwrap().entry.is_some()); diff --git a/src/utils/date.rs b/src/utils/date.rs index 57dddbf..ff6de31 100644 --- a/src/utils/date.rs +++ b/src/utils/date.rs @@ -3,11 +3,10 @@ use chrono_english::Dialect; pub type LocalDate = Date; -pub fn parse_date(date_str: &str) -> Option { +pub fn parse_date(date_str: &str) -> Result { let date_str = date_str.replace('.', "/"); - if let Ok(datetime) = chrono_english::parse_date_string(&date_str, Local::now(), Dialect::Us) { - Some(datetime.date()) - } else { - None - } + + chrono_english::parse_date_string(&date_str, Local::now(), Dialect::Us) + .map(|d| d.date()) + .map_err(|e| e.to_string()) }