diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 75ef4c2..0c4ff27 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -6,30 +6,6 @@ on: - master jobs: - clippy_solo: - runs-on: ubuntu-latest - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - - name: Install stable toolchain - uses: dtolnay/rust-toolchain@stable - - - name: Run tests - run: cargo clippy --all-features && cargo clippy - - tests_solo: - runs-on: ubuntu-latest - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - - name: Install stable toolchain - uses: dtolnay/rust-toolchain@stable - - - name: Run tests - run: cargo test --all-features --no-fail-fast && cargo test - msrv_solo: runs-on: ubuntu-latest steps: diff --git a/Cargo.toml b/Cargo.toml index c8e7e51..905da2c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,3 +20,8 @@ trybuild = "1.0.85" [features] cloneable-secret = [] +alloc = ["zeroize/alloc"] + +[package.metadata.docs.rs] +all-features = true +rustdoc-args = ["--generate-link-to-definition", "--cfg", "docsrs"] diff --git a/scripts/check-all-features.sh b/scripts/check-all-features.sh index ca07980..0f98138 100644 --- a/scripts/check-all-features.sh +++ b/scripts/check-all-features.sh @@ -1,7 +1,7 @@ #!/bin/bash # Array of feature names -features=("cloneable-secret") +features=("cloneable-secret", "alloc") # Calculate the total number of features total_features=${#features[@]} diff --git a/scripts/clippy-all-features.sh b/scripts/clippy-all-features.sh index b07fd80..a7cf6d6 100644 --- a/scripts/clippy-all-features.sh +++ b/scripts/clippy-all-features.sh @@ -1,7 +1,7 @@ #!/bin/bash # Array of feature names -features=("cloneable-secret") +features=("cloneable-secret", "alloc") # Calculate the total number of features total_features=${#features[@]} diff --git a/scripts/tests-all-features.sh b/scripts/tests-all-features.sh index 742ffb4..2981dfb 100644 --- a/scripts/tests-all-features.sh +++ b/scripts/tests-all-features.sh @@ -1,7 +1,7 @@ #!/bin/bash # Array of feature names -features=("cloneable-secret") +features=("cloneable-secret", "alloc") # Calculate the total number of features total_features=${#features[@]} diff --git a/src/lib.rs b/src/lib.rs index 86098f3..c37ce2f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,10 @@ +#![cfg_attr(not(feature = "std"), no_std)] +#![doc = include_str!("../README.md")] +#![cfg_attr(docsrs, feature(doc_auto_cfg))] + +#[cfg(feature = "alloc")] +extern crate alloc; + mod macros; mod secret; diff --git a/src/secret.rs b/src/secret.rs index 87345b0..2ddf06e 100644 --- a/src/secret.rs +++ b/src/secret.rs @@ -4,7 +4,7 @@ use core::{ ops::{Add, Deref, Drop}, }; -use crate::traits::{ExposeSecret, SecretIntoInner}; +use crate::traits::ExposeSecret; use typenum::{IsLessOrEqual, Sum, True, Unsigned, U0, U1}; use zeroize::Zeroize; @@ -29,6 +29,13 @@ where pub const fn new(value: T) -> Self { Self(ManuallyDrop::new(value), PhantomData) } + + pub fn new_with(closure: ClosureType) -> Self + where + ClosureType: FnOnce() -> T, + { + Self(ManuallyDrop::new(closure()), PhantomData) + } } impl< @@ -96,23 +103,6 @@ where } } -impl SecretIntoInner for Secret -where - T: Zeroize, - MEC: Unsigned, - EC: Unsigned + Add + IsLessOrEqual, -{ - #[inline(always)] - fn into_inner(mut self) -> T { - // SAFETY: Since compile error prevents constructing a `Secret` with `EC` > `MEC`, - // `zeroize()` is only called when `Secret` is maximally exposed - // and it is not possible to call `expose_secret(...)` - // when `Secret` is maximally exposed to access **private** `self.0` field, - // therefore, this is safe. - unsafe { ManuallyDrop::take(&mut self.0) } - } -} - #[cfg(feature = "cloneable-secret")] impl Clone for Secret where diff --git a/src/traits.rs b/src/traits.rs index 04c6ce0..cf89da5 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -1,6 +1,5 @@ use core::ops::Add; use typenum::{IsLessOrEqual, Sum, True, Unsigned, U1}; -use zeroize::Zeroize; pub trait ExposeSecret<'max, T, MEC: Unsigned, EC: Unsigned>: Sized { type Exposed<'brand> @@ -19,15 +18,6 @@ pub trait ExposeSecret<'max, T, MEC: Unsigned, EC: Unsigned>: Sized { Sum: Unsigned + Add + IsLessOrEqual; } -pub trait SecretIntoInner: Sized -where - T: Zeroize, - MEC: Unsigned, - EC: Unsigned + Add + IsLessOrEqual, -{ - fn into_inner(self) -> T; -} - #[cfg(feature = "cloneable-secret")] pub use self::cloneable_secret::CloneableSecret; @@ -40,8 +30,16 @@ mod cloneable_secret { pub trait CloneableSecret: Clone + Zeroize {} impl CloneableSecret for [T; N] {} + + #[cfg(feature = "alloc")] + use alloc::{string::String, vec::Vec}; + + #[cfg(feature = "alloc")] impl CloneableSecret for String {} + + #[cfg(feature = "alloc")] impl CloneableSecret for Vec {} + crate::impl_cloneable_secret_for_numbers!( i8, i16, i32, i64, i128, isize, u8, u16, u32, u64, u128, usize, f32, f64 ); diff --git a/tests/extern_bin.rs b/tests/extern_bin.rs index aa8f0be..7f2b548 100644 --- a/tests/extern_bin.rs +++ b/tests/extern_bin.rs @@ -15,6 +15,7 @@ fn test_expose_secret_extern() { } #[cfg(feature = "cloneable-secret")] +#[cfg(feature = "alloc")] #[test] fn test_secret_with_vec_and_clone() { let secret_vec = vec!["MySecret".to_string()]; @@ -152,11 +153,153 @@ fn test_clone_1() { } #[test] -fn test_destruct_secret_1() { - use sosecrets_rs::traits::SecretIntoInner; +fn test_with_new() { + use std::env; + use zeroize::Zeroize; - let new_secret: Secret<_, U2> = Secret::new(69); + #[derive(Clone, Debug, PartialEq)] + struct SecretString(String); + + impl Zeroize for SecretString { + fn zeroize(&mut self) { + self.0.zeroize(); + } + } + + let new_secret: Secret = Secret::new_with(|| { + SecretString( + env::var("CARGO_TARGET_DIR") + .unwrap_or("MySecret".to_string()) + .to_string(), + ) + }); + + let (new_secret, returned_value) = new_secret.expose_secret(|exposed_secret| { + let returned_value = UseSecret::new((*exposed_secret).to_owned()); + returned_value + }); + assert_eq!( + returned_value.inner, + SecretString(env::var("CARGO_TARGET_DIR").unwrap_or("MySecret".to_string())) + ); + + let (new_secret, returned_value) = new_secret.expose_secret(|exposed_secret| { + let returned_value = UseSecret::new((*exposed_secret).to_owned()); + returned_value + }); + assert_eq!( + returned_value.inner, + SecretString(env::var("CARGO_TARGET_DIR").unwrap_or("MySecret".to_string())) + ); - let got_out_inner_value = new_secret.into_inner(); - assert_eq!(got_out_inner_value, 69); + let (_new_secret, returned_value) = new_secret.expose_secret(|exposed_secret| { + let returned_value = UseSecret::new((*exposed_secret).to_owned()); + returned_value + }); + assert_eq!( + returned_value.inner, + SecretString(env::var("CARGO_TARGET_DIR").unwrap_or("MySecret".to_string())) + ); +} + +#[cfg(feature = "cloneable-secret")] +#[test] +fn test_with_new_cloneable_secret() { + use std::env; + use zeroize::Zeroize; + + #[derive(Clone, Debug, PartialEq)] + struct SecretString(String); + + impl Zeroize for SecretString { + fn zeroize(&mut self) { + self.0.zeroize(); + } + } + + let new_secret: Secret = Secret::new_with(|| { + SecretString( + env::var("CARGO_TARGET_DIR") + .unwrap_or("MySecret".to_string()) + .to_string(), + ) + }); + + let (new_secret, returned_value) = new_secret.expose_secret(|exposed_secret| { + let returned_value = UseSecret::new((*exposed_secret).to_owned()); + returned_value + }); + assert_eq!( + returned_value.inner, + SecretString(env::var("CARGO_TARGET_DIR").unwrap_or("MySecret".to_string())) + ); + + let (new_secret, returned_value) = new_secret.expose_secret(|exposed_secret| { + let returned_value = UseSecret::new((*exposed_secret).to_owned()); + returned_value + }); + assert_eq!( + returned_value.inner, + SecretString(env::var("CARGO_TARGET_DIR").unwrap_or("MySecret".to_string())) + ); + + let (_new_secret, returned_value) = new_secret.expose_secret(|exposed_secret| { + let returned_value = UseSecret::new((*exposed_secret).to_owned()); + returned_value + }); + assert_eq!( + returned_value.inner, + SecretString(env::var("CARGO_TARGET_DIR").unwrap_or("MySecret".to_string())) + ); +} + +#[cfg(feature = "alloc")] +#[test] +fn test_with_new_alloc() { + use std::env; + use zeroize::Zeroize; + + #[derive(Clone, Debug, PartialEq)] + struct SecretString(String); + + impl Zeroize for SecretString { + fn zeroize(&mut self) { + self.0.zeroize(); + } + } + + let new_secret: Secret = Secret::new_with(|| { + SecretString( + env::var("CARGO_TARGET_DIR") + .unwrap_or("MySecret".to_string()) + .to_string(), + ) + }); + + let (new_secret, returned_value) = new_secret.expose_secret(|exposed_secret| { + let returned_value = UseSecret::new((*exposed_secret).to_owned()); + returned_value + }); + assert_eq!( + returned_value.inner, + SecretString(env::var("CARGO_TARGET_DIR").unwrap_or("MySecret".to_string())) + ); + + let (new_secret, returned_value) = new_secret.expose_secret(|exposed_secret| { + let returned_value = UseSecret::new((*exposed_secret).to_owned()); + returned_value + }); + assert_eq!( + returned_value.inner, + SecretString(env::var("CARGO_TARGET_DIR").unwrap_or("MySecret".to_string())) + ); + + let (_new_secret, returned_value) = new_secret.expose_secret(|exposed_secret| { + let returned_value = UseSecret::new((*exposed_secret).to_owned()); + returned_value + }); + assert_eq!( + returned_value.inner, + SecretString(env::var("CARGO_TARGET_DIR").unwrap_or("MySecret".to_string())) + ); } diff --git a/tests/trybuild_tests.rs b/tests/trybuild_tests.rs index a9e88d4..49b65b3 100644 --- a/tests/trybuild_tests.rs +++ b/tests/trybuild_tests.rs @@ -5,15 +5,26 @@ fn test_compile_fails() { t.compile_fail("trybuild_tests/test_compile_fail_two.rs"); t.compile_fail("trybuild_tests/test_compile_fail_three.rs"); - #[cfg(feature = "cloneable-secret")] + #[cfg(all(feature = "cloneable-secret", not(feature = "alloc")))] t.compile_fail("trybuild_tests/test_compile_fail_four.rs"); - #[cfg(feature = "cloneable-secret")] + #[cfg(all(feature = "cloneable-secret", not(feature = "alloc")))] t.compile_fail("trybuild_tests/test_compile_fail_five.rs"); - #[cfg(feature = "cloneable-secret")] + #[cfg(all(feature = "alloc", feature = "cloneable-secret"))] t.compile_fail("trybuild_tests/test_compile_fail_six.rs"); + // std env + alloc + no clone, no clone should error + #[cfg(all(feature = "alloc", not(feature = "cloneable-secret")))] + t.compile_fail("trybuild_tests/test_compile_fail_seven.rs"); + + // no_std env + alloc + extern crate alloc::vec::Vec in main() + #[cfg(all(feature = "alloc", not(feature = "cloneable-secret")))] + t.compile_fail("trybuild_tests/test_compile_fail_eight.rs"); + #[cfg(feature = "cloneable-secret")] t.pass("trybuild_tests/test_compile_pass_one.rs"); + + // no_std env + no alloc + no cloneable-secret should work + t.pass("trybuild_tests/test_compile_pass_two.rs"); } diff --git a/trybuild_tests/test_compile_fail_eight.rs b/trybuild_tests/test_compile_fail_eight.rs new file mode 100644 index 0000000..983aa16 --- /dev/null +++ b/trybuild_tests/test_compile_fail_eight.rs @@ -0,0 +1,37 @@ +#![no_std] + +mod common; + +fn main() { + use common::UseSecret; + use sosecrets_rs::prelude::*; + use sosecrets_rs::traits::ExposeSecret; + use typenum::consts::U2; + + #[cfg(feature = "alloc")] + extern crate std; + + #[cfg(feature = "alloc")] + use std::{borrow::ToOwned, vec}; + + // try similar with vec + let secret_vec = vec!["MySecret".to_owned()]; + let new_secret: Secret<_, U2, _> = Secret::new(secret_vec); + let (new_secret, returned_value) = new_secret.expose_secret(|exposed_secret| { + let returned_value = UseSecret::new((*exposed_secret).to_owned()); + returned_value + }); + assert_eq!(returned_value.inner, vec!["MySecret".to_owned()]); + + let (new_secret, returned_value) = new_secret.expose_secret(|exposed_secret| { + let returned_value = UseSecret::new((*exposed_secret).to_owned()); + returned_value + }); + assert_eq!(returned_value.inner, vec!["MySecret".to_owned()]); + + let (_new_secret, returned_value) = new_secret.expose_secret(|exposed_secret| { + let returned_value = UseSecret::new((*exposed_secret).to_owned()); + returned_value + }); + assert_eq!(returned_value.inner, vec!["MySecret".to_owned()]); +} diff --git a/trybuild_tests/test_compile_fail_eight.stderr b/trybuild_tests/test_compile_fail_eight.stderr new file mode 100644 index 0000000..f64ffe3 --- /dev/null +++ b/trybuild_tests/test_compile_fail_eight.stderr @@ -0,0 +1,14 @@ +error[E0271]: type mismatch resolving `, B1> as IsLessOrEqual, B0>>>::Output == B1` + --> trybuild_tests/test_compile_fail_eight.rs:32:52 + | +32 | let (_new_secret, returned_value) = new_secret.expose_secret(|exposed_secret| { + | ^^^^^^^^^^^^^ expected `B1`, found `B0` + | +note: required by a bound in `sosecrets_rs::traits::ExposeSecret::Next` + --> src/traits.rs + | + | type Next: ExposeSecret<'max, T, MEC, Sum> + | ---- required by a bound in this associated type +... + | Sum: Unsigned + IsLessOrEqual + Add; + | ^^^^^^^^^^^^^ required by this bound in `ExposeSecret::Next` diff --git a/trybuild_tests/test_compile_fail_four.rs b/trybuild_tests/test_compile_fail_four.rs index 5ce8aed..2005c83 100644 --- a/trybuild_tests/test_compile_fail_four.rs +++ b/trybuild_tests/test_compile_fail_four.rs @@ -28,27 +28,4 @@ fn main() { returned_value }); assert_eq!(69, returned_value.inner); - - // try similar with vec - let secret_vec = vec!["MySecret".to_string()]; - let new_secret: Secret<_, U2, _> = Secret::new(secret_vec); - let (new_secret, returned_value) = new_secret.expose_secret(|exposed_secret| { - let returned_value = UseSecret::new((*exposed_secret).to_owned()); - returned_value - }); - assert_eq!(returned_value.inner, vec!["MySecret".to_owned()]); - - let cloned_secret = new_secret.clone(); - let (cloned_secret, returned_value) = cloned_secret.expose_secret(|exposed_secret| { - let returned_value = UseSecret::new((*exposed_secret).to_owned()); - returned_value - }); - assert_eq!(returned_value.inner, vec!["MySecret".to_owned()]); - - // Cloned `Secret` over exposed here - let (_cloned_secret, returned_value) = cloned_secret.expose_secret(|exposed_secret| { - let returned_value = UseSecret::new((*exposed_secret).to_owned()); - returned_value - }); - assert_eq!(returned_value.inner, vec!["MySecret".to_owned()]); } diff --git a/trybuild_tests/test_compile_fail_four.stderr b/trybuild_tests/test_compile_fail_four.stderr index 7b284ec..eadddc5 100644 --- a/trybuild_tests/test_compile_fail_four.stderr +++ b/trybuild_tests/test_compile_fail_four.stderr @@ -12,18 +12,3 @@ note: required by a bound in `sosecrets_rs::traits::ExposeSecret::Next` ... | Sum: Unsigned + IsLessOrEqual + Add; | ^^^^^^^^^^^^^ required by this bound in `ExposeSecret::Next` - -error[E0271]: type mismatch resolving `, B1> as IsLessOrEqual, B0>>>::Output == B1` - --> trybuild_tests/test_compile_fail_four.rs:49:58 - | -49 | let (_cloned_secret, returned_value) = cloned_secret.expose_secret(|exposed_secret| { - | ^^^^^^^^^^^^^ expected `B1`, found `B0` - | -note: required by a bound in `sosecrets_rs::traits::ExposeSecret::Next` - --> src/traits.rs - | - | type Next: ExposeSecret<'max, T, MEC, Sum> - | ---- required by a bound in this associated type -... - | Sum: Unsigned + IsLessOrEqual + Add; - | ^^^^^^^^^^^^^ required by this bound in `ExposeSecret::Next` diff --git a/trybuild_tests/test_compile_fail_seven.rs b/trybuild_tests/test_compile_fail_seven.rs new file mode 100644 index 0000000..858a909 --- /dev/null +++ b/trybuild_tests/test_compile_fail_seven.rs @@ -0,0 +1,31 @@ +mod common; + +fn main() { + use common::UseSecret; + use sosecrets_rs::prelude::*; + use sosecrets_rs::traits::ExposeSecret; + use typenum::consts::U2; + + // try similar with vec + let secret_vec = vec!["MySecret".to_string()]; + let new_secret: Secret<_, U2, _> = Secret::new(secret_vec); + let (new_secret, returned_value) = new_secret.expose_secret(|exposed_secret| { + let returned_value = UseSecret::new((*exposed_secret).to_owned()); + returned_value + }); + assert_eq!(returned_value.inner, vec!["MySecret".to_owned()]); + + let (new_secret, returned_value) = new_secret.expose_secret(|exposed_secret| { + let returned_value = UseSecret::new((*exposed_secret).to_owned()); + returned_value + }); + assert_eq!(returned_value.inner, vec!["MySecret".to_owned()]); + + let (_new_secret, returned_value) = new_secret.expose_secret(|exposed_secret| { + let returned_value = UseSecret::new((*exposed_secret).to_owned()); + returned_value + }); + assert_eq!(returned_value.inner, vec!["MySecret".to_owned()]); + + let cloned_secret = new_secret.clone(); +} diff --git a/trybuild_tests/test_compile_fail_seven.stderr b/trybuild_tests/test_compile_fail_seven.stderr new file mode 100644 index 0000000..765e203 --- /dev/null +++ b/trybuild_tests/test_compile_fail_seven.stderr @@ -0,0 +1,20 @@ +error[E0271]: type mismatch resolving `, B1> as IsLessOrEqual, B0>>>::Output == B1` + --> trybuild_tests/test_compile_fail_seven.rs:24:52 + | +24 | let (_new_secret, returned_value) = new_secret.expose_secret(|exposed_secret| { + | ^^^^^^^^^^^^^ expected `B1`, found `B0` + | +note: required by a bound in `sosecrets_rs::traits::ExposeSecret::Next` + --> src/traits.rs + | + | type Next: ExposeSecret<'max, T, MEC, Sum> + | ---- required by a bound in this associated type +... + | Sum: Unsigned + IsLessOrEqual + Add; + | ^^^^^^^^^^^^^ required by this bound in `ExposeSecret::Next` + +error[E0599]: no method named `clone` found for struct `sosecrets_rs::prelude::Secret` in the current scope + --> trybuild_tests/test_compile_fail_seven.rs:30:36 + | +30 | let cloned_secret = new_secret.clone(); + | ^^^^^ method not found in `Secret, UInt, B0>, UInt, B0>>` diff --git a/trybuild_tests/test_compile_fail_six.rs b/trybuild_tests/test_compile_fail_six.rs index bc1baa6..f3a2ab3 100644 --- a/trybuild_tests/test_compile_fail_six.rs +++ b/trybuild_tests/test_compile_fail_six.rs @@ -3,17 +3,29 @@ mod common; fn main() { use common::UseSecret; use sosecrets_rs::prelude::*; - use sosecrets_rs::traits::{ExposeSecret, SecretIntoInner}; + use sosecrets_rs::traits::ExposeSecret; use typenum::consts::U2; - let new_secret: Secret<_, U2> = Secret::new(69); + // try similar with vec + let secret_vec = vec!["MySecret".to_string()]; + let new_secret: Secret<_, U2, _> = Secret::new(secret_vec); + let (new_secret, returned_value) = new_secret.expose_secret(|exposed_secret| { + let returned_value = UseSecret::new((*exposed_secret).to_owned()); + returned_value + }); + assert_eq!(returned_value.inner, vec!["MySecret".to_owned()]); - let got_out_inner_value = new_secret.into_inner(); - assert_eq!(got_out_inner_value, 69); + let cloned_secret = new_secret.clone(); + let (cloned_secret, returned_value) = cloned_secret.expose_secret(|exposed_secret| { + let returned_value = UseSecret::new((*exposed_secret).to_owned()); + returned_value + }); + assert_eq!(returned_value.inner, vec!["MySecret".to_owned()]); - let (_new_secret, returned_value) = new_secret.expose_secret(|exposed_secret| { - let returned_value = UseSecret::new(*exposed_secret); + // Cloned `Secret` over exposed here + let (_cloned_secret, returned_value) = cloned_secret.expose_secret(|exposed_secret| { + let returned_value = UseSecret::new((*exposed_secret).to_owned()); returned_value }); - assert_eq!(69, returned_value.inner); + assert_eq!(returned_value.inner, vec!["MySecret".to_owned()]); } diff --git a/trybuild_tests/test_compile_fail_six.stderr b/trybuild_tests/test_compile_fail_six.stderr index 6885bf2..237c4f5 100644 --- a/trybuild_tests/test_compile_fail_six.stderr +++ b/trybuild_tests/test_compile_fail_six.stderr @@ -1,21 +1,14 @@ -error[E0382]: use of moved value: `new_secret` - --> trybuild_tests/test_compile_fail_six.rs:14:41 +error[E0271]: type mismatch resolving `, B1> as IsLessOrEqual, B0>>>::Output == B1` + --> trybuild_tests/test_compile_fail_six.rs:27:58 | -9 | let new_secret: Secret<_, U2> = Secret::new(69); - | ---------- move occurs because `new_secret` has type `sosecrets_rs::prelude::Secret, B0>>`, which does not implement the `Copy` trait -10 | -11 | let got_out_inner_value = new_secret.into_inner(); - | ------------ `new_secret` moved due to this method call -... -14 | let (_new_secret, returned_value) = new_secret.expose_secret(|exposed_secret| { - | ^^^^^^^^^^ value used here after move +27 | let (_cloned_secret, returned_value) = cloned_secret.expose_secret(|exposed_secret| { + | ^^^^^^^^^^^^^ expected `B1`, found `B0` | -note: `into_inner` takes ownership of the receiver `self`, which moves `new_secret` +note: required by a bound in `sosecrets_rs::traits::ExposeSecret::Next` --> src/traits.rs | - | fn into_inner(self) -> T; - | ^^^^ -help: you can `clone` the value and consume it, but this might not be your desired behavior - | -11 | let got_out_inner_value = new_secret.clone().into_inner(); - | ++++++++ + | type Next: ExposeSecret<'max, T, MEC, Sum> + | ---- required by a bound in this associated type +... + | Sum: Unsigned + IsLessOrEqual + Add; + | ^^^^^^^^^^^^^ required by this bound in `ExposeSecret::Next` diff --git a/trybuild_tests/test_compile_pass_two.rs b/trybuild_tests/test_compile_pass_two.rs new file mode 100644 index 0000000..554a0fe --- /dev/null +++ b/trybuild_tests/test_compile_pass_two.rs @@ -0,0 +1,27 @@ +#![no_std] + +mod common; +fn main() { + use sosecrets_rs::{prelude::*, traits::ExposeSecret}; + use typenum::consts::U2; + + // use std for allocator + panic handler + extern crate std; + + pub struct UseSecret { + pub inner: T, + } + impl UseSecret { + pub fn new(value: T) -> Self { + Self { inner: value } + } + } + + let new_secret: Secret<_, U2> = Secret::new(69); + + let (_new_secret, returned_value) = new_secret.expose_secret(|exposed_secret| { + let returned_value = UseSecret::new(*exposed_secret); + returned_value + }); + assert_eq!(69, returned_value.inner); +}