std::env::set_var safely
| -rw-r--r-- | Cargo.lock | 25 | ||||
| -rw-r--r-- | Cargo.toml | 15 | ||||
| -rw-r--r-- | README.md | 13 | ||||
| -rw-r--r-- | src/lib.rs | 79 |
4 files changed, 132 insertions, 0 deletions
diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..4bb66ed --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,25 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "env" +version = "1.0.0" +dependencies = [ + "num_threads", +] + +[[package]] +name = "libc" +version = "0.2.169" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" + +[[package]] +name = "num_threads" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9" +dependencies = [ + "libc", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..708da8f --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,15 @@ +cargo-features = ["edition2024"] +[package] +name = "env" +version = "1.0.0" +edition = "2024" +license = "MIT" +description = "std::env::{`set_var`, `remove_var`} safely" +keywords = ["environment", "safe"] +categories = ["config", "os"] +readme = "README.md" +authors = ["bendn <[email protected]>"] +repository = "https://github.com/bend-n/env" + +[dependencies] +num_threads = "0.1.7" diff --git a/README.md b/README.md new file mode 100644 index 0000000..964d1a3 --- /dev/null +++ b/README.md @@ -0,0 +1,13 @@ +# `env` + + + +Provides a safe interface for <code>[std::env](http://doc.rust-lang.org/std/env)::{[set_var](https://doc.rust-lang.org/std/env/fn.set_var.html), [remove_var](https://doc.rust-lang.org/std/env/fn.remove_var.html)}</code>. + +## Rationale + +Since [#124636](https://github.com/rust-lang/rust/pull/124636), `std::env::set_var` and `std::env::remove_var}` have become unsafe, due to their being unsafe when in a multi-threaded unix context[^1]. + +This crate wraps these functions, checking if these conditions are met at runtime, such that these functions are safe to call. + +[^1]: https://github.com/rust-lang/rust/issues/27970 diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..ce09c3a --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,79 @@ +//! Inspection and manipulation of the process's environment. +//! +//! This module contains functions to inspect various aspects such as +//! environment variables, process arguments, the current directory, and various +//! other important directories. +//! +//! There are several functions and structs in this module that have a +//! counterpart ending in `os`. Those ending in `os` will return an [`OsString`](std::ffi::OsString) +//! and those without will return a [`String`]. +//! +//! This crate provides <code>[mod@std::env]::{[set_var], [remove_var]}</code> safely. +#![forbid(clippy::missing_safety_doc)] +pub use std::env::*; +use std::ffi::OsStr; + +/// Whether the operating system has a thread-safe environment. This allows bypassing the check for if the process is multi-threaded. +const SAFE: bool = matches!( + std::env::consts::OS.as_bytes(), + b"illumos" | b"netbsd" | b"windows" +); + +/// Sets the environment variable `key` to the value `value` for the currently running +/// process. (safely!) +/// +/// # Safety +/// +/// This function is safe to call. +/// This function returns [`None`] if it would be unsafe to call this function. +/// +/// See also [`std::env::set_var`]. +/// +/// # Panics +/// +/// This function may panic if `key` is empty, contains an ASCII equals sign `'='` +/// or the NUL character `'\0'`, or when `value` contains the NUL character. +/// +/// # Examples +/// +/// ``` +/// let key = "KEY"; +/// env::set_var(key, "VALUE").expect("ok"); +/// assert_eq!(env::var(key), Ok("VALUE".to_string())); +/// ``` +#[must_use = "this function may not always run"] +pub fn set_var<K: AsRef<OsStr>, V: AsRef<OsStr>>(key: K, value: V) -> Option<()> { + (SAFE || num_threads::is_single_threaded() == Some(true)) + .then(|| unsafe { std::env::set_var(key, value) }) +} + +/// Removes an environment variable from the environment of the currently running process. (safely!) +/// +/// # Safety +/// +/// This function is safe to call. +/// This function returns [`None`] if it would be unsafe to call this function. +/// +/// See also [`std::env::remove_var`]. +/// +/// # Panics +/// +/// This function may panic if `key` is empty, contains an ASCII equals sign +/// `'='` or the NUL character `'\0'`, or when the value contains the NUL +/// character. +/// +/// # Examples +/// +/// ``` +/// let key = "KEY"; +/// env::set_var(key, "VALUE").expect("ok"); +/// assert_eq!(env::var(key), Ok("VALUE".to_string())); +/// env::remove_var(key).expect("ok"); +/// assert!(env::var(key).is_err()); +/// ``` +#[must_use = "this function may not always run"] +pub fn remove_var<K: AsRef<OsStr>>(key: K) -> Option<()> { + // SAFETY: this function is safe to call in single threaded environments or on good OS's. + (SAFE || num_threads::is_single_threaded() == Some(true)) + .then(|| unsafe { std::env::remove_var(key) }) +} |