added bible support
This commit is contained in:
parent
6fdf966e13
commit
55c3e5b175
3
.gitmodules
vendored
3
.gitmodules
vendored
@ -1,3 +1,6 @@
|
||||
[submodule "HebrewLexicon"]
|
||||
path = HebrewLexicon
|
||||
url = https://github.com/openscriptures/HebrewLexicon.git
|
||||
[submodule "Holy-Bible-XML-Format"]
|
||||
path = Holy-Bible-XML-Format
|
||||
url = https://github.com/Beblia/Holy-Bible-XML-Format.git
|
||||
|
1
Holy-Bible-XML-Format
Submodule
1
Holy-Bible-XML-Format
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit 69ac8b94c9781f25483905ee08192ee4bf982317
|
241
src/bible.rs
Normal file
241
src/bible.rs
Normal file
@ -0,0 +1,241 @@
|
||||
use std::{fmt::Display, fs::read_to_string};
|
||||
use inline_colorization::*;
|
||||
|
||||
use quick_xml::de::from_str;
|
||||
use serde::Deserialize;
|
||||
|
||||
const BOOKS_IN_ORDER: [&str; 66] = [
|
||||
"Genesis",
|
||||
"Exodus",
|
||||
"Leviticus",
|
||||
"Numbers",
|
||||
"Deuteronomy",
|
||||
"Joshua",
|
||||
"Judges",
|
||||
"Ruth",
|
||||
"1 Samuel",
|
||||
"2 Samuel",
|
||||
"1 Kings",
|
||||
"2 Kings",
|
||||
"1 Chronicles",
|
||||
"2 Chronicles",
|
||||
"Ezra",
|
||||
"Nehemiah",
|
||||
"Esther",
|
||||
"Job",
|
||||
"Psalms",
|
||||
"Proverbs",
|
||||
"Ecclesiastes",
|
||||
"Song of Solomon",
|
||||
"Isaiah",
|
||||
"Jeremiah",
|
||||
"Lamentations",
|
||||
"Ezekiel",
|
||||
"Daniel",
|
||||
"hosea",
|
||||
"Joel",
|
||||
"Amos",
|
||||
"Obadiah",
|
||||
"Jonah",
|
||||
"Micah",
|
||||
"Nahum",
|
||||
"Habakkuk",
|
||||
"Zephaniah",
|
||||
"Haggai",
|
||||
"Zechariah",
|
||||
"Malachi",
|
||||
"Matthew",
|
||||
"Mark",
|
||||
"Luke",
|
||||
"John",
|
||||
"Acts",
|
||||
"Romans",
|
||||
"1 Corinthians",
|
||||
"2 Corinthians",
|
||||
"Galations",
|
||||
"Ephesians",
|
||||
"Philippians",
|
||||
"Colossians",
|
||||
"1 Thessalonians",
|
||||
"2 Thessalonians",
|
||||
"1 Timothy",
|
||||
"2 Timothy",
|
||||
"Titus",
|
||||
"Philemon",
|
||||
"Hebrews",
|
||||
"James",
|
||||
"1 Peter",
|
||||
"2 Peter",
|
||||
"1 John",
|
||||
"2 John",
|
||||
"3 John",
|
||||
"Jude",
|
||||
"Revelation",
|
||||
];
|
||||
|
||||
pub fn get(query: &str, loc: &str) {
|
||||
// expecting query to be in format:
|
||||
// Gen 1:2
|
||||
// Gen 1:2-5
|
||||
// Genesis 1
|
||||
|
||||
// Figure out what book they are talking about
|
||||
let res = BOOKS_IN_ORDER
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter(|(_, book)| book.to_lowercase().contains(&query.to_lowercase()))
|
||||
.collect::<Vec<(usize, &&str)>>();
|
||||
|
||||
let found_book = match res.len() {
|
||||
1 => res[0],
|
||||
2.. => {
|
||||
eprintln!("Err: Ambigious input '{query}', could be any of:");
|
||||
for (_, i) in &res {
|
||||
eprintln!("\t{i}");
|
||||
}
|
||||
return;
|
||||
}
|
||||
_ => {
|
||||
eprintln!("'{query}' is not a book");
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
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/<name>.xml\n\n",
|
||||
);
|
||||
let bible: Bible = from_str(&file).unwrap();
|
||||
|
||||
// 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();
|
||||
|
||||
|
||||
if let Some(chapter) = chatper {
|
||||
if let Ok(chap_num) = chapter.parse::<usize>() {
|
||||
if let Some(chapter) = book.get_chapter_by_index(chap_num) {
|
||||
if let Some(verse) = verse {
|
||||
if let Ok(verse_num) = verse.parse::<usize>() {
|
||||
// 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::<usize>();
|
||||
let end = range[1].parse::<usize>();
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
println!("{}", chapter);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Bible {
|
||||
fn get_book_by_index(&self, idx: usize) -> Option<&Book> {
|
||||
for i in &self.testaments {
|
||||
for book in &i.books {
|
||||
if book.number == idx {
|
||||
return Some(&book);
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl Book {
|
||||
fn get_chapter_by_index(&self, idx: usize) -> Option<&Chapter> {
|
||||
for chap in &self.chapters {
|
||||
if chap.number == idx {
|
||||
return Some(&chap);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl Chapter {
|
||||
fn get_verse_by_index(&self, idx: usize) -> Option<&Verse> {
|
||||
for verse in &self.verses {
|
||||
if verse.number == idx {
|
||||
return Some(&verse)
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct Bible {
|
||||
#[serde(rename = "@translation")]
|
||||
version: String,
|
||||
#[serde(rename = "testament")]
|
||||
testaments: Vec<Testament>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct Testament {
|
||||
#[serde(rename = "@name")]
|
||||
// yes, this would make a nice enum
|
||||
testament: String,
|
||||
#[serde(rename = "book")]
|
||||
books: Vec<Book>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct Book {
|
||||
#[serde(rename = "@number")]
|
||||
number: usize,
|
||||
#[serde(rename = "chapter")]
|
||||
chapters: Vec<Chapter>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct Chapter {
|
||||
#[serde(rename = "@number")]
|
||||
number: usize,
|
||||
#[serde(rename = "verse")]
|
||||
verses: Vec<Verse>,
|
||||
}
|
||||
impl Display for Chapter {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
writeln!(f, "{style_bold}{style_underline}{}{style_reset}", self.number)?;
|
||||
for v in &self.verses {
|
||||
writeln!(f, "|\t{}", v)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct Verse {
|
||||
#[serde(rename = "@number")]
|
||||
number: usize,
|
||||
#[serde(rename = "$text")]
|
||||
text: String,
|
||||
}
|
||||
impl Display for Verse {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", self.text)
|
||||
}
|
||||
}
|
||||
|
30
src/main.rs
30
src/main.rs
@ -1,10 +1,9 @@
|
||||
use inline_colorization::*;
|
||||
use std::{env, io::{self, Write}};
|
||||
use std::env;
|
||||
|
||||
mod strong;
|
||||
mod bible;
|
||||
|
||||
fn main() {
|
||||
|
||||
let arg = env::args().collect::<Vec<String>>();
|
||||
if let Some(query) = arg.get(1) {
|
||||
if query.starts_with("H") | query.starts_with("h") {
|
||||
@ -12,28 +11,9 @@ fn main() {
|
||||
print!("{found}");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// No input args, go into interactive mode
|
||||
let stdin = io::stdin();
|
||||
let mut stdout = io::stdout();
|
||||
loop {
|
||||
print!("{color_bright_white}{style_bold}Search>{style_reset} ");
|
||||
let _ = stdout.flush();
|
||||
|
||||
let mut buf = String::new();
|
||||
stdin.read_line(&mut buf).unwrap();
|
||||
let search = buf.trim().to_string();
|
||||
|
||||
if search.starts_with("H") {
|
||||
if let Some(entry) = strong::get(&search) {
|
||||
println!("");
|
||||
print!("{}", entry);
|
||||
println!("");
|
||||
}
|
||||
} else {
|
||||
println!("Not sure what you're looking for...");
|
||||
}
|
||||
if let Some(second_query) = arg.get(2) {
|
||||
bible::get(query,second_query);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user