trust_std/lib.rs
1//! Named-argument shims over `std`.
2//!
3//! Each wrapper exists so Trust callers can write call sites with
4//! `name: value` syntax: `fs::read_to_string(path: p)` instead of the
5//! positional `std::fs::read_to_string(p)`. The wrappers carry no logic;
6//! they exist purely to make parameter names part of the signature for
7//! Trust's named-args lowering pass.
8//!
9//! This crate is `#![strict]`-marked as of RT-44. The build-time index
10//! `STD_SIGNATURES` (in `trust-lower`) used to be generated by
11//! parsing this file directly with `syn`, which would have broken the
12//! moment the file used any Trust-specific syntax. The current
13//! design generates a checked-in manifest at
14//! `crates/trust-std/std-signatures.txt` via
15//! `cargo xtask gen-std-signatures` (which lowers the source first, then
16//! parses), and `trust-lower/build.rs` reads that manifest. CI
17//! enforces freshness with `cargo xtask gen-std-signatures --check`.
18
19/// Declare a distinct newtype in one line — the ergonomic fix for R0017
20/// (`no-same-type-params`). Wrapping each same-typed parameter in its own
21/// newtype turns a silent argument swap into a compile error.
22///
23/// ```
24/// trust_std::newtype!(pub Width(u32));
25/// trust_std::newtype!(pub Height(u32));
26///
27/// pub fn make_rect(width: Width, height: Height) -> u32 {
28/// width.0 * height.0
29/// }
30/// // make_rect(Height::new(5), Width::new(10)) is now a *type error*.
31/// ```
32///
33/// Generates a tuple struct with a public field plus `new`, `into_inner`, and
34/// `From<inner>`. It derives `Debug, Clone, PartialEq` (safe for any inner
35/// type, including `f64`). Add more — `Copy`, `Eq`, `Hash`, `Ord` — with an
36/// extra `#[derive(…)]` before the declaration (don't re-list the three
37/// already derived):
38///
39/// ```
40/// trust_std::newtype!(#[derive(Copy, Eq, Hash, PartialOrd, Ord)] pub UserId(u64));
41/// ```
42#[macro_export]
43macro_rules! newtype {
44 ($(#[$meta:meta])* $vis:vis $name:ident($inner:ty)) => {
45 #[derive(Debug, Clone, PartialEq)]
46 $(#[$meta])*
47 $vis struct $name(pub $inner);
48
49 impl $name {
50 /// Wrap a raw value.
51 #[inline]
52 $vis fn new(value: $inner) -> Self {
53 $name(value)
54 }
55
56 /// Consume the newtype, returning the underlying value.
57 #[inline]
58 $vis fn into_inner(self) -> $inner {
59 self.0
60 }
61 }
62
63 impl ::core::convert::From<$inner> for $name {
64 #[inline]
65 fn from(value: $inner) -> Self {
66 $name(value)
67 }
68 }
69 };
70}
71
72pub mod fs {
73 use std::io;
74 use std::path::Path;
75
76 /// Read the entire contents of a file into a `String`.
77 pub fn read_to_string(path: &Path) -> io::Result<String> {
78 std::fs::read_to_string(path)
79 }
80
81 /// Write `contents` to a file at `path`, creating it if missing and
82 /// truncating it if it already exists.
83 pub fn write_text(path: &Path, contents: &str) -> io::Result<()> {
84 std::fs::write(path, contents)
85 }
86
87 /// Write a byte buffer to a file.
88 pub fn write_bytes(path: &Path, bytes: &[u8]) -> io::Result<()> {
89 std::fs::write(path, bytes)
90 }
91
92 /// Create a directory and any missing parents.
93 pub fn create_dir_all(path: &Path) -> io::Result<()> {
94 std::fs::create_dir_all(path)
95 }
96
97 /// Remove a single file.
98 pub fn remove_file(path: &Path) -> io::Result<()> {
99 std::fs::remove_file(path)
100 }
101
102 /// Copy `from` to `to`. Returns the number of bytes copied.
103 pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
104 std::fs::copy(from, to)
105 }
106
107 /// Rename / move `from` to `to`.
108 pub fn rename(from: &Path, to: &Path) -> io::Result<()> {
109 std::fs::rename(from, to)
110 }
111}
112
113pub mod time {
114 use std::time::Duration;
115
116 /// Build a [`Duration`] from named `secs` and `nanos`.
117 pub fn duration(secs: u64, nanos: u32) -> Duration {
118 Duration::new(secs, nanos)
119 }
120
121 /// Build a [`Duration`] from a whole number of milliseconds.
122 pub fn millis(value: u64) -> Duration {
123 Duration::from_millis(value)
124 }
125}
126
127pub mod env {
128 use std::ffi::OsString;
129
130 /// Set the value of an environment variable.
131 pub fn set_var(name: &str, value: &str) {
132 // safety: std::env::set_var is currently safe in 1.95 stable;
133 // this shim future-proofs callers if that changes.
134 // reason: documenting why this wraps a one-liner
135 unsafe {
136 std::env::set_var(name, value);
137 }
138 }
139
140 /// Get an environment variable's value, if set.
141 pub fn var(name: &str) -> Option<OsString> {
142 std::env::var_os(name)
143 }
144}
145
146pub mod thread {
147 use std::time::Duration;
148
149 /// Sleep the current thread for `duration`.
150 pub fn sleep(duration: Duration) {
151 std::thread::sleep(duration);
152 }
153}
154
155pub mod collections {
156 use std::collections::{BTreeMap, HashMap, HashSet};
157 use std::hash::Hash;
158
159 /// Insert `value` into `map` under `key`, returning the previous value.
160 pub fn insert<K: Eq + Hash, V>(map: &mut HashMap<K, V>, key: K, value: V) -> Option<V> {
161 map.insert(key, value)
162 }
163
164 /// Construct an empty [`HashMap`].
165 pub fn hashmap_new<K, V>() -> HashMap<K, V> {
166 HashMap::new()
167 }
168
169 /// Construct a [`HashMap`] pre-allocated for at least `capacity` entries.
170 pub fn hashmap_with_capacity<K, V>(capacity: usize) -> HashMap<K, V> {
171 HashMap::with_capacity(capacity)
172 }
173
174 /// Insert `value` into `map` under `key`, returning the previous value.
175 ///
176 /// Alias of [`insert`] with the more explicit `hashmap_` prefix used by
177 /// the other constructors in this module.
178 pub fn hashmap_insert<K: Eq + Hash, V>(map: &mut HashMap<K, V>, key: K, value: V) -> Option<V> {
179 map.insert(key, value)
180 }
181
182 /// Construct an empty [`BTreeMap`].
183 pub fn btreemap_new<K, V>() -> BTreeMap<K, V> {
184 BTreeMap::new()
185 }
186
187 /// Construct an empty [`HashSet`].
188 pub fn hashset_new<T>() -> HashSet<T> {
189 HashSet::new()
190 }
191}
192
193pub mod net {
194 use std::io;
195 use std::net::{TcpListener, TcpStream, UdpSocket};
196
197 /// Bind a TCP listener to `addr`.
198 pub fn tcp_listener_bind(addr: &str) -> io::Result<TcpListener> {
199 TcpListener::bind(addr)
200 }
201
202 /// Open a TCP connection to `addr`.
203 pub fn tcp_connect(addr: &str) -> io::Result<TcpStream> {
204 TcpStream::connect(addr)
205 }
206
207 /// Bind a UDP socket to `addr`.
208 pub fn udp_socket_bind(addr: &str) -> io::Result<UdpSocket> {
209 UdpSocket::bind(addr)
210 }
211}
212
213pub mod sync {
214 use std::sync::mpsc::{self, Receiver, Sender};
215 use std::sync::{Arc, Mutex, RwLock};
216
217 /// Wrap `value` in a [`Mutex`].
218 pub fn mutex_new<T>(value: T) -> Mutex<T> {
219 Mutex::new(value)
220 }
221
222 /// Wrap `value` in an [`RwLock`].
223 pub fn rwlock_new<T>(value: T) -> RwLock<T> {
224 RwLock::new(value)
225 }
226
227 /// Wrap `value` in an [`Arc`].
228 pub fn arc_new<T>(value: T) -> Arc<T> {
229 Arc::new(value)
230 }
231
232 /// Create a new asynchronous channel, returning `(sender, receiver)`.
233 ///
234 /// Takes no arguments, so named-arg lowering is a no-op here; the shim
235 /// exists for module-level discoverability alongside the other helpers.
236 pub fn channel<T>() -> (Sender<T>, Receiver<T>) {
237 mpsc::channel()
238 }
239}
240
241pub mod process {
242 use std::process::Command;
243
244 /// Construct a [`Command`] that will spawn `program`.
245 pub fn command(program: &str) -> Command {
246 Command::new(program)
247 }
248
249 /// Append a single positional `arg` to `cmd`, returning the same `cmd`
250 /// for chaining.
251 ///
252 /// Note: `Command::arg` is defined as `&mut self -> &mut Command`, which
253 /// the named-arg lowering pass treats as a method shape. This free-fn
254 /// form re-exposes it with explicit parameter names.
255 pub fn command_arg<'a>(cmd: &'a mut Command, arg: &str) -> &'a mut Command {
256 cmd.arg(arg)
257 }
258
259 /// Set an environment variable on `cmd`, returning the same `cmd` for
260 /// chaining.
261 pub fn command_env<'a>(cmd: &'a mut Command, key: &str, value: &str) -> &'a mut Command {
262 cmd.env(key, value)
263 }
264
265 /// Terminate the current process with exit status `code`.
266 pub fn exit(code: i32) -> ! {
267 std::process::exit(code)
268 }
269}
270
271pub mod string {
272 /// Construct an empty [`String`].
273 pub fn string_new() -> String {
274 String::new()
275 }
276
277 /// Construct a [`String`] with capacity for at least `capacity` bytes.
278 pub fn string_with_capacity(capacity: usize) -> String {
279 String::with_capacity(capacity)
280 }
281}
282
283pub mod vec {
284 /// Construct an empty [`Vec`].
285 pub fn vec_new<T>() -> Vec<T> {
286 Vec::new()
287 }
288
289 /// Construct a [`Vec`] pre-allocated for at least `capacity` items.
290 pub fn vec_with_capacity<T>(capacity: usize) -> Vec<T> {
291 Vec::with_capacity(capacity)
292 }
293
294 /// Push `value` onto the end of `v`.
295 pub fn vec_push<T>(v: &mut Vec<T>, value: T) {
296 v.push(value);
297 }
298}
299
300#[cfg(test)]
301mod tests;