Merge pull request #794 from uuid-rs/feat/getrandom-wasm32

Support forcing `getrandom` on `wasm32-unknown-unknown` without JavaScript
This commit is contained in:
Ashley Mannix 2025-02-05 11:17:06 +10:00 committed by GitHub
commit 6494c4b0f2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 292 additions and 75 deletions

View File

@ -133,6 +133,18 @@ jobs:
- name: Fast RNG
run: wasm-pack test --node -- --features "js v4 fast-rng"
- name: rng-getrandom
env:
RUSTFLAGS: '--cfg getrandom_backend="wasm_js"'
working-directory: ./tests/wasm32-getrandom-test
run: wasm-pack test --node
- name: rng-rand
env:
RUSTFLAGS: '--cfg getrandom_backend="wasm_js"'
working-directory: ./tests/wasm32-getrandom-test
run: wasm-pack test --node -- --features "rand"
wasi:
name: Tests / WebAssembly (WASI)
runs-on: ubuntu-latest

View File

@ -71,6 +71,9 @@ v8 = []
js = ["dep:wasm-bindgen"]
rng = ["dep:getrandom"]
rng-getrandom = ["rng", "dep:getrandom", "uuid-rng-internal-lib", "uuid-rng-internal-lib/getrandom"]
rng-rand = ["rng", "dep:rand", "uuid-rng-internal-lib", "uuid-rng-internal-lib/rand"]
fast-rng = ["rng", "dep:rand"]
sha1 = ["dep:sha1_smol"]
@ -126,13 +129,20 @@ default-features = false
# Private
# (Formally public)
[target.'cfg(not(all(target_arch = "wasm32", target_vendor = "unknown", target_os = "unknown")))'.dependencies.getrandom]
optional = true
version = "0.3"
optional = true
[target.'cfg(all(target_arch = "wasm32", target_vendor = "unknown", target_os = "unknown"))'.dependencies.uuid-rng-internal-lib]
# Work-around lack of support for both `dep:x` and `x/` in MSRV
package = "uuid-rng-internal"
version = "1.12.1"
path = "rng"
optional = true
# Private
[target.'cfg(not(all(target_arch = "wasm32", target_vendor = "unknown", target_os = "unknown")))'.dependencies.rand]
optional = true
version = "0.9"
optional = true
# Private
[dependencies.md-5]
@ -175,7 +185,7 @@ version = "1.0"
[dev-dependencies.serde_test]
version = "1.0.56"
[target.'cfg(target = "wasm32-unknown-unknown")'.dev-dependencies.wasm-bindgen]
[target.'cfg(all(target_arch = "wasm32", target_vendor = "unknown", target_os = "unknown"))'.dev-dependencies.wasm-bindgen]
version = "0.2"
[target.'cfg(all(target_arch = "wasm32", target_vendor = "unknown", target_os = "unknown"))'.dev-dependencies.wasm-bindgen-test]
@ -190,6 +200,8 @@ version = "1"
[workspace]
members = [
"macros",
"rng",
"examples",
"tests/smoke-test",
"tests/wasm32-getrandom-test",
]

26
rng/Cargo.toml Normal file
View File

@ -0,0 +1,26 @@
[package]
name = "uuid-rng-internal"
version = "1.12.1"
edition = "2018"
authors = [
"uuid-rs contributors"
]
categories = [
"data-structures",
"no-std",
"parser-implementations",
"wasm"
]
description = "Private implementation details of the uuid crate."
documentation = "https://docs.rs/uuid"
repository = "https://github.com/uuid-rs/uuid"
license = "Apache-2.0 OR MIT"
# Forces a dependency on `getrandom`
[dependencies.getrandom]
version = "0.3"
optional = true
[dependencies.rand]
version = "0.9"
optional = true

20
rng/src/lib.rs Normal file
View File

@ -0,0 +1,20 @@
//! Implementation details for the `uuid` crate.
//!
//! This crate is not meant to be used directly. It
//! allows `wasm32-unknown-unknown` users who aren't
//! in a JS-enabled runtime to configure a source of
//! randomness via `getrandom`:
//!
//! ```toml
//! [dependencies.uuid]
//! features = ["v4", "rng-getrandom"]
//! ```
#[doc(hidden)]
pub mod __private {
#[cfg(feature = "getrandom")]
pub use getrandom;
#[cfg(feature = "rand")]
pub use rand;
}

View File

@ -145,7 +145,9 @@ impl<'de> Deserialize<'de> for NonNilUuid {
{
let uuid = Uuid::deserialize(deserializer)?;
NonNilUuid::try_from(uuid).map_err(|_| de::Error::invalid_value(de::Unexpected::Other("nil UUID"), &"a non-nil UUID"))
NonNilUuid::try_from(uuid).map_err(|_| {
de::Error::invalid_value(de::Unexpected::Other("nil UUID"), &"a non-nil UUID")
})
}
}

View File

@ -438,7 +438,13 @@ pub enum Variant {
// NOTE: Also check `NonNilUuid` when ading new derives here
#[cfg_attr(
all(uuid_unstable, feature = "zerocopy"),
derive(zerocopy::IntoBytes, zerocopy::FromBytes, zerocopy::KnownLayout, zerocopy::Immutable, zerocopy::Unaligned)
derive(
zerocopy::IntoBytes,
zerocopy::FromBytes,
zerocopy::KnownLayout,
zerocopy::Immutable,
zerocopy::Unaligned
)
)]
#[cfg_attr(
feature = "borsh",

View File

@ -1,9 +1,61 @@
#[cfg(not(all(target_arch = "wasm32", target_vendor = "unknown", target_os = "unknown")))]
#![allow(dead_code)] // Keeps our cfg's from becoming too convoluted in here
trait Rng {
fn u128() -> u128;
fn u64() -> u64;
fn u16() -> u16;
}
pub(crate) fn u128() -> u128 {
imp::RngImp::u128()
}
pub(crate) fn u64() -> u64 {
imp::RngImp::u64()
}
pub(crate) fn u16() -> u16 {
imp::RngImp::u16()
}
#[cfg(not(all(
target_arch = "wasm32",
target_vendor = "unknown",
target_os = "unknown"
)))]
mod imp {
#[cfg(any(feature = "v4", feature = "v7"))]
pub(crate) fn u128() -> u128 {
#[cfg(not(feature = "fast-rng"))]
{
/*
Random support for non `wasm32-unknown-unknown` platforms.
*/
use super::*;
// Using `rand`
#[cfg(any(feature = "rng-rand", feature = "fast-rng"))]
pub(super) struct RngImp;
#[cfg(any(feature = "rng-rand", feature = "fast-rng"))]
impl Rng for RngImp {
fn u128() -> u128 {
rand::random()
}
fn u64() -> u64 {
rand::random()
}
fn u16() -> u16 {
rand::random()
}
}
// Using `getrandom`
#[cfg(all(not(feature = "fast-rng"), not(feature = "rng-rand")))]
pub(super) struct RngImp;
#[cfg(all(not(feature = "fast-rng"), not(feature = "rng-rand")))]
impl Rng for RngImp {
fn u128() -> u128 {
let mut bytes = [0u8; 16];
getrandom::fill(&mut bytes).unwrap_or_else(|err| {
@ -14,36 +66,7 @@ mod imp {
u128::from_ne_bytes(bytes)
}
#[cfg(feature = "fast-rng")]
{
rand::random()
}
}
#[cfg(any(feature = "v1", feature = "v6"))]
pub(crate) fn u16() -> u16 {
#[cfg(not(feature = "fast-rng"))]
{
let mut bytes = [0u8; 2];
getrandom::fill(&mut bytes).unwrap_or_else(|err| {
// NB: getrandom::Error has no source; this is adequate display
panic!("could not retrieve random bytes for uuid: {}", err)
});
u16::from_ne_bytes(bytes)
}
#[cfg(feature = "fast-rng")]
{
rand::random()
}
}
#[cfg(feature = "v7")]
pub(crate) fn u64() -> u64 {
#[cfg(not(feature = "fast-rng"))]
{
fn u64() -> u64 {
let mut bytes = [0u8; 8];
getrandom::fill(&mut bytes).unwrap_or_else(|err| {
@ -54,56 +77,142 @@ mod imp {
u64::from_ne_bytes(bytes)
}
#[cfg(feature = "fast-rng")]
{
rand::random()
fn u16() -> u16 {
let mut bytes = [0u8; 2];
getrandom::fill(&mut bytes).unwrap_or_else(|err| {
// NB: getrandom::Error has no source; this is adequate display
panic!("could not retrieve random bytes for uuid: {}", err)
});
u16::from_ne_bytes(bytes)
}
}
}
#[cfg(all(feature = "js", target_arch = "wasm32", target_vendor = "unknown", target_os = "unknown"))]
#[cfg(all(
target_arch = "wasm32",
target_vendor = "unknown",
target_os = "unknown"
))]
mod imp {
/*
This module preserves the stabilized behavior of `uuid` that requires the
`js` feature to enable rng on `wasm32-unknown-unknown`, which it inherited
from `getrandom` `0.2`
Random support for `wasm32-unknown-unknown`.
*/
#[cfg(any(feature = "v4", feature = "v7"))]
pub(crate) fn u128() -> u128 {
let mut bytes = [0u8; 16];
use super::*;
if !getrandom::fill(&mut bytes) {
panic!("could not retrieve random bytes for uuid")
// Using `rand`
#[cfg(feature = "rng-rand")]
pub(super) struct RngImp;
#[cfg(feature = "rng-rand")]
impl Rng for RngImp {
fn u128() -> u128 {
uuid_rng_internal_lib::__private::rand::random()
}
u128::from_ne_bytes(bytes)
}
#[cfg(any(feature = "v1", feature = "v6"))]
pub(crate) fn u16() -> u16 {
let mut bytes = [0u8; 2];
if !getrandom::fill(&mut bytes) {
panic!("could not retrieve random bytes for uuid")
fn u64() -> u64 {
uuid_rng_internal_lib::__private::rand::random()
}
u16::from_ne_bytes(bytes)
fn u16() -> u16 {
uuid_rng_internal_lib::__private::rand::random()
}
}
#[cfg(feature = "v7")]
pub(crate) fn u64() -> u64 {
let mut bytes = [0u8; 8];
// Using `getrandom`
#[cfg(all(feature = "rng-getrandom", not(feature = "rng-rand")))]
pub(super) struct RngImp;
if !getrandom::fill(&mut bytes) {
panic!("could not retrieve random bytes for uuid")
#[cfg(all(feature = "rng-getrandom", not(feature = "rng-rand")))]
impl Rng for RngImp {
fn u128() -> u128 {
let mut bytes = [0u8; 16];
uuid_rng_internal_lib::__private::getrandom::fill(&mut bytes).unwrap_or_else(|err| {
// NB: getrandom::Error has no source; this is adequate display
panic!("could not retrieve random bytes for uuid: {}", err)
});
u128::from_ne_bytes(bytes)
}
u64::from_ne_bytes(bytes)
fn u64() -> u64 {
let mut bytes = [0u8; 8];
uuid_rng_internal_lib::__private::getrandom::fill(&mut bytes).unwrap_or_else(|err| {
// NB: getrandom::Error has no source; this is adequate display
panic!("could not retrieve random bytes for uuid: {}", err)
});
u64::from_ne_bytes(bytes)
}
fn u16() -> u16 {
let mut bytes = [0u8; 2];
uuid_rng_internal_lib::__private::getrandom::fill(&mut bytes).unwrap_or_else(|err| {
// NB: getrandom::Error has no source; this is adequate display
panic!("could not retrieve random bytes for uuid: {}", err)
});
u16::from_ne_bytes(bytes)
}
}
mod getrandom {
// Using WebCrypto via `wasm-bindgen`
#[cfg(all(
feature = "js",
not(feature = "rng-rand"),
not(feature = "rng-getrandom")
))]
pub(super) struct RngImp;
#[cfg(all(
feature = "js",
not(feature = "rng-rand"),
not(feature = "rng-getrandom")
))]
impl Rng for RngImp {
fn u128() -> u128 {
let mut bytes = [0u8; 16];
if !webcrypto::fill(&mut bytes) {
panic!("could not retrieve random bytes for uuid")
}
u128::from_ne_bytes(bytes)
}
fn u64() -> u64 {
let mut bytes = [0u8; 8];
if !webcrypto::fill(&mut bytes) {
panic!("could not retrieve random bytes for uuid")
}
u64::from_ne_bytes(bytes)
}
fn u16() -> u16 {
let mut bytes = [0u8; 2];
if !webcrypto::fill(&mut bytes) {
panic!("could not retrieve random bytes for uuid")
}
u16::from_ne_bytes(bytes)
}
}
#[cfg(feature = "js")]
mod webcrypto {
/*
This module preserves the stabilized behavior of `uuid` that requires the
`js` feature to enable rng on `wasm32-unknown-unknown`, which it inherited
from `getrandom` `0.2`.
Vendored from `getrandom`: https://github.com/rust-random/getrandom/blob/ce3b017fdee0233c6ecd61e68b96a84bf6f911bf/src/backends/wasm_js.rs
Copyright (c) 2018-2024 The rust-random Project Developers
@ -180,7 +289,7 @@ mod imp {
sub_buf.copy_to_uninit(chunk);
}
true
}
@ -196,8 +305,3 @@ mod imp {
}
}
}
#[cfg(all(not(feature = "js"), target_arch = "wasm32", target_vendor = "unknown", target_os = "unknown"))]
compile_error!("the `js` feature is required for the `wasm32-unknown-unknown` target");
pub(crate) use self::imp::*;

View File

@ -0,0 +1,21 @@
[package]
name = "uuid-wasm32-getrandom-test"
version = "0.1.0"
edition = "2018"
[features]
rand = ["uuid/rng-rand"]
[dependencies.uuid]
path = "../../"
features = ["v4", "rng-getrandom"]
[dependencies.getrandom]
version = "0.3"
features = ["wasm_js"]
[dependencies.wasm-bindgen]
version = "0.2"
[dependencies.wasm-bindgen-test]
version = "0.3"

View File

@ -0,0 +1,14 @@
#![cfg(test)]
use uuid::{Uuid, Variant, Version};
use wasm_bindgen_test::*;
#[test]
#[wasm_bindgen_test]
fn test_new() {
let uuid = Uuid::new_v4();
assert_eq!(uuid.get_version(), Some(Version::Random));
assert_eq!(uuid.get_variant(), Variant::RFC4122);
}