Files
Dokumentace/Rust/DESIGN_DOCUMENT_LIB.md

6.2 KiB

Rust Library Development Guidelines

Document Version: v1

Note on Versioning:

  • This document version is independent — reused across projects
  • Project version source of truth: Cargo.toml under [package]
  • CHANGELOG.md uses project version from Cargo.toml
  • README.md — Project overview, public API description, installation and usage examples
  • AGENTS.md — Rules for AI assistants
  • PROJECT.md — Project goals and current state
  • CHANGELOG.md — Version history

1. Code Style

  • Rust edition: 2021
  • Format with rustfmt — run cargo fmt before every commit
  • Lint with clippy — run cargo clippy -- -D warnings before every commit
  • snake_case functions/variables/modules, PascalCase types/traits, SCREAMING_SNAKE_CASE constants

2. Cargo

cargo add <crate>               # Add runtime dependency
cargo add --dev <crate>         # Add dev dependency
cargo remove <crate>            # Remove dependency
cargo test                      # Run all tests
cargo doc --open                # Build and open documentation
cargo fmt                       # Format code
cargo clippy -- -D warnings     # Lint (treat warnings as errors)
cargo build                     # Build to verify compilation
cargo publish                   # Publish to crates.io

Never edit Cargo.toml dependency versions by hand — use cargo add.


2. Project Structure

project/
├── src/
│   ├── lib.rs              # Public API — re-export everything the caller needs
│   ├── error.rs            # All public error types
│   └── <module>/
│       └── mod.rs
├── tests/                  # Integration tests (test the public API only)
├── examples/               # Usage examples
├── Cargo.toml
└── Cargo.lock              # Do NOT commit — add to .gitignore

No main.rs — libraries have no entry point.


3. Public API

  • Everything intended for external use must be pub and re-exported from lib.rs
  • Use pub(crate) for internal items that cross module boundaries
  • Internal modules should be private (mod foo;) unless they are part of the public API
  • Public API must be stable across patch versions; breaking changes require a major version bump
  • Use #[non_exhaustive] on public enums and structs to allow adding fields without breaking changes
// lib.rs — public surface
pub use error::MyError;
pub use client::Client;
pub use types::{Config, Response};

4. Error Handling

  • Define all public error types in src/error.rs using thiserror
  • Export all errors from lib.rs
  • Never use .unwrap() — use ? or explicit handling
  • .expect("reason") only when the invariant is guaranteed and documented
// error.rs
use thiserror::Error;

#[derive(Debug, Error)]
pub enum MyError {
    #[error("invalid input: {0}")]
    InvalidInput(String),
    #[error("IO error: {0}")]
    Io(#[from] std::io::Error),
}

5. Logging

This is a library. Libraries must never configure logging sinks — that is the responsibility of the consuming application.

Use tracing for instrumentation:

[dependencies]
tracing = "0.1"
use tracing::{debug, info, warn, error};

pub fn do_something() {
    debug!("internal detail");
    info!("milestone reached");
}

Never call tracing_subscriber::fmt().init() or any sink setup inside library code. The consuming application configures the subscriber.

Log levels

Level When to use
debug Per-item detail: internal state, cache hits
info Significant milestones visible to the consuming application
warn Recoverable issues: fallback used, unexpected but non-fatal
error Failures the caller must know about

6. Environment and Secrets

Libraries do not read environment variables or .env files. Configuration is passed by the caller via arguments or constructor parameters.


7. Testing

  • Use Rust's built-in test framework — #[test] and #[cfg(test)]
  • Unit tests live in the same file as the code, in a mod tests block
  • Integration tests in tests/ test only the public API (as a real consumer would)
  • Test naming: test_<action>_<context>
#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_parse_valid_input() {
        assert_eq!(parse("42"), Ok(42));
    }
}

Cargo.lock is not committed for libraries — add it to .gitignore. Consumers pin their own dependency graph.


8. Documentation

All public items must have doc comments. Use cargo doc --open to verify locally.

/// Parses a value from a string.
///
/// # Errors
///
/// Returns [`MyError::InvalidInput`] if the string is not a valid value.
///
/// # Examples
///
/// ```
/// use my_crate::parse;
/// assert_eq!(parse("42"), Ok(42));
/// ```
pub fn parse(s: &str) -> Result<u32, MyError> { ... }

README.md must contain installation instructions and usage examples for the public API.


9. Tooling

Tool Purpose
rustfmt Code formatting
clippy Linting
cargo test Testing
cargo doc Documentation

Run before every commit:

cargo fmt
cargo clippy -- -D warnings
cargo test

10. Distribution

Build and publish with Cargo:

cargo package      # Verify what will be published
cargo publish      # Publish to crates.io (requires login)

Cargo.lock is not committed — add to .gitignore.


11. Versioning

  • Follow semantic versioning: MAJOR.MINOR.PATCH
  • Version is defined in Cargo.toml under [package]
  • Always ask before bumping the version — never increment automatically
  • Update CHANGELOG.md before bumping the version
  • Breaking changes to the public API require a major version bump

12. Documentation and Task Management

  • Keep PROJECT.md and CHANGELOG.md up to date when making changes
  • README.md must contain installation instructions and usage examples for the public API

Task notation

// TODO: one-liner description of a task to be done
// FIXME: one-liner description of a known bug to be fixed