commit abda6bab95343ec5455a19af977216d4e8f1d46a Author: Rushmore75 Date: Tue Mar 25 13:32:44 2025 -0600 mvp diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..3a1572e --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "HebrewLexicon"] + path = HebrewLexicon + url = https://github.com/openscriptures/HebrewLexicon.git diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..9a1c119 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,89 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "bible" +version = "0.1.0" +dependencies = [ + "inline_colorization", + "quick-xml", + "serde", +] + +[[package]] +name = "inline_colorization" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc1804bdb6a9784758b200007273a8b84e2b0b0b97a8f1e18e763eceb3e9f98a" + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "proc-macro2" +version = "1.0.94" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quick-xml" +version = "0.37.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "165859e9e55f79d67b96c5d96f4e88b6f2695a1972849c15a6a3f5c59fc2c003" +dependencies = [ + "memchr", + "serde", +] + +[[package]] +name = "quote" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "serde" +version = "1.0.219" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.219" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "syn" +version = "2.0.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..585bc1a --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "bible" +version = "0.1.0" +edition = "2024" + +[dependencies] +inline_colorization = "0.1.6" +quick-xml = { version="0.37.2", features=["serialize"] } +serde = { version = "1.0.219", features = ["derive"] } diff --git a/HebrewLexicon b/HebrewLexicon new file mode 160000 index 0000000..21c9add --- /dev/null +++ b/HebrewLexicon @@ -0,0 +1 @@ +Subproject commit 21c9add13bc727d3a951361778e97e3ff7afd1ce diff --git a/README.md b/README.md new file mode 100644 index 0000000..f9167c2 --- /dev/null +++ b/README.md @@ -0,0 +1,8 @@ +# cli bible + +## Important Links + +[Tanach.us](https://tanach.us) + +[HewbrewLexicon](https://github.com/openscriptures/HebrewLexicon/tree/master) + diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..ea46c7c --- /dev/null +++ b/src/main.rs @@ -0,0 +1,39 @@ +use inline_colorization::*; +use std::{env, io::{self, Write}}; + +mod strong; + +fn main() { + + let arg = env::args().collect::>(); + if let Some(query) = arg.get(1) { + if query.starts_with("H") | query.starts_with("h") { + if let Some(found) = strong::get(query) { + 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..."); + } + } +} diff --git a/src/strong.rs b/src/strong.rs new file mode 100644 index 0000000..43f36c7 --- /dev/null +++ b/src/strong.rs @@ -0,0 +1,200 @@ +use std::{fmt::Display, fs::read_to_string}; +use inline_colorization::*; + +use quick_xml::de::from_str; +use serde::Deserialize; + +pub fn get(query: &str) -> Option { + // TODO Put this in a singleton + let file = read_to_string("./HebrewLexicon/HebrewStrong.xml").unwrap(); + let obj: HebrewStrong = from_str(&file).unwrap(); + + // Yes, dumb search is ok. Searching for the last element took 2ms + obj.entries.into_iter().find(|entry| entry.number.eq(&query)) +} + +#[derive(Deserialize)] +pub struct HebrewStrong { + #[serde(rename = "$value")] + pub entries: Vec, +} + +#[derive(Deserialize, Debug)] +pub struct Entry { + #[serde(rename = "@id")] + pub number: String, + w: Word, + // TODO find who has multiple notes + note: Option>, + source: Option, + meaning: Option, + usage: Option, +} +impl Display for Entry { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + writeln!(f, "{color_red}Strong's ID{style_reset}: {}", self.number)?; + + if let Some(notes) = &self.note { + write!(f, "Notes:")?; + for note in notes { + writeln!(f, "{}", note)?; + } + } + + write!(f, "{}", self.w)?; + + if let Some(i) = &self.source { + writeln!(f, "{}", i)?; + } + + if let Some(i) = &self.meaning { + writeln!(f, "{}", i)?; + } + + if let Some(i) = &self.usage { + writeln!(f, "{}", i)?; + } + + Ok(()) + } +} + +#[derive(Deserialize, Debug)] +struct Word { + #[serde(rename = "@pos")] + // TODO this is actaully a link to PartsOfSpeech.xml + part_of_speech: Option, + + #[serde(rename = "@pron")] + pronunciation: Option, + + #[serde(rename = "@xlit")] + transliteration: Option, + + #[serde(rename = "@xlm:lang")] + lang: Option, + + #[serde(rename = "$value")] + inner: String, + + #[serde(rename = "@src")] + links_to: Option, +} +impl Display for Word { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if let Some(part_of_speech) = &self.part_of_speech { + writeln!(f, "{color_magenta}Part of speech{style_reset}: {}", part_of_speech)?; + } + + if let Some(i) = &self.transliteration { + writeln!(f, "{color_green}Transliteration{style_reset}: {}", i)?; + } + + if let Some(i) = &self.lang { + writeln!(f, "{color_red}Language{style_reset}: {}", i)?; + } + + writeln!(f, "{color_bright_cyan}Writen{style_reset}: {}", self.inner)?; + + if let Some(pron) = &self.pronunciation { + writeln!(f, "{color_yellow}Pronounciation{style_reset}: {}", pron)?; + } + + if let Some(i) = &self.links_to { + writeln!(f, "{color_bright_green}Also see{style_reset}: {}", i)?; + } + + Ok(()) + } +} + +#[derive(Deserialize, Debug)] +struct Note { + #[serde(rename = "$value")] + inner: String, +} +impl Display for Note { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.inner) + } +} + +#[derive(Deserialize, Debug)] +struct Source { + #[serde(rename = "$value")] + // Text vs Words make this a vec + inner: Option>, +} +impl Display for Source { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if let Some(inner) = &self.inner { + // WE ARE LOOPING + // !! Don't use writeln + write!(f, "{color_bright_green}Source{style_reset}: ")?; + for i in inner { + match i { + MixedText::Word(word) => { + if let Some(i) = &word.links_to { + write!(f, "{style_bold} (See: {}){style_reset}", i)?; + } + write!(f, "{color_bright_white} {} {style_reset}", word.inner)?; + }, + _ => write!(f, "{}", i)?, + } + } + } + Ok(()) + } +} + +#[derive(Deserialize, Debug)] +struct Meaning { + #[serde(rename = "$value")] + inner: Vec, +} +impl Display for Meaning { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{color_cyan}Meaning:{style_reset}")?; + for i in &self.inner { + write!(f, "{}", i)?; + } + Ok(()) + } +} + +#[derive(Deserialize, Debug)] +struct Usage { + #[serde(rename = "$value")] + inner: Vec, +} +impl Display for Usage { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let new = self.inner.join(" "); + write!(f, "{color_bright_magenta}Usage{style_reset}: {new}") + } +} + +#[derive(Deserialize, Debug)] +enum MixedText { + #[serde(rename = "w")] + Word(Word), + #[serde(rename = "$text")] + Text(String), + #[serde(rename = "def")] + /// The actual definition, as opposed to the supporting `MixedText::Text`, which is just + /// supporting text elements. + Definition(String), + #[serde(rename = "note")] + Note(Note), +} +impl Display for MixedText { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + MixedText::Word(word) => write!(f, "{}", word), + MixedText::Text(text) => write!(f, "{}", text), + MixedText::Definition(def) => write!(f, " {style_bold}{}{style_reset} ", def), + MixedText::Note(note) => write!(f, "{}", note), + } + } +} +