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
This commit is contained in:
@@ -1,23 +1,21 @@
|
|||||||
use system_deps;
|
use system_deps;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
let sources = vec!["key", "mgr", "module", "versekey", "versification"];
|
||||||
let deps = system_deps::Config::new().probe().unwrap();
|
let deps = system_deps::Config::new().probe().unwrap();
|
||||||
|
|
||||||
cxx_build::bridge("src/cxx/mod.rs")
|
let mut build = cxx_build::bridge("src/cxx/mod.rs");
|
||||||
.file("src/cxx/mgr.cc")
|
for src in sources {
|
||||||
.file("src/cxx/module.cc")
|
build.file(&format!("src/cxx/{}.cc", src));
|
||||||
.file("src/cxx/versification.cc")
|
println!("cargo:rerun-if-changed=src/cxx/{}.cc", src);
|
||||||
|
println!("cargo:rerun-if-changed=src/cxx/{}.h", src);
|
||||||
|
}
|
||||||
|
build
|
||||||
.includes(deps.all_include_paths())
|
.includes(deps.all_include_paths())
|
||||||
.cpp_link_stdlib("stdc++")
|
.cpp_link_stdlib("stdc++")
|
||||||
.flag("-Os")
|
.flag("-Os")
|
||||||
.compile("sword_rs");
|
.compile("sword_rs");
|
||||||
|
|
||||||
println!("cargo:rerun-if-changed=src/cxx/mod.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");
|
println!("cargo:rustc-link-lib=sword");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <sword/swkey.h>
|
||||||
|
|
||||||
|
#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);
|
||||||
|
};
|
||||||
|
}
|
||||||
+160
@@ -4,6 +4,7 @@ pub mod ffi {
|
|||||||
include!("sword_rs/src/cxx/mgr.h");
|
include!("sword_rs/src/cxx/mgr.h");
|
||||||
include!("sword_rs/src/cxx/module.h");
|
include!("sword_rs/src/cxx/module.h");
|
||||||
include!("sword_rs/src/cxx/versification.h");
|
include!("sword_rs/src/cxx/versification.h");
|
||||||
|
include!("sword_rs/src/cxx/versekey.h");
|
||||||
|
|
||||||
type Mgr;
|
type Mgr;
|
||||||
|
|
||||||
@@ -18,6 +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 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;
|
||||||
@@ -31,6 +33,56 @@ pub mod ffi {
|
|||||||
fn getVersifications(self: &Versification) -> Vec<String>;
|
fn getVersifications(self: &Versification) -> Vec<String>;
|
||||||
fn getVersification(self: &Versification, name: &mut String) -> *const VersificationSystem;
|
fn getVersification(self: &Versification, name: &mut String) -> *const VersificationSystem;
|
||||||
fn new_versification() -> UniquePtr<Versification>;
|
fn new_versification() -> UniquePtr<Versification>;
|
||||||
|
|
||||||
|
type VerseKey;
|
||||||
|
// Factory functions for VerseKey
|
||||||
|
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
|
||||||
|
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);
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,16 @@ namespace sword_rs {
|
|||||||
return rust::String(this->module->getType());
|
return rust::String(this->module->getType());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
VerseKey* Module::get_verse_key() const {
|
||||||
|
sword::VerseKey* key = dynamic_cast<sword::VerseKey*>(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() {
|
||||||
this->module->increment();
|
this->module->increment();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
#include <sword/swmodule.h>
|
#include <sword/swmodule.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include "rust/cxx.h"
|
#include "rust/cxx.h"
|
||||||
|
#include "versekey.h"
|
||||||
|
|
||||||
namespace sword_rs {
|
namespace sword_rs {
|
||||||
class Module {
|
class Module {
|
||||||
@@ -19,6 +21,8 @@ namespace sword_rs {
|
|||||||
|
|
||||||
rust::String get_type() const;
|
rust::String get_type() const;
|
||||||
|
|
||||||
|
VerseKey* get_verse_key() const;
|
||||||
|
|
||||||
void increment();
|
void increment();
|
||||||
|
|
||||||
bool is_skip_consecutive_links() const;
|
bool is_skip_consecutive_links() const;
|
||||||
|
|||||||
@@ -0,0 +1,187 @@
|
|||||||
|
#include "versekey.h"
|
||||||
|
#include <memory>
|
||||||
|
#include <sword/versekey.h>
|
||||||
|
|
||||||
|
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<VerseKey> new_verse_key() {
|
||||||
|
auto verse_key = std::unique_ptr<VerseKey>(new VerseKey{new sword::VerseKey(), true});
|
||||||
|
return verse_key;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <sword/swkey.h>
|
||||||
|
#include <sword/versekey.h>
|
||||||
|
#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<VerseKey> new_verse_key();
|
||||||
|
std::unique_ptr<VerseKey> 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);
|
||||||
|
}
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
mod cxx;
|
mod cxx;
|
||||||
pub mod mgr;
|
pub mod mgr;
|
||||||
pub mod module;
|
pub mod module;
|
||||||
|
pub mod versekey;
|
||||||
|
|||||||
+19
@@ -2,6 +2,25 @@ use rand::Rng;
|
|||||||
use sword_rs::mgr::{Format, Mgr};
|
use sword_rs::mgr::{Format, Mgr};
|
||||||
|
|
||||||
fn main() {
|
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);
|
let mut mgr = Mgr::new_with_format(Format::HTMLHref);
|
||||||
for mod_name in mgr.get_modules() {
|
for mod_name in mgr.get_modules() {
|
||||||
let mut module = mgr.get_module(&mod_name);
|
let mut module = mgr.get_module(&mod_name);
|
||||||
|
|||||||
@@ -83,4 +83,16 @@ impl Module {
|
|||||||
pub fn strip_text(&mut self) -> String {
|
pub fn strip_text(&mut self) -> String {
|
||||||
self.module.pin_mut().strip_text()
|
self.module.pin_mut().strip_text()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_verse_key(&mut self) -> Option<crate::versekey::BorrowedVerseKey> {
|
||||||
|
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,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+398
@@ -0,0 +1,398 @@
|
|||||||
|
use crate::cxx::ffi;
|
||||||
|
|
||||||
|
/// Safe wrapper for owned VerseKey instances
|
||||||
|
pub struct VerseKey {
|
||||||
|
inner: cxx::UniquePtr<ffi::VerseKey>,
|
||||||
|
}
|
||||||
|
|
||||||
|
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<Self> {
|
||||||
|
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 {}
|
||||||
Reference in New Issue
Block a user