Skip to content

Latest commit

 

History

History
99 lines (80 loc) · 7.84 KB

rust-actix.md

File metadata and controls

99 lines (80 loc) · 7.84 KB

File Structure and Module Organization

  • All source code must live in src/ directory. No exceptions.
  • HTTP handlers must be in src/handlers/ with one file per route group. File name must match the route prefix in snake_case: src/handlers/user_profile.rs for /user/profile/* routes.
  • Error types must be in src/errors.rs. Do not scatter error definitions across modules.
  • Database models must be in src/models/ with one file per entity. File name matches entity name in snake_case: src/models/user.rs for the User struct.
  • Middleware must be in src/middleware/ with one file per middleware. File name describes the middleware function: src/middleware/auth_guard.rs.
  • Configuration must be in src/config.rs. No config files scattered in subdirectories.
  • Tests must live in src/tests/ with directory structure mirroring src/. Test file name must be {module_name}_tests.rs.
  • All public modules must be explicitly declared in mod.rs files. No glob imports like mod *;.
  • Re-exports in mod.rs must use pub use only for types that are part of the public API. Internal types stay private.

Naming Conventions

  • Struct names must be PascalCase: UserProfile, CreateUserRequest, never user_profile.
  • Function names must be snake_case: create_user, validate_email, never createUser.
  • Constants must be SCREAMING_SNAKE_CASE: MAX_RETRY_ATTEMPTS, DEFAULT_TIMEOUT_MS.
  • Private module functions must be prefixed with _ if they are internal helpers: _validate_input().
  • Handler functions must end with _handler: get_user_handler, create_post_handler.
  • Middleware functions must end with _middleware: auth_middleware, cors_middleware.
  • Database query functions must start with db_: db_find_user_by_id, db_create_post.
  • Error enum variants must be descriptive and end with the error type: InvalidEmailFormat, DatabaseConnectionFailed, UnauthorizedAccess.

Error Handling - Mandatory Patterns

  • Every function that can fail must return Result<T, AppError>. No Option<T> for error cases.
  • AppError must be defined in src/errors.rs as an enum with typed variants. Each variant must include context: InvalidInput { field: String, reason: String }.
  • Never use .unwrap() or .expect() outside of src/tests/. Use .map_err() or ? operator instead.
  • Every .map_err() must provide context: .map_err(|e| AppError::DatabaseError { source: e.to_string() }).
  • Errors must be converted to HTTP responses in handlers only. Use impl From<AppError> for HttpResponse in src/errors.rs.
  • HTTP error responses must include a JSON body with error_code, message, and details fields. Never return raw error strings.
  • Async functions must have explicit error handling. No tokio::spawn() without a .await that checks the result.
  • Database errors must be logged with full context before being converted to AppError: error!("Database query failed: {:?}", err);.

Async and Concurrency

  • Every async function must be marked async. No fn that returns impl Future.
  • Blocking operations must never be called directly in async context. Use tokio::task::block_in_place() or tokio::task::spawn_blocking() for CPU-bound work.
  • Database queries must use connection pooling via sqlx::PgPool. Never create new connections in handlers.
  • Tokio tasks spawned with tokio::spawn() must have their JoinHandle awaited or explicitly dropped. Never fire-and-forget.
  • Timeouts must be explicit: wrap all external I/O with tokio::time::timeout(). Default timeout is 30 seconds unless documented otherwise.
  • Mutex usage must be minimized. If a Mutex is used, it must be held for less than one await point. Document why in a comment.

Type Safety and Rust Idioms

  • Never use as type assertions outside of src/tests/. Use TryFrom or explicit conversion functions instead.
  • Never use unsafe without a comment explaining why it is necessary and what invariants it maintains. Unsafe blocks must be reviewed by a senior engineer.
  • Generic types must have explicit trait bounds. No T without bounds. Example: fn process<T: Serialize + Send>(item: T).
  • Use newtype pattern for domain types: struct UserId(i64); not type UserId = i64;.
  • Derive Debug, Clone, Serialize, Deserialize on all public structs. Never omit these.
  • Use #[serde(rename_all = "snake_case")] on all request/response DTOs.
  • Pattern matching must be exhaustive. Never use _ catch-all in match statements unless explicitly documented.

Validation and Security

  • All user input must be validated at the handler boundary before being passed to business logic.
  • Use validator crate for struct validation. Annotate request DTOs with #[validate] attributes.
  • Email validation must use regex: ^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$ or the validator::email() function.
  • Passwords must be at least 12 characters, contain uppercase, lowercase, digit, and special character. Validate in a dedicated function validate_password_strength().
  • Never log passwords, tokens, or API keys. Use *** redaction in logs.
  • SQL queries must use parameterized queries via sqlx::query!() macro. Never concatenate strings into SQL.
  • CORS must be explicitly configured in src/config.rs. No wildcard * allowed in production. Allowed origins must be in environment variables.
  • Authentication tokens must be validated on every protected route. Use middleware, not inline checks in handlers.

Testing Requirements

  • Every public function must have at least one test. Private functions need tests only if they contain complex logic.
  • Test file location: src/tests/{module_name}_tests.rs. Test function name: test_{function_name}_{scenario}. Example: test_create_user_with_valid_email.
  • Integration tests must be in tests/ directory at project root, not in src/tests/.
  • Mock database connections using sqlx::sqlite::SqlitePool with :memory: for unit tests.
  • Every test must have a #[tokio::test] attribute if it is async. Never use #[test] for async functions.
  • Test assertions must be specific: use assert_eq!() with meaningful error messages, not bare assert!().
  • Error cases must be tested explicitly. If a function can return AppError::InvalidInput, write a test that triggers it.

Code Quality and Linting

  • All code must pass cargo clippy -- -D warnings. No warnings allowed in CI.
  • All code must be formatted with cargo fmt. Run before every commit.
  • Dead code must be removed. No commented-out code blocks.
  • Imports must be organized: standard library first, then external crates, then internal modules. Use use statements, not mod for imports.
  • Line length must not exceed 100 characters. Break long lines at logical boundaries.
  • Documentation comments must be on all public items using ///. Private items need // comments only if logic is non-obvious.
  • Dependency versions must be pinned in Cargo.toml for production dependencies. Use = not ^ or ~.

Actix-web Specific Rules

  • All routes must be registered in src/main.rs via web::scope(). No inline route registration in handlers.
  • Request extractors must use typed extractors: web::Json<CreateUserRequest>, web::Path<UserId>. Never use raw request bodies.
  • Response types must be explicit: return HttpResponse::Ok().json(data) not web::Json(data).
  • Middleware must be registered globally in App::new() or scoped to specific routes. Document the scope in a comment.
  • Path parameters must be validated in the extractor. Use serde validation or custom extractors.
  • Query parameters must be optional and have defaults. Use Option<T> with #[serde(default)].
  • All handlers must return impl Responder. Never return raw types.

Source: Codelibrium — the marketplace for AI behaviour files. Browse multiple rulesets at codelibrium.com or install via CLI: npx codelibrium-cli install <ruleset-name>