Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: SolEnum and SolInterface #153

Merged
merged 22 commits into from
Jun 30, 2023
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 73 additions & 14 deletions crates/sol-macro/src/expand/contract.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//! [`ItemContract`] expansion.

use super::{attr, r#type, ExpCtxt};
use super::{attr, ty, ExpCtxt};
use crate::utils::ExprArray;
use ast::{Item, ItemContract, ItemError, ItemFunction, SolIdent};
use heck::ToSnakeCase;
Expand Down Expand Up @@ -28,12 +28,15 @@ pub(super) fn expand(cx: &ExpCtxt<'_>, contract: &ItemContract) -> Result<TokenS

let mut functions = Vec::with_capacity(contract.body.len());
let mut errors = Vec::with_capacity(contract.body.len());
// let mut events = Vec::with_capacity(contract.body.len());

let mut item_tokens = TokenStream::new();
let d_attrs: Vec<Attribute> = attr::derives(attrs).cloned().collect();
for item in body {
match item {
Item::Function(function) => functions.push(function),
Item::Error(error) => errors.push(error),
// Item::Event(event) => events.push(event),
_ => {}
}
if !d_attrs.is_empty() {
Expand All @@ -56,13 +59,15 @@ pub(super) fn expand(cx: &ExpCtxt<'_>, contract: &ItemContract) -> Result<TokenS
CallLikeExpander::from_errors(cx, name, errors).expand(&attrs)
});

// TODO: events enum
// let events_enum = (events.len() > 1).then(|| {
// let mut attrs = d_attrs;
// let doc_str = format!("Container for all the `{name}` events.");
// attrs.push(parse_quote!(#[doc = #doc_str]));
// CallLikeExpander::from_events(cx, name, events).expand(&attrs)
// });
// TODO
/*
let events_enum = (events.len() > 1).then(|| {
let mut attrs = d_attrs;
let doc_str = format!("Container for all the `{name}` events.");
attrs.push(parse_quote!(#[doc = #doc_str]));
CallLikeExpander::from_events(cx, name, events).expand(&attrs)
});
*/

let mod_attrs = attr::docs(attrs);
let tokens = quote! {
Expand All @@ -77,6 +82,25 @@ pub(super) fn expand(cx: &ExpCtxt<'_>, contract: &ItemContract) -> Result<TokenS
Ok(tokens)
}

/// Expands a `SolInterface` enum:
///
/// ```ignore,pseudo-code
/// #name = #{contract_name}Calls | #{contract_name}Errors /* | #{contract_name}Events */;
///
/// pub enum #name {
/// #(#variants(#types),)*
/// }
///
/// impl SolInterface for #name {
/// ...
/// }
///
/// impl #name {
/// #(
/// pub fn #is_variant,#as_variant,#as_variant_mut(...) -> ... { ... }
/// )*
/// }
/// ```
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is a good refactor 😍

struct CallLikeExpander {
name: Ident,
variants: Vec<Ident>,
Expand All @@ -93,9 +117,11 @@ enum CallLikeExpanderData {
Error {
selectors: Vec<ExprArray<u8, 4>>,
},
// Event {
// selectors: Vec<ExprArray<u8, 32>>,
// },
/*
Event {
selectors: Vec<ExprArray<u8, 32>>,
},
*/
}

impl CallLikeExpander {
Expand All @@ -119,7 +145,7 @@ impl CallLikeExpander {
variants,
min_data_len: functions
.iter()
.map(|function| r#type::params_base_data_size(cx, &function.arguments))
.map(|function| ty::params_base_data_size(cx, &function.arguments))
.min()
.unwrap(),
trait_: Ident::new("SolCall", Span::call_site()),
Expand All @@ -136,14 +162,33 @@ impl CallLikeExpander {
variants: errors.iter().map(|error| error.name.0.clone()).collect(),
min_data_len: errors
.iter()
.map(|error| r#type::params_base_data_size(cx, &error.parameters))
.map(|error| ty::params_base_data_size(cx, &error.parameters))
.min()
.unwrap(),
trait_: Ident::new("SolError", Span::call_site()),
data: CallLikeExpanderData::Error { selectors },
}
}

/*
fn from_events(cx: &ExpCtxt<'_>, contract_name: &SolIdent, events: Vec<&ItemEvent>) -> Self {
let mut selectors: Vec<_> = events.iter().map(|e| cx.event_selector(e)).collect();
selectors.sort_unstable_by_key(|a| a.array);

Self {
name: format_ident!("{contract_name}Events"),
variants: events.iter().map(|event| event.name.0.clone()).collect(),
min_data_len: events
.iter()
.map(|event| ty::params_base_data_size(cx, &event.params()))
.min()
.unwrap(),
trait_: Ident::new("SolEvent", Span::call_site()),
data: CallLikeExpanderData::Event { selectors },
}
}
*/

fn expand(self, attrs: &[Attribute]) -> TokenStream {
let Self {
name,
Expand All @@ -155,10 +200,12 @@ impl CallLikeExpander {
let types = match &data {
CallLikeExpanderData::Function { types, .. } => types,
CallLikeExpanderData::Error { .. } => &variants,
// CallLikeExpanderData::Event { .. } => unreachable!(),
};
let selectors = match &data {
CallLikeExpanderData::Function { selectors, .. }
| CallLikeExpanderData::Error { selectors } => selectors,
// CallLikeExpanderData::Event { .. } => unreachable!(),
};

assert_eq!(variants.len(), types.len());
Expand All @@ -172,7 +219,7 @@ impl CallLikeExpander {
}

#[automatically_derived]
impl ::alloy_sol_types::SolCalls for #name {
impl ::alloy_sol_types::SolInterface for #name {
const NAME: &'static str = #name_s;
const MIN_DATA_LENGTH: usize = #min_data_len;
const COUNT: usize = #count;
Expand Down Expand Up @@ -270,6 +317,18 @@ impl CallLikeExpander {
}
}
}

/*
fn expand_event(self, attrs: &[Attribute]) -> TokenStream {
let Self {
name,
variants,
min_data_len,
trait_,
data,
} = self;
}
*/
}

fn generate_variant_methods((variant, ty): (&Ident, &Ident)) -> TokenStream {
Expand Down
2 changes: 1 addition & 1 deletion crates/sol-macro/src/expand/error.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//! [`ItemError`] expansion.

use super::{expand_fields, expand_from_into_tuples, r#type::expand_tokenize_func, ExpCtxt};
use super::{expand_fields, expand_from_into_tuples, ty::expand_tokenize_func, ExpCtxt};
use ast::ItemError;
use proc_macro2::TokenStream;
use quote::quote;
Expand Down
2 changes: 1 addition & 1 deletion crates/sol-macro/src/expand/event.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//! [`ItemEvent`] expansion.

use super::{anon_name, expand_tuple_types, expand_type, ExpCtxt};
use crate::expand::r#type::expand_event_tokenize_func;
use crate::expand::ty::expand_event_tokenize_func;
use ast::{EventParameter, ItemEvent, SolIdent};
use proc_macro2::TokenStream;
use quote::{quote, quote_spanned};
Expand Down
2 changes: 1 addition & 1 deletion crates/sol-macro/src/expand/function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

use super::{
expand_fields, expand_from_into_tuples, expand_from_into_unit, expand_tuple_types,
r#type::expand_tokenize_func, ExpCtxt,
ty::expand_tokenize_func, ExpCtxt,
};
use ast::ItemFunction;
use proc_macro2::TokenStream;
Expand Down
6 changes: 3 additions & 3 deletions crates/sol-macro/src/expand/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ use syn::{Error, Result};

mod attr;

mod r#type;
pub use r#type::expand_type;
mod ty;
pub use ty::expand_type;

mod contract;
mod r#enum;
Expand Down Expand Up @@ -266,7 +266,7 @@ impl<'ast> ExpCtxt<'ast> {
if !first {
name.push(',');
}
write!(name, "{}", r#type::TypePrinter::new(self, &param.ty)).unwrap();
write!(name, "{}", ty::TypePrinter::new(self, &param.ty)).unwrap();
first = false;
}
name.push(')');
Expand Down
2 changes: 1 addition & 1 deletion crates/sol-macro/src/expand/struct.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//! [`ItemStruct`] expansion.

use super::{
expand_fields, expand_from_into_tuples, expand_type, r#type::expand_tokenize_func, ExpCtxt,
expand_fields, expand_from_into_tuples, expand_type, ty::expand_tokenize_func, ExpCtxt,
};
use ast::{ItemStruct, VariableDeclaration};
use proc_macro2::TokenStream;
Expand Down
4 changes: 2 additions & 2 deletions crates/sol-types/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -182,8 +182,8 @@ mod impl_core;

mod types;
pub use types::{
data_type as sol_data, ContractError, Encodable, EventTopic, Panic, PanicKind, Revert, SolCall,
SolCalls, SolEnum, SolError, SolEvent, SolStruct, SolType, TopicList,
data_type as sol_data, ContractError, Encodable, EventTopic, Panic, PanicKind, Revert,
Selectors, SolCall, SolEnum, SolError, SolEvent, SolInterface, SolStruct, SolType, TopicList,
};

mod util;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use core::{fmt, iter::FusedIterator, marker::PhantomData};
/// We do not recommend implementing this trait directly. Instead, we recommend
/// using the [`sol`][crate::sol] proc macro to parse a Solidity contract
/// definition.
pub trait SolCalls: Sized {
pub trait SolInterface: Sized {
/// The name of this type.
const NAME: &'static str;

Expand All @@ -33,6 +33,9 @@ pub trait SolCalls: Sized {

/// The selector of this type at the given index, used in
/// [`selectors`](Self::selectors).
///
/// This **must** return `None` if `i >= Self::COUNT`, and `Some` with a
/// different selector otherwise.
fn selector_at(i: usize) -> Option<[u8; 4]>;

/// Checks if the given selector is known to this type.
Expand Down Expand Up @@ -87,7 +90,7 @@ pub enum ContractError<T> {
Panic(Panic),
}

impl<T: SolCalls> SolCalls for ContractError<T> {
impl<T: SolInterface> SolInterface for ContractError<T> {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this allows accidentally mixing calls and errors, yeah?

ContractError<MyContractCalls> is valid, as is ContractError<MyContractErrors>

is there a way to fix this in the type system?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would either require a generic or assoc type which implements Sol*, but idk.
I'd also like to not have different traits for the event enum with something like type Selector = [u8; _] but not sure if doable.
I'd leave this for later since it's not that big of a deal.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we have a trait to capture this somehow?

trait SelectorPrefixed {}
trait SolError: SelectorPrefixed {}

that sort of thing?

const NAME: &'static str = "ContractError";

// revert is 64, panic is 32
Expand Down Expand Up @@ -157,7 +160,7 @@ impl<T: SolCalls> SolCalls for ContractError<T> {
}
}

impl<T: SolCalls> From<T> for ContractError<T> {
impl<T: SolInterface> From<T> for ContractError<T> {
#[inline]
fn from(value: T) -> Self {
Self::CustomError(value)
Expand Down Expand Up @@ -282,12 +285,12 @@ impl<T> ContractError<T> {
}
}

/// Iterator over the function or error selectors of a [`SolCalls`] type.
/// Iterator over the function or error selectors of a [`SolInterface`] type.
///
/// This `struct` is created by the [`selectors`] method on [`SolCalls`].
/// This `struct` is created by the [`selectors`] method on [`SolInterface`].
/// See its documentation for more.
///
/// [`selectors`]: SolCalls::selectors
/// [`selectors`]: SolInterface::selectors
pub struct Selectors<T> {
index: usize,
_marker: PhantomData<T>,
Expand All @@ -311,15 +314,16 @@ impl<T> fmt::Debug for Selectors<T> {
}

impl<T> Selectors<T> {
fn new() -> Self {
#[inline]
const fn new() -> Self {
Self {
index: 0,
_marker: PhantomData,
}
}
}

impl<T: SolCalls> Iterator for Selectors<T> {
impl<T: SolInterface> Iterator for Selectors<T> {
type Item = [u8; 4];

#[inline]
Expand All @@ -331,19 +335,24 @@ impl<T: SolCalls> Iterator for Selectors<T> {

#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
let remaining = self.len();
(remaining, Some(remaining))
let exact = self.len();
(exact, Some(exact))
}

#[inline]
fn count(self) -> usize {
self.len()
}
}

impl<T: SolCalls> ExactSizeIterator for Selectors<T> {
impl<T: SolInterface> ExactSizeIterator for Selectors<T> {
#[inline]
fn len(&self) -> usize {
T::COUNT - self.index
}
}

impl<T: SolCalls> FusedIterator for Selectors<T> {}
impl<T: SolInterface> FusedIterator for Selectors<T> {}

#[cfg(test)]
mod tests {
Expand Down
10 changes: 5 additions & 5 deletions crates/sol-types/src/types/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
mod calls;
pub use calls::{ContractError, SolCalls};

pub mod data_type;

mod r#enum;
Expand All @@ -15,11 +12,14 @@ pub use event::{EventTopic, SolEvent, TopicList};
mod function;
pub use function::SolCall;

mod interface;
pub use interface::{ContractError, Selectors, SolInterface};

mod r#struct;
pub use r#struct::SolStruct;

mod r#type;
pub use r#type::{Encodable, SolType};
mod ty;
pub use ty::{Encodable, SolType};

// Solidity user-defined value types.
// No exports are needed as the only item is a macro.
Expand Down
2 changes: 1 addition & 1 deletion crates/sol-types/src/types/struct.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//! This module contains the [`SolStruct`] trait, which is used to implement
//! Solidity structs logic, particularly for EIP-712 encoding/decoding.

use super::{r#type::Encodable, SolType};
use super::{Encodable, SolType};
use crate::{token::TokenSeq, Eip712Domain, TokenType, Word};
use alloc::{borrow::Cow, string::String, vec::Vec};
use alloy_primitives::{keccak256, B256};
Expand Down
File renamed without changes.
2 changes: 1 addition & 1 deletion crates/sol-types/tests/doctests/contracts.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use alloy_primitives::{Address, U256};
use alloy_sol_types::{sol, SolCall, SolCalls};
use alloy_sol_types::{sol, SolCall, SolInterface};
use hex_literal::hex;

// Contracts generate a module with the same name, which contains all the items.
Expand Down