feat: add generic slice choice helper
Add OsRandom::choose for callers that want a uniformly selected reference from any non-empty slice. This covers the common generic selection use case without adding collection algorithms such as shuffle. Keep OsRandom::pick as the byte-oriented helper by delegating it through choose. This preserves the existing public API while sharing the usize range sampler and avoiding the old u32 length limit. Document choose, pick, and string_from with working examples, and add a choices example that demonstrates generic slice selection alongside byte alphabets. Test Plan: - cargo test - cargo clippy - cargo clippy --benches - cargo clippy --tests - cargo +nightly fmt Refs: IDEAS.md ergonomics backlog
This commit is contained in:
+55
-6
@@ -13,6 +13,8 @@
|
||||
//! - Convenience byte readers fill caller-provided buffers.
|
||||
//! - Sampling helpers build unbiased higher-level choices from those primitive
|
||||
//! readers.
|
||||
//! - Range helpers sample integer spans.
|
||||
//! - Slice helpers choose caller-owned values by reference.
|
||||
//! - [`charset`] contains reusable ASCII alphabets for string generation.
|
||||
|
||||
use std::{
|
||||
@@ -423,16 +425,51 @@ impl OsRandom {
|
||||
/// Returns a uniformly chosen byte from `set`.
|
||||
///
|
||||
/// # Panics
|
||||
/// Panics if `set` is empty or longer than `u32::MAX`.
|
||||
/// Panics if `set` is empty.
|
||||
///
|
||||
/// # Errors
|
||||
/// Returns any I/O error produced while reading from `/dev/urandom`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # fn main() -> std::io::Result<()> {
|
||||
/// let mut rng = ez_urandom::OsRandom::try_new()?;
|
||||
/// let digit = rng.pick(ez_urandom::charset::DIGITS)?;
|
||||
/// assert!(digit.is_ascii_digit());
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn pick(&mut self, set: &[u8]) -> io::Result<u8> {
|
||||
assert!(!set.is_empty(), "set must not be empty");
|
||||
let n = u32::try_from(set.len()).expect("set must fit in u32");
|
||||
let i = usize::try_from(self.gen_range_u32(n)?)
|
||||
.expect("u32 fits in usize on supported platforms");
|
||||
Ok(set[i])
|
||||
Ok(*self.choose(set)?)
|
||||
}
|
||||
|
||||
/// Returns a uniformly chosen item from `items`.
|
||||
///
|
||||
/// The returned reference points into the caller-provided slice; no item is
|
||||
/// cloned or moved.
|
||||
///
|
||||
/// # Panics
|
||||
/// Panics if `items` is empty.
|
||||
///
|
||||
/// # Errors
|
||||
/// Returns any I/O error produced while reading from `/dev/urandom`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # fn main() -> std::io::Result<()> {
|
||||
/// let mut rng = ez_urandom::OsRandom::try_new()?;
|
||||
/// let colors = ["red", "green", "blue"];
|
||||
/// let color = rng.choose(&colors)?;
|
||||
/// assert!(colors.contains(color));
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn choose<'a, T>(&mut self, items: &'a [T]) -> io::Result<&'a T> {
|
||||
assert!(!items.is_empty(), "items must not be empty");
|
||||
let i = self.gen_range_usize(items.len())?;
|
||||
Ok(&items[i])
|
||||
}
|
||||
|
||||
/// Returns a `String` of `len` characters drawn uniformly from `set`.
|
||||
@@ -443,6 +480,18 @@ impl OsRandom {
|
||||
///
|
||||
/// # Errors
|
||||
/// Returns any I/O error produced while reading from `/dev/urandom`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # fn main() -> std::io::Result<()> {
|
||||
/// let mut rng = ez_urandom::OsRandom::try_new()?;
|
||||
/// let token = rng.string_from(ez_urandom::charset::ALPHANUMERIC, 24)?;
|
||||
/// assert_eq!(token.len(), 24);
|
||||
/// assert!(token.is_ascii());
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn string_from(&mut self, set: &[u8], len: usize) -> io::Result<String> {
|
||||
assert!(set.is_ascii(), "set must contain only ASCII bytes");
|
||||
let mut buf = vec![0u8; len];
|
||||
|
||||
Reference in New Issue
Block a user