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
+61 -23
View File
@@ -1,9 +1,8 @@
#include "versekey.h"
#include <memory>
#include <sword/versekey.h>
namespace sword_rs {
// Key methods
// VerseKey methods - all include null pointer checks for safety
uint8_t VerseKey::get_error() const {
return ptr ? ptr->getError() : 0;
}
@@ -46,7 +45,6 @@ namespace sword_rs {
}
}
// VerseKey specific methods
rust::String VerseKey::get_book_name() const {
return ptr ? rust::String(ptr->getBookName()) : rust::String("");
}
@@ -153,35 +151,75 @@ namespace sword_rs {
}
}
// Explicit cleanup method
void VerseKey::cleanup() {
if (owned && ptr) {
delete ptr;
ptr = nullptr;
owned = false;
bool VerseKey::is_valid() const {
return ptr != nullptr;
}
// Factory function for self-managing VerseKey
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
std::unique_ptr<VerseKey> new_verse_key() {
auto verse_key = std::unique_ptr<VerseKey>(new VerseKey{new sword::VerseKey(), true});
return verse_key;
// Factory function for self-managing VerseKey from text
VerseKey* new_verse_key_from_text(const rust::String text) {
try {
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) {
auto verse_key = std::unique_ptr<VerseKey>(new VerseKey{new sword::VerseKey(std::string(text).c_str()), true});
return verse_key;
// Creates a managed VerseKey wrapper from module's sword::VerseKey
VerseKey* get_verse_key_wrapper(SwordVerseKey* sword_key) {
if (!sword_key) {
return nullptr;
}
try {
auto wrapper = new VerseKey();
wrapper->ptr = sword_key;
wrapper->managed = true; // managed by external code
return wrapper;
} catch (...) {
return nullptr;
}
}
VerseKey verse_key_from_borrowed(sword::VerseKey* key) {
VerseKey verse_key{key, false};
return verse_key;
// Clone function - always returns self-managing copy using copy constructor
VerseKey* clone_verse_key(const VerseKey& source) {
if (!source.ptr) {
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;
}
}
void delete_verse_key(VerseKey* key) {
if (key) {
key->cleanup();
delete key;
// Delete function for VerseKey wrappers
void delete_verse_key(VerseKey* verse_key) {
if (verse_key) {
verse_key->cleanup();
delete verse_key;
}
}
}