Files
sword_rs/src/versekey.rs
T
Greg Hellings 0ec0a0c7ac 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.
2025-06-26 02:19:26 -05:00

233 lines
5.5 KiB
Rust

use crate::cxx::ffi;
use std::fmt;
/// Safe Rust wrapper for VerseKey with proper memory management
pub struct VerseKey {
ptr: *mut ffi::VerseKey,
}
impl VerseKey {
/// Create a new VerseKey
pub fn new() -> Self {
let ptr = ffi::new_verse_key();
if ptr.is_null() {
panic!("Failed to create VerseKey");
}
Self { ptr }
}
/// Create a new VerseKey from text
pub fn from_text(text: &str) -> Self {
let ptr = ffi::new_verse_key_from_text(text.to_string());
if ptr.is_null() {
panic!("Failed to create VerseKey from text");
}
Self { ptr }
}
/// Create from a raw pointer (for internal use)
pub(crate) fn from_raw_ptr(ptr: *mut ffi::VerseKey) -> Self {
if ptr.is_null() {
panic!("Cannot create VerseKey from null pointer");
}
Self { ptr }
}
/// Get a reference to the underlying VerseKey
fn as_ref(&self) -> &ffi::VerseKey {
unsafe { &*self.ptr }
}
/// Get a pinned mutable reference to the underlying VerseKey
fn as_pin_mut(&mut self) -> std::pin::Pin<&mut ffi::VerseKey> {
unsafe { std::pin::Pin::new_unchecked(&mut *self.ptr) }
}
/// Check if the VerseKey is valid
pub fn is_valid(&self) -> bool {
!self.ptr.is_null() && self.as_ref().is_valid()
}
// Read-only methods
pub fn get_error(&self) -> u8 {
self.as_ref().get_error()
}
pub fn get_locale(&self) -> String {
self.as_ref().get_locale()
}
pub fn get_range_text(&self) -> String {
self.as_ref().get_range_text()
}
pub fn get_short_text(&self) -> String {
self.as_ref().get_short_text()
}
pub fn get_text(&self) -> String {
self.as_ref().get_text()
}
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) {
self.as_pin_mut().normalize(autocheck)
}
pub fn pop_error(&mut self) -> u8 {
self.as_pin_mut().pop_error()
}
pub fn set_locale(&mut self, locale: &str) {
self.as_pin_mut().set_locale(&locale.to_string())
}
pub fn set_text(&mut self, text: &str) {
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)
}
pub fn set_testament(&mut self, testament: u8) {
self.as_pin_mut().set_testament(testament)
}
pub fn set_book(&mut self, book: u8) {
self.as_pin_mut().set_book(book)
}
pub fn set_chapter(&mut self, chapter: u8) {
self.as_pin_mut().set_chapter(chapter)
}
pub fn set_verse(&mut self, verse: u8) {
self.as_pin_mut().set_verse(verse)
}
}
impl Default for VerseKey {
fn default() -> Self {
Self::new()
}
}
impl Clone for VerseKey {
fn clone(&self) -> Self {
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 Drop for VerseKey {
fn drop(&mut self) {
if !self.ptr.is_null() {
unsafe {
ffi::delete_verse_key(self.ptr);
}
}
}
}
impl fmt::Debug for VerseKey {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
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 {}