br-cache 0.3.22

This is an Cache and Message Queue abstraction layer
Documentation
# AGENTS.md - br-cache

**Generated:** 2025-02-07 | **Commit:** 0bf8955 | **Branch:** main

> Redis cache abstraction library for Rust. Unified interface for cache operations.

## Quick Reference

```bash
# Build
cargo build
cargo build --release

# Test (requires Redis on localhost:6379)
cargo test
cargo test <test_name> -- --nocapture  # Single test with output

# Lint & Format
cargo clippy --all-targets --all-features -- -D warnings
cargo fmt --check

# Pre-publish
cargo package --list
cargo publish --dry-run

# Run examples
cargo run --example index
cargo run --example redis_pool
cargo run --example subscribe
```

### Redis Setup (Required)
```bash
docker run -d --name dev-redis -p 6379:6379 redis:7-alpine redis-server --appendonly yes
```

## Structure

```
br-cache/
├── src/
│   ├── lib.rs       # Cache enum + CacheBase trait + API forwarding (monolithic)
│   ├── redis.rs     # Redis implementation (682 lines)
│   └── config.rs    # Config, Connection, CacheMode structs
├── tests/
│   └── redis.rs     # Integration tests only (no unit tests in src/)
├── examples/
│   ├── index.rs     # Comprehensive usage
│   └── config.toml
└── Cargo.toml       # Note: excludes examples/tests from publish
```

## Where to Look

| Task | Location | Notes |
|------|----------|-------|
| Add cache operation | `src/lib.rs` (trait) + `src/redis.rs` (impl) | Add to both |
| Add new backend | `src/lib.rs` (Cache enum) | Follow Redis pattern |
| Configuration | `src/config.rs` | Connection, CacheMode |
| Test new feature | `tests/redis.rs` | Use `init_0` or `init_1` helper |

## Code Conventions

### Language
- **Comments in Chinese** - maintain consistency
- **Error messages in Chinese** - `Err(format!("操作失败: {e}"))`

### Naming (Domain Prefixes)
| Prefix | Domain | Examples |
|--------|--------|----------|
| `key_*` | Key ops | exists, del, ttl, set_expireat |
| `set_*` | Set ops | add, get, delete, count, get_sinter |
| `list_*` | List ops | lpush, rpush, lpop, rpop, len, range |
| `hash_*` | Hash ops | get, add, delete, get_field_value |
| `geo_*` | Geo ops | add, get, dist, radius |
| `stream_*` | Stream ops | add, get, del, group_* |

### Error Handling
- Return `Result<T, String>` (not custom error types)
- Use `?` for propagation
- Pattern: `match data { Ok(e) => Ok(e), Err(e) => Err(format!("描述: {e}")) }`

### Types
- Use `JsonValue` from `json` crate (NOT serde_json)
- Use `object!{}` and `array![]` macros
- Use `once_cell::sync::Lazy` for statics
- Use `RwLock` for global state

### Architecture Pattern
```rust
// 1. Trait in lib.rs defines interface
pub trait CacheBase {
    fn key_exists(&mut self, key: &str) -> Result<bool, String>;
}

// 2. Impl in redis.rs
impl CacheBase for Redis { ... }

// 3. Enum wrapper in lib.rs forwards calls
impl Cache {
    pub fn key_exists(&mut self, key: &str) -> Result<bool, String> {
        Ok(match self {
            Cache::Redis(e) => e.key_exists(key)?,
            Cache::None => return Err("error".to_string()),
        })
    }
}
```

## Testing Conventions

### Test Structure (Integration Only)
```rust
#[cfg(test)]
mod test {
    // Helper: from config file
    fn init_0() -> Cache {
        unsafe { env::set_var("RUST_LOG", "debug"); }
        let _ = env_logger::try_init();
        let config = Config::create(PathBuf::new().join("examples").join("config.toml"), true);
        Cache::new(config)
    }

    // Helper: inline connection
    fn init_1() -> Cache {
        unsafe { env::set_var("RUST_LOG", "debug"); }
        let _ = env_logger::try_init();
        Cache::create("", Connection { ... })
    }

    #[test]
    fn test_name() -> Result<(), String> {
        let mut cache = init_0();
        // Use println! for debug, assertions often commented out
        Ok(())
    }
}
```

### Test Requirements
- Redis on `127.0.0.1:6379`
- Return `Result<(), String>` from tests
- Use `init_0` (config) or `init_1` (inline) helpers
- Debug with `println!` (visible with `--nocapture`)

## Anti-Patterns & Warnings

### Unimplemented Methods (todo!())
| Method | Location | Status |
|--------|----------|--------|
| `geo_dist` | redis.rs:503 | Unimplemented |
| `geo_radius` | redis.rs:507 | Unimplemented |
| `stream_group_del_user` | redis.rs:577 | Unimplemented |

### Unsafe Usage
- `unsafe { env::set_var(...) }` in tests/examples for logging
- Required due to Rust 2024 env var safety rules
- No justification comments currently

### Missing Configs
- No `.rustfmt.toml` - uses default formatting
- No `clippy.toml` - uses default lints
- No CI/CD workflows - manual testing only

## Key Dependencies

| Crate | Purpose |
|-------|---------|
| `redis` | Client (geospatial, r2d2, disable-client-setinfo) |
| `json` | JSON via `object!`/`array!` macros |
| `r2d2` | Connection pooling (64 max, 2s timeout) |
| `once_cell` | Lazy static initialization |
| `tokio` | Async runtime (examples only) |

## Common Patterns

```rust
// Initialize from config
let config = Config::create(PathBuf::from("config.toml"), true);
let mut cache = Cache::new(config);

// Initialize direct
let mut cache = Cache::create("name", Connection {
    mode: CacheMode::Redis,
    hostname: "127.0.0.1".to_string(),
    hostport: "6379".to_string(),
    userpass: "".to_string(),
});

// Database selection + chaining
cache.db(0).add("key", value, 60)?;
cache.connection("other").db(1).get("key")?;
```