trust-std 0.2.1

Standard library shims with named-arg signatures for Trust
Documentation
//! Named-argument shims over `std`.
//!
//! Each wrapper exists so Trust callers can write call sites with
//! `name: value` syntax: `fs::read_to_string(path: p)` instead of the
//! positional `std::fs::read_to_string(p)`. The wrappers carry no logic;
//! they exist purely to make parameter names part of the signature for
//! Trust's named-args lowering pass.
//!
//! This crate is `#![strict]`-marked as of RT-44. The build-time index
//! `STD_SIGNATURES` (in `trust-lower`) used to be generated by
//! parsing this file directly with `syn`, which would have broken the
//! moment the file used any Trust-specific syntax. The current
//! design generates a checked-in manifest at
//! `crates/trust-std/std-signatures.txt` via
//! `cargo xtask gen-std-signatures` (which lowers the source first, then
//! parses), and `trust-lower/build.rs` reads that manifest. CI
//! enforces freshness with `cargo xtask gen-std-signatures --check`.

/// Declare a distinct newtype in one line — the ergonomic fix for R0017
/// (`no-same-type-params`). Wrapping each same-typed parameter in its own
/// newtype turns a silent argument swap into a compile error.
///
/// ```
/// trust_std::newtype!(pub Width(u32));
/// trust_std::newtype!(pub Height(u32));
///
/// pub fn make_rect(width: Width, height: Height) -> u32 {
///     width.0 * height.0
/// }
/// // make_rect(Height::new(5), Width::new(10)) is now a *type error*.
/// ```
///
/// Generates a tuple struct with a public field plus `new`, `into_inner`, and
/// `From<inner>`. It derives `Debug, Clone, PartialEq` (safe for any inner
/// type, including `f64`). Add more — `Copy`, `Eq`, `Hash`, `Ord` — with an
/// extra `#[derive(…)]` before the declaration (don't re-list the three
/// already derived):
///
/// ```
/// trust_std::newtype!(#[derive(Copy, Eq, Hash, PartialOrd, Ord)] pub UserId(u64));
/// ```
#[macro_export]
macro_rules! newtype {
    ($(#[$meta:meta])* $vis:vis $name:ident($inner:ty)) => {
        #[derive(Debug, Clone, PartialEq)]
        $(#[$meta])*
        $vis struct $name(pub $inner);

        impl $name {
            /// Wrap a raw value.
            #[inline]
            $vis fn new(value: $inner) -> Self {
                $name(value)
            }

            /// Consume the newtype, returning the underlying value.
            #[inline]
            $vis fn into_inner(self) -> $inner {
                self.0
            }
        }

        impl ::core::convert::From<$inner> for $name {
            #[inline]
            fn from(value: $inner) -> Self {
                $name(value)
            }
        }
    };
}

pub mod fs {
    use std::io;
    use std::path::Path;

    /// Read the entire contents of a file into a `String`.
    pub fn read_to_string(path: &Path) -> io::Result<String> {
        std::fs::read_to_string(path)
    }

    /// Write `contents` to a file at `path`, creating it if missing and
    /// truncating it if it already exists.
    pub fn write_text(path: &Path, contents: &str) -> io::Result<()> {
        std::fs::write(path, contents)
    }

    /// Write a byte buffer to a file.
    pub fn write_bytes(path: &Path, bytes: &[u8]) -> io::Result<()> {
        std::fs::write(path, bytes)
    }

    /// Create a directory and any missing parents.
    pub fn create_dir_all(path: &Path) -> io::Result<()> {
        std::fs::create_dir_all(path)
    }

    /// Remove a single file.
    pub fn remove_file(path: &Path) -> io::Result<()> {
        std::fs::remove_file(path)
    }

    /// Copy `from` to `to`. Returns the number of bytes copied.
    pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
        std::fs::copy(from, to)
    }

    /// Rename / move `from` to `to`.
    pub fn rename(from: &Path, to: &Path) -> io::Result<()> {
        std::fs::rename(from, to)
    }
}

pub mod time {
    use std::time::Duration;

    /// Build a [`Duration`] from named `secs` and `nanos`.
    pub fn duration(secs: u64, nanos: u32) -> Duration {
        Duration::new(secs, nanos)
    }

    /// Build a [`Duration`] from a whole number of milliseconds.
    pub fn millis(value: u64) -> Duration {
        Duration::from_millis(value)
    }
}

pub mod env {
    use std::ffi::OsString;

    /// Set the value of an environment variable.
    pub fn set_var(name: &str, value: &str) {
        // safety: std::env::set_var is currently safe in 1.95 stable;
        // this shim future-proofs callers if that changes.
        // reason: documenting why this wraps a one-liner
        unsafe {
            std::env::set_var(name, value);
        }
    }

    /// Get an environment variable's value, if set.
    pub fn var(name: &str) -> Option<OsString> {
        std::env::var_os(name)
    }
}

pub mod thread {
    use std::time::Duration;

    /// Sleep the current thread for `duration`.
    pub fn sleep(duration: Duration) {
        std::thread::sleep(duration);
    }
}

pub mod collections {
    use std::collections::{BTreeMap, HashMap, HashSet};
    use std::hash::Hash;

    /// Insert `value` into `map` under `key`, returning the previous value.
    pub fn insert<K: Eq + Hash, V>(map: &mut HashMap<K, V>, key: K, value: V) -> Option<V> {
        map.insert(key, value)
    }

    /// Construct an empty [`HashMap`].
    pub fn hashmap_new<K, V>() -> HashMap<K, V> {
        HashMap::new()
    }

    /// Construct a [`HashMap`] pre-allocated for at least `capacity` entries.
    pub fn hashmap_with_capacity<K, V>(capacity: usize) -> HashMap<K, V> {
        HashMap::with_capacity(capacity)
    }

    /// Insert `value` into `map` under `key`, returning the previous value.
    ///
    /// Alias of [`insert`] with the more explicit `hashmap_` prefix used by
    /// the other constructors in this module.
    pub fn hashmap_insert<K: Eq + Hash, V>(map: &mut HashMap<K, V>, key: K, value: V) -> Option<V> {
        map.insert(key, value)
    }

    /// Construct an empty [`BTreeMap`].
    pub fn btreemap_new<K, V>() -> BTreeMap<K, V> {
        BTreeMap::new()
    }

    /// Construct an empty [`HashSet`].
    pub fn hashset_new<T>() -> HashSet<T> {
        HashSet::new()
    }
}

pub mod net {
    use std::io;
    use std::net::{TcpListener, TcpStream, UdpSocket};

    /// Bind a TCP listener to `addr`.
    pub fn tcp_listener_bind(addr: &str) -> io::Result<TcpListener> {
        TcpListener::bind(addr)
    }

    /// Open a TCP connection to `addr`.
    pub fn tcp_connect(addr: &str) -> io::Result<TcpStream> {
        TcpStream::connect(addr)
    }

    /// Bind a UDP socket to `addr`.
    pub fn udp_socket_bind(addr: &str) -> io::Result<UdpSocket> {
        UdpSocket::bind(addr)
    }
}

pub mod sync {
    use std::sync::mpsc::{self, Receiver, Sender};
    use std::sync::{Arc, Mutex, RwLock};

    /// Wrap `value` in a [`Mutex`].
    pub fn mutex_new<T>(value: T) -> Mutex<T> {
        Mutex::new(value)
    }

    /// Wrap `value` in an [`RwLock`].
    pub fn rwlock_new<T>(value: T) -> RwLock<T> {
        RwLock::new(value)
    }

    /// Wrap `value` in an [`Arc`].
    pub fn arc_new<T>(value: T) -> Arc<T> {
        Arc::new(value)
    }

    /// Create a new asynchronous channel, returning `(sender, receiver)`.
    ///
    /// Takes no arguments, so named-arg lowering is a no-op here; the shim
    /// exists for module-level discoverability alongside the other helpers.
    pub fn channel<T>() -> (Sender<T>, Receiver<T>) {
        mpsc::channel()
    }
}

pub mod process {
    use std::process::Command;

    /// Construct a [`Command`] that will spawn `program`.
    pub fn command(program: &str) -> Command {
        Command::new(program)
    }

    /// Append a single positional `arg` to `cmd`, returning the same `cmd`
    /// for chaining.
    ///
    /// Note: `Command::arg` is defined as `&mut self -> &mut Command`, which
    /// the named-arg lowering pass treats as a method shape. This free-fn
    /// form re-exposes it with explicit parameter names.
    pub fn command_arg<'a>(cmd: &'a mut Command, arg: &str) -> &'a mut Command {
        cmd.arg(arg)
    }

    /// Set an environment variable on `cmd`, returning the same `cmd` for
    /// chaining.
    pub fn command_env<'a>(cmd: &'a mut Command, key: &str, value: &str) -> &'a mut Command {
        cmd.env(key, value)
    }

    /// Terminate the current process with exit status `code`.
    pub fn exit(code: i32) -> ! {
        std::process::exit(code)
    }
}

pub mod string {
    /// Construct an empty [`String`].
    pub fn string_new() -> String {
        String::new()
    }

    /// Construct a [`String`] with capacity for at least `capacity` bytes.
    pub fn string_with_capacity(capacity: usize) -> String {
        String::with_capacity(capacity)
    }
}

pub mod vec {
    /// Construct an empty [`Vec`].
    pub fn vec_new<T>() -> Vec<T> {
        Vec::new()
    }

    /// Construct a [`Vec`] pre-allocated for at least `capacity` items.
    pub fn vec_with_capacity<T>(capacity: usize) -> Vec<T> {
        Vec::with_capacity(capacity)
    }

    /// Push `value` onto the end of `v`.
    pub fn vec_push<T>(v: &mut Vec<T>, value: T) {
        v.push(value);
    }
}

#[cfg(test)]
mod tests;