multihash-derive-impl-0.1.0/.cargo_vcs_info.json0000644000000001510000000000100152400ustar { "git": { "sha1": "16e68d8203c787768688dab8aed4295a32f33bf0" }, "path_in_vcs": "derive-impl" }multihash-derive-impl-0.1.0/Cargo.toml0000644000000020750000000000100132450ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2018" name = "multihash-derive-impl" version = "0.1.0" authors = ["David Craven "] description = "Internal proc-macro crate for the MultihashDigest derive" license = "MIT" repository = "https://github.com/multiformats/rust-multihash" resolver = "2" [lib] proc-macro = true [dependencies.proc-macro-crate] version = "1.3.1" [dependencies.proc-macro-error] version = "1.0.4" [dependencies.proc-macro2] version = "1.0.24" features = ["span-locations"] [dependencies.quote] version = "1.0.7" [dependencies.syn] version = "1.0.42" [dependencies.synstructure] version = "0.12.4" multihash-derive-impl-0.1.0/Cargo.toml.orig000064400000000000000000000007521046102023000167260ustar 00000000000000[package] name = "multihash-derive-impl" version = "0.1.0" authors = ["David Craven "] edition = "2018" description = "Internal proc-macro crate for the MultihashDigest derive" license = "MIT" repository = "https://github.com/multiformats/rust-multihash" [lib] proc-macro = true [dependencies] proc-macro2 = { version = "1.0.24", features = ["span-locations"] } proc-macro-crate = "1.3.1" proc-macro-error = "1.0.4" quote = "1.0.7" syn = "1.0.42" synstructure = "0.12.4" multihash-derive-impl-0.1.0/src/lib.rs000064400000000000000000000024141046102023000157370ustar 00000000000000//! This is an internal crate that implements the actual `MultihashDigest` derive. //! //! The `multihash-derive` crate acts as a facade and defines additional symbols that our derive depends on. //! For example, the actual trait that we are deriving `MultihashDigest`, as well as the `Hasher` trait and //! the `UnsupportedCode` error type. extern crate proc_macro; mod multihash; mod utils; use proc_macro::TokenStream; use proc_macro_error::proc_macro_error; use synstructure::macros::{parse, DeriveInput}; use synstructure::{MacroResult, Structure}; #[proc_macro_derive(Multihash, attributes(mh))] #[allow(non_snake_case)] #[proc_macro_error] #[deprecated(since = "0.8.1", note = "Use `MultihashDigest` derive instead.")] pub fn Multihash(i: TokenStream) -> TokenStream { match parse::(i) { Ok(p) => match Structure::try_new(&p) { Ok(s) => multihash::multihash(s).into_stream(), Err(e) => e.to_compile_error().into(), }, Err(e) => e.to_compile_error().into(), } } /// Custom derive for the `MultihashDigest` trait. #[proc_macro_derive(MultihashDigest, attributes(mh))] #[allow(non_snake_case)] #[proc_macro_error] pub fn MultihashDigest(i: TokenStream) -> TokenStream { #[allow(deprecated)] Multihash(i) } multihash-derive-impl-0.1.0/src/multihash.rs000064400000000000000000000172351046102023000171760ustar 00000000000000use std::collections::HashSet; use crate::utils; use proc_macro2::{Span, TokenStream}; use quote::quote; #[cfg(not(test))] use quote::ToTokens; use syn::parse::{Parse, ParseStream}; #[cfg(not(test))] use syn::spanned::Spanned; use synstructure::{Structure, VariantInfo}; mod kw { use syn::custom_keyword; custom_keyword!(code); custom_keyword!(hasher); custom_keyword!(mh); custom_keyword!(alloc_size); } /// Attributes for the enum items. #[derive(Debug)] #[allow(clippy::large_enum_variant)] enum MhAttr { Code(utils::Attr), Hasher(utils::Attr>), } impl Parse for MhAttr { fn parse(input: ParseStream) -> syn::Result { if input.peek(kw::code) { Ok(MhAttr::Code(input.parse()?)) } else if input.peek(kw::hasher) { Ok(MhAttr::Hasher(input.parse()?)) } else { Err(syn::Error::new(input.span(), "unknown attribute")) } } } /// Attributes of the top-level derive. #[derive(Debug)] enum DeriveAttr { AllocSize(utils::Attr), } impl Parse for DeriveAttr { fn parse(input: ParseStream) -> syn::Result { if input.peek(kw::alloc_size) { Ok(Self::AllocSize(input.parse()?)) } else { Err(syn::Error::new(input.span(), "unknown attribute")) } } } struct Params { code_enum: syn::Ident, } #[derive(Debug)] struct Hash { ident: syn::Ident, code: syn::Expr, hasher: Box, } impl Hash { fn code_into_u64(&self, params: &Params) -> TokenStream { let ident = &self.ident; let code_enum = ¶ms.code_enum; let code = &self.code; quote!(#code_enum::#ident => #code) } fn code_from_u64(&self) -> TokenStream { let ident = &self.ident; let code = &self.code; quote!(#code => Ok(Self::#ident)) } fn code_digest(&self) -> TokenStream { let ident = &self.ident; let hasher = &self.hasher; let code = &self.code; quote!(Self::#ident => { let mut hasher = #hasher::default(); hasher.update(input); Multihash::wrap(#code, hasher.finalize()).unwrap() }) } } impl<'a> From<&'a VariantInfo<'a>> for Hash { fn from(bi: &'a VariantInfo<'a>) -> Self { let mut code = None; let mut hasher = None; for attr in bi.ast().attrs { let attr: Result, _> = syn::parse2(attr.tokens.clone()); if let Ok(attr) = attr { for attr in attr.attrs { match attr { MhAttr::Code(attr) => code = Some(attr.value), MhAttr::Hasher(attr) => hasher = Some(attr.value), } } } } let ident = bi.ast().ident.clone(); let code = code.unwrap_or_else(|| { let msg = "Missing code attribute: e.g. #[mh(code = multihash::SHA3_256)]"; #[cfg(test)] panic!("{}", msg); #[cfg(not(test))] proc_macro_error::abort!(ident, msg); }); let hasher = hasher.unwrap_or_else(|| { let msg = "Missing hasher attribute: e.g. #[mh(hasher = multihash::Sha2_256)]"; #[cfg(test)] panic!("{}", msg); #[cfg(not(test))] proc_macro_error::abort!(ident, msg); }); Self { ident, code, hasher, } } } /// Parse top-level enum [#mh()] attributes. /// /// Returns the `alloc_size` and whether errors regarding to `alloc_size` should be reported or not. fn parse_code_enum_attrs(ast: &syn::DeriveInput) -> syn::LitInt { let mut alloc_size = None; for attr in &ast.attrs { let derive_attrs: Result, _> = syn::parse2(attr.tokens.clone()); if let Ok(derive_attrs) = derive_attrs { for derive_attr in derive_attrs.attrs { match derive_attr { DeriveAttr::AllocSize(alloc_size_attr) => { alloc_size = Some(alloc_size_attr.value) } } } } } match alloc_size { Some(alloc_size) => alloc_size, None => { let msg = "enum is missing `alloc_size` attribute: e.g. #[mh(alloc_size = 64)]"; #[cfg(test)] panic!("{}", msg); #[cfg(not(test))] proc_macro_error::abort!(&ast.ident, msg); } } } /// Return an error if the same code is used several times. /// /// This only checks for string equality, though this should still catch most errors caused by /// copy and pasting. fn error_code_duplicates(hashes: &[Hash]) { // Use a temporary store to determine whether a certain value is unique or not let mut uniq = HashSet::new(); hashes.iter().for_each(|hash| { let code = &hash.code; let msg = format!( "the #mh(code) attribute `{}` is defined multiple times", quote!(#code) ); // It's a duplicate if !uniq.insert(code) { #[cfg(test)] panic!("{}", msg); #[cfg(not(test))] { let already_defined = uniq.get(code).unwrap(); let line = already_defined.to_token_stream().span().start().line; proc_macro_error::emit_error!( &hash.code, msg; note = "previous definition of `{}` at line {}", quote!(#code), line; ); } } }); } /// An error that contains a span in order to produce nice error messages. #[derive(Debug)] struct ParseError(Span); pub fn multihash(s: Structure) -> TokenStream { let mh_crate = match utils::use_crate("multihash-derive") { Ok(ident) => ident, Err(e) => { let err = syn::Error::new(Span::call_site(), e).to_compile_error(); return quote!(#err); } }; let code_enum = &s.ast().ident; let alloc_size = parse_code_enum_attrs(s.ast()); let hashes: Vec<_> = s.variants().iter().map(Hash::from).collect(); error_code_duplicates(&hashes); let params = Params { code_enum: code_enum.clone(), }; let code_into_u64 = hashes.iter().map(|h| h.code_into_u64(¶ms)); let code_from_u64 = hashes.iter().map(|h| h.code_from_u64()); let code_digest = hashes.iter().map(|h| h.code_digest()); quote! { /// A Multihash with the same allocated size as the Multihashes produces by this derive. pub type Multihash = #mh_crate::Multihash<#alloc_size>; impl #mh_crate::MultihashDigest<#alloc_size> for #code_enum { fn digest(&self, input: &[u8]) -> Multihash { use #mh_crate::Hasher; match self { #(#code_digest,)* _ => unreachable!(), } } fn wrap(&self, digest: &[u8]) -> Result { Multihash::wrap((*self).into(), digest) } } impl From<#code_enum> for u64 { fn from(code: #code_enum) -> Self { match code { #(#code_into_u64,)* _ => unreachable!(), } } } impl core::convert::TryFrom for #code_enum { type Error = #mh_crate::UnsupportedCode; fn try_from(code: u64) -> Result { match code { #(#code_from_u64,)* _ => Err(#mh_crate::UnsupportedCode(code)) } } } } } multihash-derive-impl-0.1.0/src/utils.rs000064400000000000000000000023201046102023000163250ustar 00000000000000use proc_macro2::Span; use proc_macro_crate::{crate_name, FoundCrate}; use syn::parse::{Parse, ParseStream}; use syn::punctuated::Punctuated; use syn::Error; pub fn use_crate(name: &str) -> Result { match crate_name(name) { Ok(FoundCrate::Name(krate)) => Ok(syn::Ident::new(&krate, Span::call_site())), Ok(FoundCrate::Itself) => Ok(syn::Ident::new("crate", Span::call_site())), Err(err) => Err(Error::new(Span::call_site(), err)), } } #[derive(Debug)] pub struct Attrs { pub paren: syn::token::Paren, pub attrs: Punctuated, } impl Parse for Attrs { fn parse(input: ParseStream) -> syn::Result { let content; let paren = syn::parenthesized!(content in input); let attrs = content.parse_terminated(A::parse)?; Ok(Self { paren, attrs }) } } #[derive(Debug)] pub struct Attr { pub key: K, pub eq: syn::token::Eq, pub value: V, } impl Parse for Attr { fn parse(input: ParseStream) -> syn::Result { Ok(Self { key: input.parse()?, eq: input.parse()?, value: input.parse()?, }) } }