Skip to content

Commit 847c77d

Browse files
committed
mocked_with consistent for enums; made mocked_with consistent for enums with added option to add it enum-whole
1 parent eed2d09 commit 847c77d

File tree

5 files changed

+161
-40
lines changed

5 files changed

+161
-40
lines changed

src/extract.rs

Lines changed: 135 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
use crate::branch::Traitified;
22
use proc_macro::TokenStream;
3-
use proc_macro2::Span;
3+
use proc_macro2::{extra, Span};
44
use quote::quote;
55
use std::fmt::Write;
66
use std::iter::FilterMap;
77
use syn::{
8-
punctuated::Punctuated, AngleBracketedGenericArguments, GenericParam, Generics, Ident, Item,
9-
ItemEnum, ItemStruct, Meta, Path, PathArguments, TraitBoundModifier, Type, TypeParam,
10-
TypeParamBound, TypePath,
8+
punctuated::Punctuated, AngleBracketedGenericArguments, Expr, Fields, GenericParam, Generics,
9+
Ident, Item, ItemEnum, ItemStruct, Meta, Path, PathArguments, TraitBoundModifier, Type,
10+
TypeParam, TypeParamBound, TypePath,
1111
};
1212

1313
pub struct MockPrepared {
@@ -174,44 +174,78 @@ pub fn parse_fields_and_generate_for_values(schtruct: &mut ItemStruct) -> TokenS
174174
}
175175

176176
#[allow(clippy::option_if_let_else)]
177-
pub fn parse_fields_and_generate_variant(enoom: &ItemEnum) -> TokenStream {
177+
pub fn parse_fields_and_generate_variant(enoom: &mut ItemEnum) -> TokenStream {
178178
let enum_name = enoom.ident.clone();
179+
let extracted = Extracted::with_ident(enum_name.clone());
180+
let mocked = prepare_mock_name(&extracted);
179181

180-
let tok = if let Some(variant) = enoom.variants.iter().find(|field| {
182+
let mocked_name = &mocked.name;
183+
let tok = if let Some(variant) = enoom.variants.iter_mut().find(|field| {
181184
field
182185
.attrs
183186
.iter()
184-
.any(|attr| attr.meta.path().is_ident("mocked_value"))
185-
}) {
187+
.any(|attr| {
188+
if let Meta::NameValue(nv) = &attr.meta && nv.path.is_ident("mocked_with") {
189+
true
190+
} else {
191+
false
192+
}
193+
})
194+
}) {
195+
let variant_name = &mut variant.ident;
196+
if let Meta::NameValue(ref mut ml) = variant.attrs.first_mut().unwrap().meta {
197+
let mut value = &mut ml.value;
198+
replace_with_mocked(&mut value, &enum_name, &mocked);
199+
quote! {
200+
impl #enum_name {
201+
pub fn mock_new() -> #mocked_name {
202+
#value
203+
}
204+
}
205+
}
206+
} else {
207+
quote! {
208+
impl #enum_name {
209+
pub fn mock_new() -> #mocked_name {
210+
Self::#variant_name
211+
}
212+
}
213+
}
214+
}
215+
}
216+
217+
else if let (Some(variant), Some(field)) = enoom.variants.iter().fold((None, None), |(picked_value, picked_field), variant| {
218+
variant.fields.iter().fold((picked_value, picked_field), |(picked_value, picked_field), field| {
219+
field.attrs.iter().fold((picked_value, picked_field), |(picked_value, field), attr| {
220+
if let Meta::NameValue(nv) = &attr.meta && nv.path.is_ident("mocked_with") {
221+
(Some(variant), Some(nv.value.clone()))
222+
} else {
223+
(None, None)
224+
}
225+
})
226+
})
227+
})
228+
{
186229
let variant_name = &variant.ident;
187230
quote! {
188231
impl #enum_name {
189-
pub fn mock_new() -> Self {
190-
Self::#variant_name
232+
pub fn mock_new() -> #mocked_name {
233+
Self::#variant_name(#field)
191234
}
192235
}
193236
}
194-
} else if let Some(variant) = enoom.variants.iter().find(|field| {
195-
field
196-
.attrs
197-
.iter()
198-
.any(|attr| attr.meta.path().is_ident("mocked_value_with"))
199-
}) {
200-
let variant_name = &variant.ident;
201-
let value = if let Meta::List(ref ml) = variant.attrs.first().unwrap().meta {
202-
&ml.tokens
203-
} else {
204-
unreachable!()
205-
};
206-
237+
} else if let Some(mocked_value) = get_mocked_value_from_attributes(enoom, &mocked) {
207238
quote! {
208239
impl #enum_name {
209-
pub fn mock_new() -> Self {
210-
Self::#variant_name(#value)
240+
pub fn mock_new() -> #mocked_name {
241+
#mocked_value
211242
}
212243
}
213244
}
214-
} else {
245+
}
246+
247+
248+
else {
215249
quote! {
216250
impl #enum_name {
217251
pub fn mock_new() -> Self {
@@ -235,6 +269,26 @@ pub fn extract_generics_from_bounds(bounds: &mut Generics) {
235269
});
236270
}
237271

272+
pub fn get_mocked_value_from_attributes(enoom: &mut ItemEnum, mocked: &Extracted) -> Option<Expr> {
273+
let name = &enoom.ident;
274+
enoom.attrs.iter_mut().find_map(|attr| {
275+
match &mut attr.meta {
276+
Meta::NameValue(ref mut nv) if let Some(_) = nv.path.get_ident().map(|x| *x == "mocked_with") => {
277+
replace_with_mocked(&mut nv.value, name, mocked);
278+
let path = &nv.value;
279+
Some(syn::parse(TokenStream::from(quote!(<#path>))).unwrap())
280+
},
281+
Meta::NameValue(nv) if let Some(_) = nv.path.get_ident().map(|x| *x == "mocked_with_default") => {
282+
Some(syn::parse(TokenStream::from(quote!(<#name>::default()))).unwrap())
283+
},
284+
285+
_ => {
286+
None
287+
},
288+
}
289+
})
290+
}
291+
238292
pub fn clean_out_attributes(item: &mut Item) {
239293
match item {
240294
Item::Struct(s) => {
@@ -243,7 +297,7 @@ pub fn clean_out_attributes(item: &mut Item) {
243297
.attrs
244298
.extract_if(|attr| {
245299
if let Meta::Path(ref p) = attr.meta {
246-
p.get_ident().map(|x| x.to_string() == "mocked").is_some()
300+
p.get_ident().map(|x| *x == "mocked").is_some()
247301
} else {
248302
false
249303
}
@@ -252,19 +306,67 @@ pub fn clean_out_attributes(item: &mut Item) {
252306
});
253307
}
254308
Item::Enum(e) => {
309+
e.attrs.retain(|attr| {
310+
if let Meta::NameValue(p) = &attr.meta && p.path.get_ident().map(|x| *x == "mocked_with").is_some() {
311+
false
312+
} else {
313+
true
314+
}
315+
});
316+
255317
e.variants.iter_mut().for_each(|field| {
256318
field.attrs = field
257319
.attrs
258-
.extract_if(|attr| {
259-
if let Meta::Path(ref p) = attr.meta {
260-
p.get_ident().map(|x| x.to_string() == "mocked").is_some()
320+
.iter()
321+
.filter_map(|attr| {
322+
if let Meta::NameValue(ref p) = attr.meta && p.path.get_ident().map(|x| *x == "mocked_with").is_some() {
323+
None
261324
} else {
262-
false
325+
Some(attr.clone())
263326
}
264-
})
265-
.collect();
327+
}).collect();
328+
329+
field.fields.iter_mut().for_each(|field| {
330+
field.attrs = field.attrs.iter().filter_map(|attr| {
331+
if let Meta::NameValue(ref p ) = attr.meta && p.path.get_ident().map(|x| *x == "mocked_with").is_some() {
332+
None
333+
} else {
334+
Some(attr.clone())
335+
}
336+
}).collect();
337+
338+
});
339+
266340
});
267341
}
268342
_ => unreachable!(),
269343
}
270344
}
345+
346+
fn replace_with_mocked(expr: &mut Expr, name: &Ident, mocked: &Extracted) {
347+
match expr {
348+
Expr::Path(ref mut path) => {
349+
let mocked_segment = path
350+
.path
351+
.segments
352+
.iter_mut()
353+
.find(|path| path.ident == *name)
354+
.unwrap();
355+
mocked_segment.ident = mocked.name.clone();
356+
}
357+
358+
Expr::Call(call) => {
359+
if let Expr::Path(ref mut p) = &mut call.func.as_mut() {
360+
let mocked_segment = p
361+
.path
362+
.segments
363+
.iter_mut()
364+
.find(|path| path.ident == *name)
365+
.unwrap();
366+
mocked_segment.ident = mocked.name.clone();
367+
}
368+
}
369+
370+
_ => todo!("other exprs matter"),
371+
}
372+
}

src/lib.rs

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -130,20 +130,18 @@ pub fn mock(tokens: TokenStream, input: TokenStream) -> TokenStream {
130130
Item::Struct(ref mut s) => {
131131
let name = format!("{}Mock", s.ident);
132132
s.ident = parse_str(name.as_str()).unwrap();
133-
let f = extract::parse_fields_and_generate_for_values(s);
134-
extract::clean_out_attributes(&mut tokens);
135-
f
133+
extract::parse_fields_and_generate_for_values(s)
136134
}
137135
Item::Enum(ref mut e) => {
136+
let f = extract::parse_fields_and_generate_variant(e);
138137
let name = format!("{}Mock", e.ident);
139138
e.ident = parse_str(name.as_str()).unwrap();
140-
let f = extract::parse_fields_and_generate_variant(e);
141-
extract::clean_out_attributes(&mut tokens);
142139
f
143140
}
144141
_ => todo!(),
145142
};
146143

144+
extract::clean_out_attributes(&mut tokens);
147145
extract::clean_out_attributes(&mut mock);
148146
let mut original_and_mocked_struct = TokenStream::from(quote! {
149147
#tokens

src/toffel.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ impl Toffelise for ItemStruct {
5757
.collect();
5858

5959
TokenStream::from(quote! {
60+
#(#attributes)*
6061
struct #struct_name #generics(
6162
#(#fields),*
6263
);
@@ -72,6 +73,7 @@ impl Toffelise for ItemEnum {
7273
fn replace_mocks(self) -> TokenStream {
7374
let enum_name = self.ident;
7475
let generics = self.generics;
76+
let attributes = self.attrs;
7577

7678
let variants = self
7779
.variants
@@ -96,6 +98,7 @@ impl Toffelise for ItemEnum {
9698
});
9799

98100
TokenStream::from(quote! {
101+
#(#attributes)*
99102
enum #enum_name #generics {
100103
#(#variants),*
101104
}

tests/basic_mock.rs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,18 @@
11
use assert_proc::assert_proc;
22
use mocktoffel::mock;
33

4+
#[assert_proc]
5+
#[assert_duplicated = FooMock]
6+
#[mock]
7+
#[derive(Debug, Default)]
8+
struct Foo {
9+
foo: String,
10+
}
11+
412
fn main() {}
513

6-
// #[assert_proc]
7-
// #[assert_duplicated = BarMock]
14+
#[assert_proc]
15+
#[assert_duplicated = BarMock]
816
#[mock]
917
#[derive(Default)]
1018
enum Bar {

tests/basic_toffel.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,13 @@ struct Foo {
1515
#[mocked]
1616
foo: Bar,
1717
}
18+
19+
// #[assert_proc]
20+
#[toffel]
21+
enum Baz {
22+
Never(#[mocked] Bar),
23+
Gonna,
24+
Let,
25+
You,
26+
Down,
27+
}

0 commit comments

Comments
 (0)