use std::collections::HashMap;
use proc_macro2::TokenStream;
use crate::typedef::{AbiField, Parameter, StateMutability};
pub trait BinderError: std::error::Error + Send + Sync + 'static {}
impl<T> BinderError for T where T: std::error::Error + Send + Sync + 'static {}
pub struct BinderContext<'a> {
contract_name: &'a str,
fields: &'a [AbiField],
bytecode: Option<&'a str>,
components: HashMap<String, Vec<Parameter>>,
}
impl<'a> BinderContext<'a> {
pub fn new(contract_name: &'a str, fields: &'a [AbiField], bytecode: Option<&'a str>) -> Self {
let components = HashMap::new();
let mut cx = BinderContext {
contract_name,
fields,
bytecode,
components,
};
cx.index_components();
cx
}
fn index_components(&mut self) {
for field in self.fields {
match field {
AbiField::Function(function) => {
for parameter in &function.inputs {
self.index_component_of_parameter(parameter);
}
for parameter in &function.outputs {
self.index_component_of_parameter(parameter);
}
}
AbiField::Constructor(constructor) => {
for parameter in &constructor.inputs {
self.index_component_of_parameter(parameter);
}
}
AbiField::Event(field) => {
for parameter in &field.inputs {
self.index_component_of_parameter(parameter);
}
}
AbiField::Error(field) => {
for parameter in &field.inputs {
self.index_component_of_parameter(parameter);
}
}
_ => {}
}
}
}
fn index_component_of_parameter(&mut self, parameter: &Parameter) {
if let Some(components) = ¶meter.components {
let internal_type = parameter
.internal_type
.as_deref()
.expect("internal type is null");
if !self.components.contains_key(internal_type) {
self.components
.insert(internal_type.to_owned(), components.clone());
for parameter in components {
self.index_component_of_parameter(parameter);
}
}
}
}
pub fn bytecode(&self) -> Option<&str> {
self.bytecode
}
}
pub trait Binder {
type Error: BinderError;
type ContractBinder: ContractBinder<Error = Self::Error>;
fn prepare(
&mut self,
cx: &BinderContext<'_>,
contract_name: &str,
) -> Result<Self::ContractBinder, Self::Error>;
}
pub trait ContractBinder {
type Error: BinderError;
type ConstructorBinder: ConstructorBinder<Error = Self::Error>;
type FunctionBinder: FunctionBinder<Error = Self::Error>;
type EventBinder: EventBinder<Error = Self::Error>;
type ErrorBinder: EventBinder<Error = Self::Error>;
type TupleBinder: TupleBinder<Error = Self::Error>;
fn bind_tuple(
&mut self,
cx: &BinderContext<'_>,
name: &str,
) -> Result<Self::TupleBinder, Self::Error>;
fn bind_constructor(
&mut self,
cx: &BinderContext<'_>,
signature: &str,
state: &StateMutability,
) -> Result<Self::ConstructorBinder, Self::Error>;
fn bind_function(
&mut self,
cx: &BinderContext<'_>,
fn_name: &str,
signature: &str,
state: &StateMutability,
) -> Result<Self::FunctionBinder, Self::Error>;
fn bind_receiver(
&mut self,
cx: &BinderContext<'_>,
state: &StateMutability,
) -> Result<(), Self::Error>;
fn bind_fallback(
&mut self,
cx: &BinderContext<'_>,
state: &StateMutability,
) -> Result<(), Self::Error>;
fn bind_event(
&mut self,
cx: &BinderContext<'_>,
name: &str,
signature: Option<&str>,
) -> Result<Self::EventBinder, Self::Error>;
fn bind_error(
&mut self,
cx: &BinderContext<'_>,
name: &str,
signature: &str,
) -> Result<Self::ErrorBinder, Self::Error>;
fn finialize(&mut self, cx: &BinderContext<'_>) -> Result<TokenStream, Self::Error>;
}
pub trait ConstructorBinder {
type Error: BinderError;
fn bind_input(
&mut self,
cx: &BinderContext<'_>,
index: usize,
parameter: &Parameter,
) -> Result<(), Self::Error>;
fn finialize(&mut self, cx: &BinderContext<'_>) -> Result<(), Self::Error>;
}
pub trait FunctionBinder {
type Error: BinderError;
fn bind_input(
&mut self,
cx: &BinderContext<'_>,
index: usize,
parameter: &Parameter,
) -> Result<(), Self::Error>;
fn bind_output(
&mut self,
cx: &BinderContext<'_>,
index: usize,
parameter: &Parameter,
) -> Result<(), Self::Error>;
fn finialize(&mut self, cx: &BinderContext<'_>) -> Result<(), Self::Error>;
}
pub trait EventBinder {
type Error: BinderError;
fn bind_input(
&mut self,
cx: &BinderContext<'_>,
index: usize,
parameter: &Parameter,
) -> Result<(), Self::Error>;
fn finialize(&mut self, cx: &BinderContext<'_>) -> Result<(), Self::Error>;
}
pub trait TupleBinder {
type Error: BinderError;
fn bind_input(
&mut self,
cx: &BinderContext<'_>,
index: usize,
parameter: &Parameter,
) -> Result<(), Self::Error>;
fn finialize(&mut self, cx: &BinderContext<'_>) -> Result<(), Self::Error>;
}
pub fn bind<'a, B: Binder>(cx: &BinderContext<'a>, mut binder: B) -> Result<TokenStream, B::Error> {
let mut contract = binder.prepare(cx, &cx.contract_name)?;
for (name, fields) in &cx.components {
let mut binder = contract.bind_tuple(cx, name)?;
for (index, parameter) in fields.iter().enumerate() {
binder.bind_input(cx, index, parameter)?;
}
binder.finialize(cx)?;
}
for field in cx.fields {
match field {
AbiField::Function(function) => {
let mut binder = contract.bind_function(
cx,
&function.name,
function.signature().as_str(),
&function.state_mutability,
)?;
for (index, parameter) in function.inputs.iter().enumerate() {
binder.bind_input(cx, index, parameter)?;
}
for (index, parameter) in function.outputs.iter().enumerate() {
binder.bind_output(cx, index, parameter)?;
}
binder.finialize(cx)?;
}
AbiField::Constructor(constructor) => {
let mut binder = contract.bind_constructor(
cx,
constructor.signature().as_str(),
&constructor.state_mutability,
)?;
for (index, parameter) in constructor.inputs.iter().enumerate() {
binder.bind_input(cx, index, parameter)?;
}
binder.finialize(cx)?;
}
AbiField::Receive(receiver) => {
contract.bind_receiver(cx, &receiver.state_mutability)?;
}
AbiField::Fallback(fallback) => {
contract.bind_receiver(cx, &fallback.state_mutability)?;
}
AbiField::Event(event) => {
let signature = if event.anonymous {
None
} else {
Some(event.signature())
};
let mut binder = contract.bind_event(cx, &event.name, signature.as_deref())?;
for (index, parameter) in event.inputs.iter().enumerate() {
binder.bind_input(cx, index, parameter)?;
}
binder.finialize(cx)?;
}
AbiField::Error(e) => {
let mut binder = contract.bind_error(cx, &e.name, e.signature().as_str())?;
for (index, parameter) in e.inputs.iter().enumerate() {
binder.bind_input(cx, index, parameter)?;
}
binder.finialize(cx)?;
}
}
}
Ok(contract.finialize(cx)?)
}
#[cfg(test)]
mod tests {
use crate::typedef::HardhatArtifact;
use super::BinderContext;
#[test]
fn binder_context_new() {
let hardhat: HardhatArtifact = serde_json::from_str(include_str!("./abi.json")).unwrap();
BinderContext::new(
&hardhat.contract_name,
&hardhat.abi,
Some(&hardhat.bytecode),
);
}
}