rust-d1-orm 0.0.1

Query builder / ORM for Cloudflare D1, targeting wasm32-unknown-unknown
Documentation
use crate::{error::OrmError, model::D1Model, query::Query, set::Set};
use serde::Deserialize;
use std::marker::PhantomData;
use worker::D1Database;

pub struct Table<'db, M: D1Model> {
    db: &'db D1Database,
    _m: PhantomData<M>,
}

impl<'db, M: D1Model> Table<'db, M> {
    pub fn new(db: &'db D1Database) -> Self {
        Self { db, _m: PhantomData }
    }

    pub async fn insert(&self, model: &M) -> Result<M, OrmError> {
        let cols = M::COLUMNS.join(", ");
        let placeholders = (1..=M::COLUMNS.len()).map(|i| format!("?{}", i)).collect::<Vec<_>>().join(", ");
        let sql = format!("INSERT INTO {} ({}) VALUES ({}) RETURNING *", M::TABLE, cols, placeholders);
        self.db.prepare(&sql)
            .bind(&model.values())
            .map_err(|_| OrmError::Bind)?
            .first::<M>(None).await
            .map_err(|_| OrmError::Execute)?
            .ok_or(OrmError::Execute)
    }

    pub async fn insert_batch(&self, models: &[M]) -> Result<Vec<M>, OrmError> {
        if models.is_empty() { return Ok(vec![]); }
        let cols = M::COLUMNS.join(", ");
        let placeholders = (1..=M::COLUMNS.len()).map(|i| format!("?{}", i)).collect::<Vec<_>>().join(", ");
        let sql = format!("INSERT INTO {} ({}) VALUES ({}) RETURNING *", M::TABLE, cols, placeholders);
        let stmts = models.iter().map(|m| {
            self.db.prepare(&sql).bind(&m.values()).map_err(|_| OrmError::Bind)
        }).collect::<Result<Vec<_>, _>>()?;
        let results = self.db.batch(stmts).await.map_err(|_| OrmError::Execute)?;
        results.into_iter().map(|r| {
            r.results::<M>().map_err(|_| OrmError::Deserialize)?
                .into_iter().next().ok_or(OrmError::Execute)
        }).collect()
    }

    pub async fn find_one(&self, query: Query) -> Result<Option<M>, OrmError> {
        let cols = M::COLUMNS.join(", ");
        let (where_parts, values) = query.build_conditions(1);
        let where_sql = if where_parts.is_empty() { String::new() } else { format!("WHERE {}", where_parts) };
        let sql = format!("SELECT {} FROM {} {}", cols, M::TABLE, where_sql);
        self.db.prepare(&sql)
            .bind(&values)
            .map_err(|_| OrmError::Bind)?
            .first::<M>(None).await
            .map_err(|_| OrmError::Execute)
    }

    pub async fn find_all(&self, query: Query) -> Result<Vec<M>, OrmError> {
        let cols = M::COLUMNS.join(", ");
        let (where_parts, values) = query.build_conditions(1);
        let where_sql = if where_parts.is_empty() { String::new() } else { format!("WHERE {}", where_parts) };
        let tail = query.build_tail();
        let sql = format!("SELECT {} FROM {} {}{}", cols, M::TABLE, where_sql, tail);
        let result = self.db.prepare(&sql)
            .bind(&values)
            .map_err(|_| OrmError::Bind)?
            .all().await
            .map_err(|_| OrmError::Execute)?;
        result.results::<M>().map_err(|_| OrmError::Deserialize)
    }

    pub async fn update(&self, set: Set, query: Query) -> Result<Option<M>, OrmError> {
        if set.is_empty() { return Ok(None); }
        let (set_sql, mut values, next_n) = set.build(1);
        let (where_parts, where_vals) = query.build_conditions(next_n);
        values.extend(where_vals);
        let where_sql = if where_parts.is_empty() { String::new() } else { format!("WHERE {}", where_parts) };
        let sql = format!("UPDATE {} SET {} {} RETURNING *", M::TABLE, set_sql, where_sql);
        self.db.prepare(&sql)
            .bind(&values)
            .map_err(|_| OrmError::Bind)?
            .first::<M>(None).await
            .map_err(|_| OrmError::Execute)
    }

    pub async fn delete(&self, query: Query) -> Result<(), OrmError> {
        let (where_parts, values) = query.build_conditions(1);
        let where_sql = if where_parts.is_empty() { String::new() } else { format!("WHERE {}", where_parts) };
        let sql = format!("DELETE FROM {} {}", M::TABLE, where_sql);
        self.db.prepare(&sql)
            .bind(&values)
            .map_err(|_| OrmError::Bind)?
            .run().await
            .map_err(|_| OrmError::Execute)?;
        Ok(())
    }

    pub async fn count(&self, query: Query) -> Result<u64, OrmError> {
        #[derive(Deserialize)]
        struct CountRow { count: u64 }
        let (where_parts, values) = query.build_conditions(1);
        let where_sql = if where_parts.is_empty() { String::new() } else { format!("WHERE {}", where_parts) };
        let sql = format!("SELECT COUNT(*) as count FROM {} {}", M::TABLE, where_sql);
        let row = self.db.prepare(&sql)
            .bind(&values)
            .map_err(|_| OrmError::Bind)?
            .first::<CountRow>(None).await
            .map_err(|_| OrmError::Execute)?;
        Ok(row.map(|r| r.count).unwrap_or(0))
    }
}