Files
2026-02-28 20:12:08 +01:00

5.6 KiB

Coding Conventions

Analysis Date: 2026-02-28

Overview

This is an early-stage Rust project with minimal codebase (single main.rs file). Conventions are established based on:

  • Rust language defaults and community standards
  • Cargo default behavior
  • Current code patterns in the project

Naming Patterns

Files:

  • Snake case: main.rs, lib.rs are standard entry points
  • Module files use snake_case: user_service.rs, data_handler.rs
  • Test modules within files or in separate tests/ directory

Functions:

  • Snake case throughout: fn handle_connection(), fn parse_input()
  • Use descriptive verb-based names: fn calculate_sum(), fn build_response()

Variables:

  • Snake case for bindings: let user_id, let response_data
  • Constants use UPPER_CASE: const MAX_BUFFER_SIZE: usize = 1024
  • Mutable bindings explicitly marked: let mut count = 0

Types and Structs:

  • PascalCase for type names: struct User, enum Status, trait Handler
  • Generic parameters use single letters or PascalCase: T, S, Request
  • Lifetime parameters use lowercase: 'a, 'static

Code Style

Formatting:

  • Default Rust formatting applied by rustfmt (de facto standard)
  • 4-space indentation (Rust default)
  • No semicolons in final expressions that return values
  • Closing braces on same line except for block statements

Linting:

  • Clippy lints should be addressed as part of development
  • No explicit clippy.toml or linting configuration currently present
  • Default clippy warnings and errors should be resolved before commit

Edition:

  • Rust 2024 edition (specified in Cargo.toml)
  • Supports latest language features and idioms

Import Organization

Order:

  1. Standard library imports (use std::)
  2. External crate imports (use ratatui::, other external deps)
  3. Internal crate imports (use crate::)
  4. Module declarations (mod xyz;)

Example:

use std::io;
use std::collections::HashMap;

use ratatui::backend::CrosstermBackend;
use ratatui::Terminal;

use crate::ui::draw;
use crate::handler::InputHandler;

Path Aliases:

  • No path aliases configured currently
  • Can be added via workspace root configuration if needed

Error Handling

Patterns:

  • Use Result<T, E> for fallible operations
  • Propagate errors with ? operator rather than unwrapping when possible
  • Use anyhow::Result for application-level errors (dependency available)
  • Only use .unwrap() or .expect() for code that should never panic in normal operation

Example:

fn read_config(path: &str) -> Result<Config> {
    let content = std::fs::read_to_string(path)?;
    let config = serde_json::from_str(&content)?;
    Ok(config)
}

Logging

Framework: Not yet selected/integrated

When logging is added:

  • Use log crate for production logging
  • Use env_logger or similar for log configuration
  • Log levels: ERROR for failures, WARN for unusual conditions, INFO for significant events, DEBUG for detailed traces
  • Avoid logging sensitive data (credentials, tokens, PII)

Comments

When to Comment:

  • Explain the "why" not the "what"
  • Document non-obvious algorithmic decisions
  • Explain trade-offs or workarounds
  • Mark intentional violations of conventions with // FIXME: or // TODO:

Doc Comments:

  • Use /// for public item documentation
  • Document public functions, structs, enums, traits
  • Include examples for complex or non-obvious public APIs

Example:

/// Processes a user command from the input buffer.
///
/// This function parses the raw input and dispatches to appropriate handlers.
///
/// # Arguments
/// * `input` - Raw user input string
///
/// # Returns
/// `Result<CommandResult, Error>` indicating success or the error encountered
pub fn process_command(input: &str) -> Result<CommandResult> {
    // Implementation
}

Function Design

Size:

  • Keep functions small and focused (ideally < 50 lines)
  • Extract helper functions for complex logic
  • Separate parsing, validation, and execution logic

Parameters:

  • Use owned values for simple types, references for larger types
  • Prefer &[T] over &Vec<T> for function parameters
  • Use structs to group related parameters (> 3 related params)

Return Values:

  • Use Result<T, E> for operations that can fail
  • Use Option<T> for values that may be absent
  • Avoid returning Box<dyn Trait> unless necessary for dynamic dispatch

Module Design

Exports:

  • Explicitly declare public items with pub
  • Use pub use to re-export important types at module boundaries
  • Keep implementation details private (non-pub by default)

Module Structure:

// src/main.rs
mod ui;
mod handler;
mod model;

use ui::App;
use handler::InputHandler;

fn main() {
    // Implementation
}

Barrel Files:

  • Use mod.rs files in subdirectories to organize module exports
  • Example: src/ui/mod.rs exports types from ui/draw.rs, ui/state.rs

Unsafe Code

Usage:

  • Avoid unsafe blocks unless necessary for FFI or performance-critical code
  • Document unsafe code with // SAFETY: comments explaining invariants
  • Minimize unsafe scope

Example:

// SAFETY: ptr is guaranteed to point to valid, initialized memory
// returned from foreign_function, which owns its lifetime
unsafe {
    let result = process_data(ptr);
}

Workspace and Multi-Crate Structure

Currently: Single binary crate (bbs-md)

If expanding:

  • Consider separating into bbs-md (binary) and bbs-md-core (library)
  • Use workspace for shared dependencies and configuration
  • Place library code in src/lib.rs, binary-specific code in src/main.rs

Convention analysis: 2026-02-28