aboutsummaryrefslogtreecommitdiff
path: root/src/bib
diff options
context:
space:
mode:
authorlukeflo2024-10-22 21:52:36 +0200
committerlukeflo2024-10-22 21:52:36 +0200
commit66402a9c23e0975a8a3d8c2707b689b9cde98ccf (patch)
treeccba415674b13eadb6739f5a4d0cb53642dc2e62 /src/bib
parent0a74206015e764551ec2a0ade8f6853e915b6911 (diff)
downloadbibiman-66402a9c23e0975a8a3d8c2707b689b9cde98ccf.tar.gz
bibiman-66402a9c23e0975a8a3d8c2707b689b9cde98ccf.zip
rearrange code, file and folder structure
Diffstat (limited to 'src/bib')
-rw-r--r--src/bib/bibmain.rs279
-rw-r--r--src/bib/entries.rs258
-rw-r--r--src/bib/keywords.rs55
-rw-r--r--src/bib/search.rs136
4 files changed, 728 insertions, 0 deletions
diff --git a/src/bib/bibmain.rs b/src/bib/bibmain.rs
new file mode 100644
index 0000000..a7df951
--- /dev/null
+++ b/src/bib/bibmain.rs
@@ -0,0 +1,279 @@
+// 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
new file mode 100644
index 0000000..41edba8
--- /dev/null
+++ b/src/bib/entries.rs
@@ -0,0 +1,258 @@
+// 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
new file mode 100644
index 0000000..2668323
--- /dev/null
+++ b/src/bib/keywords.rs
@@ -0,0 +1,55 @@
+// 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
new file mode 100644
index 0000000..f6e8d14
--- /dev/null
+++ b/src/bib/search.rs
@@ -0,0 +1,136 @@
+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"
+ )
+ }
+}