High-performance implementation of plcbundle written in Rust
at main 6.4 kB view raw
1# plcbundle-rs Coding Rules 2 3## Core Design Principles 4 51. **Single Entry Point**: All operations go through `BundleManager` 62. **Options Pattern**: Complex operations use dedicated option structs 73. **Result Types**: Operations return structured result types, not raw tuples 84. **Streaming by Default**: Use iterators for large datasets 95. **NO DIRECT FILE ACCESS**: CLI commands and server code NEVER open bundle files directly 10 11## Critical Rules 12 13### File Access 14- ❌ **NEVER** use `std::fs::File::open` for bundle files in CLI commands or server code 15- ❌ **NEVER** use `std::fs::read` for bundle data in CLI commands or server code 16- ❌ **NEVER** use `std::fs::remove_file` for bundles in CLI commands or server code 17- ❌ **NEVER** access core components (Index, bundle_format, etc.) directly from CLI or server 18- ✅ **ALWAYS** use `BundleManager` methods for bundle operations 19- ✅ **ALWAYS** add new operations to `BundleManager` API if needed 20 21### API-First Development 22- All CLI commands in `src/bin/commands/` must use **ONLY** public `BundleManager` APIs 23- All server code must use **ONLY** public `BundleManager` APIs 24- If CLI or server code needs file access, add the method to `BundleManager` first 25- The `BundleManager` is the single source of truth for all bundle operations 26 27### Examples 28 29**❌ WRONG - Direct file access:** 30```rust 31let bundle_path = dir.join(format!("{:06}.jsonl.zst", bundle_num)); 32std::fs::remove_file(&bundle_path)?; 33``` 34 35**✅ CORRECT - Via BundleManager API:** 36```rust 37manager.delete_bundle_files(&[bundle_num])?; 38``` 39 40## Architecture 41 42``` 43CLI Commands / Server → BundleManager API → Internal Implementation 44 ↓ ↓ ↓ 45 Uses Provides Opens files 46 Only Public API Directly 47``` 48 49## When Adding New Features 50 511. Design the API method in `BundleManager` first 522. Document it in `docs/API.md` 533. Implement the method in `src/manager.rs` 544. Export types in `src/lib.rs` if needed 555. Use the API from CLI command or server 56 57## Reference Documentation 58 59- Full API design: `docs/API.md` 60- Bundle format: `docs/BUNDLE_FORMAT.md` 61- All public APIs are listed in `docs/API.md` Quick Reference 62 63## Common Patterns 64 65### Loading Bundles 66```rust 67// ❌ Don't open files directly 68let file = File::open(bundle_path)?; 69 70// ✅ Use the API 71let result = manager.load_bundle(bundle_num, LoadOptions::default())?; 72``` 73 74### Getting Operations 75```rust 76// ❌ Don't parse files directly 77let json = std::fs::read_to_string(bundle_path)?; 78 79// ✅ Use the API 80let json = manager.get_operation_raw(bundle_num, position)?; 81``` 82 83### Deleting Bundles 84```rust 85// ❌ Don't remove files directly 86std::fs::remove_file(bundle_path)?; 87 88// ✅ Use the API 89manager.delete_bundle_files(&[bundle_num])?; 90``` 91 92### JSON Parsing 93- ✅ **ALWAYS** use `sonic_rs` for JSON parsing (performance-critical) 94- ❌ **NEVER** use `serde_json` for parsing JSON from bundles or operations 95- Use `sonic_rs::from_str` for parsing JSON strings 96- Use `sonic_rs::Value` and `JsonValueTrait` for accessing JSON values 97 98**Examples:** 99```rust 100// ✅ CORRECT - Use sonic_rs 101use sonic_rs::JsonValueTrait; 102if let Ok(value) = sonic_rs::from_str::<sonic_rs::Value>(&line) { 103 if let Some(did) = value.get("did").and_then(|v| v.as_str()) { 104 // ... 105 } 106} 107 108// ❌ WRONG - Don't use serde_json 109use serde_json::Value; 110let value: Value = serde_json::from_str(&line)?; 111``` 112 113## Testing 114 115**IMPORTANT**: Always test compilation with all features enabled. 116 117When implementing features: 118- **Always run**: `cargo check --all-features` or `cargo build --all-features` before considering work complete 119- This ensures code compiles with both `cli` and `server` features enabled 120- It's very common that code compiles with default features but fails with all features 121- Fix any compilation errors that appear with all features enabled 122 123If writing actual tests: 124- Test through the public API 125- Don't test internal implementation details 126- CLI tests should use actual `BundleManager` instances 127 128## CLI Output Formatting 129 130### Bundle Numbers in Human-Readable Output 131- ✅ **ALWAYS** display bundle numbers **without leading zeros** in CLI output for humans 132- Use `{}` format, NOT `{:06}` format 133- Example: `"Bundle: 123"` not `"Bundle: 000123"` 134- This applies to all `println!`, `eprintln!`, and `log::*!` statements that display bundle numbers to users 135- Exception: File paths and internal identifiers may use zero-padded format if needed 136 137**Examples:** 138```rust 139// ✅ CORRECT - Human-readable output 140println!(" Last bundle: {}", bundle_num); 141log::info!("Bundle {} processed", bundle_num); 142 143// ❌ WRONG - Don't use leading zeros for humans 144println!(" Last bundle: {:06}", bundle_num); 145log::info!("Bundle {:06} processed", bundle_num); 146``` 147 148## Command File Structure 149 150All command files (`src/cli/cmd_*.rs`) must follow a consistent structure: 151 1521. **Imports** - All `use` statements at the top 1532. **Command Definitions** - All `#[derive(Args)]`, `#[derive(Subcommand)]` structs and enums 1543. **Trait Implementations** - Any `impl` blocks for command structs (e.g., `impl HasGlobalFlags`) 1554. **Run Function** - The main `pub fn run()` function 1565. **Helper Functions** - All private helper functions at the end 157 158**Exception**: Helper functions that are referenced in struct definitions (e.g., `value_parser = parse_duration`) must be defined before the struct that uses them. 159 160**Examples:** 161 162**✅ CORRECT - Proper structure:** 163```rust 164use anyhow::Result; 165use clap::Args; 166use plcbundle::BundleManager; 167use std::path::PathBuf; 168 169#[derive(Args)] 170#[command(about = "Example command")] 171pub struct ExampleCommand { 172 // ... 173} 174 175impl HasGlobalFlags for ExampleCommand { 176 // ... 177} 178 179pub fn run(cmd: ExampleCommand, dir: PathBuf) -> Result<()> { 180 // ... 181} 182 183fn helper_function() { 184 // ... 185} 186``` 187 188**❌ WRONG - Helper function before command struct:** 189```rust 190use anyhow::Result; 191use clap::Args; 192 193fn helper_function() { 194 // ... 195} 196 197#[derive(Args)] 198pub struct ExampleCommand { 199 // ... 200} 201``` 202 203## Questions? 204 205If you're implementing a feature and need file access: 2061. Check if `BundleManager` already has the method 2072. If not, add it to `BundleManager` first 2083. Update `docs/API.md` with the new method 2094. Then use it from the CLI or server 210 211**Remember: The CLI and server are just thin wrappers around `BundleManager`!** 212