guardrail3 0.2.0

Composable code guardrails for Rust and TypeScript projects
Documentation
# =============================================================================
# clippy.toml -- GENERATED by guardrail3 (profile: service)
# DO NOT EDIT -- regenerate with: guardrail3 generate
# =============================================================================

# THRESHOLDS
# Maximum lines per function before clippy::too_many_lines fires.
too-many-lines-threshold = 75

# Maximum cognitive complexity score per function.
cognitive-complexity-threshold = 15

# Maximum number of function parameters.
too-many-arguments-threshold = 7

# Maximum type nesting depth (e.g., Result<Option<Vec<Box<dyn Trait>>>>).
# 75 is the clippy default. Legitimate types like Option<BTreeMap<String, Vec<u8>>>
# score well under this. If legitimate types hit this, bump to 100.
type-complexity-threshold = 75

# Maximum number of bool fields in a struct before clippy::struct_excessive_bools fires.
max-struct-bools = 3

# DISALLOWED METHODS
disallowed-methods = [
    # --- Ban direct environment variable access (std::env::var*) ---
    { path = "std::env::var", reason = "Use the centralized config module -- direct env access scatters configuration and is untestable" },
    { path = "std::env::var_os", reason = "Use the centralized config module -- direct env access scatters configuration and is untestable" },
    { path = "std::env::vars", reason = "Use the centralized config module -- direct env access scatters configuration and is untestable" },
    # --- Ban environment variable mutation (unsafe in multi-threaded contexts) ---
    { path = "std::env::set_var", reason = "Unsafe in multi-threaded contexts -- environment mutation is not thread-safe" },
    { path = "std::env::remove_var", reason = "Unsafe in multi-threaded contexts -- environment mutation is not thread-safe" },
    # --- Ban process::exit and shell execution ---
    { path = "std::process::exit", reason = "Use proper error propagation (return Result from main) -- process::exit skips destructors" },
    { path = "std::process::Command::new", reason = "Shell execution not permitted in this service" },
    # --- Ban std::thread::sleep (use tokio::time::sleep) ---
    { path = "std::thread::sleep", reason = "Use tokio::time::sleep for async context -- std::thread::sleep blocks the tokio runtime" },
    # --- Ban direct filesystem operations (create a centralized fs module) ---
    # Reads
    { path = "std::fs::read_to_string", reason = "BANNED: Create a centralized fs module and route all filesystem operations through it -- no scattered std::fs calls" },
    { path = "std::fs::read", reason = "BANNED: Create a centralized fs module and route all filesystem operations through it -- no scattered std::fs calls" },
    { path = "std::fs::read_dir", reason = "BANNED: Create a centralized fs module and route all filesystem operations through it -- no scattered std::fs calls" },
    { path = "std::fs::read_link", reason = "BANNED: Create a centralized fs module and route all filesystem operations through it -- no scattered std::fs calls" },

    # Writes
    { path = "std::fs::write", reason = "BANNED: Create a centralized fs module and route all filesystem operations through it -- no scattered std::fs calls" },

    # Destructive operations
    { path = "std::fs::remove_file", reason = "BANNED: Create a centralized fs module and route all filesystem operations through it -- no scattered std::fs calls" },
    { path = "std::fs::remove_dir_all", reason = "BANNED: Create a centralized fs module and route all filesystem operations through it -- no scattered std::fs calls" },

    # Directory creation
    { path = "std::fs::create_dir_all", reason = "BANNED: Create a centralized fs module and route all filesystem operations through it -- no scattered std::fs calls" },

    # Move / copy
    { path = "std::fs::rename", reason = "BANNED: Create a centralized fs module and route all filesystem operations through it -- no scattered std::fs calls" },
    { path = "std::fs::copy", reason = "BANNED: Create a centralized fs module and route all filesystem operations through it -- no scattered std::fs calls" },

    # Metadata and inspection
    { path = "std::fs::metadata", reason = "BANNED: Create a centralized fs module and route all filesystem operations through it -- no scattered std::fs calls" },
    { path = "std::fs::symlink_metadata", reason = "BANNED: Create a centralized fs module and route all filesystem operations through it -- no scattered std::fs calls" },
    { path = "std::fs::canonicalize", reason = "BANNED: Create a centralized fs module and route all filesystem operations through it -- no scattered std::fs calls" },
    { path = "std::fs::set_permissions", reason = "BANNED: Create a centralized fs module and route all filesystem operations through it -- no scattered std::fs calls" },
    { path = "std::fs::hard_link", reason = "BANNED: Create a centralized fs module and route all filesystem operations through it -- no scattered std::fs calls" },
    # --- Ban per-request HTTP client construction (use shared client via DI) ---
    { path = "reqwest::Client::new", reason = "Use shared client from AppState -- per-request construction skips connection pooling" },
    { path = "reqwest::Client::builder", reason = "Use shared client from AppState -- construct once at startup and inject via DI" },
    # --- Ban raw serde deserialization (use Validated<T>::new() for external data) ---
    { path = "serde_json::from_str", reason = "BANNED: Use Validated<T>::new(value, &ctx) instead. (1) Add #[derive(garde::Validate)] to your response/data type. (2) Add garde rules to each field -- #[garde(length(chars, min = 1, max = N))] for strings, #[garde(range(min = 0))] for numbers, #[garde(dive)] for nested structs, #[garde(length(min = 1))] for non-empty vecs. (3) Deserialize into the raw type, then wrap with Validated::new()." },
    { path = "serde_json::from_slice", reason = "BANNED: Use Validated<T>::new(value, &ctx) instead. (1) Add #[derive(garde::Validate)] to your response/data type. (2) Add garde rules to each field -- #[garde(length(chars, min = 1, max = N))] for strings, #[garde(range(min = 0))] for numbers, #[garde(dive)] for nested structs, #[garde(length(min = 1))] for non-empty vecs. (3) Deserialize into the raw type, then wrap with Validated::new()." },
    { path = "serde_json::from_value", reason = "BANNED: Use Validated<T>::new(value, &ctx) instead. (1) Add #[derive(garde::Validate)] to your response/data type. (2) Add garde rules to each field -- #[garde(length(chars, min = 1, max = N))] for strings, #[garde(range(min = 0))] for numbers, #[garde(dive)] for nested structs, #[garde(length(min = 1))] for non-empty vecs. (3) Deserialize into the raw type, then wrap with Validated::new()." },
    { path = "serde_json::from_reader", reason = "BANNED: Use Validated<T>::new(value, &ctx) instead. (1) Add #[derive(garde::Validate)] to your response/data type. (2) Add garde rules to each field -- #[garde(length(chars, min = 1, max = N))] for strings, #[garde(range(min = 0))] for numbers, #[garde(dive)] for nested structs, #[garde(length(min = 1))] for non-empty vecs. (3) Deserialize into the raw type, then wrap with Validated::new()." },
    { path = "reqwest::Response::json", reason = "BANNED: Use Validated<T>::new(value, &ctx) instead. (1) Add #[derive(garde::Validate)] to your response/data type. (2) Add garde rules to each field -- #[garde(length(chars, min = 1, max = N))] for strings, #[garde(range(min = 0))] for numbers, #[garde(dive)] for nested structs, #[garde(length(min = 1))] for non-empty vecs. (3) Deserialize into the raw type, then wrap with Validated::new()." },
    { path = "toml::from_str", reason = "BANNED: Use Validated<T>::new(value, &ctx) instead. (1) Add #[derive(garde::Validate)] to your response/data type. (2) Add garde rules to each field -- #[garde(length(chars, min = 1, max = N))] for strings, #[garde(range(min = 0))] for numbers, #[garde(dive)] for nested structs, #[garde(length(min = 1))] for non-empty vecs. (3) Deserialize into the raw type, then wrap with Validated::new()." },
    { path = "serde_yaml::from_str", reason = "BANNED: Use Validated<T>::new(value, &ctx) instead. (1) Add #[derive(garde::Validate)] to your response/data type. (2) Add garde rules to each field -- #[garde(length(chars, min = 1, max = N))] for strings, #[garde(range(min = 0))] for numbers, #[garde(dive)] for nested structs, #[garde(length(min = 1))] for non-empty vecs. (3) Deserialize into the raw type, then wrap with Validated::new()." },
    { path = "serde_yaml::from_reader", reason = "BANNED: Use Validated<T>::new(value, &ctx) instead. (1) Add #[derive(garde::Validate)] to your response/data type. (2) Add garde rules to each field -- #[garde(length(chars, min = 1, max = N))] for strings, #[garde(range(min = 0))] for numbers, #[garde(dive)] for nested structs, #[garde(length(min = 1))] for non-empty vecs. (3) Deserialize into the raw type, then wrap with Validated::new()." },
    # --- Local overrides ---
# Additional disallowed-methods entries (TOML array-of-tables format)
# Example:
#     { path = "some::method", reason = "Use alternative instead" },

]

# DISALLOWED TYPES
disallowed-types = [
    # --- Ban HashMap/HashSet (use BTree variants for deterministic ordering) ---
    { path = "std::collections::HashMap", reason = "Use BTreeMap for deterministic iteration order" },
    { path = "std::collections::HashSet", reason = "Use BTreeSet for deterministic iteration order" },
    # --- Ban std::sync::Mutex/RwLock (use parking_lot) ---
    { path = "std::sync::Mutex", reason = "Use parking_lot::Mutex -- no poisoning, better performance" },
    { path = "std::sync::RwLock", reason = "Use parking_lot::RwLock -- no poisoning, better performance" },
    # --- Ban std::fs::File (use centralized fs module) ---
    { path = "std::fs::File", reason = "BANNED: Create a centralized fs module -- no direct file handle construction" },
    # --- Ban raw Axum extractors (use ValidatedJson/ValidatedQuery/ValidatedForm) ---
    { path = "axum::extract::Json", reason = "BANNED: Use ValidatedJson<T>/ValidatedQuery<T>/ValidatedForm<T> instead. Requires #[derive(garde::Validate)] on the request type." },
    { path = "axum::Json", reason = "BANNED: Use ValidatedJson<T>/ValidatedQuery<T>/ValidatedForm<T> instead. Requires #[derive(garde::Validate)] on the request type." },
    { path = "axum::extract::Query", reason = "BANNED: Use ValidatedJson<T>/ValidatedQuery<T>/ValidatedForm<T> instead. Requires #[derive(garde::Validate)] on the request type." },
    { path = "axum::extract::Form", reason = "BANNED: Use ValidatedJson<T>/ValidatedQuery<T>/ValidatedForm<T> instead. Requires #[derive(garde::Validate)] on the request type." },
    # --- Local overrides ---
# Additional disallowed-types entries (TOML array-of-tables format)
# Example:
#     { path = "some::Type", reason = "Use alternative instead" },

]