# Overview This project is a very simple gemini server which only serves static files, nothing else. It is meant to be generic so other people can use it. # Build/Test/Lint Commands ## Core Commands - `cargo build` - Build the project - `cargo build --release` - Build optimized release version - `cargo run` - Run the server with default config - `cargo test` - Run all unit tests - `cargo test ` - Run a specific test - `cargo test ::tests` - Run tests in a specific module - `cargo clippy` - Run linter checks for code quality - `cargo clippy --fix` - Automatically fix clippy suggestions - `cargo clippy --bin ` - Check specific binary - `cargo fmt` - Format code according to Rust standards - `cargo check` - Quick compile check without building ## Common Test Patterns - `cargo test config::tests` - Run config module tests - `cargo test request::tests` - Run request handling tests - `cargo test -- --nocapture` - Show println output in tests # Code Style Guidelines ## Imports - Group imports: std libs first, then external crates, then local modules - Use `use crate::module::function` for internal imports - Prefer specific imports over `use std::prelude::*` - Keep imports at module level, not inside functions ## Code Structure - Use `#[tokio::main]` for async main function - Keep functions small and focused (single responsibility) - Use `const` for configuration values that don't change - Error handling with `Result` and `?` operator - Use `tracing` for logging, not `println!` in production code ## Naming Conventions - `PascalCase` for types, structs, enums - `snake_case` for functions, variables, modules - `SCREAMING_SNAKE_CASE` for constants - Use descriptive names that indicate purpose ## Error Handling - Use `io::Result<()>` for I/O operations - Convert errors to appropriate types with `map_err` when needed - Use `unwrap()` only in tests and main() for unrecoverable errors - Use `expect()` with meaningful messages for debugging - Return early with `Err()` for validation failures ## Security Requirements - **Critical**: Always validate file paths with `path_security::validate_path` - Never construct paths from user input without validation - Use timeouts for network operations (`tokio::time::timeout`) - Limit request sizes (see `MAX_REQUEST_SIZE` constant) - Validate TLS certificates properly - Never expose directory listings ## Testing Guidelines - Use `tempfile::TempDir` for temporary directories in tests - Test both success and error paths - Use `#[cfg(test)]` for test modules - Create temporary test files in `tmp/` directory - Test security boundaries (path traversal, invalid inputs) - Use `assert_eq!` and `assert!` for validations ## Lint Checking - `cargo clippy` - Run linter checks for code quality - `cargo clippy --fix` - Automatically fix clippy suggestions - `cargo clippy --bin ` - Check specific binary - `cargo fmt` - Format code to match Rust standards - **Run clippy before every commit** - Address all warnings before pushing code - Current clippy warnings (2025-01-15): - src/server.rs:16-17 - Unnecessary borrows on file_path - src/logging.rs:31 - Match could be simplified to let statement ## Async Patterns - Use `.await` on async calls - Prefer `tokio::fs` over `std::fs` in async contexts - Handle timeouts for network operations - Use `Arc` for shared data across tasks ## Gemini Protocol Specific - Response format: "STATUS META\r\n" - Status 20: Success (follow with MIME type) - Status 41: Server unavailable (timeout, overload) - Status 51: Not found (resource doesn't exist) - Status 59: Bad request (malformed URL, protocol violation) - Default MIME: "text/gemini" for .gmi files - Default file: "index.gmi" for directory requests ## Error Handling - **Timeout**: Return status 41 "Server unavailable" (not 59) - **Request too large**: Return status 59 "Bad request" - **Empty request**: Return status 59 "Bad request" - **Invalid URL format**: Return status 59 "Bad request" - **Hostname mismatch**: Return status 59 "Bad request" - **Path resolution failure**: Return status 51 "Not found" (including security violations) - **File not found**: Return status 51 "Not found" - Reject requests > 1024 bytes (per Gemini spec) - Reject requests without proper `\r\n` termination - Use `tokio::time::timeout()` for request timeout handling ## Configuration - TOML config files with `serde::Deserialize` - CLI args override config file values - Required fields: root, cert, key, host - Optional: port, log_level # Development Notes - Generate self-signed certificates for local testing in `tmp/` directory - Use CN=localhost for development - Fix every compiler warning before committing any code - Create temporary files in the tmp/ directory for your tests like .gem files or images, etc., so they are gitignored - Use `path-security` crate for path validation - Default port: 1965 (standard Gemini port) - Default host: 0.0.0.0 for listening - Log level defaults to "info" ## Environment Setup - Install clippy: `rustup component add clippy` - Ensure `~/.cargo/bin` is in PATH (add `source "$HOME/.cargo/env"` to `~/.bashrc`) - Verify setup: `cargo clippy --version`