6.0 KiB
6.0 KiB
CLAUDE.md
This file provides guidance to Claude Code when working with this repository.
Project Overview
sqlx-record is a Rust library that provides derive macros for automatic CRUD operations and comprehensive audit trails for SQL entities. It supports MySQL, PostgreSQL, and SQLite via SQLx, tracking who changed what, when, and why with actor, session, and change set metadata.
Repository: https://git.awesomike.com/pub/sqlx-record.git
Architecture
Workspace Structure
sqlx-record/
├── src/ # Core library
│ ├── lib.rs # Public API exports and prelude
│ ├── models.rs # EntityChange struct, Action enum
│ ├── repositories.rs # Database query functions for entity changes
│ ├── value.rs # Type-safe Value enum, bind functions
│ ├── filter.rs # Filter enum for query conditions
│ └── helpers.rs # Utility macros
├── sqlx-record-derive/ # Procedural macro crate
│ └── src/
│ ├── lib.rs # #[derive(Entity, Update)] implementation
│ └── string_utils.rs # Pluralization helpers
├── sqlx-record-ctl/ # CLI tool for audit table management
│ └── src/main.rs
└── Cargo.toml # Workspace root
Feature Flags
derive: Enables#[derive(Entity, Update)]procedural macrosstatic-validation: Enables compile-time SQLx query validationmysql: MySQL database supportpostgres: PostgreSQL database supportsqlite: SQLite database support
Note: You must enable at least one database feature.
Development Commands
# Build with MySQL
cargo build --features mysql
# Build with derive macros
cargo build --features "mysql,derive"
# Build CLI tool
cargo build -p sqlx-record-ctl --features mysql
# Test
cargo test --features mysql
# Release tag
make tag
Derive Macro API
Entity Attributes
#[derive(Entity, FromRow)]
#[table_name = "users"] // or #[table_name("users")]
struct User {
#[primary_key] // Mark primary key field
id: Uuid,
#[rename("user_name")] // Map to different DB column
name: String,
#[version] // Auto-increment on update
version: u32,
#[field_type("BIGINT")] // SQLx type hint
count: i64,
}
Generated Methods
Insert:
insert(&pool) -> Result<PkType, Error>
Get:
get_by_{pk}(&pool, &pk) -> Result<Option<Self>, Error>get_by_{pks}(&pool, &[pk]) -> Result<Vec<Self>, Error>get_by_primary_key(&pool, &pk) -> Result<Option<Self>, Error>
Find:
find(&pool, filters, index) -> Result<Vec<Self>, Error>find_one(&pool, filters, index) -> Result<Option<Self>, Error>find_ordered(&pool, filters, index, order_by) -> Result<Vec<Self>, Error>find_ordered_with_limit(&pool, filters, index, order_by, offset_limit) -> Result<Vec<Self>, Error>count(&pool, filters, index) -> Result<u64, Error>
Update:
update(&self, &pool, form) -> Result<(), Error>update_by_{pk}(&pool, &pk, form) -> Result<(), Error>update_by_{pks}(&pool, &[pk], form) -> Result<(), Error>update_form() -> UpdateForm- Creates builder
Diff:
model_diff(&form, &model) -> serde_json::Valuedb_diff(&form, &pk, &pool) -> Result<serde_json::Value, Error>diff_modify(&mut form, &model) -> serde_json::Valueto_update_form(&self) -> UpdateForminitial_diff(&self) -> serde_json::Value
Metadata:
table_name() -> &'static strentity_key(&pk) -> Stringentity_changes_table_name() -> Stringprimary_key_field() -> &'static strprimary_key_db_field() -> &'static strprimary_key(&self) -> &PkTypeselect_fields() -> Vec<&'static str>
Version (if #[version] field exists):
get_version(&pool, &pk) -> Result<Option<VersionType>, Error>get_versions(&pool, &[pk]) -> Result<HashMap<PkType, VersionType>, Error>
Filter API
use sqlx_record::prelude::*;
// Simple filters
let f = filters![("active", true), ("role", "admin")];
// Compound filters
let f = filter_or![("status", "active"), ("status", "pending")];
let f = filter_and![("age", 18), ("verified", true)];
// Filter enum variants
Filter::Equal("field", value)
Filter::NotEqual("field", value)
Filter::GreaterThan("field", value)
Filter::LessThan("field", value)
Filter::Like("field", pattern)
Filter::ILike("field", pattern) // Case-insensitive
Filter::In("field", vec![values])
Filter::NotIn("field", vec![values])
Filter::IsNull("field")
Filter::IsNotNull("field")
Filter::And(vec![filters])
Filter::Or(vec![filters])
Database Differences
| Feature | MySQL | PostgreSQL | SQLite |
|---|---|---|---|
| Placeholder | ? |
$1, $2, ... |
? |
| Table quote | ` |
" |
" |
| UUID type | BINARY(16) |
UUID |
BLOB |
| JSON type | JSON |
JSONB |
TEXT |
| ILIKE | LOWER() LIKE LOWER() |
Native | LOWER() LIKE LOWER() |
| Index hints | USE INDEX() |
N/A | N/A |
Value Types
The Value enum supports:
- Integers:
Int8,Uint8,Int16,Uint16,Int32,Uint32,Int64,Uint64 String,Bool,VecU8UuidNaiveDate,NaiveDateTime
Entity Changes (Audit Trail)
The EntityChange struct tracks:
id: Change record UUIDentity_id: Target entity UUIDaction: insert/update/delete/restore/hard-deletechanged_at: Timestamp (milliseconds)actor_id: Who made the changesession_id: Session contextchange_set_id: Transaction groupingnew_value: JSON payload of changes
Important Notes
- Always enable a database feature (
mysql,postgres, orsqlite) - The
preludemodule exports commonly used items includingplaceholder()function - Query filters use
Filter::build_where_clause()internally - Version fields auto-increment with overflow wrapping
- The CLI tool requires a
entity_changes_metadatatable withtable_nameandis_auditablecolumns