use std::cmp::Ordering;
use std::fmt;
use std::sync::Arc;
use crate::error_chirho::ResultChirho;
use crate::keys_chirho::sw_key_chirho::{KeyErrorChirho, PositionChirho, SwKeyChirho};
use crate::versification_chirho::{
kjv_chirho, TestamentChirho, VersificationChirho,
};
#[derive(Clone)]
pub struct VerseKeyChirho {
text_chirho: String,
testament_chirho: u8,
book_chirho: u8,
chapter_chirho: u16,
verse_chirho: u16,
suffix_chirho: Option<char>,
v11n_chirho: Arc<VersificationChirho>,
error_chirho: KeyErrorChirho,
auto_normalize_chirho: bool,
intros_chirho: bool,
lower_bound_chirho: Option<Box<VerseKeyChirho>>,
upper_bound_chirho: Option<Box<VerseKeyChirho>>,
}
impl VerseKeyChirho {
pub fn new_chirho() -> Self {
Self::with_versification_chirho(Arc::new(kjv_chirho().clone()))
}
pub fn with_versification_chirho(v11n_chirho: Arc<VersificationChirho>) -> Self {
let mut key_chirho = Self {
text_chirho: String::new(),
testament_chirho: 1,
book_chirho: 0,
chapter_chirho: 1,
verse_chirho: 1,
suffix_chirho: None,
v11n_chirho,
error_chirho: KeyErrorChirho::NoneChirho,
auto_normalize_chirho: true,
intros_chirho: false,
lower_bound_chirho: None,
upper_bound_chirho: None,
};
key_chirho.refresh_text_chirho();
key_chirho
}
pub fn from_str_chirho(text_chirho: &str) -> ResultChirho<Self> {
let mut key_chirho = Self::new_chirho();
key_chirho.parse_chirho(text_chirho)?;
Ok(key_chirho)
}
pub fn parse_chirho(&mut self, text_chirho: &str) -> ResultChirho<()> {
self.error_chirho = KeyErrorChirho::NoneChirho;
let text_chirho = text_chirho.trim();
if text_chirho.is_empty() {
return Ok(());
}
let (book_part_chirho, ref_part_chirho) = self.split_book_ref_chirho(text_chirho);
if let Some((testament_chirho, book_idx_chirho)) = self.v11n_chirho.lookup_book_chirho(book_part_chirho) {
self.testament_chirho = match testament_chirho {
TestamentChirho::OldChirho => 1,
TestamentChirho::NewChirho => 2,
};
self.book_chirho = book_idx_chirho as u8;
if let Some(ref_chirho) = ref_part_chirho {
self.parse_chapter_verse_chirho(ref_chirho);
} else {
self.chapter_chirho = 1;
self.verse_chirho = 1;
}
if self.auto_normalize_chirho {
self.normalize_chirho();
}
self.refresh_text_chirho();
Ok(())
} else {
self.error_chirho = KeyErrorChirho::ParseErrorChirho;
Err(crate::error_chirho::ErrorChirho::invalid_verse_reference_chirho(text_chirho))
}
}
fn split_book_ref_chirho<'a>(&self, text_chirho: &'a str) -> (&'a str, Option<&'a str>) {
let bytes_chirho = text_chirho.as_bytes();
let mut book_end_chirho = 0;
let mut found_alpha_chirho = false;
for (i_chirho, &ch_chirho) in bytes_chirho.iter().enumerate() {
if ch_chirho.is_ascii_alphabetic() {
found_alpha_chirho = true;
}
if found_alpha_chirho && ch_chirho.is_ascii_digit() {
book_end_chirho = i_chirho;
break;
}
if found_alpha_chirho && ch_chirho == b' ' {
let rest_chirho = &text_chirho[i_chirho..].trim_start();
if !rest_chirho.is_empty() && rest_chirho.chars().next().unwrap().is_ascii_digit() {
book_end_chirho = i_chirho;
break;
}
}
}
if book_end_chirho == 0 {
(text_chirho, None)
} else {
let book_chirho = text_chirho[..book_end_chirho].trim();
let ref_chirho = text_chirho[book_end_chirho..].trim();
if ref_chirho.is_empty() {
(book_chirho, None)
} else {
(book_chirho, Some(ref_chirho))
}
}
}
fn parse_chapter_verse_chirho(&mut self, ref_chirho: &str) {
let ref_chirho = ref_chirho.trim();
if let Some(colon_pos_chirho) = ref_chirho.find(':') {
if let Ok(ch_chirho) = ref_chirho[..colon_pos_chirho].trim().parse::<u16>() {
self.chapter_chirho = ch_chirho;
}
let verse_part_chirho = &ref_chirho[colon_pos_chirho + 1..].trim();
self.parse_verse_with_suffix_chirho(verse_part_chirho);
} else {
if let Ok(ch_chirho) = ref_chirho.parse::<u16>() {
self.chapter_chirho = ch_chirho;
self.verse_chirho = 1;
}
}
}
fn parse_verse_with_suffix_chirho(&mut self, verse_str_chirho: &str) {
let verse_str_chirho = verse_str_chirho.trim();
if verse_str_chirho.len() >= 2 {
let last_char_chirho = verse_str_chirho.chars().last().unwrap();
if last_char_chirho.is_ascii_lowercase() {
let num_part_chirho = &verse_str_chirho[..verse_str_chirho.len() - 1];
if let Ok(v_chirho) = num_part_chirho.parse::<u16>() {
self.verse_chirho = v_chirho;
self.suffix_chirho = Some(last_char_chirho);
return;
}
}
}
if let Ok(v_chirho) = verse_str_chirho.parse::<u16>() {
self.verse_chirho = v_chirho;
}
self.suffix_chirho = None;
}
fn refresh_text_chirho(&mut self) {
let testament_chirho = if self.testament_chirho == 1 {
TestamentChirho::OldChirho
} else {
TestamentChirho::NewChirho
};
if let Some(book_chirho) = self.v11n_chirho.get_book_chirho(testament_chirho, self.book_chirho as usize) {
self.text_chirho = if let Some(suffix_chirho) = self.suffix_chirho {
format!(
"{} {}:{}{}",
book_chirho.name_chirho, self.chapter_chirho, self.verse_chirho, suffix_chirho
)
} else {
format!(
"{} {}:{}",
book_chirho.name_chirho, self.chapter_chirho, self.verse_chirho
)
};
} else {
self.text_chirho = format!(
"Unknown {}:{}",
self.chapter_chirho, self.verse_chirho
);
}
}
pub fn normalize_chirho(&mut self) {
let testament_chirho = if self.testament_chirho == 1 {
TestamentChirho::OldChirho
} else {
TestamentChirho::NewChirho
};
self.testament_chirho = self.testament_chirho.clamp(1, 2);
let max_book_chirho = self.v11n_chirho.book_count_chirho(testament_chirho);
if self.book_chirho as usize >= max_book_chirho {
if self.testament_chirho == 1 && !self.v11n_chirho.nt_books_chirho.is_empty() {
self.testament_chirho = 2;
self.book_chirho = 0;
} else {
self.book_chirho = (max_book_chirho - 1) as u8;
self.error_chirho = KeyErrorChirho::OutOfBoundsChirho;
}
}
let testament_chirho = if self.testament_chirho == 1 {
TestamentChirho::OldChirho
} else {
TestamentChirho::NewChirho
};
if let Some(book_info_chirho) = self.v11n_chirho.get_book_chirho(testament_chirho, self.book_chirho as usize) {
if self.chapter_chirho == 0 && !self.intros_chirho {
self.chapter_chirho = 1;
}
if self.chapter_chirho > book_info_chirho.chapter_count_chirho as u16 {
self.chapter_chirho = book_info_chirho.chapter_count_chirho as u16;
self.verse_chirho = book_info_chirho.max_verse_chirho(self.chapter_chirho as u8).unwrap_or(1) as u16;
}
if self.verse_chirho == 0 && !self.intros_chirho && self.chapter_chirho > 0 {
self.verse_chirho = 1;
}
if self.chapter_chirho > 0 {
if let Some(max_verse_chirho) = book_info_chirho.max_verse_chirho(self.chapter_chirho as u8) {
if self.verse_chirho > max_verse_chirho as u16 {
self.verse_chirho = max_verse_chirho as u16;
}
}
}
}
}
pub fn get_testament_chirho(&self) -> u8 {
self.testament_chirho
}
pub fn set_testament_chirho(&mut self, testament_chirho: u8) {
self.testament_chirho = testament_chirho;
if self.auto_normalize_chirho {
self.normalize_chirho();
}
self.refresh_text_chirho();
}
pub fn get_book_chirho(&self) -> u8 {
self.book_chirho
}
pub fn set_book_chirho(&mut self, book_chirho: u8) {
self.book_chirho = book_chirho;
if self.auto_normalize_chirho {
self.normalize_chirho();
}
self.refresh_text_chirho();
}
pub fn get_chapter_chirho(&self) -> u16 {
self.chapter_chirho
}
pub fn set_chapter_chirho(&mut self, chapter_chirho: u16) {
self.chapter_chirho = chapter_chirho;
if self.auto_normalize_chirho {
self.normalize_chirho();
}
self.refresh_text_chirho();
}
pub fn get_verse_chirho(&self) -> u16 {
self.verse_chirho
}
pub fn set_verse_chirho(&mut self, verse_chirho: u16) {
self.verse_chirho = verse_chirho;
if self.auto_normalize_chirho {
self.normalize_chirho();
}
self.refresh_text_chirho();
}
pub fn get_suffix_chirho(&self) -> Option<char> {
self.suffix_chirho
}
pub fn set_suffix_chirho(&mut self, suffix_chirho: Option<char>) {
self.suffix_chirho = suffix_chirho;
self.refresh_text_chirho();
}
pub fn get_book_name_chirho(&self) -> &str {
let testament_chirho = if self.testament_chirho == 1 {
TestamentChirho::OldChirho
} else {
TestamentChirho::NewChirho
};
self.v11n_chirho
.get_book_chirho(testament_chirho, self.book_chirho as usize)
.map(|b| b.name_chirho.as_str())
.unwrap_or("Unknown")
}
pub fn get_book_osis_chirho(&self) -> &str {
let testament_chirho = if self.testament_chirho == 1 {
TestamentChirho::OldChirho
} else {
TestamentChirho::NewChirho
};
self.v11n_chirho
.get_book_chirho(testament_chirho, self.book_chirho as usize)
.map(|b| b.osis_chirho.as_str())
.unwrap_or("Unknown")
}
pub fn get_chapter_max_chirho(&self) -> u16 {
let testament_chirho = if self.testament_chirho == 1 {
TestamentChirho::OldChirho
} else {
TestamentChirho::NewChirho
};
self.v11n_chirho
.get_book_chirho(testament_chirho, self.book_chirho as usize)
.map(|b| b.chapter_count_chirho as u16)
.unwrap_or(1)
}
pub fn get_verse_max_chirho(&self) -> u16 {
let testament_chirho = if self.testament_chirho == 1 {
TestamentChirho::OldChirho
} else {
TestamentChirho::NewChirho
};
self.v11n_chirho
.get_book_chirho(testament_chirho, self.book_chirho as usize)
.and_then(|b| b.max_verse_chirho(self.chapter_chirho as u8))
.map(|v| v as u16)
.unwrap_or(1)
}
pub fn set_auto_normalize_chirho(&mut self, auto_chirho: bool) {
self.auto_normalize_chirho = auto_chirho;
}
pub fn is_auto_normalize_chirho(&self) -> bool {
self.auto_normalize_chirho
}
pub fn set_intros_chirho(&mut self, intros_chirho: bool) {
self.intros_chirho = intros_chirho;
}
pub fn is_intros_chirho(&self) -> bool {
self.intros_chirho
}
pub fn get_versification_chirho(&self) -> &VersificationChirho {
&self.v11n_chirho
}
pub fn set_versification_chirho(&mut self, v11n_chirho: &'static VersificationChirho) {
self.v11n_chirho = Arc::new(v11n_chirho.clone());
if self.auto_normalize_chirho {
self.normalize_chirho();
}
self.refresh_text_chirho();
}
pub fn testament_enum_chirho(&self) -> TestamentChirho {
if self.testament_chirho == 1 {
TestamentChirho::OldChirho
} else {
TestamentChirho::NewChirho
}
}
pub fn calculate_index_chirho(&self) -> i64 {
let testament_chirho = if self.testament_chirho == 1 {
TestamentChirho::OldChirho
} else {
TestamentChirho::NewChirho
};
let testament_offset_chirho = if self.testament_chirho == 2 {
self.v11n_chirho.testament_size_chirho(TestamentChirho::OldChirho) as i64
} else {
0
};
let verse_offset_chirho = self.v11n_chirho.calculate_index_chirho(
testament_chirho,
self.book_chirho as usize,
self.chapter_chirho as u8,
self.verse_chirho as u8,
).unwrap_or(0) as i64;
testament_offset_chirho + verse_offset_chirho
}
pub fn set_lower_bound_chirho(&mut self, bound_chirho: &VerseKeyChirho) {
self.lower_bound_chirho = Some(Box::new(bound_chirho.clone()));
}
pub fn set_upper_bound_chirho(&mut self, bound_chirho: &VerseKeyChirho) {
self.upper_bound_chirho = Some(Box::new(bound_chirho.clone()));
}
pub fn clear_bounds_chirho(&mut self) {
self.lower_bound_chirho = None;
self.upper_bound_chirho = None;
}
}
impl fmt::Display for VerseKeyChirho {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.text_chirho)
}
}
impl fmt::Debug for VerseKeyChirho {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("VerseKeyChirho")
.field("text_chirho", &self.text_chirho)
.field("testament_chirho", &self.testament_chirho)
.field("book_chirho", &self.book_chirho)
.field("chapter_chirho", &self.chapter_chirho)
.field("verse_chirho", &self.verse_chirho)
.field("suffix_chirho", &self.suffix_chirho)
.finish()
}
}
impl SwKeyChirho for VerseKeyChirho {
fn get_text_chirho(&self) -> &str {
&self.text_chirho
}
fn set_text_chirho(&mut self, text_chirho: &str) {
let _ = self.parse_chirho(text_chirho);
}
fn get_short_text_chirho(&self) -> String {
format!(
"{} {}:{}",
self.get_book_osis_chirho(),
self.chapter_chirho,
self.verse_chirho
)
}
fn get_osis_ref_chirho(&self) -> String {
format!(
"{}.{}.{}",
self.get_book_osis_chirho(),
self.chapter_chirho,
self.verse_chirho
)
}
fn pop_error_chirho(&mut self) -> KeyErrorChirho {
let error_chirho = self.error_chirho;
self.error_chirho = KeyErrorChirho::NoneChirho;
error_chirho
}
fn has_error_chirho(&self) -> bool {
self.error_chirho.is_error_chirho()
}
fn clone_key_chirho(&self) -> Box<dyn SwKeyChirho> {
Box::new(self.clone())
}
fn get_index_chirho(&self) -> i64 {
self.calculate_index_chirho()
}
fn set_index_chirho(&mut self, _index_chirho: i64) {
}
fn increment_chirho(&mut self, steps_chirho: i32) {
for _ in 0..steps_chirho.abs() {
if steps_chirho > 0 {
self.verse_chirho += 1;
if self.verse_chirho > self.get_verse_max_chirho() {
self.verse_chirho = 1;
self.chapter_chirho += 1;
if self.chapter_chirho > self.get_chapter_max_chirho() {
self.chapter_chirho = 1;
self.book_chirho += 1;
let testament_chirho = if self.testament_chirho == 1 {
TestamentChirho::OldChirho
} else {
TestamentChirho::NewChirho
};
let max_book_chirho = self.v11n_chirho.book_count_chirho(testament_chirho);
if self.book_chirho as usize >= max_book_chirho {
if self.testament_chirho == 1 {
self.testament_chirho = 2;
self.book_chirho = 0;
} else {
self.book_chirho = (max_book_chirho - 1) as u8;
self.chapter_chirho = self.get_chapter_max_chirho();
self.verse_chirho = self.get_verse_max_chirho();
self.error_chirho = KeyErrorChirho::OutOfBoundsChirho;
}
}
}
}
} else {
self.decrement_chirho(1);
}
}
if let Some(upper_chirho) = &self.upper_bound_chirho {
if self.calculate_index_chirho() > upper_chirho.calculate_index_chirho() {
self.error_chirho = KeyErrorChirho::OutOfBoundsChirho;
}
}
self.refresh_text_chirho();
}
fn decrement_chirho(&mut self, steps_chirho: i32) {
for _ in 0..steps_chirho.abs() {
if self.verse_chirho > 1 {
self.verse_chirho -= 1;
} else if self.chapter_chirho > 1 {
self.chapter_chirho -= 1;
self.verse_chirho = self.get_verse_max_chirho();
} else if self.book_chirho > 0 {
self.book_chirho -= 1;
self.chapter_chirho = self.get_chapter_max_chirho();
self.verse_chirho = self.get_verse_max_chirho();
} else if self.testament_chirho == 2 {
self.testament_chirho = 1;
self.book_chirho = (self.v11n_chirho.book_count_chirho(TestamentChirho::OldChirho) - 1) as u8;
self.chapter_chirho = self.get_chapter_max_chirho();
self.verse_chirho = self.get_verse_max_chirho();
} else {
self.error_chirho = KeyErrorChirho::OutOfBoundsChirho;
}
}
if let Some(lower_chirho) = &self.lower_bound_chirho {
if self.calculate_index_chirho() < lower_chirho.calculate_index_chirho() {
self.error_chirho = KeyErrorChirho::OutOfBoundsChirho;
}
}
self.refresh_text_chirho();
}
fn set_position_chirho(&mut self, position_chirho: PositionChirho) {
match position_chirho {
PositionChirho::TopChirho => {
if let Some(lower_chirho) = &self.lower_bound_chirho {
self.testament_chirho = lower_chirho.testament_chirho;
self.book_chirho = lower_chirho.book_chirho;
self.chapter_chirho = lower_chirho.chapter_chirho;
self.verse_chirho = lower_chirho.verse_chirho;
} else {
self.testament_chirho = 1;
self.book_chirho = 0;
self.chapter_chirho = 1;
self.verse_chirho = 1;
}
}
PositionChirho::BottomChirho => {
if let Some(upper_chirho) = &self.upper_bound_chirho {
self.testament_chirho = upper_chirho.testament_chirho;
self.book_chirho = upper_chirho.book_chirho;
self.chapter_chirho = upper_chirho.chapter_chirho;
self.verse_chirho = upper_chirho.verse_chirho;
} else {
self.testament_chirho = 2;
let max_book_chirho = self.v11n_chirho.book_count_chirho(TestamentChirho::NewChirho);
self.book_chirho = (max_book_chirho - 1) as u8;
self.chapter_chirho = self.get_chapter_max_chirho();
self.verse_chirho = self.get_verse_max_chirho();
}
}
}
self.error_chirho = KeyErrorChirho::NoneChirho;
self.refresh_text_chirho();
}
fn compare_chirho(&self, other_chirho: &dyn SwKeyChirho) -> Ordering {
self.get_index_chirho().cmp(&other_chirho.get_index_chirho())
}
}
#[cfg(test)]
mod tests_chirho {
use super::*;
#[test]
fn test_new_verse_key_chirho() {
let key_chirho = VerseKeyChirho::new_chirho();
assert_eq!(key_chirho.get_text_chirho(), "Genesis 1:1");
assert_eq!(key_chirho.get_testament_chirho(), 1);
assert_eq!(key_chirho.get_book_chirho(), 0);
assert_eq!(key_chirho.get_chapter_chirho(), 1);
assert_eq!(key_chirho.get_verse_chirho(), 1);
}
#[test]
fn test_parse_simple_reference_chirho() {
let key_chirho = VerseKeyChirho::from_str_chirho("John 3:16").unwrap();
assert_eq!(key_chirho.get_book_name_chirho(), "John");
assert_eq!(key_chirho.get_chapter_chirho(), 3);
assert_eq!(key_chirho.get_verse_chirho(), 16);
assert_eq!(key_chirho.get_testament_chirho(), 2);
}
#[test]
fn test_parse_ot_reference_chirho() {
let key_chirho = VerseKeyChirho::from_str_chirho("Genesis 1:1").unwrap();
assert_eq!(key_chirho.get_book_name_chirho(), "Genesis");
assert_eq!(key_chirho.get_chapter_chirho(), 1);
assert_eq!(key_chirho.get_verse_chirho(), 1);
assert_eq!(key_chirho.get_testament_chirho(), 1);
}
#[test]
fn test_parse_numbered_book_chirho() {
let key_chirho = VerseKeyChirho::from_str_chirho("1 John 1:1").unwrap();
assert_eq!(key_chirho.get_book_name_chirho(), "I John");
assert_eq!(key_chirho.get_chapter_chirho(), 1);
assert_eq!(key_chirho.get_verse_chirho(), 1);
}
#[test]
fn test_osis_ref_chirho() {
let key_chirho = VerseKeyChirho::from_str_chirho("John 3:16").unwrap();
assert_eq!(key_chirho.get_osis_ref_chirho(), "John.3.16");
}
#[test]
fn test_increment_chirho() {
let mut key_chirho = VerseKeyChirho::from_str_chirho("Genesis 1:31").unwrap();
key_chirho.increment_chirho(1);
assert_eq!(key_chirho.get_chapter_chirho(), 2);
assert_eq!(key_chirho.get_verse_chirho(), 1);
}
#[test]
fn test_decrement_chirho() {
let mut key_chirho = VerseKeyChirho::from_str_chirho("Genesis 2:1").unwrap();
key_chirho.decrement_chirho(1);
assert_eq!(key_chirho.get_chapter_chirho(), 1);
assert_eq!(key_chirho.get_verse_chirho(), 31); }
#[test]
fn test_set_position_chirho() {
let mut key_chirho = VerseKeyChirho::new_chirho();
key_chirho.set_position_chirho(PositionChirho::BottomChirho);
assert_eq!(key_chirho.get_book_name_chirho(), "Revelation of John");
assert_eq!(key_chirho.get_chapter_chirho(), 22);
assert_eq!(key_chirho.get_verse_chirho(), 21);
key_chirho.set_position_chirho(PositionChirho::TopChirho);
assert_eq!(key_chirho.get_book_name_chirho(), "Genesis");
assert_eq!(key_chirho.get_chapter_chirho(), 1);
assert_eq!(key_chirho.get_verse_chirho(), 1);
}
#[test]
fn test_verse_max_chirho() {
let key_chirho = VerseKeyChirho::from_str_chirho("Psalms 119:1").unwrap();
assert_eq!(key_chirho.get_verse_max_chirho(), 176);
}
}