feature: initialize rust rules

This commit is contained in:
Tyr Chen
2025-05-31 11:09:49 -07:00
commit 8801d986c4
7 changed files with 1296 additions and 0 deletions

View File

@@ -0,0 +1,5 @@
---
description:
globs:
alwaysApply: false
---

View File

@@ -0,0 +1,5 @@
---
description:
globs:
alwaysApply: false
---

View File

@@ -0,0 +1,5 @@
---
description:
globs:
alwaysApply: false
---

243
.cursor/rules/rust/main.mdc Normal file
View File

@@ -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<br>functional domains?"}
Q1 -->|Yes| Q2{"Expected LOC<br>> 10,000?"}
Q1 -->|No| Q3{"Single domain with<br>multiple components?"}
Q2 -->|Yes| Complex["COMPLEX PROJECT<br>Multi-crate workspace"]
Q2 -->|No| Q4{"Shared libraries<br>needed?"}
Q4 -->|Yes| Complex
Q4 -->|No| Simple["SIMPLE PROJECT<br>Single crate"]
Q3 -->|Yes| Q5{"Need separate<br>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<br>needed?"}
Features --> DB{"Database<br>access needed?"}
Features --> Concurrent{"Heavy<br>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<br>(Always Loaded)"]
Main --> Complexity{"Project<br>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.

View File

@@ -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<T> = std::result::Result<T, MyLibError>;
// Error construction helpers
impl MyLibError {
pub fn invalid_input(message: impl Into<String>) -> Self {
Self::InvalidInput {
message: message.into(),
}
}
pub fn file_not_found(path: impl Into<String>) -> Self {
Self::FileNotFound {
path: path.into(),
}
}
pub fn config(message: impl Into<String>) -> Self {
Self::Config {
message: message.into(),
}
}
pub fn internal(message: impl Into<String>) -> 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<String> {
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<Config> {
// 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<String>) -> AppError {
AppError::Config {
message: message.into(),
}
}
pub fn user_error(message: impl Into<String>) -> AppError {
AppError::User {
message: message.into(),
}
}
pub fn system_error(message: impl Into<String>) -> AppError {
AppError::System {
message: message.into(),
}
}
// Type alias for the main Result type
pub type AppResult<T> = Result<T>;
```
### 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<Config> {
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<ProcessResult> {
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<String> {
ensure!(!content.is_empty(), "File content is empty");
// Complex processing logic that might fail
let processed = content
.lines()
.map(|line| process_line(line))
.collect::<Result<Vec<_>>>()?
.join("\n");
Ok(processed)
}
fn process_line(line: &str) -> Result<String> {
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<br>Error"]
Category --> Fatal["Fatal<br>Error"]
Category --> User["User<br>Error"]
Category --> System["System<br>Error"]
Recoverable --> Retry["Implement<br>Retry Logic"]
Recoverable --> Fallback["Provide<br>Fallback"]
Fatal --> Cleanup["Cleanup<br>Resources"]
Fatal --> Exit["Exit<br>Gracefully"]
User --> Validate["Validate<br>Input"]
User --> Feedback["Provide<br>Feedback"]
System --> Log["Log<br>Error"]
System --> Alert["Alert<br>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<ProcessedData> {
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<String> {
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<dyn std::error::Error>> {
// 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.

View File

@@ -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<br>(src/main.rs)"]
Project --> Library["Library Crate<br>(src/lib.rs)"]
Project --> Mixed["Mixed Crate<br>(src/main.rs + src/lib.rs)"]
Binary --> BinStructure["Binary Structure:"]
Library --> LibStructure["Library Structure:"]
Mixed --> MixedStructure["Mixed Structure:"]
BinStructure --> MainRs["src/main.rs<br>(minimal)"]
BinStructure --> AppLib["src/lib.rs<br>(core logic)"]
BinStructure --> Modules["src/modules/<br>(feature modules)"]
LibStructure --> LibRs["src/lib.rs<br>(public API)"]
LibStructure --> LibModules["src/modules/<br>(functionality)"]
LibStructure --> LibErrors["src/errors.rs<br>(centralized errors)"]
MixedStructure --> BothMain["src/main.rs<br>(binary entry)"]
MixedStructure --> BothLib["src/lib.rs<br>(library API)"]
MixedStructure --> SharedMods["src/modules/<br>(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<Self> {
// 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<Self> {
// 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<Self> {
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<Self> {
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<String>) -> Result<Self> {
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<ProcessResult> {
// 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 <your.email@example.com>"]
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 <your.email@example.com>"]
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.

0
README.md Normal file
View File