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 crate::core::bindings::{
    SqlHostState,
    codec_utils,
    generated::wasm_sql::core::{
        codecs::{PushResult, ValuePosition},
        query::QueryResults,
        query_types::SqlArguments,
        util_types::Error,
    },
};
use crate::sqlite::bindings::generated::wasm_sql::sqlite::codecs::{
    Date, Datetime, DatetimeUtc, Time, Uuid,
};
use chrono::{DateTime, NaiveDate, NaiveDateTime, NaiveTime, Utc};
use sqlx::types::Json;
use sqlx::types::JsonRawValue;

impl crate::sqlite::bindings::generated::wasm_sql::sqlite::codecs::Host for SqlHostState {
    // === Raw storage types ===

    fn push_int64(
        &mut self,
        value: Option<i64>,
        to: wasmtime::component::Resource<SqlArguments>,
    ) -> PushResult {
        codec_utils::encode(value, self.table.get(&to)?)
    }

    fn get_int64(
        &mut self,
        result: wasmtime::component::Resource<QueryResults>,
        position: ValuePosition,
    ) -> Result<Option<i64>, Error> {
        codec_utils::decode(self.table.get(&result)?, position)
    }

    fn push_float64(
        &mut self,
        value: Option<f64>,
        to: wasmtime::component::Resource<SqlArguments>,
    ) -> PushResult {
        codec_utils::encode(value, self.table.get(&to)?)
    }

    fn get_float64(
        &mut self,
        result: wasmtime::component::Resource<QueryResults>,
        position: ValuePosition,
    ) -> Result<Option<f64>, Error> {
        codec_utils::decode(self.table.get(&result)?, position)
    }

    fn push_string(
        &mut self,
        value: Option<wasmtime::component::__internal::String>,
        to: wasmtime::component::Resource<SqlArguments>,
    ) -> PushResult {
        codec_utils::encode(value, self.table.get(&to)?)
    }

    fn get_string(
        &mut self,
        result: wasmtime::component::Resource<QueryResults>,
        position: ValuePosition,
    ) -> Result<Option<wasmtime::component::__internal::String>, Error> {
        codec_utils::decode(self.table.get(&result)?, position)
    }

    fn push_blob(
        &mut self,
        value: Option<Vec<u8>>,
        to: wasmtime::component::Resource<SqlArguments>,
    ) -> PushResult {
        codec_utils::encode(value, self.table.get(&to)?)
    }

    fn get_blob(
        &mut self,
        result: wasmtime::component::Resource<QueryResults>,
        position: ValuePosition,
    ) -> Result<Option<Vec<u8>>, Error> {
        codec_utils::decode(self.table.get(&result)?, position)
    }

    // === Semantic types ===

    fn push_bool(
        &mut self,
        value: Option<bool>,
        to: wasmtime::component::Resource<SqlArguments>,
    ) -> PushResult {
        codec_utils::encode(value, self.table.get(&to)?)
    }

    fn get_bool(
        &mut self,
        result: wasmtime::component::Resource<QueryResults>,
        position: ValuePosition,
    ) -> Result<Option<bool>, Error> {
        codec_utils::decode(self.table.get(&result)?, position)
    }

    fn push_json(
        &mut self,
        value: Option<wasmtime::component::__internal::String>,
        to: wasmtime::component::Resource<SqlArguments>,
    ) -> PushResult {
        let to = self.table.get(&to)?;

        match value {
            Some(value) => {
                let raw_value =
                    JsonRawValue::from_string(value).map_err(|e| Error::Encode(e.to_string()))?;

                codec_utils::encode(Json(raw_value), to)
            }
            None => codec_utils::encode(None::<Json<Box<JsonRawValue>>>, to),
        }
    }

    fn get_json(
        &mut self,
        result: wasmtime::component::Resource<QueryResults>,
        position: ValuePosition,
    ) -> Result<Option<wasmtime::component::__internal::String>, Error> {
        let a =
            codec_utils::decode::<Option<&JsonRawValue>>(self.table.get(&result)?, position)?;

        Ok(a.map(|x| x.get().to_string()))
    }

    fn push_uuid(
        &mut self,
        value: Option<Uuid>,
        to: wasmtime::component::Resource<SqlArguments>,
    ) -> PushResult {
        let value = value
            .map(|v| sqlx::types::Uuid::try_parse(v.as_str()))
            .transpose()
            .map_err(|e| Error::Encode(e.to_string()))?;

        codec_utils::encode(value, self.table.get(&to)?)
    }

    fn get_uuid(
        &mut self,
        result: wasmtime::component::Resource<QueryResults>,
        position: ValuePosition,
    ) -> Result<Option<Uuid>, Error> {
        let value: Option<sqlx::types::Uuid> =
            codec_utils::decode(self.table.get(&result)?, position)?;

        Ok(value.map(|v| v.to_string()))
    }

    fn push_date(
        &mut self,
        value: Option<Date>,
        to: wasmtime::component::Resource<SqlArguments>,
    ) -> PushResult {
        let value = value
            .map(|v| NaiveDate::parse_from_str(&v, "%Y-%m-%d"))
            .transpose()
            .map_err(|e| Error::Encode(e.to_string()))?;

        codec_utils::encode(value, self.table.get(&to)?)
    }

    fn get_date(
        &mut self,
        result: wasmtime::component::Resource<QueryResults>,
        position: ValuePosition,
    ) -> Result<Option<Date>, Error> {
        let value: Option<NaiveDate> =
            codec_utils::decode(self.table.get(&result)?, position)?;

        Ok(value.map(|v| v.format("%Y-%m-%d").to_string()))
    }

    fn push_time(
        &mut self,
        value: Option<Time>,
        to: wasmtime::component::Resource<SqlArguments>,
    ) -> PushResult {
        let value = value
            .map(|v| NaiveTime::parse_from_str(&v, "%H:%M:%S%.f"))
            .transpose()
            .map_err(|e| Error::Encode(e.to_string()))?;

        codec_utils::encode(value, self.table.get(&to)?)
    }

    fn get_time(
        &mut self,
        result: wasmtime::component::Resource<QueryResults>,
        position: ValuePosition,
    ) -> Result<Option<Time>, Error> {
        let value: Option<NaiveTime> =
            codec_utils::decode(self.table.get(&result)?, position)?;

        Ok(value.map(|v| v.format("%H:%M:%S%.f").to_string()))
    }

    fn push_datetime(
        &mut self,
        value: Option<Datetime>,
        to: wasmtime::component::Resource<SqlArguments>,
    ) -> PushResult {
        let value = value
            .map(|v| NaiveDateTime::parse_from_str(&v, "%Y-%m-%dT%H:%M:%S%.f"))
            .transpose()
            .map_err(|e| Error::Encode(e.to_string()))?;

        codec_utils::encode(value, self.table.get(&to)?)
    }

    fn get_datetime(
        &mut self,
        result: wasmtime::component::Resource<QueryResults>,
        position: ValuePosition,
    ) -> Result<Option<Datetime>, Error> {
        let value: Option<NaiveDateTime> =
            codec_utils::decode(self.table.get(&result)?, position)?;

        Ok(value.map(|v| v.format("%Y-%m-%dT%H:%M:%S%.f").to_string()))
    }

    fn push_datetime_utc(
        &mut self,
        value: Option<DatetimeUtc>,
        to: wasmtime::component::Resource<SqlArguments>,
    ) -> PushResult {
        let value = value
            .map(|v| DateTime::parse_from_rfc3339(&v).map(|dt| dt.with_timezone(&Utc)))
            .transpose()
            .map_err(|e| Error::Encode(e.to_string()))?;

        codec_utils::encode(value, self.table.get(&to)?)
    }

    fn get_datetime_utc(
        &mut self,
        result: wasmtime::component::Resource<QueryResults>,
        position: ValuePosition,
    ) -> Result<Option<DatetimeUtc>, Error> {
        let value: Option<DateTime<Utc>> =
            codec_utils::decode(self.table.get(&result)?, position)?;

        Ok(value.map(|v| v.to_rfc3339()))
    }
}