sqlw 0.1.0

Compile-time SQL query building with schema-safe field references and automatic parameter binding
Documentation
use sqlw::*;

#[derive(Debug)]
pub struct User;

impl Schema for User {}

impl User {
    pub const TABLE: Def<Self> = Def::new("user");
    pub const NAME: Def<Self> = Def::new("name");
    pub const AGE: Def<Self> = Def::new("age");
    pub const HEIGHT: Def<Self> = Def::new("height");
    pub const ACTIVE: Def<Self> = Def::new("active");
}

schema!(
    /// Represents a product # Comment test
    Product "product" {
    /// Primary identifier # Comment test
    ID: i64 "id",
    TITLE: String "title",
    PRICE: f64 "price",
    IN_STOCK: bool "in_stock"
});

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_schema_creation() {
        assert_eq!(User::TABLE.desc(), "user");
        assert_eq!(Product::TABLE.desc(), "product");
    }

    #[test]
    fn test_field_types() {
        assert_eq!(User::NAME.desc(), "name");
        assert_eq!(User::AGE.desc(), "age");
        assert_eq!(User::HEIGHT.desc(), "height");
        assert_eq!(User::ACTIVE.desc(), "active");

        assert_eq!(Product::ID.desc(), "id");
        assert_eq!(Product::TITLE.desc(), "title");
        assert_eq!(Product::PRICE.desc(), "price");
        assert_eq!(Product::IN_STOCK.desc(), "in_stock");
    }

    #[test]
    fn test_schema_impl() {
        let user_schema = User::TABLE;
        assert_eq!(user_schema.desc(), "user");

        let product_schema = Product::TABLE;
        assert_eq!(product_schema.desc(), "product");
    }

    #[test]
    fn test_value_struct_fields() {
        let product = Product {
            id: 1,
            title: "Widget".to_string(),
            price: 9.99,
            in_stock: true,
        };
        assert_eq!(product.id, 1);
        assert_eq!(product.title, "Widget");
        assert_eq!(product.price, 9.99);
        assert!(product.in_stock);

        let default_product = Product::default();
        assert_eq!(default_product.id, 0);
        assert_eq!(default_product.title, String::new());
        assert_eq!(default_product.price, 0.0);
        assert!(!default_product.in_stock);
    }

    #[test]
    fn test_from_row_impl() {
        fn assert_from_row<T: sqlw::FromRow>() {}
        assert_from_row::<Product>();
    }

    #[test]
    fn test_custom_from_row() {
        use sqlw::{RowCell, RowLike, RowError, Value};

        #[derive(Debug, PartialEq)]
        struct UserSummary {
            name: String,
            is_adult: bool,
            post_count: i64,
        }

        impl sqlw::FromRow for UserSummary {
            fn from_row<R: RowLike>(row: &R) -> Result<Self, RowError> {
                let name: String = row.get_typed("name")?;
                let age: i64 = row.get_typed("age")?;
                let post_count: i64 = row.get_typed("post_count")?;
                Ok(UserSummary {
                    name,
                    is_adult: age >= 18,
                    post_count,
                })
            }
        }

        struct MockRow;

        impl RowLike for MockRow {
            fn cell<'a>(&'a self, name: &str) -> Result<RowCell<'a>, RowError> {
                match name {
                    "name" => Ok(RowCell::Owned(Value::Text("Alice".into()))),
                    "age" => Ok(RowCell::Owned(Value::Int(25))),
                    "post_count" => Ok(RowCell::Owned(Value::Int(3))),
                    _ => Err(RowError::ColumnNotFound { name: name.into() }),
                }
            }
        }

        let summary = UserSummary::from_row(&MockRow).unwrap();
        assert_eq!(summary.name, "Alice");
        assert!(summary.is_adult);
        assert_eq!(summary.post_count, 3);
    }
}