#![deny(missing_docs, unsafe_code)]
#[cfg(test)]
#[allow(missing_docs)]
#[macro_use]
#[path = "test/macros.rs"]
mod test_macros;
mod contract;
mod rustfmt;
mod source;
mod util;
pub use crate::source::Source;
pub use crate::util::parse_address;
use anyhow::Result;
pub use ethcontract_common::Address;
use proc_macro2::TokenStream;
use std::collections::HashMap;
use std::fs::File;
use std::io::Write;
use std::path::Path;
pub(crate) struct Args {
artifact_source: Source,
runtime_crate_name: String,
visibility_modifier: Option<String>,
contract_mod_override: Option<String>,
contract_name_override: Option<String>,
deployments: HashMap<u32, Address>,
method_aliases: HashMap<String, String>,
event_derives: Vec<String>,
}
impl Args {
pub fn new(source: Source) -> Self {
Args {
artifact_source: source,
runtime_crate_name: "ethcontract".to_owned(),
visibility_modifier: None,
contract_mod_override: None,
contract_name_override: None,
deployments: HashMap::new(),
method_aliases: HashMap::new(),
event_derives: Vec::new(),
}
}
}
struct SerializationOptions {
rustfmt: bool,
}
impl Default for SerializationOptions {
fn default() -> Self {
SerializationOptions { rustfmt: true }
}
}
pub struct Builder {
args: Args,
options: SerializationOptions,
}
impl Builder {
pub fn new<P>(artifact_path: P) -> Self
where
P: AsRef<Path>,
{
Builder::with_source(Source::local(artifact_path))
}
pub fn from_source_url<S>(source_url: S) -> Result<Self>
where
S: AsRef<str>,
{
let source = Source::parse(source_url)?;
Ok(Builder::with_source(source))
}
pub fn with_source(source: Source) -> Self {
Builder {
args: Args::new(source),
options: SerializationOptions::default(),
}
}
pub fn with_runtime_crate_name<S>(mut self, name: S) -> Self
where
S: Into<String>,
{
self.args.runtime_crate_name = name.into();
self
}
pub fn with_visibility_modifier<S>(mut self, vis: Option<S>) -> Self
where
S: Into<String>,
{
self.args.visibility_modifier = vis.map(S::into);
self
}
pub fn with_contract_mod_override<S>(mut self, name: Option<S>) -> Self
where
S: Into<String>,
{
self.args.contract_mod_override = name.map(S::into);
self
}
pub fn with_contract_name_override<S>(mut self, name: Option<S>) -> Self
where
S: Into<String>,
{
self.args.contract_name_override = name.map(S::into);
self
}
pub fn add_deployment(mut self, network_id: u32, address: Address) -> Self {
self.args.deployments.insert(network_id, address);
self
}
pub fn add_deployment_str<S>(self, network_id: u32, address: S) -> Self
where
S: AsRef<str>,
{
self.add_deployment(
network_id,
parse_address(address).expect("failed to parse address"),
)
}
pub fn add_method_alias<S1, S2>(mut self, signature: S1, alias: S2) -> Self
where
S1: Into<String>,
S2: Into<String>,
{
self.args
.method_aliases
.insert(signature.into(), alias.into());
self
}
pub fn with_rustfmt(mut self, rustfmt: bool) -> Self {
self.options.rustfmt = rustfmt;
self
}
pub fn add_event_derive<S>(mut self, derive: S) -> Self
where
S: Into<String>,
{
self.args.event_derives.push(derive.into());
self
}
pub fn generate(self) -> Result<ContractBindings> {
let tokens = contract::expand(self.args)?;
Ok(ContractBindings {
tokens,
options: self.options,
})
}
}
pub struct ContractBindings {
tokens: TokenStream,
options: SerializationOptions,
}
impl ContractBindings {
pub fn write<W>(&self, mut w: W) -> Result<()>
where
W: Write,
{
let source = {
let raw = self.tokens.to_string();
if self.options.rustfmt {
rustfmt::format(&raw).unwrap_or(raw)
} else {
raw
}
};
w.write_all(source.as_bytes())?;
Ok(())
}
pub fn write_to_file<P>(&self, path: P) -> Result<()>
where
P: AsRef<Path>,
{
let file = File::create(path)?;
self.write(file)
}
pub fn into_tokens(self) -> TokenStream {
self.tokens
}
}