Code Style Guide

This guide outlines the coding standards and style guidelines for MockForge development. Consistent code style improves readability, maintainability, and collaboration.

Rust Code Style

MockForge follows the official Rust style guidelines with some project-specific conventions.

Formatting

Use rustfmt for automatic code formatting:

# Format all code
cargo fmt

# Check formatting without modifying files
cargo fmt --check

Linting

Use clippy for additional code quality checks:

# Run clippy with project settings
cargo clippy

# Run with pedantic mode for stricter checks
cargo clippy -- -W clippy::pedantic

Naming Conventions

Functions and Variables

#![allow(unused)]
fn main() {
// Good: snake_case for functions and variables
fn process_user_data(user_id: i32, data: &str) -> Result<User, Error> {
    let processed_data = validate_and_clean(data)?;
    let user_record = create_user_record(user_id, &processed_data)?;
    Ok(user_record)
}

// Bad: camelCase or PascalCase
fn processUserData(userId: i32, data: &str) -> Result<User, Error> {
    let ProcessedData = validate_and_clean(data)?;
    let userRecord = create_user_record(userId, &ProcessedData)?;
    Ok(userRecord)
}
}

Types and Traits

#![allow(unused)]
fn main() {
// Good: PascalCase for types
pub struct HttpServer {
    config: ServerConfig,
    router: Router,
}

pub trait RequestHandler {
    fn handle_request(&self, request: Request) -> Response;
}

// Bad: snake_case for types
pub struct http_server {
    config: ServerConfig,
    router: Router,
}
}

Constants

#![allow(unused)]
fn main() {
// Good: SCREAMING_SNAKE_CASE for constants
const MAX_CONNECTIONS: usize = 1000;
const DEFAULT_TIMEOUT_SECS: u64 = 30;

// Bad: camelCase or PascalCase
const maxConnections: usize = 1000;
const DefaultTimeoutSecs: u64 = 30;
}

Modules and Files

#![allow(unused)]
fn main() {
// Good: snake_case for module names
pub mod request_handler;
pub mod template_engine;

// File: request_handler.rs
// Module: request_handler
}

Documentation

Function Documentation

#![allow(unused)]
fn main() {
/// Processes a user request and returns a response.
///
/// This function handles the complete request processing pipeline:
/// 1. Validates the request data
/// 2. Applies business logic
/// 3. Returns appropriate response
///
/// # Arguments
///
/// * `user_id` - The ID of the user making the request
/// * `request_data` - The request payload as JSON
///
/// # Returns
///
/// Returns a `Result<Response, Error>` where:
/// - `Ok(response)` contains the successful response
/// - `Err(error)` contains details about what went wrong
///
/// # Errors
///
/// This function will return an error if:
/// - The user ID is invalid
/// - The request data is malformed
/// - Database operations fail
///
/// # Examples
///
/// ```rust
/// let user_id = 123;
/// let request_data = r#"{"action": "update_profile"}"#;
/// let response = process_user_request(user_id, request_data)?;
/// assert_eq!(response.status(), 200);
/// ```
pub fn process_user_request(user_id: i32, request_data: &str) -> Result<Response, Error> {
    // Implementation
}
}

Module Documentation

#![allow(unused)]
fn main() {
//! # HTTP Server Module
//!
//! This module provides HTTP server functionality for MockForge,
//! including request routing, middleware support, and response handling.
//!
//! ## Architecture
//!
//! The HTTP server uses axum as the underlying web framework and provides:
//! - OpenAPI specification integration
//! - Template-based response generation
//! - Middleware for logging and validation
//!
//! ## Example
//!
//! ```rust
//! use mockforge_http::HttpServer;
//!
//! let server = HttpServer::new(config);
//! server.serve("127.0.0.1:3000").await?;
//! ```
}

Error Handling

Custom Error Types

#![allow(unused)]
fn main() {
use thiserror::Error;

#[derive(Error, Debug)]
pub enum MockForgeError {
    #[error("Configuration error: {message}")]
    Config { message: String },

    #[error("I/O error: {source}")]
    Io {
        #[from]
        source: std::io::Error,
    },

    #[error("Template rendering error: {message}")]
    Template { message: String },

    #[error("HTTP error: {status} - {message}")]
    Http { status: u16, message: String },
}
}

Result Types

#![allow(unused)]
fn main() {
// Good: Use Result<T, MockForgeError> for fallible operations
pub fn load_config(path: &Path) -> Result<Config, MockForgeError> {
    let content = fs::read_to_string(path)
        .map_err(|e| MockForgeError::Io { source: e })?;

    let config: Config = serde_yaml::from_str(&content)
        .map_err(|e| MockForgeError::Config {
            message: format!("Failed to parse YAML: {}", e),
        })?;

    Ok(config)
}

// Bad: Using Option when you should use Result
pub fn load_config_bad(path: &Path) -> Option<Config> {
    // This loses error information
    None
}
}

Async Code

Async Function Signatures

#![allow(unused)]
fn main() {
// Good: Clear async function signatures
pub async fn process_request(request: Request) -> Result<Response, Error> {
    let data = validate_request(&request).await?;
    let result = process_data(data).await?;
    Ok(create_response(result))
}

// Bad: Unclear async boundaries
pub fn process_request(request: Request) -> impl Future<Output = Result<Response, Error>> {
    async move {
        // Implementation
    }
}
}

Tokio Usage

#![allow(unused)]
fn main() {
use tokio::sync::{Mutex, RwLock};

// Good: Use appropriate synchronization primitives
pub struct SharedState {
    data: RwLock<HashMap<String, String>>,
    counter: Mutex<i64>,
}

impl SharedState {
    pub async fn get_data(&self, key: &str) -> Option<String> {
        let data = self.data.read().await;
        data.get(key).cloned()
    }

    pub async fn increment_counter(&self) -> i64 {
        let mut counter = self.counter.lock().await;
        *counter += 1;
        *counter
    }
}
}

Testing

Unit Test Structure

#![allow(unused)]
fn main() {
#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_function_basic_case() {
        // Given
        let input = "test input";
        let expected = "expected output";

        // When
        let result = process_input(input);

        // Then
        assert_eq!(result, expected);
    }

    #[test]
    fn test_function_error_case() {
        // Given
        let input = "";

        // When
        let result = process_input(input);

        // Then
        assert!(result.is_err());
        assert!(matches!(result.unwrap_err(), Error::InvalidInput(_)));
    }

    #[tokio::test]
    async fn test_async_function() {
        // Given
        let client = create_test_client().await;

        // When
        let response = client.make_request().await.unwrap();

        // Then
        assert_eq!(response.status(), 200);
    }
}
}

Test Organization

#![allow(unused)]
fn main() {
// tests/integration_tests.rs
#[cfg(test)]
mod integration_tests {
    use mockforge_core::config::MockForgeConfig;

    #[tokio::test]
    async fn test_full_http_flow() {
        // Test complete request/response cycle
        let server = TestServer::new().await;
        let client = TestClient::new(server.url());

        let response = client.get("/api/users").await;
        assert_eq!(response.status(), 200);
    }
}
}

Performance Considerations

Memory Management

#![allow(unused)]
fn main() {
// Good: Use references when possible
pub fn process_data(data: &str) -> Result<String, Error> {
    // Avoid cloning unless necessary
    if data.is_empty() {
        return Err(Error::EmptyInput);
    }
    Ok(data.to_uppercase())
}

// Good: Use Cow for flexible ownership
use std::borrow::Cow;

pub fn normalize_string<'a>(input: &'a str) -> Cow<'a, str> {
    if input.chars().all(|c| c.is_lowercase()) {
        Cow::Borrowed(input)
    } else {
        Cow::Owned(input.to_lowercase())
    }
}
}

Zero-Cost Abstractions

#![allow(unused)]
fn main() {
// Good: Use iterators for memory efficiency
pub fn find_active_users(users: &[User]) -> impl Iterator<Item = &User> {
    users.iter().filter(|user| user.is_active)
}

// Bad: Collect into Vec unnecessarily
pub fn find_active_users_bad(users: &[User]) -> Vec<&User> {
    users.iter().filter(|user| user.is_active).collect()
}
}

Project-Specific Conventions

Configuration Handling

#![allow(unused)]
fn main() {
// Good: Use builder pattern for complex configuration
#[derive(Debug, Clone)]
pub struct ServerConfig {
    pub host: String,
    pub port: u16,
    pub tls: Option<TlsConfig>,
}

impl Default for ServerConfig {
    fn default() -> Self {
        Self {
            host: "127.0.0.1".to_string(),
            port: 3000,
            tls: None,
        }
    }
}

impl ServerConfig {
    pub fn builder() -> ServerConfigBuilder {
        ServerConfigBuilder::default()
    }
}
}

Logging

#![allow(unused)]
fn main() {
use tracing::{info, warn, error, debug, instrument};

// Good: Use structured logging
#[instrument(skip(config))]
pub async fn start_server(config: &ServerConfig) -> Result<(), Error> {
    info!("Starting server", host = %config.host, port = config.port);

    if let Err(e) = setup_server(config).await {
        error!("Failed to start server", error = %e);
        return Err(e);
    }

    info!("Server started successfully");
    Ok(())
}
}

Feature Flags

#![allow(unused)]
fn main() {
// Good: Use feature flags for optional functionality
#[cfg(feature = "grpc")]
pub mod grpc {
    // gRPC-specific code
}

#[cfg(feature = "websocket")]
pub mod websocket {
    // WebSocket-specific code
}
}

Code Review Checklist

Before submitting code for review, ensure:

  • Code is formatted with cargo fmt
  • No clippy warnings remain
  • All tests pass
  • Documentation is updated
  • No TODO comments left in production code
  • Error messages are user-friendly
  • Performance considerations are addressed
  • Security implications are reviewed

Tools and Automation

Pre-commit Hooks

#!/bin/bash
# .git/hooks/pre-commit

# Format code
cargo fmt --check
if [ $? -ne 0 ]; then
    echo "Code is not formatted. Run 'cargo fmt' to fix."
    exit 1
fi

# Run clippy
cargo clippy -- -D warnings
if [ $? -ne 0 ]; then
    echo "Clippy found issues. Fix them before committing."
    exit 1
fi

# Run tests
cargo test
if [ $? -ne 0 ]; then
    echo "Tests are failing. Fix them before committing."
    exit 1
fi

CI Configuration

# .github/workflows/ci.yml
name: CI

on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest

    steps:
    - uses: actions/checkout@v2
    - uses: actions-rs/toolchain@v1
      with:
        toolchain: stable

    - name: Check formatting
      run: cargo fmt --check

    - name: Run clippy
      run: cargo clippy -- -D warnings

    - name: Run tests
      run: cargo test --verbose

    - name: Run security audit
      run: cargo audit

This style guide ensures MockForge maintains high code quality and consistency across the entire codebase. Following these guidelines makes the code more readable, maintainable, and collaborative.