ethcontract_common/
artifact.rs1use crate::contract::{Documentation, Interface, Network};
12use crate::{Abi, Bytecode, Contract};
13use std::collections::hash_map::Entry;
14use std::collections::HashMap;
15use std::ops::Deref;
16use std::sync::Arc;
17
18pub mod hardhat;
19pub mod truffle;
20
21pub struct Artifact {
23 origin: String,
24 contracts: HashMap<String, Contract>,
25}
26
27impl Artifact {
28 pub fn new() -> Self {
30 Artifact {
31 origin: "<unknown>".to_string(),
32 contracts: HashMap::new(),
33 }
34 }
35
36 pub fn with_origin(origin: impl Into<String>) -> Self {
38 Artifact {
39 origin: origin.into(),
40 contracts: HashMap::new(),
41 }
42 }
43
44 pub fn origin(&self) -> &str {
49 &self.origin
50 }
51
52 pub fn set_origin(&mut self, origin: impl Into<String>) {
58 self.origin = origin.into();
59 }
60
61 pub fn len(&self) -> usize {
63 self.contracts.len()
64 }
65
66 pub fn is_empty(&self) -> bool {
68 self.contracts.is_empty()
69 }
70
71 pub fn contains(&self, name: &str) -> bool {
73 self.contracts.contains_key(name)
74 }
75
76 pub fn get(&self, name: &str) -> Option<&Contract> {
81 self.contracts.get(name)
82 }
83
84 pub fn get_mut(&mut self, name: &str) -> Option<ContractMut> {
90 self.contracts.get_mut(name).map(ContractMut)
91 }
92
93 pub fn insert(&mut self, contract: Contract) -> InsertResult {
98 match self.contracts.entry(contract.name.clone()) {
99 Entry::Occupied(mut o) => {
100 let old_contract = o.insert(contract);
101 InsertResult {
102 inserted_contract: ContractMut(o.into_mut()),
103 old_contract: Some(old_contract),
104 }
105 }
106 Entry::Vacant(v) => InsertResult {
107 inserted_contract: ContractMut(v.insert(contract)),
108 old_contract: None,
109 },
110 }
111 }
112
113 pub fn remove(&mut self, name: &str) -> Option<Contract> {
118 self.contracts.remove(name)
119 }
120
121 pub fn iter(&self) -> impl Iterator<Item = &Contract> + '_ {
123 self.contracts.values()
124 }
125
126 pub fn drain(&mut self) -> impl Iterator<Item = Contract> + '_ {
129 self.contracts.drain().map(|(_, contract)| contract)
130 }
131}
132
133impl Default for Artifact {
134 fn default() -> Self {
135 Artifact::new()
136 }
137}
138
139pub struct InsertResult<'a> {
141 pub inserted_contract: ContractMut<'a>,
143
144 pub old_contract: Option<Contract>,
146}
147
148pub struct ContractMut<'a>(&'a mut Contract);
151
152impl ContractMut<'_> {
153 pub fn abi_mut(&mut self) -> &mut Abi {
155 &mut Arc::make_mut(&mut self.0.interface).abi
156 }
157
158 pub fn bytecode_mut(&mut self) -> &mut Bytecode {
160 &mut self.0.bytecode
161 }
162
163 pub fn deployed_bytecode_mut(&mut self) -> &mut Bytecode {
165 &mut self.0.deployed_bytecode
166 }
167
168 pub fn networks_mut(&mut self) -> &mut HashMap<String, Network> {
170 &mut self.0.networks
171 }
172
173 pub fn devdoc_mut(&mut self) -> &mut Documentation {
175 &mut self.0.devdoc
176 }
177
178 pub fn userdoc_mut(&mut self) -> &mut Documentation {
180 &mut self.0.userdoc
181 }
182}
183
184impl Deref for ContractMut<'_> {
185 type Target = Contract;
186
187 fn deref(&self) -> &Self::Target {
188 self.0
189 }
190}
191
192impl Drop for ContractMut<'_> {
193 fn drop(&mut self) {
194 let abi = self.0.interface.abi.clone();
199 let interface = Interface::from(abi);
200 *Arc::make_mut(&mut self.0.interface) = interface;
201 }
202}
203
204#[cfg(test)]
205mod test {
206 use super::*;
207
208 fn make_contract(name: &str) -> Contract {
209 let mut contract = Contract::empty();
210 contract.name = name.to_string();
211 contract
212 }
213
214 #[test]
215 fn insert() {
216 let mut artifact = Artifact::new();
217
218 assert_eq!(artifact.len(), 0);
219
220 {
221 let insert_res = artifact.insert(make_contract("C1"));
222
223 assert_eq!(insert_res.inserted_contract.name, "C1");
224 assert!(insert_res.old_contract.is_none());
225 }
226
227 assert_eq!(artifact.len(), 1);
228 assert!(artifact.contains("C1"));
229
230 {
231 let insert_res = artifact.insert(make_contract("C2"));
232
233 assert_eq!(insert_res.inserted_contract.name, "C2");
234 assert!(insert_res.old_contract.is_none());
235 }
236
237 assert_eq!(artifact.len(), 2);
238 assert!(artifact.contains("C2"));
239
240 {
241 let insert_res = artifact.insert(make_contract("C1"));
242
243 assert_eq!(insert_res.inserted_contract.name, "C1");
244 assert!(insert_res.old_contract.is_some());
245 }
246
247 assert_eq!(artifact.len(), 2);
248 }
249
250 #[test]
251 fn remove() {
252 let mut artifact = Artifact::new();
253
254 artifact.insert(make_contract("C1"));
255 artifact.insert(make_contract("C2"));
256
257 assert_eq!(artifact.len(), 2);
258 assert!(artifact.contains("C1"));
259 assert!(artifact.contains("C2"));
260
261 let c0 = artifact.remove("C0");
262 assert!(c0.is_none());
263
264 assert_eq!(artifact.len(), 2);
265 assert!(artifact.contains("C1"));
266 assert!(artifact.contains("C2"));
267
268 let c1 = artifact.remove("C1");
269
270 assert!(c1.is_some());
271 assert_eq!(c1.unwrap().name, "C1");
272
273 assert_eq!(artifact.len(), 1);
274 assert!(!artifact.contains("C1"));
275 assert!(artifact.contains("C2"));
276 }
277}