#![doc = include_str!("../README.md")]
#[allow(non_upper_case_globals)]
#[allow(non_camel_case_types)]
#[allow(non_snake_case)]
pub mod libsqlite3;
#[allow(non_upper_case_globals)]
#[allow(non_camel_case_types)]
#[allow(non_snake_case)]
pub mod c;
pub mod wasm;
mod fragile;
use fragile::FragileComfirmed;
use js_sys::{Object, WebAssembly};
use serde::{Deserialize, Serialize};
use std::{error::Error, fmt::Display, result::Result};
use tokio::sync::OnceCell;
use wasm::{CApi, OpfsSAHPoolUtil, Wasm};
use wasm_bindgen::JsCast;
static SQLITE: OnceCell<SQLite> = OnceCell::const_new();
pub async fn init_sqlite() -> Result<&'static SQLite, SQLiteError> {
SQLITE.get_or_try_init(SQLite::new).await
}
pub fn sqlite() -> Option<&'static SQLite> {
SQLITE.get()
}
const WASM: &[u8] = include_bytes!("jswasm/sqlite3.wasm");
#[derive(Serialize)]
struct InitOpts {
#[serde(rename = "wasmBinary")]
pub wasm_binary: &'static [u8],
#[serde(rename = "proxyUri")]
pub proxy_uri: String,
}
#[derive(Deserialize, Clone, Debug)]
pub struct Version {
#[serde(rename = "libVersion")]
pub lib_version: String,
#[serde(rename = "libVersionNumber")]
pub lib_version_number: u32,
#[serde(rename = "sourceId")]
pub source_id: String,
#[serde(rename = "downloadVersion")]
pub download_version: u32,
}
#[derive(Serialize, Clone, Debug)]
pub struct OpfsSAHPoolCfg {
#[serde(rename = "clearOnInit")]
pub clear_on_init: bool,
#[serde(rename = "initialCapacity")]
pub initial_capacity: u32,
pub directory: String,
pub name: String,
#[serde(rename = "forceReinitIfPreviouslyFailed")]
pub force_reinit_if_previously_failed: bool,
}
#[derive(Debug)]
pub enum SQLiteError {
InitModule(js_sys::Error),
Serde(serde_wasm_bindgen::Error),
InstallOpfsSAHPoolVfs(js_sys::Error),
}
impl Display for SQLiteError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::InitModule(msg) => f.debug_tuple("InitModule").field(msg).finish(),
Self::Serde(msg) => f.debug_tuple("Serde").field(msg).finish(),
Self::InstallOpfsSAHPoolVfs(msg) => {
f.debug_tuple("InstallOpfsSAHPoolVfs").field(msg).finish()
}
}
}
}
impl Error for SQLiteError {}
pub struct SQLite {
ffi: FragileComfirmed<wasm::SQLite>,
version: Version,
}
impl SQLite {
async fn new() -> Result<Self, SQLiteError> {
let proxy_uri = wasm_bindgen::link_to!(module = "/src/jswasm/sqlite3-opfs-async-proxy.js");
let opts = InitOpts {
wasm_binary: WASM,
proxy_uri,
};
let opts = serde_wasm_bindgen::to_value(&opts).map_err(SQLiteError::Serde)?;
let module = wasm::SQLite::init(&Object::from(opts))
.await
.map_err(SQLiteError::InitModule)?;
let sqlite = wasm::SQLite::new(module);
let version =
serde_wasm_bindgen::from_value(sqlite.version()).map_err(SQLiteError::Serde)?;
let sqlite = Self {
ffi: FragileComfirmed::new(sqlite),
version,
};
Ok(sqlite)
}
pub async fn install_opfs_sahpool(
&self,
cfg: Option<&OpfsSAHPoolCfg>,
) -> Result<OpfsSAHPoolUtil, SQLiteError> {
let cfg = cfg
.map(|cfg| {
serde_wasm_bindgen::to_value(&cfg)
.map_err(SQLiteError::Serde)
.map(Object::from)
})
.transpose()?;
self.ffi
.handle()
.install_opfs_sahpool(cfg.as_ref())
.await
.map_err(SQLiteError::InstallOpfsSAHPoolVfs)
}
#[must_use]
pub fn version(&self) -> &Version {
&self.version
}
#[must_use]
fn capi(&self) -> CApi {
self.ffi.handle().capi()
}
#[must_use]
fn wasm(&self) -> Wasm {
self.ffi.handle().wasm()
}
}
impl SQLite {
unsafe fn poke_buf(&self, src: &[u8], dst: *mut u8) {
let buf = wasm_bindgen::memory();
let mem = buf.unchecked_ref::<WebAssembly::Memory>();
self.ffi.poke_buf(mem, src.as_ptr(), dst, src.len() as u32)
}
unsafe fn peek<T>(&self, from: *mut u8, dst: &mut T) {
let dst = std::ptr::from_ref(dst) as *mut u8;
let slice = unsafe { std::slice::from_raw_parts_mut(dst, size_of::<T>()) };
self.peek_buf(from, slice);
}
unsafe fn peek_buf(&self, src: *const u8, dst: &mut [u8]) {
let buf = wasm_bindgen::memory();
let mem = buf.unchecked_ref::<WebAssembly::Memory>();
self.ffi
.peek_buf(mem, src, dst.as_mut_ptr(), dst.len() as u32)
}
}