1#![allow(dead_code)]23use quote::quote;4use darling::FromMeta;5use inflector::cases;6use std::fmt::Write;7use syn::{FnArg, Generics, Ident, ImplItem, ImplItemMethod, ItemImpl, Meta, NestedMeta, PatType, Path, ReturnType, Type, spanned::Spanned};89use crate::{10 fn_selector_str, parse_ident_from_pat, parse_ident_from_path, parse_ident_from_type,11 parse_result_ok, pascal_ident_to_call, pascal_ident_to_snake_call, snake_ident_to_pascal,12 snake_ident_to_screaming,13};1415struct Is {16 name: Ident,17 pascal_call_name: Ident,18 snake_call_name: Ident,19}20impl Is {21 fn try_from(path: &Path) -> syn::Result<Self> {22 let name = parse_ident_from_path(path, false)?.clone();23 Ok(Self {24 pascal_call_name: pascal_ident_to_call(&name),25 snake_call_name: pascal_ident_to_snake_call(&name),26 name,27 })28 }2930 fn expand_call_def(&self) -> proc_macro2::TokenStream {31 let name = &self.name;32 let pascal_call_name = &self.pascal_call_name;33 quote! {34 #name(#pascal_call_name)35 }36 }3738 fn expand_interface_id(&self) -> proc_macro2::TokenStream {39 let pascal_call_name = &self.pascal_call_name;40 quote! {41 interface_id ^= #pascal_call_name::interface_id();42 }43 }4445 fn expand_supports_interface(&self) -> proc_macro2::TokenStream {46 let pascal_call_name = &self.pascal_call_name;47 quote! {48 #pascal_call_name::supports_interface(interface_id)49 }50 }5152 fn expand_variant_call(&self) -> proc_macro2::TokenStream {53 let name = &self.name;54 let pascal_call_name = &self.pascal_call_name;55 quote! {56 InternalCall::#name(call) => return <Self as ::evm_coder::Callable<#pascal_call_name>>::call(self, Msg {57 call,58 caller: c.caller,59 value: c.value,60 })61 }62 }6364 fn expand_parse(&self) -> proc_macro2::TokenStream {65 let name = &self.name;66 let pascal_call_name = &self.pascal_call_name;67 quote! {68 if let Some(parsed_call) = #pascal_call_name::parse(method_id, reader)? {69 return Ok(Some(Self::#name(parsed_call)))70 }71 }72 }73}7475#[derive(Default)]76struct IsList(Vec<Is>);77impl FromMeta for IsList {78 fn from_list(items: &[NestedMeta]) -> darling::Result<Self> {79 let mut out = Vec::new();80 for item in items {81 match item {82 NestedMeta::Meta(Meta::Path(path)) => out.push(Is::try_from(path)?),83 _ => return Err(syn::Error::new(item.span(), "expected path").into()),84 }85 }86 Ok(Self(out))87 }88}8990#[derive(FromMeta)]91pub struct InterfaceInfo {92 name: Ident,93 #[darling(default)]94 is: IsList,95 #[darling(default)]96 inline_is: IsList,97 #[darling(default)]98 events: IsList,99}100101#[derive(FromMeta)]102struct MethodInfo {103 #[darling(default)]104 rename_selector: Option<String>,105}106107struct MethodArg {108 name: Ident,109 ty: Ident,110}111impl MethodArg {112 fn try_from(value: &PatType) -> syn::Result<Self> {113 Ok(Self {114 name: parse_ident_from_pat(&value.pat)?.clone(),115 ty: parse_ident_from_type(&value.ty, false)?.clone(),116 })117 }118 fn is_value(&self) -> bool {119 self.ty == "value"120 }121 fn is_caller(&self) -> bool {122 self.ty == "caller"123 }124 fn is_special(&self) -> bool {125 self.is_value() || self.is_caller()126 }127 fn selector_ty(&self) -> &Ident {128 assert!(!self.is_special());129 &self.ty130 }131132 fn expand_call_def(&self) -> proc_macro2::TokenStream {133 assert!(!self.is_special());134 let name = &self.name;135 let ty = &self.ty;136137 quote! {138 #name: #ty139 }140 }141142 fn expand_parse(&self) -> proc_macro2::TokenStream {143 assert!(!self.is_special());144 let name = &self.name;145 quote! {146 #name: reader.abi_read()?147 }148 }149150 fn expand_call_arg(&self) -> proc_macro2::TokenStream {151 if self.is_value() {152 quote! {153 c.value.clone()154 }155 } else if self.is_caller() {156 quote! {157 c.caller.clone()158 }159 } else {160 let name = &self.name;161 quote! {162 #name163 }164 }165 }166}167168#[derive(PartialEq)]169enum Mutability {170 Mutable,171 View,172 Pure,173}174175struct Method {176 name: Ident,177 camel_name: String,178 pascal_name: Ident,179 screaming_name: Ident,180 selector_str: String,181 selector: u32,182 args: Vec<MethodArg>,183 has_normal_args: bool,184 mutability: Mutability,185 result: Type,186}187impl Method {188 fn try_from(value: &ImplItemMethod) -> syn::Result<Self> {189 let mut info = MethodInfo {190 rename_selector: None,191 };192 for attr in &value.attrs {193 let ident = parse_ident_from_path(&attr.path, false)?;194 if ident == "solidity" {195 let args = attr.parse_meta().unwrap();196 info = MethodInfo::from_meta(&args).unwrap();197 } else if ident == "doc" {198 199 }200 }201 let ident = &value.sig.ident;202 let ident_str = ident.to_string();203 if !cases::snakecase::is_snake_case(&ident_str) {204 return Err(syn::Error::new(ident.span(), "method name should be snake_cased\nif alternative solidity name needs to be set - use #[solidity] attribute"));205 }206207 let mut mutability = Mutability::Pure;208209 if let Some(FnArg::Receiver(receiver)) = value210 .sig211 .inputs212 .iter()213 .find(|arg| matches!(arg, FnArg::Receiver(_)))214 {215 if receiver.reference.is_none() {216 return Err(syn::Error::new(217 receiver.span(),218 "receiver should be by ref",219 ));220 }221 if receiver.mutability.is_some() {222 mutability = Mutability::Mutable;223 } else {224 mutability = Mutability::View;225 }226 }227 let mut args = Vec::new();228 for typ in value229 .sig230 .inputs231 .iter()232 .filter(|arg| matches!(arg, FnArg::Typed(_)))233 {234 let typ = match typ {235 FnArg::Typed(typ) => typ,236 _ => unreachable!(),237 };238 args.push(MethodArg::try_from(typ)?);239 }240241 if mutability != Mutability::Mutable && args.iter().any(|arg| arg.is_value()) {242 return Err(syn::Error::new(243 args.iter().find(|arg| arg.is_value()).unwrap().ty.span(),244 "payable function should be mutable",245 ));246 }247248 let result = match &value.sig.output {249 ReturnType::Type(_, ty) => ty,250 _ => return Err(syn::Error::new(value.sig.output.span(), "interface method should return Result<value>\nif there is no value to return - specify void (which is alias to unit)")),251 };252 let result = parse_result_ok(result)?;253254 let camel_name = info255 .rename_selector256 .unwrap_or_else(|| cases::camelcase::to_camel_case(&ident.to_string()));257 let mut selector_str = camel_name.clone();258 selector_str.push('(');259 let mut has_normal_args = false;260 for (i, arg) in args.iter().filter(|arg| !arg.is_special()).enumerate() {261 if i != 0 {262 selector_str.push(',');263 }264 write!(selector_str, "{}", arg.selector_ty()).unwrap();265 has_normal_args = true;266 }267 selector_str.push(')');268 let selector = fn_selector_str(&selector_str);269270 Ok(Self {271 name: ident.clone(),272 camel_name,273 pascal_name: snake_ident_to_pascal(ident),274 screaming_name: snake_ident_to_screaming(ident),275 selector_str,276 selector,277 args,278 has_normal_args,279 mutability,280 result: result.clone(),281 })282 }283 fn expand_call_def(&self) -> proc_macro2::TokenStream {284 let defs = self285 .args286 .iter()287 .filter(|a| !a.is_special())288 .map(|a| a.expand_call_def());289 let pascal_name = &self.pascal_name;290291 if self.has_normal_args {292 quote! {293 #pascal_name {294 #(295 #defs,296 )*297 }298 }299 } else {300 quote! {#pascal_name}301 }302 }303304 fn expand_const(&self) -> proc_macro2::TokenStream {305 let screaming_name = &self.screaming_name;306 let selector = self.selector;307 let selector_str = &self.selector_str;308 quote! {309 #[doc = #selector_str]310 const #screaming_name: u32 = #selector;311 }312 }313314 fn expand_interface_id(&self) -> proc_macro2::TokenStream {315 let screaming_name = &self.screaming_name;316 quote! {317 interface_id ^= Self::#screaming_name;318 }319 }320321 fn expand_parse(&self) -> proc_macro2::TokenStream {322 let pascal_name = &self.pascal_name;323 let screaming_name = &self.screaming_name;324 if self.has_normal_args {325 let parsers = self326 .args327 .iter()328 .filter(|a| !a.is_special())329 .map(|a| a.expand_parse());330 quote! {331 Self::#screaming_name => return Ok(Some(Self::#pascal_name {332 #(333 #parsers,334 )*335 }))336 }337 } else {338 quote! { Self::#screaming_name => return Ok(Some(Self::#pascal_name)) }339 }340 }341342 fn expand_variant_call(&self) -> proc_macro2::TokenStream {343 let pascal_name = &self.pascal_name;344 let name = &self.name;345346 let matcher = if self.has_normal_args {347 let names = self348 .args349 .iter()350 .filter(|a| !a.is_special())351 .map(|a| &a.name);352353 quote! {{354 #(355 #names,356 )*357 }}358 } else {359 quote! {}360 };361362 let receiver = match self.mutability {363 Mutability::Mutable | Mutability::View => quote! {self.},364 Mutability::Pure => quote! {Self::},365 };366 let args = self.args.iter().map(|a| a.expand_call_arg());367368 quote! {369 InternalCall::#pascal_name #matcher => {370 let result = #receiver #name(371 #(372 #args,373 )*374 )?;375 (&result).abi_write(&mut writer);376 }377 }378 }379}380381pub struct SolidityInterface {382 generics: Generics,383 name: Box<syn::Type>,384 info: InterfaceInfo,385 methods: Vec<Method>,386}387impl SolidityInterface {388 pub fn try_from(info: InterfaceInfo, value: &ItemImpl) -> syn::Result<Self> {389 let mut methods = Vec::new();390391 for item in &value.items {392 if let ImplItem::Method(method) = item {393 methods.push(Method::try_from(method)?)394 }395 }396 Ok(Self {397 generics: value.generics.clone(),398 name: value.self_ty.clone(),399 info,400 methods,401 })402 }403 pub fn expand(self) -> proc_macro2::TokenStream {404 let name = self.name;405406 let call_name = pascal_ident_to_call(&self.info.name);407 let generics = self.generics;408409 let call_sub = self410 .info411 .inline_is412 .0413 .iter()414 .chain(self.info.is.0.iter())415 .map(Is::expand_call_def);416 let call_parse = self417 .info418 .inline_is419 .0420 .iter()421 .chain(self.info.is.0.iter())422 .map(Is::expand_parse);423 let call_variants = self424 .info425 .inline_is426 .0427 .iter()428 .chain(self.info.is.0.iter())429 .map(Is::expand_variant_call);430431 let inline_interface_id = self.info.inline_is.0.iter().map(Is::expand_interface_id);432 let supports_interface = self.info.is.0.iter().map(Is::expand_supports_interface);433434 let calls = self.methods.iter().map(Method::expand_call_def);435 let consts = self.methods.iter().map(Method::expand_const);436 let interface_id = self.methods.iter().map(Method::expand_interface_id);437 let parsers = self.methods.iter().map(Method::expand_parse);438 let call_variants_this = self.methods.iter().map(Method::expand_variant_call);439440 441442 quote! {443 #[derive(Debug)]444 pub enum #call_name {445 #(446 #calls,447 )*448 #(449 #call_sub,450 )*451 }452 impl #call_name {453 #(454 #consts455 )*456 pub const fn interface_id() -> u32 {457 let mut interface_id = 0;458 #(#interface_id)*459 #(#inline_interface_id)*460 interface_id461 }462 pub fn supports_interface(interface_id: u32) -> bool {463 interface_id != 0xffffff && (464 interface_id == Self::interface_id()465 #(466 || #supports_interface467 )*468 )469 }470 }471 impl ::evm_coder::Call for #call_name {472 fn parse(method_id: u32, reader: &mut ::evm_coder::abi::AbiReader) -> ::evm_coder::execution::Result<Option<Self>> {473 use ::evm_coder::abi::AbiRead;474 match method_id {475 #(476 #parsers,477 )*478 _ => {},479 }480 #(481 #call_parse482 )else*483 return Ok(None);484 }485 }486 impl #generics ::evm_coder::Callable<#call_name> for #name {487 #[allow(unreachable_code)] 488 fn call(&mut self, c: Msg<#call_name>) -> Result<::evm_coder::abi::AbiWriter> {489 use ::evm_coder::abi::AbiWrite;490 type InternalCall = #call_name;491 match c.call {492 #(493 #call_variants,494 )*495 _ => {},496 }497 let mut writer = ::evm_coder::abi::AbiWriter::default();498 match c.call {499 #(500 #call_variants_this,501 )*502 _ => unreachable!()503 }504 Ok(writer)505 }506 }507 }508 }509}