1 // vim: tw=80 2 use proc_macro2::Span; 3 use quote::{ToTokens, format_ident, quote}; 4 use std::{ 5 collections::hash_map::DefaultHasher, 6 hash::{Hash, Hasher} 7 }; 8 use syn::{ 9 *, 10 spanned::Spanned 11 }; 12 13 use crate::{ 14 AttrFormatter, 15 mock_function::{self, MockFunction}, 16 compile_error 17 }; 18 19 pub(crate) struct MockTrait { 20 pub attrs: Vec<Attribute>, 21 pub consts: Vec<ImplItemConst>, 22 pub generics: Generics, 23 pub methods: Vec<MockFunction>, 24 /// Internally-used name of the trait used. 25 pub ss_name: Ident, 26 /// Fully-qualified name of the trait 27 pub trait_path: Path, 28 /// Path on which the trait is implemented. Usually will be the same as 29 /// structname, but might include concrete generic parameters. 30 self_path: PathSegment, 31 pub types: Vec<ImplItemType>, 32 pub unsafety: Option<Token![unsafe]> 33 } 34 35 impl MockTrait { ss_name_priv(trait_path: &Path) -> Ident36 fn ss_name_priv(trait_path: &Path) -> Ident { 37 let path_args = &trait_path.segments.last().unwrap().arguments; 38 if path_args.is_empty() { 39 // Skip the hashing step for easie debugging of generated code 40 format_ident!("{}", trait_path.segments.last().unwrap().ident) 41 } else { 42 // Hash the path args to permit mocking structs that implement 43 // multiple traits distinguished only by their path args 44 let mut hasher = DefaultHasher::new(); 45 path_args.hash(&mut hasher); 46 format_ident!("{}_{}", trait_path.segments.last().unwrap().ident, 47 hasher.finish()) 48 } 49 } 50 ss_name(&self) -> &Ident51 pub fn ss_name(&self) -> &Ident { 52 &self.ss_name 53 } 54 55 /// Create a new MockTrait 56 /// 57 /// # Arguments 58 /// * `structname` - name of the struct that implements this trait 59 /// * `struct_generics` - Generics of the parent structure 60 /// * `impl_` - Mockable ItemImpl for a trait 61 /// * `vis` - Visibility of the struct new(structname: &Ident, struct_generics: &Generics, impl_: ItemImpl, vis: &Visibility) -> Self62 pub fn new(structname: &Ident, 63 struct_generics: &Generics, 64 impl_: ItemImpl, 65 vis: &Visibility) -> Self 66 { 67 let mut consts = Vec::new(); 68 let mut methods = Vec::new(); 69 let mut types = Vec::new(); 70 let trait_path = if let Some((_, path, _)) = impl_.trait_ { 71 path 72 } else { 73 compile_error(impl_.span(), "impl block must implement a trait"); 74 Path::from(format_ident!("__mockall_invalid")) 75 }; 76 let ss_name = MockTrait::ss_name_priv(&trait_path); 77 let self_path = match *impl_.self_ty { 78 Type::Path(mut type_path) => 79 type_path.path.segments.pop().unwrap().into_value(), 80 x => { 81 compile_error(x.span(), 82 "mockall_derive only supports mocking traits and structs"); 83 PathSegment::from(Ident::new("", Span::call_site())) 84 } 85 }; 86 87 for ii in impl_.items.into_iter() { 88 match ii { 89 ImplItem::Const(iic) => { 90 consts.push(iic); 91 }, 92 ImplItem::Fn(iif) => { 93 let mf = mock_function::Builder::new(&iif.sig, vis) 94 .attrs(&iif.attrs) 95 .levels(2) 96 .call_levels(0) 97 .struct_(structname) 98 .struct_generics(struct_generics) 99 .trait_(&ss_name) 100 .build(); 101 methods.push(mf); 102 }, 103 ImplItem::Type(iit) => { 104 types.push(iit); 105 }, 106 _ => { 107 compile_error(ii.span(), 108 "This impl item is not yet supported by MockAll"); 109 } 110 } 111 } 112 MockTrait { 113 attrs: impl_.attrs, 114 consts, 115 generics: impl_.generics, 116 methods, 117 ss_name, 118 trait_path, 119 self_path, 120 types, 121 unsafety: impl_.unsafety 122 } 123 } 124 125 /// Generate code for the trait implementation on the mock struct 126 /// 127 /// # Arguments 128 /// 129 /// * `modname`: Name of the parent struct's private module 130 // Supplying modname is an unfortunately hack. Ideally MockTrait 131 // wouldn't need to know that. trait_impl(&self, modname: &Ident) -> impl ToTokens132 pub fn trait_impl(&self, modname: &Ident) -> impl ToTokens { 133 let trait_impl_attrs = &self.attrs; 134 let impl_attrs = AttrFormatter::new(&self.attrs) 135 .async_trait(false) 136 .doc(false) 137 .format(); 138 let (ig, _tg, wc) = self.generics.split_for_impl(); 139 let consts = &self.consts; 140 let path_args = &self.self_path.arguments; 141 let calls = self.methods.iter() 142 .map(|meth| meth.call(Some(modname))) 143 .collect::<Vec<_>>(); 144 let contexts = self.methods.iter() 145 .filter(|meth| meth.is_static()) 146 .map(|meth| meth.context_fn(Some(modname))) 147 .collect::<Vec<_>>(); 148 let expects = self.methods.iter() 149 .filter(|meth| !meth.is_static()) 150 .map(|meth| { 151 if meth.is_method_generic() { 152 // Specific impls with generic methods are TODO. 153 meth.expect(modname, None) 154 } else { 155 meth.expect(modname, Some(path_args)) 156 } 157 }).collect::<Vec<_>>(); 158 let trait_path = &self.trait_path; 159 let self_path = &self.self_path; 160 let types = &self.types; 161 let unsafety = &self.unsafety; 162 quote!( 163 #(#trait_impl_attrs)* 164 #unsafety impl #ig #trait_path for #self_path #wc { 165 #(#consts)* 166 #(#types)* 167 #(#calls)* 168 } 169 #(#impl_attrs)* 170 impl #ig #self_path #wc { 171 #(#expects)* 172 #(#contexts)* 173 } 174 ) 175 } 176 } 177