vtx-sdk 0.1.14

Official SDK for developing VTX plugins using Rust and WebAssembly.
Documentation
//! Host-side SQL helpers.

use crate::bindings::vtx::api::vtx_sql::{self, DbValue};
use crate::error::{VtxError, VtxResult};
use serde::de::DeserializeOwned;

/// Trait: Converts Rust types to the `DbValue` defined by WIT.
///
/// Suitable for passing parameters across database boundaries.
pub trait ToDbValue {
    fn to_db_value(&self) -> DbValue;
}

// --- Implementation of mapping basic types to DbValue ---

impl ToDbValue for String {
    fn to_db_value(&self) -> DbValue {
        DbValue::Text(self.clone())
    }
}

impl ToDbValue for &str {
    fn to_db_value(&self) -> DbValue {
        DbValue::Text(self.to_string())
    }
}

impl ToDbValue for i64 {
    fn to_db_value(&self) -> DbValue {
        DbValue::Integer(*self)
    }
}

impl ToDbValue for i32 {
    fn to_db_value(&self) -> DbValue {
        DbValue::Integer(*self as i64)
    }
}

impl ToDbValue for f64 {
    fn to_db_value(&self) -> DbValue {
        DbValue::Real(*self)
    }
}

impl ToDbValue for f32 {
    fn to_db_value(&self) -> DbValue {
        DbValue::Real(*self as f64)
    }
}

impl ToDbValue for u64 {
    fn to_db_value(&self) -> DbValue {
        DbValue::Integer(*self as i64)
    }
}

impl ToDbValue for u32 {
    fn to_db_value(&self) -> DbValue {
        DbValue::Integer(*self as i64)
    }
}

impl ToDbValue for bool {
    fn to_db_value(&self) -> DbValue {
        DbValue::Integer(if *self { 1 } else { 0 })
    }
}

impl ToDbValue for () {
    fn to_db_value(&self) -> DbValue {
        DbValue::NullVal
    }
}

impl<T: ToDbValue> ToDbValue for Option<T> {
    fn to_db_value(&self) -> DbValue {
        match self {
            Some(v) => v.to_db_value(),
            None => DbValue::NullVal,
        }
    }
}

/// Executes non-query SQL statements (INSERT / UPDATE / DELETE).
///
/// # Parameters
/// - `sql`: Raw SQL string, supports `?` placeholders.
/// - `params`: Array of parameters, elements must implement `ToDbValue`.
///
/// # Returns
/// - Success: Returns the number of affected rows.
/// - Failure: Maps to `VtxError::DatabaseError`.
///
/// # Note
/// Calling this interface is prohibited under the Restricted security policy.
pub fn execute(sql: &str, params: &[&dyn ToDbValue]) -> VtxResult<u64> {
    let wit_params: Vec<DbValue> = params.iter().map(|p| p.to_db_value()).collect();

    vtx_sql::execute(sql, &wit_params).map_err(|e| {
        if e.to_lowercase().contains("permission denied") {
            VtxError::PermissionDenied(e)
        } else {
            VtxError::DatabaseError(e)
        }
    })
}

/// Executes query SQL statements (SELECT) and deserializes the result into a list of target types.
///
/// # Parameters
/// - `sql`: SQL string (supports `?` placeholders).
/// - `params`: Array of parameters (implementing `ToDbValue`).
///
/// # Returns
/// - Success: Collection of deserialized results.
/// - Failure: `DatabaseError` or `SerializationError`.
///
/// # Notes
/// - The host interface returns a JSON string.
/// - To ensure performance, it is recommended to limit a single response to within 1MB (pagination via LIMIT is suggested).
/// - Generic `T` must implement `DeserializeOwned` (lifetimes are not required).
pub fn query<T: DeserializeOwned>(sql: &str, params: &[&dyn ToDbValue]) -> VtxResult<Vec<T>> {
    let wit_params: Vec<DbValue> = params.iter().map(|p| p.to_db_value()).collect();

    let json_str = vtx_sql::query_json(sql, &wit_params).map_err(|e| {
        if e.to_lowercase().contains("permission denied") {
            VtxError::PermissionDenied(e)
        } else {
            VtxError::DatabaseError(e)
        }
    })?;

    serde_json::from_str(&json_str).map_err(|e| VtxError::SerializationError(e.to_string()))
}