sqlx-record/.claude/skills/sqlx-record.md

223 lines
5.1 KiB
Markdown

# sqlx-record Skill
Expert guidance for using the sqlx-record Rust library.
## Triggers
- "create entity", "define entity", "entity struct"
- "sqlx record", "sqlx-record"
- "crud operations", "database entity"
- "audit trail", "change tracking"
- "lookup table", "lookup options"
## Overview
sqlx-record provides derive macros for automatic CRUD operations and audit trails for SQL entities. It supports MySQL, PostgreSQL, and SQLite.
## Quick Reference
### Entity Definition
```rust
use sqlx_record::prelude::*;
use sqlx::FromRow;
#[derive(Entity, FromRow)]
#[table_name = "users"]
struct User {
#[primary_key]
id: Uuid,
#[rename("user_name")] // Maps to different DB column
name: String,
#[version] // Auto-increment on updates
version: u32,
#[field_type("TEXT")] // SQLx type hint
bio: Option<String>,
}
```
### CRUD Operations
```rust
// Insert
let user = User { id: new_uuid(), name: "Alice".into(), version: 0 };
user.insert(&pool).await?;
// Get
let user = User::get_by_id(&pool, &id).await?;
let users = User::get_by_ids(&pool, &ids).await?;
// Find with filters
let users = User::find(&pool, filters![("is_active", true)], None).await?;
let user = User::find_one(&pool, filters![("email", email)], None).await?;
// Find with ordering and pagination
let page = User::find_ordered_with_limit(
&pool,
filters![("role", "admin")],
None,
vec![("created_at", false)], // DESC
Some((0, 10)) // offset, limit
).await?;
// Count
let count = User::count(&pool, filters![("is_active", true)], None).await?;
// Update
User::update_by_id(&pool, &id, User::update_form().with_name("Bob")).await?;
```
### Filter System
```rust
// Simple equality
filters![("field", value)]
// Multiple conditions (AND)
filters![("active", true), ("role", "admin")]
// OR conditions
filter_or![("status", "active"), ("status", "pending")]
// Operators
"age".gt(18) // >
"age".ge(18) // >=
"age".lt(65) // <
"age".le(65) // <=
"name".eq("Bob") // =
"name".ne("Bob") // !=
// Other filters
Filter::Like("name", "%alice%".into())
Filter::In("status", vec!["active".into(), "pending".into()])
Filter::IsNull("deleted_at")
Filter::IsNotNull("email")
```
### Lookup Tables
```rust
// With database entity
lookup_table!(OrderStatus, "pending", "shipped", "delivered");
// Generates: struct OrderStatus, enum OrderStatusCode, constants
// Without database entity
lookup_options!(PaymentMethod, "credit-card", "paypal", "bank-transfer");
// Generates: enum PaymentMethodCode, struct PaymentMethod (constants only)
// Usage
let status = OrderStatus::PENDING; // "pending"
let code = OrderStatusCode::try_from("pending")?; // OrderStatusCode::Pending
```
### Time-Ordered UUIDs
```rust
let id = new_uuid(); // Timestamp prefix for better indexing
```
## Feature Flags
```toml
[dependencies]
sqlx-record = { version = "0.3", features = ["mysql", "derive"] }
# Database: "mysql", "postgres", or "sqlite" (pick one)
# Optional: "derive", "static-validation"
```
## Soft Delete, Timestamps, Batch Operations
```rust
#[derive(Entity, FromRow)]
struct User {
#[primary_key] id: Uuid,
name: String,
#[soft_delete] // Enables delete/restore/hard_delete
is_deleted: bool,
#[created_at] // Auto-set on insert
created_at: i64,
#[updated_at] // Auto-set on update
updated_at: i64,
}
// Soft delete
user.delete(&pool).await?; // is_deleted = true
user.restore(&pool).await?; // is_deleted = false
user.hard_delete(&pool).await?; // DELETE FROM
// Batch insert
User::insert_many(&pool, &users).await?;
// Upsert (insert or update on conflict)
user.upsert(&pool).await?;
```
## Pagination
```rust
use sqlx_record::prelude::{Page, PageRequest};
let page = User::paginate(&pool, filters![], None,
vec![("name", true)], PageRequest::new(1, 20)).await?;
page.items // Vec<User>
page.total_count // Total matching records
page.total_pages() // Calculated pages
page.has_next() // bool
page.has_prev() // bool
```
## Transaction Helper
```rust
use sqlx_record::transaction;
transaction!(&pool, |tx| {
user.insert(&mut *tx).await?;
order.insert(&mut *tx).await?;
Ok::<_, sqlx::Error>(())
}).await?;
```
## Advanced Updates (UpdateExpr)
```rust
use sqlx_record::prelude::UpdateExpr;
// Arithmetic: score = score + 10
User::update_by_id(&pool, &id,
User::update_form().eval_score(UpdateExpr::Add(10.into()))
).await?;
// CASE/WHEN
User::update_by_id(&pool, &id,
User::update_form().eval_tier(UpdateExpr::Case {
branches: vec![("score".gt(100), "gold".into())],
default: "bronze".into(),
})
).await?;
// Raw SQL escape hatch
User::update_by_id(&pool, &id,
User::update_form().raw("computed", "COALESCE(a, 0) + b")
).await?;
```
## ConnProvider (Flexible Connections)
```rust
use sqlx_record::ConnProvider;
// Borrowed or owned pool connections
let conn = ConnProvider::Borrowed(&pool);
let users = User::find(&*conn, filters![], None).await?;
```
## Database Differences
| Feature | MySQL | PostgreSQL | SQLite |
|---------|-------|------------|--------|
| Placeholder | `?` | `$1, $2` | `?` |
| Table quote | `` ` `` | `"` | `"` |
| Index hints | Supported | N/A | N/A |