3.6 KiB
3.6 KiB
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 projectcargo build --release- Build optimized release versioncargo run- Run the server with default configcargo test- Run all unit testscargo test <test_name>- Run a specific testcargo test <module>::tests- Run tests in a specific modulecargo clippy- Run linter checkscargo fmt- Format code according to Rust standardscargo check- Quick compile check without building
Common Test Patterns
cargo test config::tests- Run config module testscargo test request::tests- Run request handling testscargo 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::functionfor 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
constfor configuration values that don't change - Error handling with
Result<T, E>and?operator - Use
tracingfor logging, notprintln!in production code
Naming Conventions
PascalCasefor types, structs, enumssnake_casefor functions, variables, modulesSCREAMING_SNAKE_CASEfor constants- Use descriptive names that indicate purpose
Error Handling
- Use
io::Result<()>for I/O operations - Convert errors to appropriate types with
map_errwhen 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_SIZEconstant) - Validate TLS certificates properly
- Never expose directory listings
Testing Guidelines
- Use
tempfile::TempDirfor 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!andassert!for validations
Async Patterns
- Use
.awaiton async calls - Prefer
tokio::fsoverstd::fsin async contexts - Handle timeouts for network operations
- Use
Arc<Clone>for shared data across tasks
Gemini Protocol Specific
- Response format: "STATUS META\r\n"
- Status 20: Success (follow with MIME type)
- Status 51: Not found
- Status 59: Bad request
- Default MIME: "text/gemini" for .gmi files
- Default file: "index.gmi" for directory requests
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-securitycrate for path validation - Default port: 1965 (standard Gemini port)
- Default host: 0.0.0.0 for listening
- Log level defaults to "info"