# AGENTS.md - br-fields
> Database field standardization library for Rust. Generates SQL DDL for SQLite, MySQL, and PostgreSQL.
## Quick Reference
```bash
# Build
cargo build
cargo build --release
# Test
cargo test # Run all tests
cargo test str # Run tests matching "str"
cargo test int:: # Run tests in int module
cargo test -- --nocapture # Show println! output
cargo test <test_name> -- --exact # Run single test by exact name
# Lint & Format
cargo clippy --all-targets --all-features -- -D warnings
cargo fmt # Format code
cargo fmt -- --check # Check formatting without changes
# Pre-publish validation
cargo package --list # Check package contents
cargo publish --dry-run # Validate before publishing
```
## Project Structure
```
br-fields/
├── Cargo.toml # Package manifest (edition 2021)
├── src/
│ ├── lib.rs # Main: Field trait, FieldMode enum, field() & verify()
│ ├── str.rs # String types: Str, Pass, Key, Tel, Email, Code, BarCode, QrCode, Ident, Color
│ ├── int.rs # Integer types: Int, Switch
│ ├── float.rs # Float type
│ ├── text.rs # Text types: Text, Editor, Json, Array, Object, Url
│ ├── datetime.rs # DateTime types: Year, Datetime, Time, Date, Timestamp
│ ├── select.rs # Selection types: Radio, Select
│ ├── dict.rs # Dictionary reference type
│ ├── files.rs # File attachment type
│ ├── location.rs # Geolocation type
│ └── table.rs # Table reference types: Table, Tree
└── tests/
└── test.rs # Integration tests
```
## Code Style Guidelines
### Imports
```rust
// Standard library first (if any)
use std::sync::atomic::{AtomicU16, Ordering};
// External crates
use chrono::{DateTime, Local};
use json::{object, JsonValue};
use lazy_static::lazy_static;
use regex::Regex;
// Internal crate imports
use crate::Field;
use crate::datetime::Datetime;
```
### Naming Conventions
- **Structs**: PascalCase (`BarCode`, `Timestamp`, `JsonValue`)
- **Functions/Methods**: snake_case (`timestamp_to_datetime`, `random_pass`)
- **Fields**: snake_case (`field`, `require`, `def`, `mode`)
- **Modules**: snake_case (`datetime`, `select`, `str`)
- **Constants**: SCREAMING_SNAKE_CASE (`SEQ`, `CN_RE`)
### Struct Definition Pattern
```rust
/// Chinese description of the struct
///
/// * field_name description
/// * another_field description
#[derive(Debug, Clone)] // Add derives as needed
pub struct FieldType {
pub require: bool,
pub field: String,
pub mode: String,
pub title: String,
pub def: String, // Default value
pub length: usize,
pub show: bool,
pub describe: String,
pub example: JsonValue,
}
```
### Constructor Pattern
```rust
impl FieldType {
pub fn new(require: bool, field: &str, title: &str, default: &str) -> Self {
Self {
require,
field: field.to_string(),
mode: "typename".to_string(),
title: title.to_string(),
def: default.to_string(),
length: 0,
show: true,
describe: "".to_string(),
example: JsonValue::Null,
}
}
}
```
### Builder Pattern Methods
```rust
// Consuming self (for chaining during construction)
pub fn length(mut self, length: i32) -> Self {
self.length = length;
self
}
// Borrowing self (for modification after construction)
pub fn table_name(&mut self, name: &str) -> &mut Self {
self.table_name = name.to_string();
self
}
```
### Field Trait Implementation
```rust
impl Field for FieldType {
fn sql(&mut self, model: &str) -> String {
let not_null = if self.require { " not null" } else { "" };
match model {
"sqlite" => format!("{} varchar({}){} default '{}'",
self.field, self.length, not_null, self.def),
"pgsql" => format!(r#""{}" varchar({}){} default '{}'"#,
self.field, self.length, not_null, self.def),
_ => { // MySQL default
let sql = format!("`{}` varchar({}){} default '{}'",
self.field, self.length, not_null, self.def);
format!("{} comment '{}|{}|{}|{}'", sql, self.mode, self.require, self.title, self.def)
}
}
}
fn hide(&mut self) -> &mut Self {
self.show = false;
self
}
fn describe(&mut self, text: &str) -> &mut Self {
self.describe = text.to_string();
self
}
fn field(&mut self) -> JsonValue {
let mut field = object! {};
field.insert("require", JsonValue::from(self.require)).unwrap();
field.insert("field", JsonValue::from(self.field.clone())).unwrap();
// ... add all fields
field
}
fn swagger(&mut self) -> JsonValue {
object! {
"type": self.mode.clone(),
": self.example.clone(),
}
}
fn example(&mut self, data: JsonValue) -> &mut Self {
self.example = data.clone();
self
}
}
```
### Error Handling
- Use `.unwrap()` for JSON operations (this codebase assumes valid data)
- No explicit Result/Option error propagation in field methods
- Validation logic returns data as-is in default `verify()` implementation
### Regex with lazy_static
```rust
lazy_static! {
static ref EMAIL_RE: Regex =
Regex::new(r"(?i)^[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,}$").unwrap();
}
```
### Documentation Style
- Use Chinese for doc comments (this is a Chinese-language project)
- Use `///` for public API documentation
- List parameters with `* param_name description` format
### Test Pattern
```rust
#[cfg(test)]
mod tests {
#[test]
fn test_name() {
let res = module::Type::new(true, "field", "title", "").sql("sqlite");
assert_eq!("expected output", res);
// Use println! for debugging (run with --nocapture)
println!("{res}");
}
}
```
## Database Support
The library generates SQL for three database types:
- `"sqlite"` - SQLite syntax (unquoted field names)
- `"pgsql"` - PostgreSQL syntax (double-quoted field names)
- `"mysql"` or default - MySQL syntax (backtick-quoted, with comments)
## Dependencies
| `json` | JSON serialization/deserialization |
| `chrono` | Date/time handling |
| `log` | Logging facade |
| `rand` | Random number generation |
| `lazy_static` | Lazy static initialization |
| `regex` | Regular expression matching |
## Common Patterns
### Adding a New Field Type
1. Create struct with standard fields (require, field, mode, title, def, show, describe, example)
2. Implement `new()` constructor
3. Add builder methods as needed
4. Implement `Field` trait with `sql()`, `hide()`, `describe()`, `field()`, `swagger()`, `example()`
5. Add to `FieldMode` enum in `lib.rs`
6. Add match arm in `field()` and `verify()` functions in `lib.rs`
7. Add tests in `tests/test.rs`
### SQL Generation Pattern
- SQLite: Simple field names, basic types
- PostgreSQL: Double-quoted identifiers, PostgreSQL-specific types
- MySQL: Backtick-quoted identifiers, includes metadata in comments