diff --git a/src/bible.rs b/src/bible.rs index 77914d6..9a09474 100644 --- a/src/bible.rs +++ b/src/bible.rs @@ -1,5 +1,5 @@ -use std::{fmt::Display, fs::read_to_string}; use inline_colorization::*; +use std::{fmt::Display, fs}; use quick_xml::de::from_str; use serde::Deserialize; @@ -73,12 +73,24 @@ const BOOKS_IN_ORDER: [&str; 66] = [ "Revelation", ]; +// This would eventaully be a list built by the user +const BIBLES: [&str; 2] = [ + "/usr/local/bible/EnglishNASBBible.xml", + "/usr/local/bible/EnglishNIVBible.xml", +]; + pub fn get(query: &str, loc: &str) { // expecting query to be in format: // Gen 1:2 // Gen 1:2-5 - // Genesis 1 + // Gen 1 + // The book name needs to be just long enough to be unique + let mut splits = loc.split(':'); + let chapter = splits.next(); + let verse = splits.next(); + + // ########################################### // Figure out what book they are talking about let res = BOOKS_IN_ORDER .iter() @@ -86,7 +98,7 @@ pub fn get(query: &str, loc: &str) { .filter(|(_, book)| book.to_lowercase().contains(&query.to_lowercase())) .collect::>(); - let found_book = match res.len() { + let (book_idx, book_name) = match res.len() { 1 => res[0], 2.. => { eprintln!("Err: Ambigious input '{query}', could be any of:"); @@ -101,50 +113,87 @@ pub fn get(query: &str, loc: &str) { } }; - let file = read_to_string("/usr/local/bible/EnglishNASBBible.xml").expect( - "Failed to get the bible xml file. It's expect to be at:\n/usr/local/bible/.xml\n\n", - ); - let bible: Bible = from_str(&file).unwrap(); + // Load Bibles into memory + let bibles = BIBLES + .iter() + .filter_map(|path| fs::read_to_string(path).ok()) + .filter_map(|contents| from_str::(&contents).ok()) + .collect::>(); + // Select the book in each Bible + let books = bibles + .iter() + // Book are 1 indexed in the xml spec + .map(|bible| (bible, bible.get_book_by_index(book_idx + 1))) + .filter(|(_,e)| e.is_some()) + .map(|(bible,book)| (bible,book.unwrap())) + .collect::>(); - // Book are 1 indexed in the xml spec - if let Some(book) = bible.get_book_by_index(found_book.0 + 1) { - // Figure out what verse they want - let mut splits = loc.split(':'); - let chatper = splits.next(); - let verse = splits.next(); + // Select the chapter in each Bible + let chapters = if let Some(chapter) = chapter { + if let Ok(ch_num) = chapter.parse::() { + books + .iter() + .map(|(bible,book)| (bible, book, book.get_chapter_by_index(ch_num))) + .filter(|(_,_,book)| book.is_some()) + .map(|(bible,book,chapter)| (*bible,*book,chapter.unwrap())) + .collect::>() + } else { + return; + } + } else { + return; + }; + // Get the verse in each Bible + match verse { + Some(verse) => { + let mut splits = verse.split("-"); + let start = splits.next(); + let end = splits.next(); - if let Some(chapter) = chatper { - if let Ok(chap_num) = chapter.trim().parse::() { - if let Some(chapter) = book.get_chapter_by_index(chap_num) { - if let Some(verse) = verse { - if let Ok(verse_num) = verse.trim().parse::() { - // return specific verse - if let Some(verse) = chapter.get_verse_by_index(verse_num) { - println!("{style_underline}{style_bold}{} {}:{}{style_reset}: {}", found_book.1, chapter.number, verse.number, verse); - } - } else { - // might have input a verse range - let range: Vec<&str> = verse.split("-").collect(); - if range.len() == 2 { - let start = range[0].parse::(); - let end = range[1].parse::(); - - if let (Ok(s), Ok(e)) = (start,end) { - println!("{style_underline}{style_bold}{} {}:{}-{}{style_reset}:", found_book.1, chapter.number, s, e); - for verse_idx in s..=e { - if let Some(v) = chapter.get_verse_by_index(verse_idx) { - println!("{style_underline}{style_bold}{}{style_reset}: {}", v.number, v); - } - } + match (start, end) { + // range of verses + (Some(sn), Some(en)) => { + if let (Ok(start), Ok(end)) = (sn.parse::(), en.parse::()) { + for num in start..=end { + for (bible, _book, chapter) in &chapters { + if let Some(verse) = chapter.get_verse_by_index(num) { + println!( + "{style_bold}[{}] {style_underline}{book_name} {}:{}{style_reset}: {verse}", + bible.translation_name, chapter.number, verse.number + ); } } } - } else { - println!("{style_bold}{style_underline}{} {}{style_reset}:", found_book.1, chapter.number); - print!("{}", chapter); } } + // only one verse + (Some(ver_num), None) => { + if let Ok(num) = ver_num.parse::() { + for (bible, _book, chapter) in chapters { + if let Some(verse) = chapter.get_verse_by_index(num) { + println!( + "{style_bold}[{}] {style_underline}{book_name} {}:{}{style_reset}: {verse}", + bible.translation_name, chapter.number, verse.number + ); + } + } + } + } + _ => { + // couldn't parse verse + return; + } + } + } + None => { + // only chapter + for (bible, _book, chapter) in chapters { + println!( + "{style_bold}[{}] {style_underline}{book_name} {}{style_reset}:", + bible.translation_name, chapter.number + ); + print!("{}", chapter); } } } @@ -178,7 +227,7 @@ impl Chapter { fn get_verse_by_index(&self, idx: usize) -> Option<&Verse> { for verse in &self.verses { if verse.number == idx { - return Some(&verse) + return Some(&verse); } } None @@ -188,7 +237,7 @@ impl Chapter { #[derive(Deserialize)] struct Bible { #[serde(rename = "@translation")] - _version: String, + translation_name: String, #[serde(rename = "testament")] testaments: Vec, } @@ -220,7 +269,11 @@ struct Chapter { impl Display for Chapter { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { for v in &self.verses { - writeln!(f, "{style_bold}{style_underline}{}{style_reset}: {}", v.number, v)?; + writeln!( + f, + "{style_bold}{style_underline}{}{style_reset}: {}", + v.number, v + )?; } Ok(()) } @@ -238,4 +291,3 @@ impl Display for Verse { write!(f, "{}", self.text) } } -