- 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.rsfor/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.rsfor 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 mirroringsrc/. Test file name must be{module_name}_tests.rs. - All public modules must be explicitly declared in
mod.rsfiles. No glob imports likemod *;. - Re-exports in
mod.rsmust usepub useonly for types that are part of the public API. Internal types stay private.
- Struct names must be PascalCase:
UserProfile,CreateUserRequest, neveruser_profile. - Function names must be snake_case:
create_user,validate_email, nevercreateUser. - 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.
- Every function that can fail must return
Result<T, AppError>. NoOption<T>for error cases. AppErrormust be defined insrc/errors.rsas an enum with typed variants. Each variant must include context:InvalidInput { field: String, reason: String }.- Never use
.unwrap()or.expect()outside ofsrc/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 HttpResponseinsrc/errors.rs. - HTTP error responses must include a JSON body with
error_code,message, anddetailsfields. Never return raw error strings. - Async functions must have explicit error handling. No
tokio::spawn()without a.awaitthat checks the result. - Database errors must be logged with full context before being converted to AppError:
error!("Database query failed: {:?}", err);.
- Every async function must be marked
async. Nofnthat returnsimpl Future. - Blocking operations must never be called directly in async context. Use
tokio::task::block_in_place()ortokio::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 theirJoinHandleawaited 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.
- Never use
astype assertions outside ofsrc/tests/. UseTryFromor explicit conversion functions instead. - Never use
unsafewithout 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
Twithout bounds. Example:fn process<T: Serialize + Send>(item: T). - Use
newtypepattern for domain types:struct UserId(i64);nottype UserId = i64;. - Derive
Debug,Clone,Serialize,Deserializeon 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.
- All user input must be validated at the handler boundary before being passed to business logic.
- Use
validatorcrate 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 thevalidator::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.
- 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 insrc/tests/. - Mock database connections using
sqlx::sqlite::SqlitePoolwith: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 bareassert!(). - Error cases must be tested explicitly. If a function can return
AppError::InvalidInput, write a test that triggers it.
- 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
usestatements, notmodfor 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.tomlfor production dependencies. Use=not^or~.
- All routes must be registered in
src/main.rsviaweb::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)notweb::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
serdevalidation 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>