diff --git a/.cursor/rules/rust/complex/workspace.mdc b/.cursor/rules/rust/complex/workspace.mdc index a7d443a..7fc4a55 100644 --- a/.cursor/rules/rust/complex/workspace.mdc +++ b/.cursor/rules/rust/complex/workspace.mdc @@ -7,6 +7,39 @@ alwaysApply: false > **TL;DR:** Guidelines for organizing complex Rust projects using workspace architecture with multiple crates, clear subsystem boundaries, and domain-driven design principles. +## 🔍 WORKSPACE ARCHITECTURE STRATEGY + +```mermaid +graph TD + Start["Complex Project"] --> ArchType{"Architecture
Pattern?"} + + ArchType -->|Domain-Driven| DomainArch["Domain-Based Architecture"] + ArchType -->|Service-Oriented| ServiceArch["Service-Based Architecture"] + ArchType -->|Layered| LayeredArch["Layered Architecture"] + + DomainArch --> DomainCrates["Domain-Specific Crates"] + ServiceArch --> ServiceCrates["Service-Specific Crates"] + LayeredArch --> LayerCrates["Layer-Specific Crates"] + + DomainCrates --> SharedInfra["Shared Infrastructure"] + ServiceCrates --> SharedInfra + LayerCrates --> SharedInfra + + SharedInfra --> WorkspaceDeps["Workspace Dependencies"] + WorkspaceDeps --> CrateOrganization["Crate Organization"] + + CrateOrganization --> IntegrationTesting["Integration Testing"] + IntegrationTesting --> BuildOptimization["Build Optimization"] + BuildOptimization --> Documentation["Workspace Documentation"] + + Documentation --> Production["Production Workspace"] + + style Start fill:#4da6ff,stroke:#0066cc,color:white + style DomainArch fill:#4dbb5f,stroke:#36873f,color:white + style ServiceArch fill:#ffa64d,stroke:#cc7a30,color:white + style LayeredArch fill:#d94dbb,stroke:#a3378a,color:white +``` + ## 🏗️ SUBSYSTEM-BASED WORKSPACE ARCHITECTURE ```mermaid diff --git a/.cursor/rules/rust/core/code-quality.mdc b/.cursor/rules/rust/core/code-quality.mdc index 65ea9d9..2184d81 100644 --- a/.cursor/rules/rust/core/code-quality.mdc +++ b/.cursor/rules/rust/core/code-quality.mdc @@ -7,6 +7,66 @@ alwaysApply: false > **TL;DR:** Essential code quality rules for all Rust projects, focusing on maintainable, production-ready code that follows modern Rust 2024 idioms. +## 🔍 CODE QUALITY STRATEGY SELECTION + +```mermaid +graph TD + Start["Code Quality Requirements"] --> ProjectType{"Project Type?"} + + ProjectType -->|"Simple Library"| SimpleLib["Simple Library Rules"] + ProjectType -->|"Complex Application"| ComplexApp["Complex Application Rules"] + ProjectType -->|"CLI Tool"| CLITool["CLI Tool Rules"] + ProjectType -->|"Web Service"| WebService["Web Service Rules"] + + SimpleLib --> BasicRules["Basic Quality Rules"] + ComplexApp --> AdvancedRules["Advanced Quality Rules"] + CLITool --> BasicRules + WebService --> AdvancedRules + + BasicRules --> Naming["Naming Strategy"] + AdvancedRules --> Naming + + Naming --> NamingRules["• Functionality-based files
• Descriptive specific names
• No implementation suffixes"] + + NamingRules --> Structure["Code Structure"] + + Structure --> StructureRules["• File-based organization
• Size limitations (500 lines)
• Single responsibility
• Function size (150 lines)"] + + StructureRules --> Safety["Safety Requirements"] + + Safety --> SafetyRules["• Rust 2024 edition
• No unsafe code
• No unwrap() in production
• Proper error handling"] + + SafetyRules --> Testing["Testing Strategy"] + + Testing --> TestingRules["• Unit tests same file
• Integration tests separate
• Doc tests with examples"] + + TestingRules --> Verification["Quality Verification"] + + Verification --> Build["cargo build"] + Verification --> Test["cargo test"] + Verification --> Clippy["cargo clippy"] + + Build --> AllPass{"All Pass?"} + Test --> AllPass + Clippy --> AllPass + + AllPass -->|"Yes"| Success["✅ Quality Standards Met"] + AllPass -->|"No"| FixIssues["🔧 Fix Issues"] + + FixIssues --> Verification + + style Start fill:#4da6ff,stroke:#0066cc,color:white + style ProjectType fill:#ffa64d,stroke:#cc7a30,color:white + style SimpleLib fill:#4dbb5f,stroke:#36873f,color:white + style ComplexApp fill:#d94dbb,stroke:#a3378a,color:white + style CLITool fill:#9f4dbb,stroke:#7a3787,color:white + style WebService fill:#bb4d4d,stroke:#873636,color:white + style BasicRules fill:#bbbb4d,stroke:#878736,color:white + style AdvancedRules fill:#4dbbbb,stroke:#368787,color:white + style Success fill:#5fbb5f,stroke:#4a8f4a,color:white + style FixIssues fill:#ff6b6b,stroke:#cc5555,color:white +``` + ## 🎯 FUNDAMENTAL PRINCIPLES ### Code Organization diff --git a/.cursor/rules/rust/quality/testing.mdc b/.cursor/rules/rust/quality/testing.mdc new file mode 100644 index 0000000..68c92d9 --- /dev/null +++ b/.cursor/rules/rust/quality/testing.mdc @@ -0,0 +1,676 @@ +--- +description: +globs: +alwaysApply: false +--- +# 🧪 RUST TESTING STANDARDS + +> **TL;DR:** Comprehensive testing guidelines for Rust projects covering unit tests, integration tests, property testing, benchmarks, and CI integration with best practices for test organization and coverage. + +## 🔍 TESTING STRATEGY SELECTION + +```mermaid +graph TD + Start["Testing Requirements"] --> Strategy{"Testing Strategy?"} + + Strategy -->|"Unit Testing"| Unit["Unit Tests"] + Strategy -->|"Integration Testing"| Integration["Integration Tests"] + Strategy -->|"Property Testing"| Property["Property Testing"] + Strategy -->|"Benchmarking"| Benchmark["Performance Benchmarks"] + Strategy -->|"End-to-End Testing"| E2E["E2E Tests"] + + Unit --> UnitFeatures["Unit Test Features:"] + Integration --> IntegrationFeatures["Integration Features:"] + Property --> PropertyFeatures["Property Features:"] + Benchmark --> BenchmarkFeatures["Benchmark Features:"] + E2E --> E2EFeatures["E2E Features:"] + + UnitFeatures --> UnitItems["• Individual function testing
• Mock dependencies
• Fast execution
• High coverage"] + IntegrationFeatures --> IntegrationItems["• Multi-module testing
• Real dependencies
• Realistic scenarios
• API testing"] + PropertyFeatures --> PropertyItems["• Random input testing
• Edge case discovery
• Invariant verification
• Fuzzing"] + BenchmarkFeatures --> BenchmarkItems["• Performance regression
• Memory usage tracking
• Throughput measurement
• Optimization validation"] + E2EFeatures --> E2EItems["• Full system testing
• User workflow validation
• External service mocking
• Production-like environment"] + + UnitItems --> Tools["Testing Tools"] + IntegrationItems --> Tools + PropertyItems --> Tools + BenchmarkItems --> Tools + E2EItems --> Tools + + Tools --> MockTool["Mocking: mockall"] + Tools --> PropertyTool["Property: proptest"] + Tools --> BenchTool["Benchmarks: criterion"] + Tools --> WebTool["Web Testing: axum-test"] + Tools --> HttpTool["HTTP Mocking: wiremock"] + + style Start fill:#4da6ff,stroke:#0066cc,color:white + style Strategy fill:#ffa64d,stroke:#cc7a30,color:white + style Unit fill:#4dbb5f,stroke:#36873f,color:white + style Integration fill:#d94dbb,stroke:#a3378a,color:white + style Property fill:#9f4dbb,stroke:#7a3787,color:white + style Benchmark fill:#bb4d4d,stroke:#873636,color:white + style E2E fill:#bbbb4d,stroke:#878736,color:white +``` + +## 🏗️ CARGO TEST ORGANIZATION + +### Project Structure for Testing + +```rust +// Project structure with comprehensive testing +my_project/ +├── Cargo.toml +├── src/ +│ ├── lib.rs // Library root with unit tests +│ ├── main.rs // Binary entry point +│ ├── user/ +│ │ ├── mod.rs // Module with inline unit tests +│ │ ├── service.rs // Service with unit tests +│ │ └── repository.rs // Repository with unit tests +│ └── product/ +│ ├── mod.rs +│ └── catalog.rs +├── tests/ // Integration tests directory +│ ├── integration_test.rs +│ ├── api_test.rs +│ └── common/ // Shared test utilities +│ └── mod.rs +└── benches/ // Benchmark tests + ├── user_benchmarks.rs + └── product_benchmarks.rs +``` + +### Unit Test Configuration + +```rust +// src/user/service.rs +use crate::user::{User, UserRepository, UserError}; +use mockall::predicate::*; + +pub struct UserService { + repository: R, +} + +impl UserService { + pub fn new(repository: R) -> Self { + Self { repository } + } + + pub async fn create_user(&self, user: User) -> Result { + // Validate user data + if user.email.is_empty() { + return Err(UserError::InvalidInput("Email cannot be empty".to_string())); + } + + // Save to repository + self.repository.save(user).await + } + + pub async fn find_user(&self, id: &str) -> Result, UserError> { + if id.is_empty() { + return Err(UserError::InvalidInput("ID cannot be empty".to_string())); + } + + self.repository.find_by_id(id).await + } +} + +// ✅ Unit tests in the same file +#[cfg(test)] +mod tests { + use super::*; + use mockall::mock; + + // Mock the repository trait + mock! { + TestUserRepository {} + + #[async_trait::async_trait] + impl UserRepository for TestUserRepository { + async fn save(&self, user: User) -> Result; + async fn find_by_id(&self, id: &str) -> Result, UserError>; + } + } + + #[tokio::test] + async fn test_create_user_success() { + let mut mock_repo = MockTestUserRepository::new(); + let user = User { + id: "123".to_string(), + email: "test@example.com".to_string(), + name: "Test User".to_string(), + }; + + mock_repo + .expect_save() + .with(eq(user.clone())) + .times(1) + .returning(|user| Ok(user)); + + let service = UserService::new(mock_repo); + let result = service.create_user(user.clone()).await; + + assert!(result.is_ok()); + assert_eq!(result.unwrap(), user); + } + + #[tokio::test] + async fn test_create_user_empty_email_fails() { + let mock_repo = MockTestUserRepository::new(); + let user = User { + id: "123".to_string(), + email: "".to_string(), // Empty email + name: "Test User".to_string(), + }; + + let service = UserService::new(mock_repo); + let result = service.create_user(user).await; + + assert!(result.is_err()); + assert!(matches!(result.unwrap_err(), UserError::InvalidInput(_))); + } + + #[tokio::test] + async fn test_find_user_success() { + let mut mock_repo = MockTestUserRepository::new(); + let expected_user = User { + id: "123".to_string(), + email: "test@example.com".to_string(), + name: "Test User".to_string(), + }; + + mock_repo + .expect_find_by_id() + .with(eq("123")) + .times(1) + .returning(move |_| Ok(Some(expected_user.clone()))); + + let service = UserService::new(mock_repo); + let result = service.find_user("123").await; + + assert!(result.is_ok()); + assert!(result.unwrap().is_some()); + } + + #[tokio::test] + async fn test_find_user_empty_id_fails() { + let mock_repo = MockTestUserRepository::new(); + let service = UserService::new(mock_repo); + + let result = service.find_user("").await; + + assert!(result.is_err()); + assert!(matches!(result.unwrap_err(), UserError::InvalidInput(_))); + } +} +``` + +## 🔗 INTEGRATION TESTING + +### Integration Test Structure + +```rust +// tests/integration_test.rs +use my_project::{User, UserService, PostgresUserRepository}; +use testcontainers::{clients::Cli, images::postgres::Postgres, Container}; +use sqlx::PgPool; + +struct TestContext { + pool: PgPool, + _container: Container<'static, Postgres>, +} + +impl TestContext { + async fn new() -> Self { + let docker = Cli::default(); + let container = docker.run(Postgres::default()); + let host_port = container.get_host_port_ipv4(5432); + + let database_url = format!("postgres://postgres:password@localhost:{}/test", host_port); + let pool = PgPool::connect(&database_url).await.unwrap(); + + // Run migrations + sqlx::migrate!("./migrations").run(&pool).await.unwrap(); + + Self { + pool, + _container: container, + } + } +} + +#[tokio::test] +async fn test_user_service_integration() { + let ctx = TestContext::new().await; + let repository = PostgresUserRepository::new(ctx.pool.clone()); + let service = UserService::new(repository); + + let user = User { + id: "integration-test-123".to_string(), + email: "integration@example.com".to_string(), + name: "Integration User".to_string(), + }; + + // Test create + let created_user = service.create_user(user.clone()).await.unwrap(); + assert_eq!(created_user.email, user.email); + + // Test find + let found_user = service.find_user(&user.id).await.unwrap(); + assert!(found_user.is_some()); + assert_eq!(found_user.unwrap().id, user.id); +} + +#[tokio::test] +async fn test_duplicate_user_creation_fails() { + let ctx = TestContext::new().await; + let repository = PostgresUserRepository::new(ctx.pool.clone()); + let service = UserService::new(repository); + + let user = User { + id: "duplicate-test-123".to_string(), + email: "duplicate@example.com".to_string(), + name: "Duplicate User".to_string(), + }; + + // First creation should succeed + service.create_user(user.clone()).await.unwrap(); + + // Second creation should fail + let result = service.create_user(user).await; + assert!(result.is_err()); +} +``` + +## 🧬 PROPERTY TESTING WITH PROPTEST + +### Property Test Configuration + +```rust +// Cargo.toml +[dev-dependencies] +proptest = "1.5" + +// src/user/validation.rs +use proptest::prelude::*; + +pub fn validate_email(email: &str) -> bool { + email.contains('@') && email.len() > 3 && email.len() < 255 +} + +pub fn validate_username(username: &str) -> bool { + username.len() >= 3 && username.len() <= 50 && username.chars().all(|c| c.is_alphanumeric() || c == '_') +} + +#[cfg(test)] +mod property_tests { + use super::*; + use proptest::prelude::*; + + proptest! { + #[test] + fn test_valid_email_always_contains_at(email in r"[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}") { + prop_assert!(validate_email(&email)); + } + + #[test] + fn test_invalid_email_without_at(email in r"[a-zA-Z0-9._%+-]+[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}") { + prop_assert!(!validate_email(&email)); + } + + #[test] + fn test_username_length_bounds(username in r"[a-zA-Z0-9_]{3,50}") { + prop_assert!(validate_username(&username)); + } + + #[test] + fn test_username_too_short(username in r"[a-zA-Z0-9_]{1,2}") { + prop_assert!(!validate_username(&username)); + } + + #[test] + fn test_username_too_long(username in r"[a-zA-Z0-9_]{51,100}") { + prop_assert!(!validate_username(&username)); + } + + #[test] + fn test_username_invalid_chars(username in r"[a-zA-Z0-9_]*[^a-zA-Z0-9_]+[a-zA-Z0-9_]*") { + prop_assume!(!username.is_empty()); + prop_assert!(!validate_username(&username)); + } + } + + // Custom strategy for generating valid users + fn valid_user_strategy() -> impl Strategy { + ( + r"user[0-9]{1,6}", + r"[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}", + r"[A-Z][a-z]{2,20} [A-Z][a-z]{2,20}", + ) + .prop_map(|(id, email, name)| User { id, email, name }) + } + + proptest! { + #[test] + fn test_user_serialization_roundtrip(user in valid_user_strategy()) { + let json = serde_json::to_string(&user).unwrap(); + let deserialized: User = serde_json::from_str(&json).unwrap(); + prop_assert_eq!(user, deserialized); + } + } +} +``` + +## 🚀 PERFORMANCE BENCHMARKING + +### Criterion Benchmark Setup + +```rust +// Cargo.toml +[[bench]] +name = "user_benchmarks" +harness = false + +[dev-dependencies] +criterion = { version = "0.5", features = ["html_reports"] } + +// benches/user_benchmarks.rs +use criterion::{black_box, criterion_group, criterion_main, Criterion, BenchmarkId}; +use my_project::{User, UserService, InMemoryUserRepository}; + +fn create_user_benchmark(c: &mut Criterion) { + let repository = InMemoryUserRepository::new(); + let service = UserService::new(repository); + + c.bench_function("create_user", |b| { + b.iter(|| { + let user = User { + id: format!("bench-{}", fastrand::u64(..)), + email: "bench@example.com".to_string(), + name: "Benchmark User".to_string(), + }; + + black_box(service.create_user(user)) + }) + }); +} + +fn find_user_benchmark(c: &mut Criterion) { + let mut group = c.benchmark_group("find_user"); + + for size in [100, 1000, 10000].iter() { + let repository = InMemoryUserRepository::new(); + let service = UserService::new(repository); + + // Pre-populate with users + for i in 0..*size { + let user = User { + id: format!("user-{}", i), + email: format!("user{}@example.com", i), + name: format!("User {}", i), + }; + service.create_user(user).unwrap(); + } + + group.bench_with_input(BenchmarkId::new("size", size), size, |b, &size| { + b.iter(|| { + let user_id = format!("user-{}", fastrand::usize(0..size)); + black_box(service.find_user(&user_id)) + }) + }); + } + group.finish(); +} + +criterion_group!(benches, create_user_benchmark, find_user_benchmark); +criterion_main!(benches); +``` + +## 🌐 WEB API TESTING + +### Axum Test Integration + +```rust +// Cargo.toml +[dev-dependencies] +axum-test = "16" +wiremock = "0.6" + +// tests/api_test.rs +use axum_test::TestServer; +use my_project::{create_app, AppState}; +use wiremock::{MockServer, Mock, ResponseTemplate}; +use wiremock::matchers::{method, path}; + +#[tokio::test] +async fn test_create_user_api() { + let app_state = AppState::new_test(); + let app = create_app(app_state); + let server = TestServer::new(app).unwrap(); + + let user_data = serde_json::json!({ + "email": "api@example.com", + "name": "API User" + }); + + let response = server + .post("/users") + .json(&user_data) + .await; + + response.assert_status_created(); + response.assert_json(&serde_json::json!({ + "id": response.json::()["id"], + "email": "api@example.com", + "name": "API User" + })); +} + +#[tokio::test] +async fn test_external_service_integration() { + // Mock external service + let mock_server = MockServer::start().await; + + Mock::given(method("GET")) + .and(path("/external/users/123")) + .respond_with(ResponseTemplate::new(200) + .set_body_json(&serde_json::json!({ + "id": "123", + "verified": true + }))) + .mount(&mock_server) + .await; + + let app_state = AppState::new_test_with_external_url(&mock_server.uri()); + let app = create_app(app_state); + let server = TestServer::new(app).unwrap(); + + let response = server + .get("/users/123/verification") + .await; + + response.assert_status_ok(); + response.assert_json(&serde_json::json!({ + "verified": true + })); +} +``` + +## 📊 COVERAGE AND CI INTEGRATION + +### Coverage Configuration + +```bash +# Install tarpaulin for coverage +cargo install cargo-tarpaulin + +# Generate coverage report +cargo tarpaulin --out html --output-dir coverage + +# CI-friendly coverage +cargo tarpaulin --out xml --output-dir coverage +``` + +### GitHub Actions Configuration + +```yaml +# .github/workflows/test.yml +name: Tests + +on: + push: + branches: [ main, develop ] + pull_request: + branches: [ main ] + +jobs: + test: + runs-on: ubuntu-latest + + services: + postgres: + image: postgres:15 + env: + POSTGRES_PASSWORD: password + POSTGRES_DB: test + options: >- + --health-cmd pg_isready + --health-interval 10s + --health-timeout 5s + --health-retries 5 + ports: + - 5432:5432 + + steps: + - uses: actions/checkout@v4 + + - name: Install Rust + uses: dtolnay/rust-toolchain@stable + with: + components: rustfmt, clippy + + - name: Cache dependencies + uses: actions/cache@v4 + with: + path: | + ~/.cargo/bin/ + ~/.cargo/registry/index/ + ~/.cargo/registry/cache/ + ~/.cargo/git/db/ + target/ + key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} + + - name: Check formatting + run: cargo fmt --all -- --check + + - name: Clippy + run: cargo clippy --all-targets --all-features -- -D warnings + + - name: Unit Tests + run: cargo test --lib + env: + DATABASE_URL: postgres://postgres:password@localhost:5432/test + + - name: Integration Tests + run: cargo test --test '*' + env: + DATABASE_URL: postgres://postgres:password@localhost:5432/test + + - name: Doc Tests + run: cargo test --doc + + - name: Benchmarks (check only) + run: cargo bench --no-run + + coverage: + runs-on: ubuntu-latest + needs: test + + services: + postgres: + image: postgres:15 + env: + POSTGRES_PASSWORD: password + POSTGRES_DB: test + options: >- + --health-cmd pg_isready + --health-interval 10s + --health-timeout 5s + --health-retries 5 + ports: + - 5432:5432 + + steps: + - uses: actions/checkout@v4 + + - name: Install Rust + uses: dtolnay/rust-toolchain@stable + + - name: Install tarpaulin + run: cargo install cargo-tarpaulin + + - name: Generate coverage + run: cargo tarpaulin --out xml --output-dir coverage + env: + DATABASE_URL: postgres://postgres:password@localhost:5432/test + + - name: Upload coverage to Codecov + uses: codecov/codecov-action@v4 + with: + file: coverage/cobertura.xml + fail_ci_if_error: true +``` + +## ✅ TESTING CHECKLIST + +```markdown +### Testing Completeness Verification +- [ ] Unit tests for all public functions +- [ ] Unit tests use mocking for external dependencies +- [ ] Integration tests cover realistic scenarios +- [ ] Property tests validate invariants +- [ ] Benchmarks track performance regressions +- [ ] Test coverage > 80% for core modules +- [ ] Tests run in CI/CD pipeline +- [ ] No `unwrap()` or `expect()` in test code +- [ ] Test data is deterministic or properly seeded +- [ ] Async tests use `#[tokio::test]` +- [ ] Web API tests cover error cases +- [ ] External services are properly mocked +- [ ] Database tests use transactions or containers +- [ ] Tests are organized by functionality +- [ ] Test names clearly describe scenarios +``` + +## 📚 TESTING DEPENDENCIES + +### Essential Testing Crates + +```toml +[dev-dependencies] +# Core testing +tokio-test = "0.4" # Async testing utilities + +# Mocking +mockall = "0.13" # Mock generation +wiremock = "0.6" # HTTP service mocking + +# Property testing +proptest = "1.5" # Property-based testing + +# Benchmarking +criterion = { version = "0.5", features = ["html_reports"] } + +# Web testing +axum-test = "16" # Axum application testing + +# Database testing +testcontainers = "0.21" # Docker containers for tests +sqlx = { version = "0.8", features = ["testing"] } + +# Utilities +tempfile = "3.13" # Temporary files for tests +serde_json = "1.0" # JSON serialization for tests +``` + +This comprehensive testing standard ensures robust, reliable Rust applications with thorough test coverage and quality assurance. diff --git a/specs/instructions.md b/specs/instructions.md index 3e78c3f..c173b14 100644 --- a/specs/instructions.md +++ b/specs/instructions.md @@ -169,3 +169,13 @@ async pub fn say_hello( 6. TypedBuilder 用法遵循:并对每个字段根据情况引入 default, default_code, 以及 setter(strip_option), setter(into), 或者 setter(strip_option, into)。比如 Option 要使用 `#[builder(default, setter(strip_option, into)]`. 不要滥用 default。 请仔细审核 @/rust rule set,看各部分内容是否有重复,是否正交,请相应修改和重构,另外,如果还有 best practice 和 rust 最佳实践和设计模式没有写进去,请添加。 + +请再次仔细审视 @/rust rules set,请确保: + +1. rust best practices 已涵盖 +2. rust rules 是正交的,且通过 @main.mdc 可以根据不同的场景动态加载不同的 rules。 +3. 目前的子目录设置是合理的 +4. 核心规则 @/core 是合理的,且利于产生高质量且简洁的代码 +5. @/features 规则是合理的,且利于产生高质量且简洁的代码 +6. 这些 rules 无论对新项目还是老项目都能够产生高质量且简洁的代码 +如果你发现需要任何修改,请在 ./spec/ 下生成一个详细的修改计划 diff --git a/specs/rust-rules-review-plan.md b/specs/rust-rules-review-plan.md new file mode 100644 index 0000000..a5c3c64 --- /dev/null +++ b/specs/rust-rules-review-plan.md @@ -0,0 +1,51 @@ +## 📝 Rust Rules Review – Modification Plan + +> Author: AI assistant +> Date: {{DATE}} + +### 1. Overview + +After a thorough review of the current Rust rule-set under `.cursor/rules/rust/`, the guidelines are largely complete, orthogonal, and dynamically loadable via **`main.mdc`**. However, two gaps were identified: + +1. **Testing best-practices module is missing.** `main.mdc` references `quality/testing.mdc`, but the file does not exist. This breaks the rule-loading table and leaves projects without unified testing standards. +2. **`core/code-quality.mdc` lacks the mandatory decision-making Mermaid chart.** All other core/feature files now include a chart; adding one keeps the format consistent and aids discoverability. + +No other structural issues were found. Directory layout remains logical (`core`, `features`, `quality`, `simple`, `complex`, `workflows`). Core and feature rules comprehensively cover Rust best-practices and remain orthogonal. + +### 2. Proposed Changes + +| # | Change | Affected File | Rationale | +| --- | ------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- | +| 1 | Create **`quality/testing.mdc`** with complete Rust testing standards and a decision-flow Mermaid chart | `./.cursor/rules/rust/quality/testing.mdc` | Fulfils missing reference in `main.mdc`; provides unit/integration/bench test guidelines, mocking, CI hints, coverage, property testing, etc. | +| 2 | Add **Mermaid decision chart** to the top of **`core/code-quality.mdc`** | `./.cursor/rules/rust/core/code-quality.mdc` | Brings file in line with the "chart-first" convention adopted across the rule-set. | +| 3 | Update module table in **`main.mdc`** (if needed) to confirm `quality/testing.mdc` exists | `./.cursor/rules/rust/main.mdc` | Keeps documentation accurate. | +| 4 | *Optional*: add CI/CD & release workflow rules in future (`workflows/ci-cd.mdc`) – **not required immediately** but noted for roadmap | – | Complete lifecycle guidance (build, test, release) | + +### 3. Deliverables + +1. **`quality/testing.mdc`** – roughly 8-10 sections: + * Testing strategy selection chart (unit vs integration vs e2e vs property vs benchmark) + * Cargo test organisation (`#[cfg(test)]`, `/tests`, `/benches`) + * Mocking & test doubles (`mockall`, `axum_test`, `wiremock`) + * Async testing with Tokio + * Property testing (`proptest`) + * Coverage (`cargo tarpaulin`, GitHub Actions snippet) + * Benchmarks (`criterion`) + * CI integration checklist +2. **Chart insertion** at top of `core/code-quality.mdc` – simple flow for adopting code-quality rules (naming, file size, lint enforcement). + +### 4. Compatibility + +The additions are non-breaking and purely additive: + +* **Legacy projects** gain a clear path to adopt unified testing standards without altering existing code. +* **New projects** continue to rely on dynamic rule loading – the missing file removal fixes table inconsistencies. + +### 5. Next Steps + +1. Implement the above changes via follow-up code edits. +2. Run `cargo clippy` on representative sample projects to verify lint rules referenced still align. +3. Regenerate rule index documentation if tooling exists. + +--- +*End of plan*