From 8801d986c46bf90a4510468f886ba89448b6401a Mon Sep 17 00:00:00 2001 From: Tyr Chen Date: Sat, 31 May 2025 11:09:49 -0700 Subject: [PATCH] feature: initialize rust rules --- .cursor/rules/rust/complex/workspace.mdc | 5 + .cursor/rules/rust/core/code-quality.mdc | 5 + .cursor/rules/rust/features/axum.mdc | 5 + .cursor/rules/rust/main.mdc | 243 ++++++++ .cursor/rules/rust/quality/error-handling.mdc | 484 +++++++++++++++ .cursor/rules/rust/simple/single-crate.mdc | 554 ++++++++++++++++++ README.md | 0 7 files changed, 1296 insertions(+) create mode 100644 .cursor/rules/rust/complex/workspace.mdc create mode 100644 .cursor/rules/rust/core/code-quality.mdc create mode 100644 .cursor/rules/rust/features/axum.mdc create mode 100644 .cursor/rules/rust/main.mdc create mode 100644 .cursor/rules/rust/quality/error-handling.mdc create mode 100644 .cursor/rules/rust/simple/single-crate.mdc create mode 100644 README.md diff --git a/.cursor/rules/rust/complex/workspace.mdc b/.cursor/rules/rust/complex/workspace.mdc new file mode 100644 index 0000000..b93c988 --- /dev/null +++ b/.cursor/rules/rust/complex/workspace.mdc @@ -0,0 +1,5 @@ +--- +description: +globs: +alwaysApply: false +--- diff --git a/.cursor/rules/rust/core/code-quality.mdc b/.cursor/rules/rust/core/code-quality.mdc new file mode 100644 index 0000000..b93c988 --- /dev/null +++ b/.cursor/rules/rust/core/code-quality.mdc @@ -0,0 +1,5 @@ +--- +description: +globs: +alwaysApply: false +--- diff --git a/.cursor/rules/rust/features/axum.mdc b/.cursor/rules/rust/features/axum.mdc new file mode 100644 index 0000000..b93c988 --- /dev/null +++ b/.cursor/rules/rust/features/axum.mdc @@ -0,0 +1,5 @@ +--- +description: +globs: +alwaysApply: false +--- diff --git a/.cursor/rules/rust/main.mdc b/.cursor/rules/rust/main.mdc new file mode 100644 index 0000000..737b34d --- /dev/null +++ b/.cursor/rules/rust/main.mdc @@ -0,0 +1,243 @@ +--- +description: +globs: Cargo.toml,*.rs +alwaysApply: false +--- +# 🦀 RUST PROJECT RULE SET SYSTEM + +> **TL;DR:** This system provides comprehensive guidelines for Rust project development with complexity-based rule loading, following industry best practices for scalable, maintainable Rust code. + +## 🔍 PROJECT COMPLEXITY DETERMINATION + +```mermaid +graph TD + Start["New Rust Project"] --> Analyze["Analyze Project Requirements"] + Analyze --> Q1{"Multiple distinct
functional domains?"} + Q1 -->|Yes| Q2{"Expected LOC
> 10,000?"} + Q1 -->|No| Q3{"Single domain with
multiple components?"} + + Q2 -->|Yes| Complex["COMPLEX PROJECT
Multi-crate workspace"] + Q2 -->|No| Q4{"Shared libraries
needed?"} + Q4 -->|Yes| Complex + Q4 -->|No| Simple["SIMPLE PROJECT
Single crate"] + + Q3 -->|Yes| Q5{"Need separate
deployable units?"} + Q3 -->|No| Simple + Q5 -->|Yes| Complex + Q5 -->|No| Simple + + Complex --> LoadComplex["Load Complex Rules"] + Simple --> LoadSimple["Load Simple Rules"] + + LoadComplex --> Features["Load Feature-Specific Rules"] + LoadSimple --> Features + + Features --> Web{"Web Framework
needed?"} + Features --> DB{"Database
access needed?"} + Features --> Concurrent{"Heavy
concurrency?"} + + Web -->|Yes| WebRules["Load Axum Rules"] + DB -->|Yes| DBRules["Load SQLx Rules"] + Concurrent -->|Yes| ConcurrencyRules["Load Concurrency Rules"] + + style Start fill:#4da6ff,stroke:#0066cc,color:white + style Complex fill:#d94dbb,stroke:#a3378a,color:white + style Simple fill:#4dbb5f,stroke:#36873f,color:white + style LoadComplex fill:#ffa64d,stroke:#cc7a30,color:white + style LoadSimple fill:#4dbbbb,stroke:#368787,color:white +``` + +## 📊 COMPLEXITY INDICATORS + +### Simple Project Indicators +- **Single Domain**: One clear functional area (CLI tool, library, simple service) +- **Codebase Size**: Expected < 10,000 lines of code +- **Team Size**: 1-3 developers +- **Dependencies**: Minimal external integrations +- **Deployment**: Single binary or library + +### Complex Project Indicators +- **Multiple Domains**: Authentication, business logic, data processing, etc. +- **Codebase Size**: Expected > 10,000 lines of code +- **Team Size**: 4+ developers or multiple teams +- **Shared Components**: Common libraries across multiple binaries +- **Microservices**: Multiple deployable units + +## 🔄 RULE LOADING HIERARCHY + +```mermaid +graph TD + Main["main.mdc"] --> Core["Core Rules
(Always Loaded)"] + Main --> Complexity{"Project
Complexity?"} + + Core --> Quality["Code Quality Rules"] + Core --> Testing["Testing Standards"] + Core --> ErrorHandling["Error Handling"] + + Complexity -->|Simple| SimpleRules["Simple Project Rules"] + Complexity -->|Complex| ComplexRules["Complex Project Rules"] + + SimpleRules --> SingleCrate["Single Crate Structure"] + ComplexRules --> Workspace["Workspace Management"] + ComplexRules --> MultiCrate["Multi-crate Architecture"] + + SimpleRules & ComplexRules --> FeatureDetection["Feature Detection"] + + FeatureDetection --> WebFeature{"Web Framework?"} + FeatureDetection --> DBFeature{"Database?"} + FeatureDetection --> SerdeFeature{"Serialization?"} + FeatureDetection --> BuilderFeature{"Complex Types?"} + + WebFeature -->|Yes| AxumRules["Axum Framework Rules"] + DBFeature -->|Yes| SQLxRules["SQLx Database Rules"] + SerdeFeature -->|Yes| SerdeRules["Serde Best Practices"] + BuilderFeature -->|Yes| TypedBuilderRules["TypedBuilder Rules"] + + style Main fill:#4da6ff,stroke:#0066cc,color:white + style Core fill:#ffa64d,stroke:#cc7a30,color:white + style SimpleRules fill:#4dbb5f,stroke:#36873f,color:white + style ComplexRules fill:#d94dbb,stroke:#a3378a,color:white +``` + +## 📋 CORE PRINCIPLES (ALWAYS APPLIED) + +1. **Code Quality**: Follow DRY/SRP principles, function size limits +2. **File Organization**: Functionality-based structure, not type-based +3. **Error Handling**: Consistent error handling patterns +4. **Testing**: Comprehensive unit test coverage +5. **Documentation**: Clear, maintainable code documentation + +## 🚀 PROJECT INITIALIZATION WORKFLOW + +```mermaid +sequenceDiagram + participant Dev as Developer + participant System as Rule System + participant Cargo as Cargo + participant Files as File Structure + + Dev->>System: Initialize Rust project + System->>System: Determine complexity + System->>Dev: Complexity assessment result + + alt Simple Project + System->>Cargo: cargo new project_name + System->>Files: Create single crate structure + else Complex Project + System->>Cargo: cargo new --name workspace project_name + System->>Files: Create workspace structure + System->>Files: Initialize member crates + end + + System->>System: Detect required features + System->>Files: Apply feature-specific templates + System->>Dev: Project ready for development +``` + +## 📝 FEATURE DETECTION CHECKLIST + +```markdown +## Feature Detection for Rule Loading + +### Web Framework Requirements +- [ ] HTTP server needed +- [ ] REST API endpoints +- [ ] OpenAPI documentation required +- [ ] → Load Axum rules if YES to any + +### Database Requirements +- [ ] Database queries needed +- [ ] SQL database integration +- [ ] Database migrations +- [ ] → Load SQLx rules if YES to any + +### Serialization Requirements +- [ ] JSON handling required +- [ ] External API integration +- [ ] Configuration files +- [ ] → Load Serde rules if YES to any + +### Builder Pattern Requirements +- [ ] Complex data structures (4+ fields) +- [ ] Optional fields in constructors +- [ ] Fluent API needed +- [ ] → Load TypedBuilder rules if YES to any + +### Concurrency Requirements +- [ ] Multi-threading needed +- [ ] Shared state across threads +- [ ] High-performance requirements +- [ ] → Load Concurrency rules if YES to any +``` + +## 🔧 RULE LOADING COMMANDS + +Based on project analysis, load specific rule sets: + +```bash +# For simple projects +# Loads: core + simple + detected features + +# For complex projects +# Loads: core + complex + workspace + detected features + +# Feature-specific loading examples: +# Web: core + axum + serde + typed-builder +# Database: core + sqlx + error-handling +# CLI: core + simple + clap + error-handling +``` + +## 📚 AVAILABLE RULE MODULES + +| Module | File | Description | +|--------|------|-------------| +| **Core** | `core/code-quality.mdc` | DRY/SRP, function size limits | +| **Simple** | `simple/single-crate.mdc` | Single crate project structure | +| **Complex** | `complex/workspace.mdc` | Multi-crate workspace management | +| **Web** | `features/axum.mdc` | Axum framework best practices | +| **Database** | `features/sqlx.mdc` | SQLx patterns and testing | +| **Serialization** | `features/serde.mdc` | Serde configuration and patterns | +| **Builders** | `features/typed-builder.mdc` | TypedBuilder usage patterns | +| **Concurrency** | `features/concurrency.mdc` | Rust concurrency best practices | +| **Testing** | `quality/testing.mdc` | Unit testing standards | +| **Errors** | `quality/error-handling.mdc` | Error handling patterns | + +## 🎯 PROJECT TYPE EXAMPLES + +### Simple Project Examples +- CLI utilities (grep clone, file converter) +- Single-purpose libraries (parsing, algorithms) +- Simple HTTP servers (< 10 endpoints) +- Desktop applications (single-window apps) + +### Complex Project Examples +- Multi-service applications (auth + business + gateway) +- Enterprise applications (multiple domains) +- Large web applications (> 20 endpoints) +- Database systems with multiple engines +- Distributed systems + +## ⚡ OPTIMIZATION FEATURES + +- **Lazy Loading**: Feature-specific rules loaded only when needed +- **Context Preservation**: Project decisions cached across sessions +- **Template Generation**: Auto-generate boilerplate based on detected patterns +- **Incremental Updates**: Update only changed components + +## 🚨 MANDATORY VERIFICATION + +Before starting any Rust development: + +```markdown +✓ RUST PROJECT VERIFICATION +- Project complexity determined? [SIMPLE/COMPLEX] +- Required features detected? [List] +- Appropriate rules loaded? [YES/NO] +- Cargo.toml structure verified? [YES/NO] +- Error handling strategy selected? [thiserror/anyhow] + +→ If all verified: Proceed with development +→ If missing: Complete project setup +``` + +This system ensures optimal Rust development practices while maintaining flexibility for different project scales and requirements. diff --git a/.cursor/rules/rust/quality/error-handling.mdc b/.cursor/rules/rust/quality/error-handling.mdc new file mode 100644 index 0000000..db64693 --- /dev/null +++ b/.cursor/rules/rust/quality/error-handling.mdc @@ -0,0 +1,484 @@ +--- +description: +globs: +alwaysApply: false +--- +# 🚨 RUST ERROR HANDLING STANDARDS + +> **TL;DR:** Comprehensive error handling guidelines using thiserror for libraries and anyhow for binaries, with centralized error definitions per crate. + +## 🔍 ERROR HANDLING STRATEGY SELECTION + +```mermaid +graph TD + Project["Project Analysis"] --> CrateType{"Crate Type?"} + + CrateType -->|"lib"| LibCrate["Library Crate"] + CrateType -->|"bin"| BinCrate["Binary Crate"] + + LibCrate --> ThiserrorChoice["Use thiserror"] + BinCrate --> AnyhowChoice["Use anyhow"] + + ThiserrorChoice --> LibFeatures["Library Features:"] + AnyhowChoice --> BinFeatures["Binary Features:"] + + LibFeatures --> Structured["• Structured errors"] + LibFeatures --> Derived["• Derive Error trait"] + LibFeatures --> Transparent["• Error forwarding"] + LibFeatures --> Documentation["• Error documentation"] + + BinFeatures --> Simple["• Simple error handling"] + BinFeatures --> Context["• Rich context"] + BinFeatures --> Chaining["• Error chaining"] + BinFeatures --> EndUser["• End-user messages"] + + style Project fill:#4da6ff,stroke:#0066cc,color:white + style CrateType fill:#ffa64d,stroke:#cc7a30,color:white + style LibCrate fill:#4dbb5f,stroke:#36873f,color:white + style BinCrate fill:#d94dbb,stroke:#a3378a,color:white +``` + +## 📚 LIBRARY CRATE ERROR HANDLING (thiserror) + +### Error Definition Pattern + +```rust +// lib_crate/src/errors.rs +use thiserror::Error; + +#[derive(Error, Debug)] +pub enum MyLibError { + #[error("Invalid input: {message}")] + InvalidInput { message: String }, + + #[error("File not found: {path}")] + FileNotFound { path: String }, + + #[error("Permission denied")] + PermissionDenied, + + #[error("Network error")] + Network(#[from] reqwest::Error), + + #[error("IO error")] + Io(#[from] std::io::Error), + + #[error("Parse error: {0}")] + Parse(#[from] serde_json::Error), + + #[error("Configuration error: {message}")] + Config { message: String }, + + #[error("Internal error: {0}")] + Internal(String), +} + +// Additional result type alias for convenience +pub type Result = std::result::Result; + +// Error construction helpers +impl MyLibError { + pub fn invalid_input(message: impl Into) -> Self { + Self::InvalidInput { + message: message.into(), + } + } + + pub fn file_not_found(path: impl Into) -> Self { + Self::FileNotFound { + path: path.into(), + } + } + + pub fn config(message: impl Into) -> Self { + Self::Config { + message: message.into(), + } + } + + pub fn internal(message: impl Into) -> Self { + Self::Internal(message.into()) + } +} +``` + +### Usage in Library Functions + +```rust +// lib_crate/src/lib.rs +use crate::errors::{MyLibError, Result}; + +pub fn process_file(path: &str) -> Result { + if path.is_empty() { + return Err(MyLibError::invalid_input("Path cannot be empty")); + } + + let content = std::fs::read_to_string(path) + .map_err(|_| MyLibError::file_not_found(path))?; + + if content.is_empty() { + return Err(MyLibError::invalid_input("File is empty")); + } + + Ok(content.to_uppercase()) +} + +pub fn parse_config(data: &str) -> Result { + // The #[from] attribute automatically converts serde_json::Error + let config: Config = serde_json::from_str(data)?; + Ok(config) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_empty_path_error() { + let result = process_file(""); + assert!(matches!(result, Err(MyLibError::InvalidInput { .. }))); + } + + #[test] + fn test_file_not_found_error() { + let result = process_file("nonexistent.txt"); + assert!(matches!(result, Err(MyLibError::FileNotFound { .. }))); + } +} +``` + +## 🏃 BINARY CRATE ERROR HANDLING (anyhow) + +### Error Definition Pattern + +```rust +// bin_crate/src/errors.rs +use anyhow::{Context, Result}; +use thiserror::Error; + +// Define application-specific errors that need structure +#[derive(Error, Debug)] +pub enum AppError { + #[error("Configuration error: {message}")] + Config { message: String }, + + #[error("User error: {message}")] + User { message: String }, + + #[error("System error: {message}")] + System { message: String }, +} + +// Helper functions for common error patterns +pub fn config_error(message: impl Into) -> AppError { + AppError::Config { + message: message.into(), + } +} + +pub fn user_error(message: impl Into) -> AppError { + AppError::User { + message: message.into(), + } +} + +pub fn system_error(message: impl Into) -> AppError { + AppError::System { + message: message.into(), + } +} + +// Type alias for the main Result type +pub type AppResult = Result; +``` + +### Usage in Binary Application + +```rust +// bin_crate/src/main.rs +use anyhow::{Context, Result, bail, ensure}; +use crate::errors::{AppResult, user_error, config_error}; + +fn main() -> Result<()> { + let config = load_config() + .context("Failed to load application configuration")?; + + let result = process_data(&config) + .context("Failed to process data")?; + + save_results(&result) + .context("Failed to save results")?; + + println!("Processing completed successfully"); + Ok(()) +} + +fn load_config() -> AppResult { + let config_path = std::env::var("CONFIG_PATH") + .context("CONFIG_PATH environment variable not set")?; + + ensure!(!config_path.is_empty(), config_error("Config path is empty")); + + let content = std::fs::read_to_string(&config_path) + .with_context(|| format!("Failed to read config file: {}", config_path))?; + + let config: Config = toml::from_str(&content) + .context("Failed to parse config file as TOML")?; + + Ok(config) +} + +fn process_data(config: &Config) -> AppResult { + if config.input_files.is_empty() { + bail!(user_error("No input files specified")); + } + + let mut results = Vec::new(); + + for file_path in &config.input_files { + let content = std::fs::read_to_string(file_path) + .with_context(|| format!("Failed to read input file: {}", file_path))?; + + let processed = process_file_content(&content) + .with_context(|| format!("Failed to process file: {}", file_path))?; + + results.push(processed); + } + + Ok(ProcessResult { results }) +} + +fn process_file_content(content: &str) -> Result { + ensure!(!content.is_empty(), "File content is empty"); + + // Complex processing logic that might fail + let processed = content + .lines() + .map(|line| process_line(line)) + .collect::>>()? + .join("\n"); + + Ok(processed) +} + +fn process_line(line: &str) -> Result { + if line.trim().is_empty() { + return Ok(String::new()); + } + + // Some processing that might fail + Ok(line.to_uppercase()) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_process_line_success() { + let result = process_line("hello world").unwrap(); + assert_eq!(result, "HELLO WORLD"); + } + + #[test] + fn test_process_empty_line() { + let result = process_line("").unwrap(); + assert_eq!(result, ""); + } +} +``` + +## 🔄 ERROR HANDLING WORKFLOW + +```mermaid +sequenceDiagram + participant Code as Application Code + participant Lib as Library + participant Error as Error Handler + participant User as End User + + Code->>Lib: Call library function + Lib->>Lib: Process request + + alt Success Case + Lib->>Code: Return Ok(result) + Code->>User: Success response + else Library Error (thiserror) + Lib->>Code: Return Err(LibError) + Code->>Error: Convert to anyhow + Error->>Error: Add context + Error->>User: Formatted error message + else System Error + Lib->>Code: Return Err(SystemError) + Code->>Error: Add application context + Error->>User: User-friendly message + end +``` + +## 📋 ERROR CATEGORIES AND HANDLING + +```mermaid +graph TD + Error["Error Occurred"] --> Category["Categorize Error"] + + Category --> Recoverable["Recoverable
Error"] + Category --> Fatal["Fatal
Error"] + Category --> User["User
Error"] + Category --> System["System
Error"] + + Recoverable --> Retry["Implement
Retry Logic"] + Recoverable --> Fallback["Provide
Fallback"] + + Fatal --> Cleanup["Cleanup
Resources"] + Fatal --> Exit["Exit
Gracefully"] + + User --> Validate["Validate
Input"] + User --> Feedback["Provide
Feedback"] + + System --> Log["Log
Error"] + System --> Alert["Alert
Operators"] + + style Error fill:#4da6ff,stroke:#0066cc,color:white + style Recoverable fill:#4dbb5f,stroke:#36873f,color:white + style Fatal fill:#d94dbb,stroke:#a3378a,color:white + style User fill:#ffa64d,stroke:#cc7a30,color:white + style System fill:#4dbbbb,stroke:#368787,color:white +``` + +## 🛠️ ERROR HANDLING PATTERNS + +### Pattern 1: Early Return with Context + +```rust +fn complex_operation(input: &str) -> Result { + let validated = validate_input(input) + .context("Input validation failed")?; + + let parsed = parse_data(&validated) + .context("Failed to parse input data")?; + + let transformed = transform_data(parsed) + .context("Data transformation failed")?; + + let result = finalize_data(transformed) + .context("Failed to finalize processing")?; + + Ok(result) +} +``` + +### Pattern 2: Error Mapping + +```rust +fn convert_external_error() -> Result { + external_library::fetch_data() + .map_err(|e| anyhow::anyhow!("External service failed: {}", e))?; + + Ok("success".to_string()) +} +``` + +### Pattern 3: Conditional Error Handling + +```rust +fn conditional_processing(config: &Config) -> Result<()> { + if config.strict_mode { + process_strictly() + .context("Strict processing failed")?; + } else { + match process_leniently() { + Ok(result) => result, + Err(e) => { + eprintln!("Warning: Lenient processing failed: {}", e); + default_result() + } + } + } + + Ok(()) +} +``` + +## 📝 ERROR HANDLING CHECKLIST + +```markdown +## Error Handling Verification + +### Library Crates (thiserror) +- [ ] All errors defined in centralized errors.rs +- [ ] Error types derive Error, Debug +- [ ] Meaningful error messages with #[error] +- [ ] Proper use of #[from] for conversions +- [ ] Helper constructors for complex errors +- [ ] Result type alias defined +- [ ] Comprehensive error documentation + +### Binary Crates (anyhow) +- [ ] anyhow::Result used consistently +- [ ] Context added to all error chains +- [ ] User-friendly error messages +- [ ] Proper cleanup on fatal errors +- [ ] Structured errors for app-specific cases +- [ ] Graceful error handling in main() + +### General +- [ ] No unwrap() or expect() in production code +- [ ] Errors properly propagated +- [ ] Error tests included +- [ ] Error logging implemented +- [ ] Recovery strategies defined +``` + +## 🚨 ANTI-PATTERNS TO AVOID + +### ❌ Don't Do This + +```rust +// ❌ Using unwrap in production code +let value = risky_operation().unwrap(); + +// ❌ Ignoring errors +let _ = might_fail(); + +// ❌ Generic error messages +return Err("Something went wrong".into()); + +// ❌ Mixing error handling approaches +fn mixed_errors() -> Result<(), Box> { + // Inconsistent error handling +} + +// ❌ Not providing context +let data = load_file(path)?; // No context about which file failed +``` + +### ✅ Do This Instead + +```rust +// ✅ Proper error handling with context +let value = risky_operation() + .context("Failed to perform risky operation")?; + +// ✅ Handle or propagate errors explicitly +if let Err(e) = might_fail() { + eprintln!("Operation failed: {}", e); + return Err(e); +} + +// ✅ Specific error messages +return Err(MyError::InvalidInput { + field: "email".to_string(), + value: input.to_string(), +}); + +// ✅ Consistent error handling approach +fn consistent_errors() -> Result<(), MyAppError> { + // Consistent with project patterns +} + +// ✅ Rich context for debugging +let data = load_file(path) + .with_context(|| format!("Failed to load config file: {}", path))?; +``` + +This error handling strategy ensures robust, maintainable error management across your Rust project while following industry best practices. diff --git a/.cursor/rules/rust/simple/single-crate.mdc b/.cursor/rules/rust/simple/single-crate.mdc new file mode 100644 index 0000000..dfa21d1 --- /dev/null +++ b/.cursor/rules/rust/simple/single-crate.mdc @@ -0,0 +1,554 @@ +--- +description: +globs: +alwaysApply: false +--- +# 📦 SINGLE CRATE PROJECT STRUCTURE + +> **TL;DR:** Guidelines for organizing simple Rust projects using a single crate structure with clean separation of concerns and maintainable file organization. + +## 🏗️ PROJECT STRUCTURE OVERVIEW + +```mermaid +graph TD + Project["Single Crate Project"] --> Binary["Binary Crate
(src/main.rs)"] + Project --> Library["Library Crate
(src/lib.rs)"] + Project --> Mixed["Mixed Crate
(src/main.rs + src/lib.rs)"] + + Binary --> BinStructure["Binary Structure:"] + Library --> LibStructure["Library Structure:"] + Mixed --> MixedStructure["Mixed Structure:"] + + BinStructure --> MainRs["src/main.rs
(minimal)"] + BinStructure --> AppLib["src/lib.rs
(core logic)"] + BinStructure --> Modules["src/modules/
(feature modules)"] + + LibStructure --> LibRs["src/lib.rs
(public API)"] + LibStructure --> LibModules["src/modules/
(functionality)"] + LibStructure --> LibErrors["src/errors.rs
(centralized errors)"] + + MixedStructure --> BothMain["src/main.rs
(binary entry)"] + MixedStructure --> BothLib["src/lib.rs
(library API)"] + MixedStructure --> SharedMods["src/modules/
(shared logic)"] + + style Project fill:#4da6ff,stroke:#0066cc,color:white + style Binary fill:#4dbb5f,stroke:#36873f,color:white + style Library fill:#ffa64d,stroke:#cc7a30,color:white + style Mixed fill:#d94dbb,stroke:#a3378a,color:white +``` + +## 📁 RECOMMENDED FILE STRUCTURE + +### Binary Crate Structure + +``` +my_project/ +├── Cargo.toml +├── README.md +├── src/ +│ ├── main.rs # Entry point (minimal, delegates to lib.rs) +│ ├── lib.rs # Core application logic +│ ├── errors.rs # Centralized error definitions +│ ├── config.rs # Configuration handling +│ ├── cli.rs # Command-line interface (if applicable) +│ └── modules/ # Feature-based modules +│ ├── mod.rs # Module declarations +│ ├── auth.rs # Authentication logic +│ ├── database.rs # Database operations +│ └── handlers.rs # Request/command handlers +├── tests/ # Integration tests +│ └── integration_test.rs +└── examples/ # Usage examples + └── basic_usage.rs +``` + +### Library Crate Structure + +``` +my_lib/ +├── Cargo.toml +├── README.md +├── src/ +│ ├── lib.rs # Public API and module declarations +│ ├── errors.rs # Error types (using thiserror) +│ └── modules/ # Functionality modules +│ ├── mod.rs # Module re-exports +│ ├── core.rs # Core functionality +│ ├── utils.rs # Utility functions +│ └── types.rs # Public type definitions +├── tests/ # Integration tests +│ └── lib_test.rs +├── examples/ # Usage examples +│ └── quick_start.rs +└── benches/ # Benchmarks (optional) + └── benchmark.rs +``` + +## 🎯 BINARY CRATE PATTERNS + +### Minimal main.rs Pattern + +```rust +// src/main.rs - Keep this minimal and delegate to lib.rs +use anyhow::Result; + +fn main() -> Result<()> { + my_project::run() +} +``` + +### Comprehensive lib.rs for Binary + +```rust +// src/lib.rs - Contains the main application logic +mod config; +mod errors; +mod cli; +mod modules; + +pub use errors::AppError; +use config::Config; +use anyhow::{Context, Result}; + +/// Main application entry point +pub fn run() -> Result<()> { + let config = Config::load() + .context("Failed to load configuration")?; + + let app = Application::new(config)?; + app.start() + .context("Application failed to start")?; + + Ok(()) +} + +/// Core application struct +pub struct Application { + config: Config, + // Other application state +} + +impl Application { + pub fn new(config: Config) -> Result { + // Initialize application + Ok(Self { config }) + } + + pub fn start(&self) -> Result<()> { + // Main application logic + println!("Application started with config: {:?}", self.config); + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_application_creation() { + let config = Config::default(); + let app = Application::new(config); + assert!(app.is_ok()); + } +} +``` + +### Configuration Module Pattern + +```rust +// src/config.rs +use serde::{Deserialize, Serialize}; +use std::env; +use anyhow::{Context, Result}; + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct Config { + pub database_url: String, + pub server_port: u16, + pub log_level: String, + pub debug_mode: bool, +} + +impl Config { + pub fn load() -> Result { + // Try environment variables first + if let Ok(config) = Self::from_env() { + return Ok(config); + } + + // Fall back to config file + Self::from_file("config.toml") + .context("Failed to load configuration from file") + } + + fn from_env() -> Result { + Ok(Self { + database_url: env::var("DATABASE_URL") + .context("DATABASE_URL not set")?, + server_port: env::var("SERVER_PORT") + .unwrap_or_else(|_| "8080".to_string()) + .parse() + .context("Invalid SERVER_PORT")?, + log_level: env::var("LOG_LEVEL") + .unwrap_or_else(|_| "info".to_string()), + debug_mode: env::var("DEBUG_MODE") + .map(|v| v.parse().unwrap_or(false)) + .unwrap_or(false), + }) + } + + fn from_file(path: &str) -> Result { + let content = std::fs::read_to_string(path) + .with_context(|| format!("Failed to read config file: {}", path))?; + + toml::from_str(&content) + .context("Failed to parse config file") + } +} + +impl Default for Config { + fn default() -> Self { + Self { + database_url: "sqlite::memory:".to_string(), + server_port: 8080, + log_level: "info".to_string(), + debug_mode: false, + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_default_config() { + let config = Config::default(); + assert_eq!(config.server_port, 8080); + assert_eq!(config.log_level, "info"); + assert!(!config.debug_mode); + } + + #[test] + fn test_config_serialization() { + let config = Config::default(); + let json = serde_json::to_string(&config).unwrap(); + let deserialized: Config = serde_json::from_str(&json).unwrap(); + assert_eq!(config.server_port, deserialized.server_port); + } +} +``` + +## 📚 LIBRARY CRATE PATTERNS + +### Public API lib.rs Pattern + +```rust +// src/lib.rs - Clean public API +//! # My Library +//! +//! This library provides functionality for... +//! +//! ## Quick Start +//! +//! ```rust +//! use my_lib::MyStruct; +//! +//! let instance = MyStruct::new("example")?; +//! let result = instance.process()?; +//! ``` + +mod modules; +mod errors; + +// Public API exports +pub use errors::{MyLibError, Result}; +pub use modules::{ + core::{MyStruct, ProcessResult}, + utils::{helper_function, UtilityTrait}, +}; + +// Re-export commonly used types +pub use modules::types::{PublicType, Configuration}; + +/// Library version information +pub const VERSION: &str = env!("CARGO_PKG_VERSION"); + +/// Initialize the library with default settings +pub fn init() -> Result<()> { + // Library initialization logic + Ok(()) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_library_initialization() { + assert!(init().is_ok()); + } + + #[test] + fn test_version_info() { + assert!(!VERSION.is_empty()); + } +} +``` + +### Module Organization Pattern + +```rust +// src/modules/mod.rs +pub mod core; +pub mod utils; +pub mod types; + +// Re-export for internal use +pub(crate) use core::*; +pub(crate) use utils::*; +pub(crate) use types::*; +``` + +```rust +// src/modules/core.rs +use crate::errors::{MyLibError, Result}; +use crate::modules::types::Configuration; + +/// Main functionality struct +#[derive(Debug, Clone)] +pub struct MyStruct { + config: Configuration, + data: String, +} + +impl MyStruct { + /// Create a new instance + pub fn new(data: impl Into) -> Result { + let data = data.into(); + if data.is_empty() { + return Err(MyLibError::invalid_input("Data cannot be empty")); + } + + Ok(Self { + config: Configuration::default(), + data, + }) + } + + /// Process the data + pub fn process(&self) -> Result { + // Main processing logic + let processed = self.data.to_uppercase(); + + Ok(ProcessResult { + original: self.data.clone(), + processed, + metadata: self.config.clone(), + }) + } + + /// Update configuration + pub fn with_config(mut self, config: Configuration) -> Self { + self.config = config; + self + } +} + +/// Result of processing operation +#[derive(Debug, Clone, PartialEq)] +pub struct ProcessResult { + pub original: String, + pub processed: String, + pub metadata: Configuration, +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_mystruct_creation() { + let instance = MyStruct::new("test").unwrap(); + assert_eq!(instance.data, "test"); + } + + #[test] + fn test_empty_data_error() { + let result = MyStruct::new(""); + assert!(result.is_err()); + } + + #[test] + fn test_processing() { + let instance = MyStruct::new("hello").unwrap(); + let result = instance.process().unwrap(); + assert_eq!(result.processed, "HELLO"); + assert_eq!(result.original, "hello"); + } + + #[test] + fn test_with_config() { + let config = Configuration::new("custom"); + let instance = MyStruct::new("test") + .unwrap() + .with_config(config.clone()); + + let result = instance.process().unwrap(); + assert_eq!(result.metadata, config); + } +} +``` + +## 🔧 CARGO.TOML CONFIGURATION + +### Binary Crate Cargo.toml + +```toml +[package] +name = "my_project" +version = "0.1.0" +edition = "2021" +authors = ["Your Name "] +description = "A brief description of your project" +readme = "README.md" +repository = "https://github.com/yourusername/my_project" +license = "MIT OR Apache-2.0" +keywords = ["cli", "tool", "utility"] +categories = ["command-line-utilities"] + +[dependencies] +anyhow = "1.0" +serde = { version = "1.0", features = ["derive"] } +toml = "0.8" +clap = { version = "4.0", features = ["derive"] } + +[dev-dependencies] +tempfile = "3.0" + +[[bin]] +name = "my_project" +path = "src/main.rs" + +[profile.release] +lto = true +codegen-units = 1 +panic = "abort" +``` + +### Library Crate Cargo.toml + +```toml +[package] +name = "my_lib" +version = "0.1.0" +edition = "2021" +authors = ["Your Name "] +description = "A useful library for..." +readme = "README.md" +repository = "https://github.com/yourusername/my_lib" +license = "MIT OR Apache-2.0" +keywords = ["library", "utility", "parsing"] +categories = ["development-tools"] + +[dependencies] +thiserror = "1.0" +serde = { version = "1.0", features = ["derive"] } + +[dev-dependencies] +serde_json = "1.0" + +[lib] +name = "my_lib" +path = "src/lib.rs" + +[features] +default = [] +extra_features = ["serde"] + +[[example]] +name = "quick_start" +path = "examples/quick_start.rs" +``` + +## 📝 SINGLE CRATE CHECKLIST + +```markdown +## Single Crate Structure Verification + +### Project Setup +- [ ] Appropriate crate type chosen (bin/lib/mixed) +- [ ] Cargo.toml properly configured +- [ ] README.md with clear documentation +- [ ] License file included + +### File Organization +- [ ] src/main.rs minimal (for binary crates) +- [ ] src/lib.rs contains core logic +- [ ] src/errors.rs centralizes error definitions +- [ ] Feature modules in src/modules/ +- [ ] Each file ≤ 500 lines (excluding tests) + +### Code Quality +- [ ] Functions ≤ 150 lines +- [ ] Functionality-based file organization +- [ ] Comprehensive unit tests in each file +- [ ] Public API well-documented +- [ ] Error handling consistent throughout + +### Testing +- [ ] Unit tests in each module +- [ ] Integration tests in tests/ directory +- [ ] Examples in examples/ directory +- [ ] All public APIs tested + +### Documentation +- [ ] Public APIs documented with /// comments +- [ ] Examples included in documentation +- [ ] README with usage instructions +- [ ] Changelog for version tracking +``` + +## 🚨 COMMON ANTI-PATTERNS TO AVOID + +### ❌ Don't Do This + +```rust +// ❌ Fat main.rs with business logic +fn main() { + // Hundreds of lines of business logic + let config = load_config(); + let database = connect_database(); + // ... more logic +} + +// ❌ Type-based file organization +// src/types.rs - All types mixed together +// src/traits.rs - All traits mixed together +// src/impls.rs - All implementations mixed together + +// ❌ Single massive file +// src/lib.rs with 2000+ lines of mixed functionality +``` + +### ✅ Do This Instead + +```rust +// ✅ Minimal main.rs +fn main() -> anyhow::Result<()> { + my_project::run() +} + +// ✅ Function-based organization +// src/auth.rs - Authentication-related types, traits, and implementations +// src/database.rs - Database-related functionality +// src/config.rs - Configuration handling + +// ✅ Modular lib.rs +// src/lib.rs - Clean public API with module re-exports +// src/modules/ - Separate files for different functionality +``` + +This single crate structure provides a solid foundation for simple Rust projects while maintaining clean organization and scalability. diff --git a/README.md b/README.md new file mode 100644 index 0000000..e69de29