wasm-sql 0.1.6

Wasmtime host implementation for a SQL component WIT interface. Enables Wasm components to interact with SQL databases via the WebAssembly Component Model.
Documentation
use std::sync::Arc;

use sqlx::pool::PoolConnection;
use tokio::sync::RwLock;

use crate::{
    core::bindings::{
        SqlHostState,
        error::IgnoreNotPresent,
        executor::{ErasedExecutor, QueryOrRaw},
        generated::wasm_sql::core::{
            connection::Connection, transaction::Transaction, util_types::Error,
        },
        transaction::{ConnectionBoundTask, TransactionCommand},
    },
    execute_with,
    sqldb::SqlDatabase,
};

use crate::core::bindings::transaction::TransactionImpl;

#[derive(Clone)]
#[allow(dead_code)]
pub struct ConnectionImpl {
    pub(crate) connection: Arc<RwLock<PoolConnection<SqlDatabase>>>,
}

impl crate::core::bindings::generated::wasm_sql::core::connection::Host for SqlHostState {}

impl crate::core::bindings::generated::wasm_sql::core::connection::HostConnection for SqlHostState {
    async fn drop(
        &mut self,
        rep: wasmtime::component::Resource<Connection>,
    ) -> wasmtime::Result<()> {
        self.table.delete(rep).option_not_present()?;
        Ok(())
    }

    async fn release(&mut self, _this: wasmtime::component::Resource<Connection>) {
        let _ = self.drop(_this).await;
    }
}

impl crate::core::bindings::generated::wasm_sql::core::connection::HostConnectionWithStore
    for SqlHostState
{
    async fn begin_transaction<T>(
        accessor: &wasmtime::component::Accessor<T, Self>,
        self_: wasmtime::component::Resource<Connection>,
    ) -> Result<wasmtime::component::Resource<Transaction>, Error> {
        let (sender, receiver) = tokio::sync::mpsc::channel::<TransactionCommand>(1);
        let handle = accessor.spawn(ConnectionBoundTask {
            resource: self_,
            receiver,
        });

        let tx_impl = TransactionImpl::ConnectionBound {
            handle: Arc::new(handle),
            sender,
        };

        let resource = accessor.with(|mut access| {
            let state = access.get();

            state.table.push(tx_impl)
        })?;

        return Ok(resource);
    }
}

impl ErasedExecutor<SqlHostState> for ConnectionImpl {
    async fn fetch_all<T>(
        &self,
        query: QueryOrRaw,
        accessor: &wasmtime::component::Accessor<T, SqlHostState>,
    ) -> Result<Vec<<SqlDatabase as sqlx::Database>::Row>, Error> {
        let mut guard = self.connection.write().await;

        execute_with!(guard, accessor, query, fetch_all)
    }

    async fn execute<T>(
        &self,
        query: QueryOrRaw,
        accessor: &wasmtime::component::Accessor<T, SqlHostState>,
    ) -> Result<<SqlDatabase as sqlx::Database>::QueryResult, Error> {
        let mut guard = self.connection.write().await;

        execute_with!(guard, accessor, query, execute)
    }
}