diff --git a/Cargo.lock b/Cargo.lock index ad3089a..442539f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -151,6 +151,18 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "getrandom" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasi", +] + [[package]] name = "hashbrown" version = "0.15.3" @@ -196,6 +208,12 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "libc" +version = "0.2.172" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" + [[package]] name = "log" version = "0.4.27" @@ -232,6 +250,12 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "r-efi" +version = "5.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" + [[package]] name = "rustversion" version = "1.0.21" @@ -337,11 +361,21 @@ version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3cf4199d1e5d15ddd86a694e4d0dffa9c323ce759fea589f00fef9d81cc1931d" dependencies = [ + "getrandom", "js-sys", "serde", "wasm-bindgen", ] +[[package]] +name = "wasi" +version = "0.14.2+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" +dependencies = [ + "wit-bindgen-rt", +] + [[package]] name = "wasm-bindgen" version = "0.2.100" @@ -399,3 +433,12 @@ checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" dependencies = [ "unicode-ident", ] + +[[package]] +name = "wit-bindgen-rt" +version = "0.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" +dependencies = [ + "bitflags", +] diff --git a/benzina/Cargo.toml b/benzina/Cargo.toml index 0392046..95823fe 100644 --- a/benzina/Cargo.toml +++ b/benzina/Cargo.toml @@ -10,7 +10,14 @@ repository.workspace = true rust-version.workspace = true [package.metadata.docs.rs] -features = ["postgres", "mysql", "serde", "typed-uuid", "example-generated"] +features = [ + "postgres", + "mysql", + "serde", + "typed-uuid", + "example-generated", + "dangerous-construction", +] rustdoc-args = ["--cfg", "docsrs"] [dependencies] @@ -21,7 +28,8 @@ benzina-derive = { path = "../benzina-derive", version = "=0.3.3", optional = tr uuid = { version = ">=0.7.0, <2.0.0", default-features = false, optional = true } [dev-dependencies] -serde_test = "1" +serde_test = "1" +uuid = { version = ">=0.7.0, <2.0.0", default-features = false, features = ["v4"] } [features] default = ["derive"] @@ -35,6 +43,7 @@ serde = ["dep:serde", "uuid?/serde"] utoipa = ["dep:utoipa"] example-generated = ["typed-uuid"] +dangerous-construction = ["typed-uuid"] [lints] workspace = true diff --git a/benzina/src/typed_uuid.rs b/benzina/src/typed_uuid.rs index 1df9600..30703b0 100644 --- a/benzina/src/typed_uuid.rs +++ b/benzina/src/typed_uuid.rs @@ -4,7 +4,7 @@ /// generates code which implements many useful traits, including the ones needed to work with /// [`diesel`]. /// -/// The generated structs do not expose any method or trait to create an arbitrary instance, in +/// The generated structs do not expose any method or trait to create an arbitrary instance[^See note], in /// order to provide the guarantee that the `UUID` is valid. However, it is possible to choose to /// add traits and methods to customize the behavior. /// @@ -48,7 +48,7 @@ /// } /// ``` /// -/// Keep in mind that there is no way to construct an instance unless the instantiation is done +/// Keep in mind that there is no way[^See note] to construct an instance unless the instantiation is done /// inside the module containing the new type or a submodule of it: /// /// ```compile_fail,E0603 @@ -62,6 +62,12 @@ /// let foo = inner::Foo(Uuid::default()); /// // ^^^ private tuple struct constructor /// ``` +/// +/// [^See note]: There is no way in normal usage to construct an instance. The exception is with the +/// `dangerous_new` method, which is gated behind the `dangerous-construction` feature and intended +/// for special cases (including testing). If the `dangerous-construction` feature is enabled, it is +/// recommended to use [`clippy::disallowed_methods`](https://rust-lang.github.io/rust-clippy/stable/index.html#disallowed_methods) to prevent the usage of `dangerous_new` outside +/// of the desired situations. #[macro_export] macro_rules! typed_uuid { ( @@ -85,6 +91,15 @@ macro_rules! typed_uuid { $vis struct $name($crate::__private::uuid::Uuid); impl $name { + /// Creates a new typed [`Uuid`] which does not come from the database. + /// + /// [`Uuid`]: $crate::__private::uuid::Uuid + #[must_use] + #[cfg(feature = "dangerous-construction")] + pub fn dangerous_new(inner: $crate::__private::uuid::Uuid) -> Self { + Self(inner) + } + /// Gets the actual [`Uuid`]. /// /// [`Uuid`]: $crate::__private::uuid::Uuid @@ -361,3 +376,16 @@ macro_rules! __typed_uuid__impl_serde { macro_rules! __typed_uuid__impl_serde { ($name:ident) => {}; } + +#[cfg(test)] +mod test { + use uuid::Uuid; + + #[test] + fn creates_new_typed_uuid() { + crate::typed_uuid!(pub FooId); + let inner = Uuid::new_v4(); + let new = FooId::dangerous_new(inner); + assert_eq!(new.get(), inner); + } +}