Builds, runs, and tests succeed*

*Tests only succeed if you run them in a single-threaded instance. With
cargo this means invoking them as `cargo run -- --test-threads=1`. There
is an instance of global state in the Sword engine with the
VersificationMgr class that can easily cause a race condition. Since
VersificationMgr is intended to be a global singleton, when it is first
being instantiated the pointer is null. Sometimes the tests get into
that method at the same time and overwrite one another with the instance
of the manager, thus causing some of the VerseKeys that get instantiated
to be invalid.

TODO: On the Rust side of the code, create an instance of the
VersificationMgr before allowing any VerseKey objects to be created.
Probably also necessary to run it before allowing SWMgr to be
instantiated, as well, just to be safe.
This commit is contained in:
Greg Hellings
2025-06-26 02:19:26 -05:00
parent c8dd1ce0f0
commit 0ec0a0c7ac
8 changed files with 373 additions and 429 deletions
+1
View File
@@ -46,6 +46,7 @@
cargo cargo
clippy clippy
libgcc libgcc
lldb
rustc.llvm rustc.llvm
rustc rustc
rustfmt rustfmt
+108 -51
View File
@@ -19,7 +19,7 @@ pub mod ffi {
fn decrement(self: Pin<&mut Module>); fn decrement(self: Pin<&mut Module>);
fn get_name(self: &Module) -> String; fn get_name(self: &Module) -> String;
fn get_type(self: &Module) -> String; fn get_type(self: &Module) -> String;
fn get_verse_key(self: &Module) -> *mut VerseKey; fn get_verse_key(self: &Module) -> *mut SwordVerseKey;
fn increment(self: Pin<&mut Module>); fn increment(self: Pin<&mut Module>);
fn is_skip_consecutive_links(self: &Module) -> bool; fn is_skip_consecutive_links(self: &Module) -> bool;
fn render_text(self: Pin<&mut Module>) -> String; fn render_text(self: Pin<&mut Module>) -> String;
@@ -35,19 +35,28 @@ pub mod ffi {
fn new_versification() -> UniquePtr<Versification>; fn new_versification() -> UniquePtr<Versification>;
type VerseKey; type VerseKey;
// Factory functions for VerseKey type SwordVerseKey;
fn new_verse_key() -> UniquePtr<VerseKey>;
fn new_verse_key_from_text(text: &String) -> UniquePtr<VerseKey>;
unsafe fn delete_verse_key(key: *mut VerseKey);
// From Key // Factory functions for VerseKey - return raw pointers for manual management
fn new_verse_key() -> *mut VerseKey;
fn new_verse_key_from_text(text: String) -> *mut VerseKey;
// Wrapper function for module VerseKeys
unsafe fn get_verse_key_wrapper(sword_key: *mut SwordVerseKey) -> *mut VerseKey;
// Clone function
fn clone_verse_key(source: &VerseKey) -> *mut VerseKey;
// Delete function for cleanup
unsafe fn delete_verse_key(verse_key: *mut VerseKey);
// VerseKey methods
fn get_error(self: &VerseKey) -> u8; fn get_error(self: &VerseKey) -> u8;
fn get_locale(self: &VerseKey) -> String; fn get_locale(self: &VerseKey) -> String;
fn get_range_text(self: &VerseKey) -> String; fn get_range_text(self: &VerseKey) -> String;
fn get_short_text(self: &VerseKey) -> String; fn get_short_text(self: &VerseKey) -> String;
fn get_text(self: &VerseKey) -> String; fn get_text(self: &VerseKey) -> String;
fn normalize(self: Pin<&mut VerseKey>, autocheck: bool); fn normalize(self: Pin<&mut VerseKey>, autocheck: bool);
// From VerseKey
fn get_book_name(self: &VerseKey) -> String; fn get_book_name(self: &VerseKey) -> String;
fn get_book_abbrev(self: &VerseKey) -> String; fn get_book_abbrev(self: &VerseKey) -> String;
fn get_osis_book_name(self: &VerseKey) -> String; fn get_osis_book_name(self: &VerseKey) -> String;
@@ -62,17 +71,12 @@ pub mod ffi {
fn get_chapter_max(self: &VerseKey) -> u8; fn get_chapter_max(self: &VerseKey) -> u8;
fn get_verse(self: &VerseKey) -> u8; fn get_verse(self: &VerseKey) -> u8;
fn get_verse_max(self: &VerseKey) -> u8; fn get_verse_max(self: &VerseKey) -> u8;
// From VerseKey
fn is_autonormalize(self: &VerseKey) -> bool; fn is_autonormalize(self: &VerseKey) -> bool;
fn is_intros(self: &VerseKey) -> bool; fn is_intros(self: &VerseKey) -> bool;
// From Key fn is_valid(self: &VerseKey) -> bool;
fn pop_error(self: Pin<&mut VerseKey>) -> u8; fn pop_error(self: Pin<&mut VerseKey>) -> u8;
// From Key
fn set_locale(self: Pin<&mut VerseKey>, locale: &String); fn set_locale(self: Pin<&mut VerseKey>, locale: &String);
fn set_text(self: Pin<&mut VerseKey>, text: &String); fn set_text(self: Pin<&mut VerseKey>, text: &String);
// From VerseKey
fn set_autonormalize(self: Pin<&mut VerseKey>, autoNormalize: bool); fn set_autonormalize(self: Pin<&mut VerseKey>, autoNormalize: bool);
fn set_book_name(self: Pin<&mut VerseKey>, bookName: &String); fn set_book_name(self: Pin<&mut VerseKey>, bookName: &String);
fn set_intros(self: Pin<&mut VerseKey>, intros: bool); fn set_intros(self: Pin<&mut VerseKey>, intros: bool);
@@ -80,9 +84,6 @@ pub mod ffi {
fn set_book(self: Pin<&mut VerseKey>, book: u8); fn set_book(self: Pin<&mut VerseKey>, book: u8);
fn set_chapter(self: Pin<&mut VerseKey>, chapter: u8); fn set_chapter(self: Pin<&mut VerseKey>, chapter: u8);
fn set_verse(self: Pin<&mut VerseKey>, verse: u8); fn set_verse(self: Pin<&mut VerseKey>, verse: u8);
// Explicit cleanup
fn cleanup(self: Pin<&mut VerseKey>);
} }
} }
@@ -128,8 +129,8 @@ mod tests {
assert!(mgr.get_modules().len() == 1); assert!(mgr.get_modules().len() == 1);
assert!(mgr.get_modules().contains(&String::from("KJVdummy"))); assert!(mgr.get_modules().contains(&String::from("KJVdummy")));
let mut module = mgr.pin_mut().get_module(&String::from("KJVdummy")); let module = mgr.pin_mut().get_module(&String::from("KJVdummy"));
assert!(module.pin_mut().get_name() == "KJVdummy"); assert!(module.get_name() == "KJVdummy");
} }
#[test] #[test]
@@ -147,8 +148,12 @@ mod tests {
#[test] #[test]
fn can_create_verse_key() { fn can_create_verse_key() {
let verse_key = ffi::new_verse_key(); let verse_key_ptr = ffi::new_verse_key();
assert!(!verse_key.is_null()); assert!(!verse_key_ptr.is_null());
unsafe {
let verse_key = &*verse_key_ptr;
assert!(verse_key.is_valid());
// Test that we can call methods without errors // Test that we can call methods without errors
let _testament = verse_key.get_testament(); let _testament = verse_key.get_testament();
@@ -157,32 +162,89 @@ mod tests {
let _verse = verse_key.get_verse(); let _verse = verse_key.get_verse();
let _book_name = verse_key.get_book_name(); let _book_name = verse_key.get_book_name();
let _text = verse_key.get_text(); let _text = verse_key.get_text();
// Cleanup
ffi::delete_verse_key(verse_key_ptr);
}
} }
#[test] #[test]
fn can_create_verse_key_from_text() { fn can_create_verse_key_from_text() {
let verse_key = ffi::new_verse_key_from_text(&"Genesis 1:1".to_string()); let verse_key_ptr = ffi::new_verse_key_from_text("Genesis 1:1".to_string());
assert!(!verse_key.is_null()); assert!(!verse_key_ptr.is_null());
unsafe {
let verse_key = &*verse_key_ptr;
assert!(verse_key.is_valid());
// Test that we can get values without errors // Test that we can get values without errors
let _chapter = verse_key.get_chapter(); let chapter = verse_key.get_chapter();
let _verse = verse_key.get_verse(); assert_eq!(chapter, 1);
let _text = verse_key.get_text(); let verse = verse_key.get_verse();
assert_eq!(verse, 1);
let text = verse_key.get_text();
assert_eq!(text, "Genesis 1:1");
// Cleanup
ffi::delete_verse_key(verse_key_ptr);
}
} }
#[test] #[test]
fn verse_key_can_be_modified() { fn verse_key_can_be_modified() {
let mut verse_key = ffi::new_verse_key(); let verse_key_ptr = ffi::new_verse_key();
assert!(!verse_key_ptr.is_null());
unsafe {
let verse_key = &*verse_key_ptr;
assert!(verse_key.is_valid());
// Test that we can call setter methods without panicking // Test that we can call setter methods without panicking
verse_key.pin_mut().set_chapter(5); std::pin::Pin::new_unchecked(&mut *(verse_key_ptr as *mut ffi::VerseKey))
verse_key.pin_mut().set_verse(10); .set_chapter(5);
verse_key.pin_mut().set_text(&"Genesis 1:1".to_string()); assert_eq!(verse_key.get_chapter(), 5);
std::pin::Pin::new_unchecked(&mut *(verse_key_ptr as *mut ffi::VerseKey)).set_verse(10);
assert_eq!(verse_key.get_verse(), 10);
std::pin::Pin::new_unchecked(&mut *(verse_key_ptr as *mut ffi::VerseKey))
.set_text(&"Genesis 1:2".to_string());
// Test that we can call getter methods after setting // Test that we can call getter methods after setting
let _chapter = verse_key.get_chapter(); let chapter = verse_key.get_chapter();
let _verse = verse_key.get_verse(); assert_eq!(chapter, 1);
let _text = verse_key.get_text(); let verse = verse_key.get_verse();
assert_eq!(verse, 2);
let text = verse_key.get_text();
assert_eq!(text, "Genesis 1:2");
// Cleanup
ffi::delete_verse_key(verse_key_ptr);
}
}
#[test]
fn can_clone_verse_key() {
let verse_key_ptr = ffi::new_verse_key_from_text("John 3:16".to_string());
assert!(!verse_key_ptr.is_null());
unsafe {
let verse_key = &*verse_key_ptr;
assert!(verse_key.is_valid());
let cloned_ptr = ffi::clone_verse_key(verse_key);
assert!(!cloned_ptr.is_null());
let cloned = &*cloned_ptr;
assert!(cloned.is_valid());
// Both should have the same text
assert_eq!(verse_key.get_text(), cloned.get_text());
assert_eq!(verse_key.get_chapter(), cloned.get_chapter());
assert_eq!(verse_key.get_verse(), cloned.get_verse());
// Cleanup both
ffi::delete_verse_key(verse_key_ptr);
ffi::delete_verse_key(cloned_ptr);
}
} }
#[test] #[test]
@@ -195,10 +257,8 @@ mod tests {
// Test that we can call methods without panicking // Test that we can call methods without panicking
verse_key.set_chapter(3); verse_key.set_chapter(3);
verse_key.set_verse(16); verse_key.set_verse(16);
verse_key.set_text("John 3:16");
// Test getters // Test getters
let _book_name = verse_key.get_book_name();
let _text = verse_key.get_text(); let _text = verse_key.get_text();
let _chapter = verse_key.get_chapter(); let _chapter = verse_key.get_chapter();
let _verse = verse_key.get_verse(); let _verse = verse_key.get_verse();
@@ -206,10 +266,14 @@ mod tests {
// Test creation from text // Test creation from text
let verse_key2 = VerseKey::from_text("Romans 8:28"); let verse_key2 = VerseKey::from_text("Romans 8:28");
let _text2 = verse_key2.get_text(); let _text2 = verse_key2.get_text();
// Test cloning
let verse_key3 = verse_key2.clone();
assert_eq!(verse_key2.get_text(), verse_key3.get_text());
} }
#[test] #[test]
fn borrowed_verse_key_from_module() { fn module_verse_key_integration() {
use std::fs::{create_dir, File}; use std::fs::{create_dir, File};
use std::io::Write; use std::io::Write;
use tempfile; use tempfile;
@@ -232,24 +296,17 @@ mod tests {
// Create manager and get module // Create manager and get module
let mut mgr = ffi::new_mgr_with_path(&dir.path().to_str().unwrap().to_string()); 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")); let module = mgr.pin_mut().get_module(&String::from("KJVdummy"));
// Test that we can get a VerseKey from the module // Test that we can get a VerseKey from the module using high-level API
let verse_key_ptr = module.pin_mut().get_verse_key(); if let Some(verse_key) = crate::module::Module::new(module).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 // Test that we can call methods without panicking
let _book_name = borrowed_vk.get_book_name(); let _book_name = verse_key.get_book_name();
let _text = borrowed_vk.get_text(); let _text = verse_key.get_text();
borrowed_vk.set_chapter(1);
borrowed_vk.set_verse(1); // Test that we can clone it
let _chapter = borrowed_vk.get_chapter(); let cloned = verse_key.clone();
let _verse = borrowed_vk.get_verse(); assert_eq!(verse_key.get_text(), cloned.get_text());
}
} }
} }
} }
+2 -8
View File
@@ -15,14 +15,8 @@ namespace sword_rs {
return rust::String(this->module->getType()); return rust::String(this->module->getType());
} }
VerseKey* Module::get_verse_key() const { SwordVerseKey* Module::get_verse_key() const {
sword::VerseKey* key = dynamic_cast<sword::VerseKey*>(this->module->getKey()); return dynamic_cast<SwordVerseKey*>(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() { void Module::increment() {
+1 -1
View File
@@ -21,7 +21,7 @@ namespace sword_rs {
rust::String get_type() const; rust::String get_type() const;
VerseKey* get_verse_key() const; SwordVerseKey* get_verse_key() const;
void increment(); void increment();
+61 -23
View File
@@ -1,9 +1,8 @@
#include "versekey.h" #include "versekey.h"
#include <memory>
#include <sword/versekey.h> #include <sword/versekey.h>
namespace sword_rs { namespace sword_rs {
// Key methods // VerseKey methods - all include null pointer checks for safety
uint8_t VerseKey::get_error() const { uint8_t VerseKey::get_error() const {
return ptr ? ptr->getError() : 0; return ptr ? ptr->getError() : 0;
} }
@@ -46,7 +45,6 @@ namespace sword_rs {
} }
} }
// VerseKey specific methods
rust::String VerseKey::get_book_name() const { rust::String VerseKey::get_book_name() const {
return ptr ? rust::String(ptr->getBookName()) : rust::String(""); return ptr ? rust::String(ptr->getBookName()) : rust::String("");
} }
@@ -153,35 +151,75 @@ namespace sword_rs {
} }
} }
// Explicit cleanup method bool VerseKey::is_valid() const {
void VerseKey::cleanup() { return ptr != nullptr;
if (owned && ptr) { }
delete ptr;
ptr = nullptr; // Factory function for self-managing VerseKey
owned = false; VerseKey* new_verse_key() {
try {
auto sword_key = new sword::VerseKey();
auto wrapper = new VerseKey();
wrapper->ptr = sword_key;
wrapper->managed = false; // self-managing
return wrapper;
} catch (...) {
return nullptr;
} }
} }
// Factory functions // Factory function for self-managing VerseKey from text
std::unique_ptr<VerseKey> new_verse_key() { VerseKey* new_verse_key_from_text(const rust::String text) {
auto verse_key = std::unique_ptr<VerseKey>(new VerseKey{new sword::VerseKey(), true}); try {
return verse_key; auto sword_key = new sword::VerseKey(std::string(text).c_str());
auto wrapper = new VerseKey();
wrapper->ptr = sword_key;
wrapper->managed = false; // self-managing
return wrapper;
} catch (...) {
return nullptr;
}
} }
std::unique_ptr<VerseKey> new_verse_key_from_text(const rust::String& text) { // Creates a managed VerseKey wrapper from module's sword::VerseKey
auto verse_key = std::unique_ptr<VerseKey>(new VerseKey{new sword::VerseKey(std::string(text).c_str()), true}); VerseKey* get_verse_key_wrapper(SwordVerseKey* sword_key) {
return verse_key; if (!sword_key) {
return nullptr;
} }
VerseKey verse_key_from_borrowed(sword::VerseKey* key) { try {
VerseKey verse_key{key, false}; auto wrapper = new VerseKey();
return verse_key; wrapper->ptr = sword_key;
wrapper->managed = true; // managed by external code
return wrapper;
} catch (...) {
return nullptr;
}
} }
void delete_verse_key(VerseKey* key) { // Clone function - always returns self-managing copy using copy constructor
if (key) { VerseKey* clone_verse_key(const VerseKey& source) {
key->cleanup(); if (!source.ptr) {
delete key; return nullptr;
}
try {
// Create a new sword::VerseKey using copy constructor
auto sword_key = new sword::VerseKey(*source.ptr);
auto wrapper = new VerseKey();
wrapper->ptr = sword_key;
wrapper->managed = false; // clones are always self-managing
return wrapper;
} catch (...) {
return nullptr;
}
}
// Delete function for VerseKey wrappers
void delete_verse_key(VerseKey* verse_key) {
if (verse_key) {
verse_key->cleanup();
delete verse_key;
} }
} }
} }
+25 -14
View File
@@ -1,18 +1,26 @@
#pragma once #pragma once
#include <memory> #include <memory>
#include <sword/swkey.h>
#include <sword/versekey.h> #include <sword/versekey.h>
#include "rust/cxx.h" #include "rust/cxx.h"
namespace sword_rs { namespace sword_rs {
// Trivial wrapper that just holds a pointer and ownership flag // Type alias for cxx bridge compatibility
// No destructor - cleanup is handled explicitly using SwordVerseKey = sword::VerseKey;
// Simple struct that holds a pointer to sword::VerseKey and tracks ownership
struct VerseKey { struct VerseKey {
sword::VerseKey* ptr; sword::VerseKey* ptr;
bool owned; bool managed; // true if managed by SWMgr instance, false if self-managing
// Key methods // Manual cleanup method (no destructor to keep struct trivial)
void cleanup() {
if (!managed && ptr) {
delete ptr;
ptr = nullptr;
}
}
// All VerseKey methods
uint8_t get_error() const; uint8_t get_error() const;
rust::String get_locale() const; rust::String get_locale() const;
rust::String get_range_text() const; rust::String get_range_text() const;
@@ -23,7 +31,6 @@ namespace sword_rs {
void set_locale(const rust::String &locale); void set_locale(const rust::String &locale);
void set_text(const rust::String &text); void set_text(const rust::String &text);
// VerseKey methods
rust::String get_book_name() const; rust::String get_book_name() const;
rust::String get_book_abbrev() const; rust::String get_book_abbrev() const;
rust::String get_osis_book_name() const; rust::String get_osis_book_name() const;
@@ -50,15 +57,19 @@ namespace sword_rs {
void set_chapter(uint8_t chapter); void set_chapter(uint8_t chapter);
void set_verse(uint8_t verse); void set_verse(uint8_t verse);
// Explicit cleanup method bool is_valid() const;
void cleanup();
}; };
// Factory functions for creating VerseKeys // Factory functions - return raw pointers for manual management
std::unique_ptr<VerseKey> new_verse_key(); VerseKey* new_verse_key();
std::unique_ptr<VerseKey> new_verse_key_from_text(const rust::String& text); VerseKey* new_verse_key_from_text(const rust::String text);
VerseKey verse_key_from_borrowed(sword::VerseKey* key);
// Memory management for raw VerseKey pointers // Creates a managed VerseKey from a module's sword::VerseKey pointer
void delete_verse_key(VerseKey* key); VerseKey* get_verse_key_wrapper(SwordVerseKey* sword_key);
// Clone function - always returns self-managing copy
VerseKey* clone_verse_key(const VerseKey& source);
// Delete function for VerseKey wrappers
void delete_verse_key(VerseKey* verse_key);
} }
+16 -7
View File
@@ -84,14 +84,23 @@ impl Module {
self.module.pin_mut().strip_text() self.module.pin_mut().strip_text()
} }
pub fn get_verse_key(&mut self) -> Option<crate::versekey::BorrowedVerseKey> { pub fn get_verse_key(&mut self) -> Option<crate::versekey::VerseKey> {
match self.get_type() { match self.get_type() {
Type::BiblicalTexts => unsafe { Type::BiblicalTexts | Type::Commentaries => {
crate::versekey::BorrowedVerseKey::from_ptr(self.module.pin_mut().get_verse_key()) let sword_ptr = self.module.get_verse_key();
}, if sword_ptr.is_null() {
Type::Commentaries => unsafe { None
crate::versekey::BorrowedVerseKey::from_ptr(self.module.pin_mut().get_verse_key()) } else {
}, unsafe {
let wrapper_ptr = ffi::get_verse_key_wrapper(sword_ptr);
if wrapper_ptr.is_null() {
None
} else {
Some(crate::versekey::VerseKey::from_raw_ptr(wrapper_ptr))
}
}
}
}
_ => None, _ => None,
} }
} }
+149 -315
View File
@@ -1,183 +1,181 @@
use crate::cxx::ffi; use crate::cxx::ffi;
use std::fmt;
/// Safe wrapper for owned VerseKey instances /// Safe Rust wrapper for VerseKey with proper memory management
pub struct VerseKey { pub struct VerseKey {
inner: cxx::UniquePtr<ffi::VerseKey>, ptr: *mut ffi::VerseKey,
} }
impl VerseKey { impl VerseKey {
/// Create a new VerseKey /// Create a new VerseKey
pub fn new() -> Self { pub fn new() -> Self {
Self { let ptr = ffi::new_verse_key();
inner: ffi::new_verse_key(), if ptr.is_null() {
panic!("Failed to create VerseKey");
} }
Self { ptr }
} }
/// Create a new VerseKey from text /// Create a new VerseKey from text
pub fn from_text(text: &str) -> Self { pub fn from_text(text: &str) -> Self {
Self { let ptr = ffi::new_verse_key_from_text(text.to_string());
inner: ffi::new_verse_key_from_text(&text.to_string()), if ptr.is_null() {
panic!("Failed to create VerseKey from text");
} }
Self { ptr }
} }
/// Get the book name /// Create from a raw pointer (for internal use)
pub fn get_book_name(&self) -> String { pub(crate) fn from_raw_ptr(ptr: *mut ffi::VerseKey) -> Self {
self.inner.get_book_name() if ptr.is_null() {
panic!("Cannot create VerseKey from null pointer");
}
Self { ptr }
} }
/// Get the book abbreviation /// Get a reference to the underlying VerseKey
pub fn get_book_abbrev(&self) -> String { fn as_ref(&self) -> &ffi::VerseKey {
self.inner.get_book_abbrev() unsafe { &*self.ptr }
} }
/// Get the OSIS book name /// Get a pinned mutable reference to the underlying VerseKey
pub fn get_osis_book_name(&self) -> String { fn as_pin_mut(&mut self) -> std::pin::Pin<&mut ffi::VerseKey> {
self.inner.get_osis_book_name() unsafe { std::pin::Pin::new_unchecked(&mut *self.ptr) }
} }
/// Get the OSIS reference /// Check if the VerseKey is valid
pub fn get_osis_ref(&self) -> String { pub fn is_valid(&self) -> bool {
self.inner.get_osis_ref() !self.ptr.is_null() && self.as_ref().is_valid()
} }
/// Get the OSIS reference range text // Read-only methods
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 { pub fn get_error(&self) -> u8 {
self.inner.get_error() self.as_ref().get_error()
} }
/// Get the locale
pub fn get_locale(&self) -> String { pub fn get_locale(&self) -> String {
self.inner.get_locale() self.as_ref().get_locale()
} }
/// Get the range text
pub fn get_range_text(&self) -> String { pub fn get_range_text(&self) -> String {
self.inner.get_range_text() self.as_ref().get_range_text()
} }
/// Get the short text
pub fn get_short_text(&self) -> String { pub fn get_short_text(&self) -> String {
self.inner.get_short_text() self.as_ref().get_short_text()
} }
/// Get the text
pub fn get_text(&self) -> String { pub fn get_text(&self) -> String {
self.inner.get_text() self.as_ref().get_text()
} }
/// Normalize the key pub fn get_book_name(&self) -> String {
self.as_ref().get_book_name()
}
pub fn get_book_abbrev(&self) -> String {
self.as_ref().get_book_abbrev()
}
pub fn get_osis_book_name(&self) -> String {
self.as_ref().get_osis_book_name()
}
pub fn get_osis_ref(&self) -> String {
self.as_ref().get_osis_ref()
}
pub fn get_osis_ref_range_text(&self) -> String {
self.as_ref().get_osis_ref_range_text()
}
pub fn get_short_range_text(&self) -> String {
self.as_ref().get_short_range_text()
}
pub fn get_testament(&self) -> u8 {
self.as_ref().get_testament()
}
pub fn get_testament_max(&self) -> u8 {
self.as_ref().get_testament_max()
}
pub fn get_book(&self) -> u8 {
self.as_ref().get_book()
}
pub fn get_book_max(&self) -> u8 {
self.as_ref().get_book_max()
}
pub fn get_chapter(&self) -> u8 {
self.as_ref().get_chapter()
}
pub fn get_chapter_max(&self) -> u8 {
self.as_ref().get_chapter_max()
}
pub fn get_verse(&self) -> u8 {
self.as_ref().get_verse()
}
pub fn get_verse_max(&self) -> u8 {
self.as_ref().get_verse_max()
}
pub fn is_autonormalize(&self) -> bool {
self.as_ref().is_autonormalize()
}
pub fn is_intros(&self) -> bool {
self.as_ref().is_intros()
}
// Mutable methods
pub fn normalize(&mut self, autocheck: bool) { pub fn normalize(&mut self, autocheck: bool) {
self.inner.pin_mut().normalize(autocheck) self.as_pin_mut().normalize(autocheck)
} }
/// Pop an error
pub fn pop_error(&mut self) -> u8 { pub fn pop_error(&mut self) -> u8 {
self.inner.pin_mut().pop_error() self.as_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) { pub fn set_locale(&mut self, locale: &str) {
self.inner.pin_mut().set_locale(&locale.to_string()) self.as_pin_mut().set_locale(&locale.to_string())
} }
/// Set the text
pub fn set_text(&mut self, text: &str) { pub fn set_text(&mut self, text: &str) {
self.inner.pin_mut().set_text(&text.to_string()) self.as_pin_mut().set_text(&text.to_string())
}
pub fn set_autonormalize(&mut self, auto_normalize: bool) {
self.as_pin_mut().set_autonormalize(auto_normalize)
}
pub fn set_book_name(&mut self, book_name: &str) {
self.as_pin_mut().set_book_name(&book_name.to_string())
}
pub fn set_intros(&mut self, intros: bool) {
self.as_pin_mut().set_intros(intros)
} }
/// Set the testament
pub fn set_testament(&mut self, testament: u8) { pub fn set_testament(&mut self, testament: u8) {
self.inner.pin_mut().set_testament(testament) self.as_pin_mut().set_testament(testament)
} }
/// Set the book
pub fn set_book(&mut self, book: u8) { pub fn set_book(&mut self, book: u8) {
self.inner.pin_mut().set_book(book) self.as_pin_mut().set_book(book)
} }
/// Set the chapter
pub fn set_chapter(&mut self, chapter: u8) { pub fn set_chapter(&mut self, chapter: u8) {
self.inner.pin_mut().set_chapter(chapter) self.as_pin_mut().set_chapter(chapter)
} }
/// Set the verse
pub fn set_verse(&mut self, verse: u8) { pub fn set_verse(&mut self, verse: u8) {
self.inner.pin_mut().set_verse(verse) self.as_pin_mut().set_verse(verse)
} }
} }
@@ -187,203 +185,17 @@ impl Default for VerseKey {
} }
} }
/// Wrapper for borrowed VerseKey instances (from modules) impl Clone for VerseKey {
pub struct BorrowedVerseKey { fn clone(&self) -> Self {
ptr: *mut ffi::VerseKey, let cloned_ptr = ffi::clone_verse_key(self.as_ref());
if cloned_ptr.is_null() {
panic!("Failed to clone VerseKey");
} }
Self { ptr: cloned_ptr }
impl BorrowedVerseKey {
/// Create from a raw pointer (unsafe)
pub unsafe fn from_ptr(ptr: *mut ffi::VerseKey) -> Option<Self> {
if ptr.is_null() {
None
} else {
Some(Self { ptr })
} }
} }
/// Get the book name impl Drop for VerseKey {
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) { fn drop(&mut self) {
if !self.ptr.is_null() { if !self.ptr.is_null() {
unsafe { unsafe {
@@ -393,6 +205,28 @@ impl Drop for BorrowedVerseKey {
} }
} }
// Safety: BorrowedVerseKey can be sent between threads safely as long as impl fmt::Debug for VerseKey {
// the underlying SWORD library is thread-safe for reading fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
unsafe impl Send for BorrowedVerseKey {} if self.is_valid() {
f.debug_struct("VerseKey")
.field("text", &self.get_text())
.field("osis_ref", &self.get_osis_ref())
.finish()
} else {
f.debug_struct("VerseKey").field("valid", &false).finish()
}
}
}
impl fmt::Display for VerseKey {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.is_valid() {
write!(f, "{}", self.get_text())
} else {
write!(f, "<invalid>")
}
}
}
unsafe impl Send for VerseKey {}
unsafe impl Sync for VerseKey {}