Skip to content

Commit 2ff500c

Browse files
authored
Merge pull request #150 from greyblake/generics-arbitrary
Implement support of derive(Arbitrary) for generic newtypes
2 parents 7c423f7 + abd542b commit 2ff500c

File tree

9 files changed

+80
-25
lines changed

9 files changed

+80
-25
lines changed

.github/workflows/ci.yml

+11-5
Original file line numberDiff line numberDiff line change
@@ -19,30 +19,36 @@ jobs:
1919
with:
2020
toolchain: stable
2121

22-
- name: cargo test --features nutype_test
22+
- name: cargo test
2323
uses: actions-rs/cargo@v1
2424
with:
2525
command: test
2626

27-
- name: cargo test --features nutype_test,serde
27+
- name: cargo test --features serde
2828
uses: actions-rs/cargo@v1
2929
with:
3030
command: test
3131
args: --features serde
3232

33-
- name: cargo test --features nutype_test,regex
33+
- name: cargo test --features regex
3434
uses: actions-rs/cargo@v1
3535
with:
3636
command: test
3737
args: --features regex
3838

39-
- name: cargo test --features nutype_test,new_unchecked
39+
- name: cargo test --features new_unchecked
4040
uses: actions-rs/cargo@v1
4141
with:
4242
command: test
4343
args: --features new_unchecked
4444

45-
- name: cargo test --features nutype_test,schemars08
45+
- name: cargo test --features arbitrary
46+
uses: actions-rs/cargo@v1
47+
with:
48+
command: test
49+
args: --features arbitrary
50+
51+
- name: cargo test --features schemars08
4652
uses: actions-rs/cargo@v1
4753
with:
4854
command: test

Justfile

+1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ test-all:
66
cargo test --features regex
77
cargo test --features new_unchecked
88
cargo test --features schemars08
9+
cargo test --features arbitrary
910
cargo test --all-features
1011

1112
test:

dummy/src/main.rs

+12-6
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,15 @@
1+
use arbitrary::Arbitrary;
12
use nutype::nutype;
23

3-
#[nutype(
4-
validate(predicate = |n| n.is_even()),
5-
derive(Debug, FromStr),
6-
)]
7-
struct Even<T: ::num::Integer>(T);
4+
#[nutype(derive(Debug, Arbitrary))]
5+
struct Wrapper<T>(Vec<T>);
86

9-
fn main() {}
7+
fn main() {
8+
fn gen(bytes: &[u8]) -> Wrapper<bool> {
9+
let mut u = arbitrary::Unstructured::new(bytes);
10+
Wrapper::<bool>::arbitrary(&mut u).unwrap()
11+
}
12+
assert_eq!(gen(&[]).into_inner(), vec![]);
13+
assert_eq!(gen(&[1]).into_inner(), vec![false]);
14+
assert_eq!(gen(&[1, 3, 5]).into_inner(), vec![true, false]);
15+
}
+16-7
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
11
use proc_macro2::{Span, TokenStream};
22
use quote::quote;
3+
use syn::Generics;
34

45
use crate::{
56
any::models::{AnyGuard, AnyInnerType},
7+
common::gen::{add_bound_to_all_type_params, add_param, strip_trait_bounds_on_generics},
68
common::models::TypeName,
79
};
810

911
pub fn gen_impl_trait_arbitrary(
1012
type_name: &TypeName,
13+
generics: &Generics,
1114
inner_type: &AnyInnerType,
1215
guard: &AnyGuard,
1316
) -> Result<TokenStream, syn::Error> {
@@ -22,18 +25,24 @@ pub fn gen_impl_trait_arbitrary(
2225

2326
// Generate implementation of `Arbitrary` trait, assuming that inner type implements Arbitrary
2427
// too.
28+
let generics_without_bounds = strip_trait_bounds_on_generics(generics);
29+
let generics_with_lifetime = add_param(&generics_without_bounds, quote!('nu_arb));
30+
let generics_with_bounds = add_bound_to_all_type_params(
31+
&generics_with_lifetime,
32+
quote!(::arbitrary::Arbitrary<'nu_arb>),
33+
);
2534
Ok(quote!(
26-
impl ::arbitrary::Arbitrary<'_> for #type_name {
27-
fn arbitrary(u: &mut ::arbitrary::Unstructured<'_>) -> ::arbitrary::Result<Self> {
35+
impl #generics_with_bounds ::arbitrary::Arbitrary<'nu_arb> for #type_name #generics_without_bounds {
36+
fn arbitrary(u: &mut ::arbitrary::Unstructured<'nu_arb>) -> ::arbitrary::Result<Self> {
2837
let inner_value: #inner_type = u.arbitrary()?;
2938
Ok(#type_name::new(inner_value))
3039
}
31-
}
3240

33-
#[inline]
34-
fn size_hint(_depth: usize) -> (usize, Option<usize>) {
35-
let n = ::core::mem::size_of::<#inner_type>();
36-
(n, Some(n))
41+
#[inline]
42+
fn size_hint(_depth: usize) -> (usize, Option<usize>) {
43+
let n = ::core::mem::size_of::<#inner_type>();
44+
(n, Some(n))
45+
}
3746
}
3847
))
3948
}

nutype_macros/src/any/gen/traits/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,7 @@ fn gen_implemented_traits(
182182
AnyIrregularTrait::SerdeDeserialize => Ok(
183183
gen_impl_trait_serde_deserialize(type_name, generics, inner_type, maybe_error_type_name.as_ref())
184184
),
185-
AnyIrregularTrait::ArbitraryArbitrary => arbitrary::gen_impl_trait_arbitrary(type_name, inner_type, guard),
185+
AnyIrregularTrait::ArbitraryArbitrary => arbitrary::gen_impl_trait_arbitrary(type_name, generics, inner_type, guard),
186186
})
187187
.collect()
188188
}

nutype_macros/src/common/gen/mod.rs

+18-2
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ pub fn gen_impl_into_inner(
156156
///
157157
/// Output:
158158
/// <T, U>
159-
fn strip_trait_bounds_on_generics(original: &Generics) -> Generics {
159+
pub fn strip_trait_bounds_on_generics(original: &Generics) -> Generics {
160160
let mut generics = original.clone();
161161
for param in &mut generics.params {
162162
if let syn::GenericParam::Type(syn::TypeParam { bounds, .. }) = param {
@@ -174,7 +174,7 @@ fn strip_trait_bounds_on_generics(original: &Generics) -> Generics {
174174
///
175175
/// Output:
176176
/// <T: Serialize, U: Serialize>
177-
fn add_bound_to_all_type_params(generics: &Generics, bound: TokenStream) -> Generics {
177+
pub fn add_bound_to_all_type_params(generics: &Generics, bound: TokenStream) -> Generics {
178178
let mut generics = generics.clone();
179179
let parsed_bound: syn::TypeParamBound =
180180
syn::parse2(bound).expect("Failed to parse TypeParamBound");
@@ -186,6 +186,22 @@ fn add_bound_to_all_type_params(generics: &Generics, bound: TokenStream) -> Gene
186186
generics
187187
}
188188

189+
/// Add a parameter to generics.
190+
///
191+
/// Input:
192+
/// <T, U>
193+
/// 'a
194+
///
195+
/// Output:
196+
/// <'a, T, U>
197+
///
198+
pub fn add_param(generics: &Generics, param: TokenStream) -> Generics {
199+
let mut generics = generics.clone();
200+
let parsed_param: syn::GenericParam = syn::parse2(param).expect("Failed to parse GenericParam");
201+
generics.params.push(parsed_param);
202+
generics
203+
}
204+
189205
pub trait GenerateNewtype {
190206
type Sanitizer;
191207
type Validator;

nutype_macros/src/common/gen/traits.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ pub struct GeneratedTraits {
2020
pub implement_traits: TokenStream,
2121
}
2222

23-
/// Split traits into 2 groups for generatation:
23+
/// Split traits into 2 groups for generation:
2424
/// * Transparent traits can be simply derived, e.g. `derive(Debug)`.
2525
/// * Irregular traits requires implementation to be generated.
2626
pub enum GeneratableTrait<TransparentTrait, IrregularTrait> {

test_suite/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ num = "0.4.3"
2424
[features]
2525
serde = ["nutype/serde", "dep:serde", "dep:serde_json"]
2626
regex = ["nutype/regex", "dep:regex", "dep:lazy_static", "dep:once_cell"]
27+
arbitrary = ["nutype/arbitrary"]
2728
schemars08 = ["schemars"]
2829
new_unchecked = []
2930
ui = []

test_suite/tests/any.rs

+19-3
Original file line numberDiff line numberDiff line change
@@ -751,9 +751,25 @@ mod with_generics {
751751
}
752752
}
753753

754-
#[test]
755-
fn test_generic_boundaries_arbitrary() {
756-
// TODO
754+
mod generics_and_arbitrary {
755+
use super::*;
756+
use arbitrary::Arbitrary;
757+
758+
#[nutype(derive(Debug, Arbitrary))]
759+
struct Arbaro<T>(Vec<T>);
760+
761+
fn gen(bytes: &[u8]) -> Vec<bool> {
762+
let mut u = arbitrary::Unstructured::new(&bytes);
763+
let arbraro = Arbaro::<bool>::arbitrary(&mut u).unwrap();
764+
arbraro.into_inner()
765+
}
766+
767+
#[test]
768+
fn test_generic_boundaries_arbitrary() {
769+
assert_eq!(gen(&[]), Vec::<bool>::new());
770+
assert_eq!(gen(&[1]), vec![false]);
771+
assert_eq!(gen(&[1, 3, 5]), vec![true, false]);
772+
}
757773
}
758774

759775
#[test]

0 commit comments

Comments
 (0)