support for multiple bibles at the same time

This commit is contained in:
Rushmore75 2025-03-26 10:50:00 -06:00
parent d26c02d239
commit c2e985e10a

View File

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