mirror of
https://github.com/tyrchen/cursor-rust-rules.git
synced 2025-12-22 17:19:59 +00:00
feature: initialize rust rules
This commit is contained in:
5
.cursor/rules/rust/complex/workspace.mdc
Normal file
5
.cursor/rules/rust/complex/workspace.mdc
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
description:
|
||||
globs:
|
||||
alwaysApply: false
|
||||
---
|
||||
5
.cursor/rules/rust/core/code-quality.mdc
Normal file
5
.cursor/rules/rust/core/code-quality.mdc
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
description:
|
||||
globs:
|
||||
alwaysApply: false
|
||||
---
|
||||
5
.cursor/rules/rust/features/axum.mdc
Normal file
5
.cursor/rules/rust/features/axum.mdc
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
description:
|
||||
globs:
|
||||
alwaysApply: false
|
||||
---
|
||||
243
.cursor/rules/rust/main.mdc
Normal file
243
.cursor/rules/rust/main.mdc
Normal 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.
|
||||
484
.cursor/rules/rust/quality/error-handling.mdc
Normal file
484
.cursor/rules/rust/quality/error-handling.mdc
Normal 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.
|
||||
554
.cursor/rules/rust/simple/single-crate.mdc
Normal file
554
.cursor/rules/rust/simple/single-crate.mdc
Normal 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.
|
||||
Reference in New Issue
Block a user