diff options
| author | lukeflo | 2024-10-24 12:53:09 +0200 |
|---|---|---|
| committer | lukeflo | 2024-10-24 12:53:09 +0200 |
| commit | f32b6a19851b8b103ac843503ab008197f0639cd (patch) | |
| tree | 49216019cf71b4c3161eb5b1a74d9ee7e06eceb0 /src/bib | |
| parent | 66402a9c23e0975a8a3d8c2707b689b9cde98ccf (diff) | |
| download | bibiman-f32b6a19851b8b103ac843503ab008197f0639cd.tar.gz bibiman-f32b6a19851b8b103ac843503ab008197f0639cd.zip | |
rearrange code again, prepare for command-action setup
Diffstat (limited to 'src/bib')
| -rw-r--r-- | src/bib/bibmain.rs | 279 | ||||
| -rw-r--r-- | src/bib/entries.rs | 258 | ||||
| -rw-r--r-- | src/bib/keywords.rs | 55 | ||||
| -rw-r--r-- | src/bib/search.rs | 136 |
4 files changed, 0 insertions, 728 deletions
diff --git a/src/bib/bibmain.rs b/src/bib/bibmain.rs deleted file mode 100644 index a7df951..0000000 --- a/src/bib/bibmain.rs +++ /dev/null @@ -1,279 +0,0 @@ -// bibiman - a TUI for managing BibLaTeX databases -// Copyright (C) 2024 lukeflo -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see <https://www.gnu.org/licenses/>. -///// - -use biblatex::{self, Bibliography}; -use biblatex::{ChunksExt, Type}; -use itertools::Itertools; -use std::{fs, path::PathBuf}; - -#[derive(Debug)] -pub enum FileFormat { - BibLatex, - Hayagriva, -} - -// Set necessary fields -// TODO: can surely be made more efficient/simpler -#[derive(Debug)] -pub struct BibiMain { - pub bibfile: PathBuf, // path to bibfile - pub bibfile_format: FileFormat, // Format of passed file - pub bibfilestring: String, // content of bibfile as string - pub bibliography: Bibliography, // parsed bibliography - pub citekeys: Vec<String>, // list of all citekeys - pub keyword_list: Vec<String>, // list of all available keywords - pub entry_list: Vec<BibiData>, // List of all entries -} - -#[derive(Debug, Clone)] -pub struct BibiData { - pub authors: String, - pub title: String, - pub year: String, - pub pubtype: String, - pub keywords: String, - pub citekey: String, - pub abstract_text: String, - pub doi_url: String, - pub filepath: String, -} - -impl BibiMain { - pub fn new(main_bibfile: PathBuf) -> Self { - // TODO: Needs check for config file path as soon as config file is impl - let bibfile_format = Self::check_file_format(&main_bibfile); - let bibfile = main_bibfile; - let bibfilestring = fs::read_to_string(&bibfile).unwrap(); - let bibliography = biblatex::Bibliography::parse(&bibfilestring).unwrap(); - let citekeys = Self::get_citekeys(&bibliography); - let keyword_list = Self::collect_tag_list(&citekeys, &bibliography); - let entry_list = Self::create_entry_list(&citekeys, &bibliography); - Self { - bibfile, - bibfile_format, - bibfilestring, - bibliography, - citekeys, - keyword_list, - entry_list, - } - } - - // Check which file format the passed file has - fn check_file_format(main_bibfile: &PathBuf) -> FileFormat { - let extension = main_bibfile.extension().unwrap().to_str(); - - match extension { - Some("yml") => FileFormat::Hayagriva, - Some("yaml") => FileFormat::Hayagriva, - Some("bib") => FileFormat::BibLatex, - Some(_) => panic!("The extension {:?} is no valid bibfile", extension.unwrap()), - None => panic!("The given path {:?} holds no valid file", main_bibfile), - } - } - - fn create_entry_list(citekeys: &[String], bibliography: &Bibliography) -> Vec<BibiData> { - citekeys - .into_iter() - .map(|k| BibiData { - authors: Self::get_authors(&k, &bibliography), - title: Self::get_title(&k, &bibliography), - year: Self::get_year(&k, &bibliography), - pubtype: Self::get_pubtype(&k, &bibliography), - keywords: Self::get_keywords(&k, &bibliography), - citekey: k.to_owned(), - abstract_text: Self::get_abstract(&k, &bibliography), - doi_url: Self::get_weblink(&k, &bibliography), - filepath: Self::get_filepath(&k, &bibliography), - }) - .collect() - } - - // get list of citekeys from the given bibfile - // this list is the base for further operations on the bibentries - // since it is the entry point of the biblatex crate. - pub fn get_citekeys(bibstring: &Bibliography) -> Vec<String> { - let citekeys: Vec<String> = bibstring.keys().map(|k| k.to_owned()).collect(); - citekeys - } - - // collect all keywords present in the bibliography - // sort them and remove duplicates - // this list is for fast filtering entries by topics/keyowrds - pub fn collect_tag_list(citekeys: &[String], biblio: &Bibliography) -> Vec<String> { - // Initialize vector collecting all keywords - let mut keyword_list = vec![]; - - // Loop over entries and collect all keywords - for i in citekeys { - if biblio.get(&i).unwrap().keywords().is_ok() { - let items = biblio - .get(&i) - .unwrap() - .keywords() - .unwrap() - .format_verbatim(); - // Split keyword string into slices, trim leading and trailing - // whitespaces, remove empty slices, and collect them - let mut key_vec: Vec<String> = items - .split(',') - .map(|s| s.trim().to_string()) - .filter(|s| !s.is_empty()) - .collect(); - // Append keywords to vector - keyword_list.append(&mut key_vec); - } - } - - // Sort the vector and remove duplicates - keyword_list.sort_by(|a, b| a.to_lowercase().cmp(&b.to_lowercase())); - keyword_list.dedup(); - keyword_list - } - - pub fn get_authors(citekey: &str, biblio: &Bibliography) -> String { - if biblio.get(&citekey).unwrap().author().is_ok() { - let authors = biblio.get(&citekey).unwrap().author().unwrap(); - if authors.len() > 1 { - let all_authors = authors.iter().map(|a| &a.name).join(", "); - all_authors - } else if authors.len() == 1 { - let authors = authors[0].name.to_string(); - authors - } else { - let editors_authors = format!("empty"); - editors_authors - } - } else { - if !biblio.get(&citekey).unwrap().editors().unwrap().is_empty() { - let editors = biblio.get(&citekey).unwrap().editors().unwrap(); - if editors[0].0.len() > 1 { - // let editors = format!("{} (ed.) et al.", editors[0].0[0].name); - let mut editors = editors[0].0.iter().map(|e| &e.name).join(", "); - editors.push_str(" (ed.)"); - editors - } else if editors[0].0.len() == 1 { - let editors = format!("{} (ed.)", editors[0].0[0].name); - editors - } else { - let editors_authors = format!("empty"); - editors_authors - } - } else { - let editors_authors = format!("empty"); - editors_authors - } - } - } - - pub fn get_title(citekey: &str, biblio: &Bibliography) -> String { - let title = { - if biblio.get(&citekey).unwrap().title().is_ok() { - let title = biblio - .get(&citekey) - .unwrap() - .title() - .unwrap() - .format_verbatim(); - title - } else { - let title = format!("no title"); - title - } - }; - title - } - - pub fn get_year(citekey: &str, biblio: &Bibliography) -> String { - let year = biblio.get(&citekey).unwrap(); - let year = { - if year.date().is_ok() { - let year = year.date().unwrap().to_chunks().format_verbatim(); - let year = year[..4].to_string(); - year - } else { - let year = format!("n.d."); - year - } - }; - year - } - - pub fn get_pubtype(citekey: &str, biblio: &Bibliography) -> String { - let pubtype = biblio.get(&citekey).unwrap().entry_type.to_string(); - pubtype - } - - pub fn get_keywords(citekey: &str, biblio: &Bibliography) -> String { - let keywords = { - if biblio.get(&citekey).unwrap().keywords().is_ok() { - let keywords = biblio - .get(&citekey) - .unwrap() - .keywords() - .unwrap() - .format_verbatim(); - keywords - } else { - let keywords = String::from(""); - keywords - } - }; - keywords - } - - pub fn get_abstract(citekey: &str, biblio: &Bibliography) -> String { - let text = { - if biblio.get(&citekey).unwrap().abstract_().is_ok() { - let abstract_text = biblio - .get(&citekey) - .unwrap() - .abstract_() - .unwrap() - .format_verbatim(); - abstract_text - } else { - let abstract_text = format!("No abstract"); - abstract_text - } - }; - text - } - - pub fn get_weblink(citekey: &str, biblio: &Bibliography) -> String { - if let true = biblio.get(&citekey).unwrap().doi().is_ok() { - let url = biblio.get(&citekey).unwrap().doi().unwrap(); - url - } else if let true = biblio.get(&citekey).unwrap().url().is_ok() { - let url = biblio.get(&citekey).unwrap().url().unwrap(); - url - } else { - let url = "".to_string(); - url - } - } - - pub fn get_filepath(citekey: &str, biblio: &Bibliography) -> String { - if let true = biblio.get(&citekey).unwrap().file().is_ok() { - let file = biblio.get(&citekey).unwrap().file().unwrap(); - file - } else { - let file = "".to_string(); - file - } - } -} diff --git a/src/bib/entries.rs b/src/bib/entries.rs deleted file mode 100644 index 41edba8..0000000 --- a/src/bib/entries.rs +++ /dev/null @@ -1,258 +0,0 @@ -// bibiman - a TUI for managing BibLaTeX databases -// Copyright (C) 2024 lukeflo -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see <https://www.gnu.org/licenses/>. -///// - -use crate::bib::bibmain::BibiData; -use ratatui::widgets::{ScrollbarState, TableState}; - -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum EntryTableColumn { - Authors, - Title, - Year, - Pubtype, -} - -// Define list containing entries as table -#[derive(Debug, PartialEq, Eq)] -pub struct EntryTable { - pub entry_table_items: Vec<EntryTableItem>, - pub entry_table_at_search_start: Vec<EntryTableItem>, - pub entry_table_selected_column: EntryTableColumn, - pub entry_table_sorted_by_col: EntryTableColumn, - pub entry_table_reversed_sort: bool, - pub entry_table_state: TableState, - pub entry_scroll_state: ScrollbarState, - pub entry_info_scroll: u16, - pub entry_info_scroll_state: ScrollbarState, -} - -impl EntryTable { - pub fn new(entry_list: Vec<BibiData>) -> Self { - let entry_table_items = Self::set_entry_table(entry_list); - let entry_table_state = TableState::default().with_selected(0); - let entry_scroll_state = ScrollbarState::new(entry_table_items.len()); - let entry_info_scroll_state = ScrollbarState::default(); - Self { - entry_table_items, - entry_table_at_search_start: Vec::new(), - entry_table_selected_column: EntryTableColumn::Authors, - entry_table_sorted_by_col: EntryTableColumn::Authors, - entry_table_reversed_sort: false, - entry_table_state, - entry_scroll_state, - entry_info_scroll: 0, - entry_info_scroll_state, - } - } - - pub fn set_entry_table(entry_list: Vec<BibiData>) -> Vec<EntryTableItem> { - let mut entry_table: Vec<EntryTableItem> = entry_list - .into_iter() - .map(|e| EntryTableItem { - authors: e.authors, - short_author: String::new(), - title: e.title, - year: e.year, - pubtype: e.pubtype, - keywords: e.keywords, - citekey: e.citekey, - abstract_text: e.abstract_text, - doi_url: e.doi_url, - filepath: e.filepath, - }) - .collect(); - - entry_table.sort_by(|a, b| a.authors.to_lowercase().cmp(&b.authors.to_lowercase())); - entry_table - } - - // Sort entry table by specific column. - // Toggle sorting by hitting same key again - pub fn sort_entry_table(&mut self, toggle: bool) { - if toggle { - self.entry_table_reversed_sort = !self.entry_table_reversed_sort; - } - if self.entry_table_selected_column != self.entry_table_sorted_by_col { - self.entry_table_reversed_sort = false - } - self.entry_table_sorted_by_col = self.entry_table_selected_column.clone(); - if self.entry_table_reversed_sort { - match self.entry_table_selected_column { - EntryTableColumn::Authors => self - .entry_table_items - .sort_by(|a, b| b.authors.to_lowercase().cmp(&a.authors.to_lowercase())), - EntryTableColumn::Title => self - .entry_table_items - .sort_by(|a, b| b.title.to_lowercase().cmp(&a.title.to_lowercase())), - EntryTableColumn::Year => self - .entry_table_items - .sort_by(|a, b| b.year.to_lowercase().cmp(&a.year.to_lowercase())), - EntryTableColumn::Pubtype => self - .entry_table_items - .sort_by(|a, b| b.pubtype.to_lowercase().cmp(&a.pubtype.to_lowercase())), - } - } else if !self.entry_table_reversed_sort { - match self.entry_table_selected_column { - EntryTableColumn::Authors => self - .entry_table_items - .sort_by(|a, b| a.authors.to_lowercase().cmp(&b.authors.to_lowercase())), - EntryTableColumn::Title => self - .entry_table_items - .sort_by(|a, b| a.title.to_lowercase().cmp(&b.title.to_lowercase())), - EntryTableColumn::Year => self - .entry_table_items - .sort_by(|a, b| a.year.to_lowercase().cmp(&b.year.to_lowercase())), - EntryTableColumn::Pubtype => self - .entry_table_items - .sort_by(|a, b| a.pubtype.to_lowercase().cmp(&b.pubtype.to_lowercase())), - } - } - } -} - -// Define contents of each entry table row -#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] -pub struct EntryTableItem { - pub authors: String, - pub short_author: String, - pub title: String, - pub year: String, - pub pubtype: String, - pub keywords: String, - pub citekey: String, - pub abstract_text: String, - pub doi_url: String, - pub filepath: String, -} - -impl EntryTableItem { - // This functions decides which fields are rendered in the entry table - // Fields which should be usable but not visible can be left out - pub fn ref_vec(&mut self) -> Vec<&str> { - self.short_author = match self.authors.split_once(",") { - Some((first, _rest)) => { - if self.authors.contains("(ed.)") { - let first_author = format!("{} et al. (ed.)", first); - first_author - } else { - let first_author = format!("{} et al.", first); - first_author - } - } - None => String::from(""), - }; - - vec![ - { - if self.short_author.is_empty() { - &self.authors - } else { - &self.short_author - } - }, - &self.title, - &self.year, - &self.pubtype, - ] - } - - pub fn authors(&self) -> &str { - &self.authors - } - - pub fn title(&self) -> &str { - &self.title - } - - pub fn year(&self) -> &str { - &self.year - } - - pub fn pubtype(&self) -> &str { - &self.pubtype - } - - pub fn citekey(&self) -> &str { - &self.citekey - } - - pub fn doi_url(&self) -> &str { - &self.doi_url - } - - pub fn filepath(&self) -> &str { - &self.filepath - } -} - -#[cfg(test)] -mod tests { - use super::EntryTableItem; - - #[test] - fn check_os() { - let os = std::env::consts::OS; - assert_eq!( - os, - "linux", - "You're not coding on linux, but on {}... Switch to linux, now!", - std::env::consts::OS - ) - } - - #[test] - fn shorten_authors() { - let mut entry: EntryTableItem = EntryTableItem { - authors: "Miller, Schmitz, Bernard".to_string(), - short_author: "".to_string(), - title: "A title".to_string(), - year: "2000".to_string(), - pubtype: "article".to_string(), - keywords: "key1, key2".to_string(), - citekey: "miller_2000".to_string(), - abstract_text: "An abstract".to_string(), - doi_url: "www.text.org".to_string(), - filepath: "/home/test".to_string(), - }; - - let entry_vec = EntryTableItem::ref_vec(&mut entry); - - let mut entry_editors: EntryTableItem = EntryTableItem { - authors: "Miller, Schmitz, Bernard (ed.)".to_string(), - short_author: "".to_string(), - title: "A title".to_string(), - year: "2000".to_string(), - pubtype: "article".to_string(), - keywords: "key1, key2".to_string(), - citekey: "miller_2000".to_string(), - abstract_text: "An abstract".to_string(), - doi_url: "www.text.org".to_string(), - filepath: "/home/test".to_string(), - }; - - let entry_vec_editors = EntryTableItem::ref_vec(&mut entry_editors); - - assert_eq!( - entry_vec, - vec!["Miller et al.", "A title", "2000", "article"] - ); - assert_eq!( - entry_vec_editors, - vec!["Miller et al. (ed.)", "A title", "2000", "article"] - ) - } -} diff --git a/src/bib/keywords.rs b/src/bib/keywords.rs deleted file mode 100644 index 2668323..0000000 --- a/src/bib/keywords.rs +++ /dev/null @@ -1,55 +0,0 @@ -// bibiman - a TUI for managing BibLaTeX databases -// Copyright (C) 2024 lukeflo -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see <https://www.gnu.org/licenses/>. -///// - -use ratatui::widgets::{ListState, ScrollbarState}; - -#[derive(Debug)] -pub struct TagList { - pub tag_list_items: Vec<String>, - pub tag_list_state: ListState, - pub tag_scroll_state: ScrollbarState, - pub selected_keywords: Vec<String>, -} - -// Structure of the list items. -#[derive(Debug)] -pub struct TagListItem { - pub keyword: String, -} - -// Function to process inputed characters and convert them (to string, or more complex function) -impl TagListItem { - pub fn new(info: &str) -> Self { - Self { - keyword: info.to_string(), - } - } -} - -impl TagList { - pub fn new(keyword_list: Vec<String>) -> Self { - let tag_list_items = keyword_list; - let tag_list_state = ListState::default(); // for preselection: .with_selected(Some(0)); - let tag_scroll_state = ScrollbarState::new(tag_list_items.len()); - Self { - tag_list_items, - tag_list_state, - tag_scroll_state, - selected_keywords: Vec::new(), - } - } -} diff --git a/src/bib/search.rs b/src/bib/search.rs deleted file mode 100644 index f6e8d14..0000000 --- a/src/bib/search.rs +++ /dev/null @@ -1,136 +0,0 @@ -use super::entries::EntryTableItem; -use nucleo_matcher::{ - pattern::{CaseMatching, Normalization, Pattern}, - Config, Matcher, -}; -use std::collections::HashMap; - -#[derive(Debug)] -pub struct BibiSearch { - pub search_string: String, // Search string show in footer, used for search - pub inner_search: bool, // True, if we trigger a search for already filtered list - pub filtered_tag_list: Vec<String>, -} - -impl Default for BibiSearch { - fn default() -> Self { - Self { - search_string: String::new(), - inner_search: false, - filtered_tag_list: Vec::new(), - } - } -} - -impl BibiSearch { - // Stringify EntryTableItem by joining/concat - fn convert_to_string(inner_vec: &EntryTableItem) -> String { - let entry_table_item_str = { - format!( - "{} {} {} {} {} {}", - &inner_vec.authors, - &inner_vec.title, - &inner_vec.year, - &inner_vec.pubtype, - &inner_vec.keywords, - &inner_vec.citekey - ) - }; - entry_table_item_str - } - - // Return a filtered entry list - pub fn search_entry_list( - search_pattern: &str, - orig_list: Vec<EntryTableItem>, - ) -> Vec<EntryTableItem> { - // Create a hashmap to connect stingified entry with entry vec - let mut entry_string_hm: HashMap<String, EntryTableItem> = HashMap::new(); - - // Convert all entries to string and insert them into the hashmap - // next to the original inner Vec<String> of the entry list - for entry in orig_list { - entry_string_hm.insert(Self::convert_to_string(&entry), entry); - } - - // Set up matcher (TODO: One time needed only, move to higher level) - let mut matcher = Matcher::new(Config::DEFAULT); - - // Filter the stringified entries and collect them into a vec - let filtered_matches: Vec<String> = { - let matches = - Pattern::parse(search_pattern, CaseMatching::Ignore, Normalization::Smart) - .match_list(entry_string_hm.keys(), &mut matcher); - matches.into_iter().map(|f| f.0.to_string()).collect() - }; - - // Create filtered entry list and push the inner entry vec's to it - // Use the filtered stringified hm-key as index - let mut filtered_list: Vec<EntryTableItem> = Vec::new(); - for m in filtered_matches { - filtered_list.push(entry_string_hm[&m].to_owned()); - } - filtered_list.sort(); - filtered_list - } - - pub fn search_tag_list(search_pattern: &str, orig_list: Vec<String>) -> Vec<String> { - // Set up matcher (TODO: One time needed only) - let mut matcher = Matcher::new(Config::DEFAULT); - - // Filter the list items by search pattern - let filtered_matches: Vec<String> = { - let matches = - Pattern::parse(search_pattern, CaseMatching::Ignore, Normalization::Smart) - .match_list(orig_list, &mut matcher); - matches.into_iter().map(|f| f.0.to_string()).collect() - }; - filtered_matches - } - - pub fn filter_entries_by_tag( - keyword: &str, - orig_list: &Vec<EntryTableItem>, - ) -> Vec<EntryTableItem> { - let mut filtered_list: Vec<EntryTableItem> = Vec::new(); - - // Loop over the whole given entry table - // Check if the selected keyword is present in the current entry - // If present, push the entry to the filtered list - for e in orig_list { - if e.keywords.contains(keyword) { - filtered_list.push(e.to_owned()); - } - } - - filtered_list - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_vector_join() { - let bibvec: EntryTableItem = EntryTableItem { - authors: "Author".to_string(), - short_author: "".to_string(), - title: "Title".to_string(), - year: "1999".to_string(), - pubtype: "article".to_string(), - keywords: "hello, bye".to_string(), - citekey: "author_1999".to_string(), - abstract_text: "An abstract with multiple sentences. Here is the second".to_string(), - doi_url: "https://www.bibiman.org".to_string(), - filepath: "/home/file/path.pdf".to_string(), - }; - - let joined_vec = BibiSearch::convert_to_string(&bibvec); - - assert_eq!( - joined_vec, - "Author Title 1999 article hello, bye author_1999" - ) - } -} |
