bare-types 0.1.8

A zero-cost foundation for type-safe domain modeling in Rust. Implements the 'Parse, don't validate' philosophy to eliminate primitive obsession and ensure data integrity at the system boundary.
Documentation

bare-types

Latest Version Documentation License

A zero-cost foundation for type-safe domain modeling in Rust.

Overview

bare-types is a collection of strongly-typed, zero-cost abstractions for domain modeling in Rust. It implements the Rust API Guidelines and follows strict design principles to ensure type safety, performance, and correctness.

Design Philosophy

Parse, Don't Validate

This project follows the "Parse, don't validate" philosophy. Instead of validating data throughout your codebase, parse it once at the system boundary and use strong types to ensure invariants are maintained.

This approach provides:

  • Type safety: Invalid states are unrepresentable
  • Zero-cost abstractions: Validation happens once at construction
  • Clear error handling: Errors are caught early and explicitly
  • Self-documenting code: Types convey meaning

Why bare-types? Counterexamples

Without proper type abstractions, your code faces several security and reliability risks:

1. Integer Overflow in Port Numbers

// Without bare-types: Silent overflow, undefined behavior
fn set_port(port: u16) -> u16 {
    port + 1  // Can wrap around to 0!
}

// With bare-types: Compile-time rejection of invalid values
let port: Port = 65536.try_into()?; // ERROR: value out of range

2. Invalid Hostnames Leading to DNS Rebinding Attacks

// Without bare-types: Accepts invalid hostnames that could be exploited
fn connect_to_host(hostname: &str) { /* ... */ }
connect_to_host("-invalid"); // Might bypass security checks

// With bare-types: RFC 1123 validation at construction
let hostname: Hostname = "-invalid".parse()?; // ERROR: Invalid character

3. Unvalidated URLs Leading to SSRF Vulnerabilities

// Without bare-types: URL parsed lazily, vulnerabilities discovered late
let url = request.url(); // May contain malicious schemes or hosts

// With bare-types: URL validated at construction, failures caught immediately
let url: Url = request.url().parse()?; // Fails fast on invalid URLs

4. Missing Port Range Validation Leading to Security Misconfigurations

// Without bare-types: Using privileged ports by accident
fn start_server(port: u16) { /* ... */ }
start_server(22); // Accidentally using SSH port!

// With bare-types: Explicit constants and range checking
let port = Port::SSH; // Compile-time known constant
let custom: Port = 80.try_into()?;
custom.is_system_port(); // true - warns about privileged ports

5. Process ID Validation Failures Leading to Signal Injection

// Without bare-types: Negative PIDs or zero values
fn send_signal(pid: i32, sig: i32) { /* ... */ }
send_signal(-1, 15); // Undefined behavior!
send_signal(0, 15);  // Sends to process group, not self!

// With bare-types: Only valid positive PIDs accepted
let pid: Pid = (-1).try_into()?; // ERROR: must be positive
let pid: Pid = 0.try_into()?;    // ERROR: invalid PID
let pid: Pid = 1234.try_into()?; // OK - valid positive PID

By using bare-types, these security vulnerabilities are caught at compile time or at the boundary of your system, preventing runtime exploitation.

Design Rules

This project adheres to the Rust API Guidelines and implements the following design rules:

1. Type Safety (C-NEWTYPE, C-CUSTOM-TYPE)

  • Use newtype pattern to provide static distinctions between types
  • Arguments convey meaning through types, not bool or Option
  • All validation is performed at construction time
  • Invalid states are unrepresentable

2. Zero-Cost Abstractions

  • All validation is performed at compile time or construction time
  • No runtime cost for accessing validated data
  • Use #[repr(transparent)] for newtypes over primitive types
  • Memory layout matches underlying types

3. RFC Compliance

Strictly follow relevant standards:

  • Domain names: RFC 1035
  • Hostnames: RFC 1123
  • IP addresses: Standard IPv4/IPv6

4. Composability (C-COMMON-TRAITS, C-CONV-TRAITS)

All types implement standard traits for easy composition:

  • Common traits: Clone, Debug, Display, Eq, Hash, Ord, PartialEq, PartialOrd
  • Conversion traits: From, TryFrom, AsRef, AsMut
    • Note: Into and TryInto are automatically provided via blanket impls when From/TryFrom are implemented
  • Collection traits: FromIterator, Extend (for collection types)

5. Security (C-SEND-SYNC, C-GOOD-ERR)

  • Types are Send and Sync where possible
  • Error types implement std::error::Error, Send, Sync
  • No unsafe code allowed (unsafe_code = "forbid")
  • Sensitive data uses Zeroize for automatic memory clearing

6. Explicit Over Implicit (C-DEREF, C-CTOR)

  • Only smart pointers implement Deref and DerefMut
  • Constructors are static, inherent methods
  • Prefer explicit code over implicit behavior
  • No declarative macros except for serde derives

7. Strict Linting

The project enforces strict linting rules:

  • unsafe_code = "forbid"
  • missing_docs = "warn"
  • Deny-level clippy rules for safety and correctness

Design Goals

1. Performance

Zero-cost abstractions that compile to the same code as raw primitives:

  • Validation happens once at construction time
  • No runtime overhead for accessing validated data
  • #[repr(transparent)] ensures memory layout matches underlying types

2. Portability

Designed for diverse deployment targets:

  • Platforms: Linux, macOS, Windows, BSD variants
  • Embedded: no_std support for resource-constrained environments
  • WebAssembly: Compatible with WASM targets (browser and WASI)
  • Dependencies: Minimal external dependencies to reduce attack surface and compile times

3. Ergonomics

Developer experience is a first-class concern:

  • Type-safe APIs catch errors at compile time
  • Detailed error messages with context for debugging
  • Extensive documentation with runnable examples
  • Consistent naming following RFC 430
  • AI-friendly: Clear constraints and patterns for automated tooling

4. Security

Defense in depth through type safety:

  • Memory safety via Rust's ownership system
  • Input validation at system boundaries ("Parse, don't validate")
  • No unsafe code in the entire codebase
  • Optional zeroize integration for sensitive data

Features

All features are additive and composable. You can enable any combination without conflicts.

Default Behavior:

  • No features are enabled by default
  • All core types work with no_std (using core library)
  • Opt-in features provide additional functionality

Available Features:

  • std - Enable standard library support
    • Provides: std::error::Error implementations for error types
    • Optional: All core types work without std
  • net - Network-related types
    • Includes: IP addresses, ports, hostnames, domain names, socket addresses
    • Built on: core::net (Rust 1.82+), fully no_std compatible
  • serde - Serialization support via serde
    • Derives: Serialize and Deserialize for all public types
    • Works with: no_std + alloc or std
  • arbitrary - Fuzzing support via arbitrary
    • Enables: Property-based testing and fuzzing workflows
    • Works with: no_std or std
  • zeroize - Secure memory clearing via zeroize
    • Provides: Automatic memory zeroing for sensitive data types
    • Works with: no_std or std

no_std Support

This crate is designed for no_std environments by default:

[dependencies]
bare-types = { version = "0.1", default-features = false }

Compatibility Matrix:

Feature no_std no_std + alloc std
Core types
net module
sys module
serde
arbitrary
zeroize

Notes:

  • net module uses core::net (Rust 1.82+) and is fully no_std compatible
  • serde feature requires alloc for owned data types
  • std feature is optional and only adds std::error::Error implementations

Modules

net (requires net feature)

Network-related types for building type-safe network applications:

  • IP addresses (IPv4 and IPv6) with validation
  • Port numbers with IANA range validation
  • Hostnames with RFC 1123 validation
  • Domain names with RFC 1035 validation
  • Host type unifying IP, domain name, and hostname
  • Socket addresses combining host and port

sys (requires sys feature)

System information types for building type-safe system-level applications:

  • CPU architecture (Arch) with compile-time detection
  • Operating system type (OsType) with compile-time detection
  • OS version (OsVersion) with semantic versioning
  • Kernel version (KernelVersion) with release strings
  • System hostname (Hostname) with RFC 1123 validation (re-exported from net module)
  • System username (Username) with POSIX validation
  • OS distribution name (Distro) with family detection
  • Process ID (Pid) with validation and utility methods
  • File descriptor (Fd) with validation and standard FD constants

Getting Started

Add bare-types to your Cargo.toml:

[dependencies]
bare-types = "0.1.3"

Enable specific features as needed:

[dependencies]
bare-types = { version = "0.1.3", features = ["net", "serde"] }

Usage Examples

Network Types (net feature)

use bare_types::net::{IpAddr, Port, Hostname, DomainName, SocketAddr};

// IP addresses with validation
let ipv4: IpAddr = "192.168.1.1".parse()?;
let ipv6: IpAddr = "::1".parse()?;

// Port numbers with IANA range validation
let http_port = Port::HTTP; // 80
let custom_port: Port = 8080.try_into()?;

// Hostnames with RFC 1123 validation
let hostname: Hostname = "my-server".parse()?;

// Domain names with RFC 1035 validation
let domain: DomainName = "example.com".parse()?;

// Socket addresses combining host and port
let socket: SocketAddr = "example.com:443".parse()?;
let (host, port) = socket.into_parts();

System Types (sys feature)

use bare_types::sys::{Arch, OsType, OsVersion, KernelVersion, Username, Distro, Pid, Fd};

// Compile-time architecture detection
let arch = Arch::current();
assert!(arch.is_64_bit());

// Compile-time OS detection
let os = OsType::current();
assert!(os.is_unix());

// Semantic versioning for OS versions
let os_ver: OsVersion = "22.04".parse()?;
assert_eq!(os_ver.major(), 22);

// Kernel versions with release strings
let kernel: KernelVersion = "6.8.0-40-generic".parse()?;
assert!(kernel.is_stable());

// POSIX-compliant usernames
let username: Username = "www-data".parse()?;

// OS distribution names
let distro: Distro = "ubuntu".parse()?;
assert!(distro.is_debian_based());

// Process IDs with validation
let pid: Pid = 1234.try_into()?;
assert!(!pid.is_init());

// File descriptors with standard FD constants
assert_eq!(Fd::STDIN.as_i32(), 0);
assert_eq!(Fd::STDOUT.as_i32(), 1);
assert_eq!(Fd::STDERR.as_i32(), 2);

Documentation

Full API documentation is available on docs.rs.

Tutorials

Contributing

Contributions are welcome! Please see CONTRIBUTING.md for guidelines.

Support

Need help? See SUPPORT.md for resources and how to get assistance.

Governance

Learn about project governance and decision-making in GOVERNANCE.md.

Changelog

See CHANGELOG.md for version history.

Security

See SECURITY.md for security policy and vulnerability reporting.

Code of Conduct

This project follows the Rust Code of Conduct.

License

Licensed under either of:

at your option.

Contribution

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.

Related Projects