From c8dd1ce0f06c1b6edebece6adb5cba4d4f2edf46 Mon Sep 17 00:00:00 2001 From: Greg Hellings Date: Wed, 25 Jun 2025 02:30:42 -0500 Subject: [PATCH] With a little help, we now have VerseKey Went with a basic wrapper that cxx is able to handle for the bridge between the languages, along with simple wrapper methods --- build.rs | 18 +- src/cxx/key.cc | 35 ++++ src/cxx/key.h | 21 +++ src/cxx/mod.rs | 160 ++++++++++++++++++ src/cxx/module.cc | 10 ++ src/cxx/module.h | 4 + src/cxx/versekey.cc | 187 +++++++++++++++++++++ src/cxx/versekey.h | 64 +++++++ src/lib.rs | 1 + src/main.rs | 19 +++ src/module.rs | 12 ++ src/versekey.rs | 398 ++++++++++++++++++++++++++++++++++++++++++++ 12 files changed, 919 insertions(+), 10 deletions(-) create mode 100644 src/cxx/key.cc create mode 100644 src/cxx/key.h create mode 100644 src/cxx/versekey.cc create mode 100644 src/cxx/versekey.h create mode 100644 src/versekey.rs diff --git a/build.rs b/build.rs index e4908de..46a1fc1 100644 --- a/build.rs +++ b/build.rs @@ -1,23 +1,21 @@ use system_deps; fn main() { + let sources = vec!["key", "mgr", "module", "versekey", "versification"]; let deps = system_deps::Config::new().probe().unwrap(); - cxx_build::bridge("src/cxx/mod.rs") - .file("src/cxx/mgr.cc") - .file("src/cxx/module.cc") - .file("src/cxx/versification.cc") + let mut build = cxx_build::bridge("src/cxx/mod.rs"); + for src in sources { + build.file(&format!("src/cxx/{}.cc", src)); + println!("cargo:rerun-if-changed=src/cxx/{}.cc", src); + println!("cargo:rerun-if-changed=src/cxx/{}.h", src); + } + build .includes(deps.all_include_paths()) .cpp_link_stdlib("stdc++") .flag("-Os") .compile("sword_rs"); println!("cargo:rerun-if-changed=src/cxx/mod.rs"); - println!("cargo:rerun-if-changed=src/cxx/mgr.cc"); - println!("cargo:rerun-if-changed=src/cxx/mgr.h"); - println!("cargo:rerun-if-changed=src/cxx/module.cc"); - println!("cargo:rerun-if-changed=src/cxx/module.h"); - println!("cargo:rerun-if-changed=src/cxx/versification.h"); - println!("cargo:rerun-if-changed=src/cxx/versification.cc"); println!("cargo:rustc-link-lib=sword"); } diff --git a/src/cxx/key.cc b/src/cxx/key.cc new file mode 100644 index 0000000..9bd7128 --- /dev/null +++ b/src/cxx/key.cc @@ -0,0 +1,35 @@ +#include "key.h" + +namespace sword_rs { + uint8_t Key::get_error() const { + return this->getError(); + } + + rust::String Key::get_locale() const { + return rust::String(this->getLocale()); + } + + rust::String Key::get_range_text() const { + return rust::String(this->getRangeText()); + } + + rust::String Key::get_short_text() const { + return rust::String(this->getShortText()); + } + + rust::String Key::get_text() const { + return rust::String(this->getText()); + } + + uint8_t Key::pop_error() { + return this->popError(); + } + + void Key::set_locale(const rust::String &locale) { + this->setLocale(std::string(locale).c_str()); + } + + void Key::set_text(const rust::String &text) { + this->setText(std::string(text).c_str()); + } +} diff --git a/src/cxx/key.h b/src/cxx/key.h new file mode 100644 index 0000000..87d6a07 --- /dev/null +++ b/src/cxx/key.h @@ -0,0 +1,21 @@ +#pragma once + +#include + +#include "rust/cxx.h" + +namespace sword_rs { + class Key : public sword::SWKey { + public: + uint8_t get_error() const; + rust::String get_locale() const; + rust::String get_range_text() const; + rust::String get_short_text() const; + rust::String get_text() const; + + uint8_t pop_error(); + + void set_locale(const rust::String &locale); + void set_text(const rust::String &text); + }; +} diff --git a/src/cxx/mod.rs b/src/cxx/mod.rs index 19c425e..e23ca56 100644 --- a/src/cxx/mod.rs +++ b/src/cxx/mod.rs @@ -4,6 +4,7 @@ pub mod ffi { include!("sword_rs/src/cxx/mgr.h"); include!("sword_rs/src/cxx/module.h"); include!("sword_rs/src/cxx/versification.h"); + include!("sword_rs/src/cxx/versekey.h"); type Mgr; @@ -18,6 +19,7 @@ pub mod ffi { fn decrement(self: Pin<&mut Module>); fn get_name(self: &Module) -> String; fn get_type(self: &Module) -> String; + fn get_verse_key(self: &Module) -> *mut VerseKey; fn increment(self: Pin<&mut Module>); fn is_skip_consecutive_links(self: &Module) -> bool; fn render_text(self: Pin<&mut Module>) -> String; @@ -31,6 +33,56 @@ pub mod ffi { fn getVersifications(self: &Versification) -> Vec; fn getVersification(self: &Versification, name: &mut String) -> *const VersificationSystem; fn new_versification() -> UniquePtr; + + type VerseKey; + // Factory functions for VerseKey + fn new_verse_key() -> UniquePtr; + fn new_verse_key_from_text(text: &String) -> UniquePtr; + unsafe fn delete_verse_key(key: *mut VerseKey); + + // From Key + fn get_error(self: &VerseKey) -> u8; + fn get_locale(self: &VerseKey) -> String; + fn get_range_text(self: &VerseKey) -> String; + fn get_short_text(self: &VerseKey) -> String; + fn get_text(self: &VerseKey) -> String; + fn normalize(self: Pin<&mut VerseKey>, autocheck: bool); + // From VerseKey + fn get_book_name(self: &VerseKey) -> String; + fn get_book_abbrev(self: &VerseKey) -> String; + fn get_osis_book_name(self: &VerseKey) -> String; + fn get_osis_ref(self: &VerseKey) -> String; + fn get_osis_ref_range_text(self: &VerseKey) -> String; + fn get_short_range_text(self: &VerseKey) -> String; + fn get_testament(self: &VerseKey) -> u8; + fn get_testament_max(self: &VerseKey) -> u8; + fn get_book(self: &VerseKey) -> u8; + fn get_book_max(self: &VerseKey) -> u8; + fn get_chapter(self: &VerseKey) -> u8; + fn get_chapter_max(self: &VerseKey) -> u8; + fn get_verse(self: &VerseKey) -> u8; + fn get_verse_max(self: &VerseKey) -> u8; + + // From VerseKey + fn is_autonormalize(self: &VerseKey) -> bool; + fn is_intros(self: &VerseKey) -> bool; + // From Key + fn pop_error(self: Pin<&mut VerseKey>) -> u8; + + // From Key + fn set_locale(self: Pin<&mut VerseKey>, locale: &String); + fn set_text(self: Pin<&mut VerseKey>, text: &String); + // From VerseKey + fn set_autonormalize(self: Pin<&mut VerseKey>, autoNormalize: bool); + fn set_book_name(self: Pin<&mut VerseKey>, bookName: &String); + fn set_intros(self: Pin<&mut VerseKey>, intros: bool); + fn set_testament(self: Pin<&mut VerseKey>, testament: u8); + fn set_book(self: Pin<&mut VerseKey>, book: u8); + fn set_chapter(self: Pin<&mut VerseKey>, chapter: u8); + fn set_verse(self: Pin<&mut VerseKey>, verse: u8); + + // Explicit cleanup + fn cleanup(self: Pin<&mut VerseKey>); } } @@ -92,4 +144,112 @@ mod tests { ); assert!(mgr.get_modules().len() == 0); } + + #[test] + fn can_create_verse_key() { + let verse_key = ffi::new_verse_key(); + assert!(!verse_key.is_null()); + + // Test that we can call methods without errors + let _testament = verse_key.get_testament(); + let _book = verse_key.get_book(); + let _chapter = verse_key.get_chapter(); + let _verse = verse_key.get_verse(); + let _book_name = verse_key.get_book_name(); + let _text = verse_key.get_text(); + } + + #[test] + fn can_create_verse_key_from_text() { + let verse_key = ffi::new_verse_key_from_text(&"Genesis 1:1".to_string()); + assert!(!verse_key.is_null()); + + // Test that we can get values without errors + let _chapter = verse_key.get_chapter(); + let _verse = verse_key.get_verse(); + let _text = verse_key.get_text(); + } + + #[test] + fn verse_key_can_be_modified() { + let mut verse_key = ffi::new_verse_key(); + + // Test that we can call setter methods without panicking + verse_key.pin_mut().set_chapter(5); + verse_key.pin_mut().set_verse(10); + verse_key.pin_mut().set_text(&"Genesis 1:1".to_string()); + + // Test that we can call getter methods after setting + let _chapter = verse_key.get_chapter(); + let _verse = verse_key.get_verse(); + let _text = verse_key.get_text(); + } + + #[test] + fn high_level_verse_key_api() { + use crate::versekey::VerseKey; + + // Test owned VerseKey creation and usage + let mut verse_key = VerseKey::new(); + + // Test that we can call methods without panicking + verse_key.set_chapter(3); + verse_key.set_verse(16); + verse_key.set_text("John 3:16"); + + // Test getters + let _book_name = verse_key.get_book_name(); + let _text = verse_key.get_text(); + let _chapter = verse_key.get_chapter(); + let _verse = verse_key.get_verse(); + + // Test creation from text + let verse_key2 = VerseKey::from_text("Romans 8:28"); + let _text2 = verse_key2.get_text(); + } + + #[test] + fn borrowed_verse_key_from_module() { + use std::fs::{create_dir, File}; + use std::io::Write; + use tempfile; + + // Create a temporary directory with a module + let dir = tempfile::tempdir().unwrap(); + let mods_d = dir.path().join("mods.d"); + create_dir(&mods_d).unwrap(); + + // Write a dummy conf file + let kjv_conf = mods_d.join("kjv.conf"); + { + let mut file = File::create(&kjv_conf).unwrap(); + writeln!(file, "[KJVdummy]").unwrap(); + writeln!(file, "DataPath=./modules/texts/ztext/kjv").unwrap(); + writeln!(file, "ModDrv=RawText").unwrap(); + writeln!(file, "Description=Test description").unwrap(); + writeln!(file, "About=A test conf file").unwrap(); + } + + // Create manager and get module + let mut mgr = ffi::new_mgr_with_path(&dir.path().to_str().unwrap().to_string()); + let mut module = mgr.pin_mut().get_module(&String::from("KJVdummy")); + + // Test that we can get a VerseKey from the module + let verse_key_ptr = module.pin_mut().get_verse_key(); + if !verse_key_ptr.is_null() { + // Test that we can create a BorrowedVerseKey and use it + let borrowed_verse_key = + unsafe { crate::versekey::BorrowedVerseKey::from_ptr(verse_key_ptr) }; + + if let Some(mut borrowed_vk) = borrowed_verse_key { + // Test that we can call methods without panicking + let _book_name = borrowed_vk.get_book_name(); + let _text = borrowed_vk.get_text(); + borrowed_vk.set_chapter(1); + borrowed_vk.set_verse(1); + let _chapter = borrowed_vk.get_chapter(); + let _verse = borrowed_vk.get_verse(); + } + } + } } diff --git a/src/cxx/module.cc b/src/cxx/module.cc index 57acc37..15fc4b0 100644 --- a/src/cxx/module.cc +++ b/src/cxx/module.cc @@ -15,6 +15,16 @@ namespace sword_rs { return rust::String(this->module->getType()); } + VerseKey* Module::get_verse_key() const { + sword::VerseKey* key = dynamic_cast(this->module->getKey()); + if (key) { + // Create a borrowed VerseKey on the heap - caller must delete but not the inner ptr + VerseKey* result = new VerseKey{key, false}; + return result; + } + return nullptr; + } + void Module::increment() { this->module->increment(); } diff --git a/src/cxx/module.h b/src/cxx/module.h index b3e383a..a7e3100 100644 --- a/src/cxx/module.h +++ b/src/cxx/module.h @@ -1,9 +1,11 @@ #pragma once +#include #include #include #include "rust/cxx.h" +#include "versekey.h" namespace sword_rs { class Module { @@ -19,6 +21,8 @@ namespace sword_rs { rust::String get_type() const; + VerseKey* get_verse_key() const; + void increment(); bool is_skip_consecutive_links() const; diff --git a/src/cxx/versekey.cc b/src/cxx/versekey.cc new file mode 100644 index 0000000..293e2b2 --- /dev/null +++ b/src/cxx/versekey.cc @@ -0,0 +1,187 @@ +#include "versekey.h" +#include +#include + +namespace sword_rs { + // Key methods + uint8_t VerseKey::get_error() const { + return ptr ? ptr->getError() : 0; + } + + rust::String VerseKey::get_locale() const { + return ptr ? rust::String(ptr->getLocale()) : rust::String(""); + } + + rust::String VerseKey::get_range_text() const { + return ptr ? rust::String(ptr->getRangeText()) : rust::String(""); + } + + rust::String VerseKey::get_short_text() const { + return ptr ? rust::String(ptr->getShortText()) : rust::String(""); + } + + rust::String VerseKey::get_text() const { + return ptr ? rust::String(ptr->getText()) : rust::String(""); + } + + void VerseKey::normalize(bool autocheck) { + if (ptr) { + ptr->normalize(autocheck); + } + } + + uint8_t VerseKey::pop_error() { + return ptr ? ptr->popError() : 0; + } + + void VerseKey::set_locale(const rust::String &locale) { + if (ptr) { + ptr->setLocale(std::string(locale).c_str()); + } + } + + void VerseKey::set_text(const rust::String &text) { + if (ptr) { + ptr->setText(std::string(text).c_str()); + } + } + + // VerseKey specific methods + rust::String VerseKey::get_book_name() const { + return ptr ? rust::String(ptr->getBookName()) : rust::String(""); + } + + rust::String VerseKey::get_book_abbrev() const { + return ptr ? rust::String(ptr->getBookAbbrev()) : rust::String(""); + } + + rust::String VerseKey::get_osis_book_name() const { + return ptr ? rust::String(ptr->getOSISBookName()) : rust::String(""); + } + + rust::String VerseKey::get_osis_ref() const { + return ptr ? rust::String(ptr->getOSISRef()) : rust::String(""); + } + + rust::String VerseKey::get_osis_ref_range_text() const { + return ptr ? rust::String(ptr->getOSISRefRangeText()) : rust::String(""); + } + + rust::String VerseKey::get_short_range_text() const { + return ptr ? rust::String(ptr->getShortRangeText()) : rust::String(""); + } + + uint8_t VerseKey::get_testament() const { + return ptr ? ptr->getTestament() : 0; + } + + uint8_t VerseKey::get_testament_max() const { + return ptr ? ptr->getTestamentMax() : 0; + } + + uint8_t VerseKey::get_book() const { + return ptr ? ptr->getBook() : 0; + } + + uint8_t VerseKey::get_book_max() const { + return ptr ? ptr->getBookMax() : 0; + } + + uint8_t VerseKey::get_chapter() const { + return ptr ? ptr->getChapter() : 0; + } + + uint8_t VerseKey::get_chapter_max() const { + return ptr ? ptr->getChapterMax() : 0; + } + + uint8_t VerseKey::get_verse() const { + return ptr ? ptr->getVerse() : 0; + } + + uint8_t VerseKey::get_verse_max() const { + return ptr ? ptr->getVerseMax() : 0; + } + + bool VerseKey::is_autonormalize() const { + return ptr ? ptr->isAutoNormalize() : false; + } + + bool VerseKey::is_intros() const { + return ptr ? ptr->isIntros() : false; + } + + void VerseKey::set_autonormalize(bool autoNormalize) { + if (ptr) { + ptr->setAutoNormalize(autoNormalize); + } + } + + void VerseKey::set_book_name(const rust::String &bookName) { + if (ptr) { + ptr->setBookName(std::string(bookName).c_str()); + } + } + + void VerseKey::set_intros(bool intros) { + if (ptr) { + ptr->setIntros(intros); + } + } + + void VerseKey::set_testament(uint8_t testament) { + if (ptr) { + ptr->setTestament(testament); + } + } + + void VerseKey::set_book(uint8_t book) { + if (ptr) { + ptr->setBook(book); + } + } + + void VerseKey::set_chapter(uint8_t chapter) { + if (ptr) { + ptr->setChapter(chapter); + } + } + + void VerseKey::set_verse(uint8_t verse) { + if (ptr) { + ptr->setVerse(verse); + } + } + + // Explicit cleanup method + void VerseKey::cleanup() { + if (owned && ptr) { + delete ptr; + ptr = nullptr; + owned = false; + } + } + + // Factory functions + std::unique_ptr new_verse_key() { + auto verse_key = std::unique_ptr(new VerseKey{new sword::VerseKey(), true}); + return verse_key; + } + + std::unique_ptr new_verse_key_from_text(const rust::String& text) { + auto verse_key = std::unique_ptr(new VerseKey{new sword::VerseKey(std::string(text).c_str()), true}); + return verse_key; + } + + VerseKey verse_key_from_borrowed(sword::VerseKey* key) { + VerseKey verse_key{key, false}; + return verse_key; + } + + void delete_verse_key(VerseKey* key) { + if (key) { + key->cleanup(); + delete key; + } + } +} diff --git a/src/cxx/versekey.h b/src/cxx/versekey.h new file mode 100644 index 0000000..c2c4d24 --- /dev/null +++ b/src/cxx/versekey.h @@ -0,0 +1,64 @@ +#pragma once + +#include +#include +#include +#include "rust/cxx.h" + +namespace sword_rs { + // Trivial wrapper that just holds a pointer and ownership flag + // No destructor - cleanup is handled explicitly + struct VerseKey { + sword::VerseKey* ptr; + bool owned; + + // Key methods + uint8_t get_error() const; + rust::String get_locale() const; + rust::String get_range_text() const; + rust::String get_short_text() const; + rust::String get_text() const; + void normalize(bool autocheck); + uint8_t pop_error(); + void set_locale(const rust::String &locale); + void set_text(const rust::String &text); + + // VerseKey methods + rust::String get_book_name() const; + rust::String get_book_abbrev() const; + rust::String get_osis_book_name() const; + rust::String get_osis_ref() const; + rust::String get_osis_ref_range_text() const; + rust::String get_short_range_text() const; + uint8_t get_testament() const; + uint8_t get_testament_max() const; + uint8_t get_book() const; + uint8_t get_book_max() const; + uint8_t get_chapter() const; + uint8_t get_chapter_max() const; + uint8_t get_verse() const; + uint8_t get_verse_max() const; + + bool is_autonormalize() const; + bool is_intros() const; + + void set_autonormalize(bool autoNormalize); + void set_book_name(const rust::String &bookName); + void set_intros(bool intros); + void set_testament(uint8_t testament); + void set_book(uint8_t book); + void set_chapter(uint8_t chapter); + void set_verse(uint8_t verse); + + // Explicit cleanup method + void cleanup(); + }; + + // Factory functions for creating VerseKeys + std::unique_ptr new_verse_key(); + std::unique_ptr new_verse_key_from_text(const rust::String& text); + VerseKey verse_key_from_borrowed(sword::VerseKey* key); + + // Memory management for raw VerseKey pointers + void delete_verse_key(VerseKey* key); +} diff --git a/src/lib.rs b/src/lib.rs index b137cae..d2e6c97 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,4 @@ mod cxx; pub mod mgr; pub mod module; +pub mod versekey; diff --git a/src/main.rs b/src/main.rs index 98b2b7a..9ec79b2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,6 +2,25 @@ use rand::Rng; use sword_rs::mgr::{Format, Mgr}; fn main() { + one_chapter(); + // iterate(); +} + +fn one_chapter() { + let mut mgr = Mgr::new_with_format(Format::HTMLHref); + let mut module = mgr.get_module(&String::from("KJV")); + let mut key = module + .get_verse_key() + .expect("This was not a verse based module?!"); + key.set_book_name(&String::from("John")); + key.set_chapter(3); + for verse in 1..=key.get_verse_max() { + key.set_verse(verse); + println!("{}: {}", key.get_short_text(), module.render_text()); + } +} + +fn iterate() { let mut mgr = Mgr::new_with_format(Format::HTMLHref); for mod_name in mgr.get_modules() { let mut module = mgr.get_module(&mod_name); diff --git a/src/module.rs b/src/module.rs index 4cbaef7..a4aac61 100644 --- a/src/module.rs +++ b/src/module.rs @@ -83,4 +83,16 @@ impl Module { pub fn strip_text(&mut self) -> String { self.module.pin_mut().strip_text() } + + pub fn get_verse_key(&mut self) -> Option { + match self.get_type() { + Type::BiblicalTexts => unsafe { + crate::versekey::BorrowedVerseKey::from_ptr(self.module.pin_mut().get_verse_key()) + }, + Type::Commentaries => unsafe { + crate::versekey::BorrowedVerseKey::from_ptr(self.module.pin_mut().get_verse_key()) + }, + _ => None, + } + } } diff --git a/src/versekey.rs b/src/versekey.rs new file mode 100644 index 0000000..72a3b61 --- /dev/null +++ b/src/versekey.rs @@ -0,0 +1,398 @@ +use crate::cxx::ffi; + +/// Safe wrapper for owned VerseKey instances +pub struct VerseKey { + inner: cxx::UniquePtr, +} + +impl VerseKey { + /// Create a new VerseKey + pub fn new() -> Self { + Self { + inner: ffi::new_verse_key(), + } + } + + /// Create a new VerseKey from text + pub fn from_text(text: &str) -> Self { + Self { + inner: ffi::new_verse_key_from_text(&text.to_string()), + } + } + + /// Get the book name + pub fn get_book_name(&self) -> String { + self.inner.get_book_name() + } + + /// Get the book abbreviation + pub fn get_book_abbrev(&self) -> String { + self.inner.get_book_abbrev() + } + + /// Get the OSIS book name + pub fn get_osis_book_name(&self) -> String { + self.inner.get_osis_book_name() + } + + /// Get the OSIS reference + pub fn get_osis_ref(&self) -> String { + self.inner.get_osis_ref() + } + + /// Get the OSIS reference range text + pub fn get_osis_ref_range_text(&self) -> String { + self.inner.get_osis_ref_range_text() + } + + /// Get the short range text + pub fn get_short_range_text(&self) -> String { + self.inner.get_short_range_text() + } + + /// Get the testament number + pub fn get_testament(&self) -> u8 { + self.inner.get_testament() + } + + /// Get the maximum testament number + pub fn get_testament_max(&self) -> u8 { + self.inner.get_testament_max() + } + + /// Get the book number + pub fn get_book(&self) -> u8 { + self.inner.get_book() + } + + /// Get the maximum book number + pub fn get_book_max(&self) -> u8 { + self.inner.get_book_max() + } + + /// Get the chapter number + pub fn get_chapter(&self) -> u8 { + self.inner.get_chapter() + } + + /// Get the maximum chapter number + pub fn get_chapter_max(&self) -> u8 { + self.inner.get_chapter_max() + } + + /// Get the verse number + pub fn get_verse(&self) -> u8 { + self.inner.get_verse() + } + + /// Get the maximum verse number + pub fn get_verse_max(&self) -> u8 { + self.inner.get_verse_max() + } + + /// Check if auto-normalize is enabled + pub fn is_autonormalize(&self) -> bool { + self.inner.is_autonormalize() + } + + /// Check if intros are enabled + pub fn is_intros(&self) -> bool { + self.inner.is_intros() + } + + /// Get the error code + pub fn get_error(&self) -> u8 { + self.inner.get_error() + } + + /// Get the locale + pub fn get_locale(&self) -> String { + self.inner.get_locale() + } + + /// Get the range text + pub fn get_range_text(&self) -> String { + self.inner.get_range_text() + } + + /// Get the short text + pub fn get_short_text(&self) -> String { + self.inner.get_short_text() + } + + /// Get the text + pub fn get_text(&self) -> String { + self.inner.get_text() + } + + /// Normalize the key + pub fn normalize(&mut self, autocheck: bool) { + self.inner.pin_mut().normalize(autocheck) + } + + /// Pop an error + pub fn pop_error(&mut self) -> u8 { + self.inner.pin_mut().pop_error() + } + + /// Set auto-normalize + pub fn set_autonormalize(&mut self, auto_normalize: bool) { + self.inner.pin_mut().set_autonormalize(auto_normalize) + } + + /// Set the book name + pub fn set_book_name(&mut self, book_name: &str) { + self.inner.pin_mut().set_book_name(&book_name.to_string()) + } + + /// Set intros + pub fn set_intros(&mut self, intros: bool) { + self.inner.pin_mut().set_intros(intros) + } + + /// Set the locale + pub fn set_locale(&mut self, locale: &str) { + self.inner.pin_mut().set_locale(&locale.to_string()) + } + + /// Set the text + pub fn set_text(&mut self, text: &str) { + self.inner.pin_mut().set_text(&text.to_string()) + } + + /// Set the testament + pub fn set_testament(&mut self, testament: u8) { + self.inner.pin_mut().set_testament(testament) + } + + /// Set the book + pub fn set_book(&mut self, book: u8) { + self.inner.pin_mut().set_book(book) + } + + /// Set the chapter + pub fn set_chapter(&mut self, chapter: u8) { + self.inner.pin_mut().set_chapter(chapter) + } + + /// Set the verse + pub fn set_verse(&mut self, verse: u8) { + self.inner.pin_mut().set_verse(verse) + } +} + +impl Default for VerseKey { + fn default() -> Self { + Self::new() + } +} + +/// Wrapper for borrowed VerseKey instances (from modules) +pub struct BorrowedVerseKey { + ptr: *mut ffi::VerseKey, +} + +impl BorrowedVerseKey { + /// Create from a raw pointer (unsafe) + pub unsafe fn from_ptr(ptr: *mut ffi::VerseKey) -> Option { + if ptr.is_null() { + None + } else { + Some(Self { ptr }) + } + } + + /// Get the book name + pub fn get_book_name(&self) -> String { + unsafe { (&*self.ptr).get_book_name() } + } + + /// Get the book abbreviation + pub fn get_book_abbrev(&self) -> String { + unsafe { (&*self.ptr).get_book_abbrev() } + } + + /// Get the OSIS book name + pub fn get_osis_book_name(&self) -> String { + unsafe { (&*self.ptr).get_osis_book_name() } + } + + /// Get the OSIS reference + pub fn get_osis_ref(&self) -> String { + unsafe { (&*self.ptr).get_osis_ref() } + } + + /// Get the OSIS reference range text + pub fn get_osis_ref_range_text(&self) -> String { + unsafe { (&*self.ptr).get_osis_ref_range_text() } + } + + /// Get the short range text + pub fn get_short_range_text(&self) -> String { + unsafe { (&*self.ptr).get_short_range_text() } + } + + /// Get the testament number + pub fn get_testament(&self) -> u8 { + unsafe { (&*self.ptr).get_testament() } + } + + /// Get the maximum testament number + pub fn get_testament_max(&self) -> u8 { + unsafe { (&*self.ptr).get_testament_max() } + } + + /// Get the book number + pub fn get_book(&self) -> u8 { + unsafe { (&*self.ptr).get_book() } + } + + /// Get the maximum book number + pub fn get_book_max(&self) -> u8 { + unsafe { (&*self.ptr).get_book_max() } + } + + /// Get the chapter number + pub fn get_chapter(&self) -> u8 { + unsafe { (&*self.ptr).get_chapter() } + } + + /// Get the maximum chapter number + pub fn get_chapter_max(&self) -> u8 { + unsafe { (&*self.ptr).get_chapter_max() } + } + + /// Get the verse number + pub fn get_verse(&self) -> u8 { + unsafe { (&*self.ptr).get_verse() } + } + + /// Get the maximum verse number + pub fn get_verse_max(&self) -> u8 { + unsafe { (&*self.ptr).get_verse_max() } + } + + /// Check if auto-normalize is enabled + pub fn is_autonormalize(&self) -> bool { + unsafe { (&*self.ptr).is_autonormalize() } + } + + /// Check if intros are enabled + pub fn is_intros(&self) -> bool { + unsafe { (&*self.ptr).is_intros() } + } + + /// Get the error code + pub fn get_error(&self) -> u8 { + unsafe { (&*self.ptr).get_error() } + } + + /// Get the locale + pub fn get_locale(&self) -> String { + unsafe { (&*self.ptr).get_locale() } + } + + /// Get the range text + pub fn get_range_text(&self) -> String { + unsafe { (&*self.ptr).get_range_text() } + } + + /// Get the short text + pub fn get_short_text(&self) -> String { + unsafe { (&*self.ptr).get_short_text() } + } + + /// Get the text + pub fn get_text(&self) -> String { + unsafe { (&*self.ptr).get_text() } + } + + /// Normalize the key + pub fn normalize(&mut self, autocheck: bool) { + unsafe { + std::pin::Pin::new_unchecked(&mut *self.ptr).normalize(autocheck); + } + } + + /// Pop an error + pub fn pop_error(&mut self) -> u8 { + unsafe { std::pin::Pin::new_unchecked(&mut *self.ptr).pop_error() } + } + + /// Set auto-normalize + pub fn set_autonormalize(&mut self, auto_normalize: bool) { + unsafe { + std::pin::Pin::new_unchecked(&mut *self.ptr).set_autonormalize(auto_normalize); + } + } + + /// Set the book name + pub fn set_book_name(&mut self, book_name: &str) { + unsafe { + std::pin::Pin::new_unchecked(&mut *self.ptr).set_book_name(&book_name.to_string()); + } + } + + /// Set intros + pub fn set_intros(&mut self, intros: bool) { + unsafe { + std::pin::Pin::new_unchecked(&mut *self.ptr).set_intros(intros); + } + } + + /// Set the locale + pub fn set_locale(&mut self, locale: &str) { + unsafe { + std::pin::Pin::new_unchecked(&mut *self.ptr).set_locale(&locale.to_string()); + } + } + + /// Set the text + pub fn set_text(&mut self, text: &str) { + unsafe { + std::pin::Pin::new_unchecked(&mut *self.ptr).set_text(&text.to_string()); + } + } + + /// Set the testament + pub fn set_testament(&mut self, testament: u8) { + unsafe { + std::pin::Pin::new_unchecked(&mut *self.ptr).set_testament(testament); + } + } + + /// Set the book + pub fn set_book(&mut self, book: u8) { + unsafe { + std::pin::Pin::new_unchecked(&mut *self.ptr).set_book(book); + } + } + + /// Set the chapter + pub fn set_chapter(&mut self, chapter: u8) { + unsafe { + std::pin::Pin::new_unchecked(&mut *self.ptr).set_chapter(chapter); + } + } + + /// Set the verse + pub fn set_verse(&mut self, verse: u8) { + unsafe { + std::pin::Pin::new_unchecked(&mut *self.ptr).set_verse(verse); + } + } +} + +impl Drop for BorrowedVerseKey { + fn drop(&mut self) { + if !self.ptr.is_null() { + unsafe { + ffi::delete_verse_key(self.ptr); + } + } + } +} + +// Safety: BorrowedVerseKey can be sent between threads safely as long as +// the underlying SWORD library is thread-safe for reading +unsafe impl Send for BorrowedVerseKey {}