use crate::contract::{Documentation, Interface, Network};
use crate::{Abi, Bytecode, Contract};
use std::collections::hash_map::Entry;
use std::collections::HashMap;
use std::ops::Deref;
use std::sync::Arc;
pub mod hardhat;
pub mod truffle;
pub struct Artifact {
origin: String,
contracts: HashMap<String, Contract>,
}
impl Artifact {
pub fn new() -> Self {
Artifact {
origin: "<unknown>".to_string(),
contracts: HashMap::new(),
}
}
pub fn with_origin(origin: impl Into<String>) -> Self {
Artifact {
origin: origin.into(),
contracts: HashMap::new(),
}
}
pub fn origin(&self) -> &str {
&self.origin
}
pub fn set_origin(&mut self, origin: impl Into<String>) {
self.origin = origin.into();
}
pub fn len(&self) -> usize {
self.contracts.len()
}
pub fn is_empty(&self) -> bool {
self.contracts.is_empty()
}
pub fn contains(&self, name: &str) -> bool {
self.contracts.contains_key(name)
}
pub fn get(&self, name: &str) -> Option<&Contract> {
self.contracts.get(name)
}
pub fn get_mut(&mut self, name: &str) -> Option<ContractMut> {
self.contracts.get_mut(name).map(ContractMut)
}
pub fn insert(&mut self, contract: Contract) -> InsertResult {
match self.contracts.entry(contract.name.clone()) {
Entry::Occupied(mut o) => {
let old_contract = o.insert(contract);
InsertResult {
inserted_contract: ContractMut(o.into_mut()),
old_contract: Some(old_contract),
}
}
Entry::Vacant(v) => InsertResult {
inserted_contract: ContractMut(v.insert(contract)),
old_contract: None,
},
}
}
pub fn remove(&mut self, name: &str) -> Option<Contract> {
self.contracts.remove(name)
}
pub fn iter(&self) -> impl Iterator<Item = &Contract> + '_ {
self.contracts.values()
}
pub fn drain(&mut self) -> impl Iterator<Item = Contract> + '_ {
self.contracts.drain().map(|(_, contract)| contract)
}
}
impl Default for Artifact {
fn default() -> Self {
Artifact::new()
}
}
pub struct InsertResult<'a> {
pub inserted_contract: ContractMut<'a>,
pub old_contract: Option<Contract>,
}
pub struct ContractMut<'a>(&'a mut Contract);
impl ContractMut<'_> {
pub fn abi_mut(&mut self) -> &mut Abi {
&mut Arc::make_mut(&mut self.0.interface).abi
}
pub fn bytecode_mut(&mut self) -> &mut Bytecode {
&mut self.0.bytecode
}
pub fn deployed_bytecode_mut(&mut self) -> &mut Bytecode {
&mut self.0.deployed_bytecode
}
pub fn networks_mut(&mut self) -> &mut HashMap<String, Network> {
&mut self.0.networks
}
pub fn devdoc_mut(&mut self) -> &mut Documentation {
&mut self.0.devdoc
}
pub fn userdoc_mut(&mut self) -> &mut Documentation {
&mut self.0.userdoc
}
}
impl Deref for ContractMut<'_> {
type Target = Contract;
fn deref(&self) -> &Self::Target {
self.0
}
}
impl Drop for ContractMut<'_> {
fn drop(&mut self) {
let abi = self.0.interface.abi.clone();
let interface = Interface::from(abi);
*Arc::make_mut(&mut self.0.interface) = interface;
}
}
#[cfg(test)]
mod test {
use super::*;
fn make_contract(name: &str) -> Contract {
let mut contract = Contract::empty();
contract.name = name.to_string();
contract
}
#[test]
fn insert() {
let mut artifact = Artifact::new();
assert_eq!(artifact.len(), 0);
{
let insert_res = artifact.insert(make_contract("C1"));
assert_eq!(insert_res.inserted_contract.name, "C1");
assert!(insert_res.old_contract.is_none());
}
assert_eq!(artifact.len(), 1);
assert!(artifact.contains("C1"));
{
let insert_res = artifact.insert(make_contract("C2"));
assert_eq!(insert_res.inserted_contract.name, "C2");
assert!(insert_res.old_contract.is_none());
}
assert_eq!(artifact.len(), 2);
assert!(artifact.contains("C2"));
{
let insert_res = artifact.insert(make_contract("C1"));
assert_eq!(insert_res.inserted_contract.name, "C1");
assert!(insert_res.old_contract.is_some());
}
assert_eq!(artifact.len(), 2);
}
#[test]
fn remove() {
let mut artifact = Artifact::new();
artifact.insert(make_contract("C1"));
artifact.insert(make_contract("C2"));
assert_eq!(artifact.len(), 2);
assert!(artifact.contains("C1"));
assert!(artifact.contains("C2"));
let c0 = artifact.remove("C0");
assert!(c0.is_none());
assert_eq!(artifact.len(), 2);
assert!(artifact.contains("C1"));
assert!(artifact.contains("C2"));
let c1 = artifact.remove("C1");
assert!(c1.is_some());
assert_eq!(c1.unwrap().name, "C1");
assert_eq!(artifact.len(), 1);
assert!(!artifact.contains("C1"));
assert!(artifact.contains("C2"));
}
}