ethcontract_common/artifact/
truffle.rs

1//! Implements the most common artifact format used in Truffle, Waffle
2//! and some other libraries.
3//!
4//! This artifact is represented as a JSON file containing information about
5//! a single contract. We parse the following fields:
6//!
7//! - `contractName`: name of the contract (optional);
8//! - `abi`: information about contract's interface;
9//! - `bytecode`: contract's compiled bytecode (optional);
10//! - `networks`: info about known contract deployments (optional);
11//! - `devdoc`, `userdoc`: additional documentation for contract's methods.
12
13use crate::artifact::Artifact;
14use crate::errors::ArtifactError;
15use crate::Contract;
16use serde_json::{from_reader, from_slice, from_str, from_value, to_string, Value};
17use std::fs::File;
18use std::io::{BufReader, Read};
19use std::path::Path;
20
21/// Loads truffle artifacts.
22#[must_use = "truffle loaders do nothing unless you load them"]
23pub struct TruffleLoader {
24    /// Override for artifact's origin.
25    ///
26    /// If empty, origin will be derived automatically.
27    pub origin: Option<String>,
28
29    /// Override for contract's name.
30    ///
31    /// Truffle artifacts contain a single contract which may be unnamed.
32    pub name: Option<String>,
33}
34
35impl TruffleLoader {
36    /// Creates a new truffle loader.
37    pub fn new() -> Self {
38        TruffleLoader {
39            origin: None,
40            name: None,
41        }
42    }
43
44    /// Creates a new truffle loader and sets an override for artifact's origins.
45    pub fn with_origin(origin: impl Into<String>) -> Self {
46        TruffleLoader {
47            origin: Some(origin.into()),
48            name: None,
49        }
50    }
51
52    /// Sets new override for artifact's origin. See [`origin`] for more info.
53    ///
54    /// [`origin`]: #structfield.origin
55    pub fn origin(mut self, origin: impl Into<String>) -> Self {
56        self.origin = Some(origin.into());
57        self
58    }
59
60    /// Sets new override for artifact's name. See [`name`] for more info.
61    ///
62    /// [`name`]: #structfield.name
63    pub fn name(mut self, name: impl Into<String>) -> Self {
64        self.name = Some(name.into());
65        self
66    }
67
68    /// Loads an artifact from a loaded JSON value.
69    pub fn load_from_reader(&self, v: impl Read) -> Result<Artifact, ArtifactError> {
70        self.load_artifact("<unknown>", v, from_reader)
71    }
72
73    /// Loads an artifact from bytes of JSON text.
74    pub fn load_from_slice(&self, v: &[u8]) -> Result<Artifact, ArtifactError> {
75        self.load_artifact("<unknown>", v, from_slice)
76    }
77
78    /// Loads an artifact from string of JSON text.
79    pub fn load_from_str(&self, v: &str) -> Result<Artifact, ArtifactError> {
80        self.load_artifact("<unknown>", v, from_str)
81    }
82
83    /// Loads an artifact from a loaded JSON value.
84    pub fn load_from_value(&self, v: Value) -> Result<Artifact, ArtifactError> {
85        self.load_artifact("<unknown>", v, from_value)
86    }
87
88    /// Loads an artifact from disk.
89    pub fn load_from_file(&self, p: impl AsRef<Path>) -> Result<Artifact, ArtifactError> {
90        let path = p.as_ref();
91        let file = File::open(path)?;
92        let reader = BufReader::new(file);
93        self.load_artifact(path.display(), reader, from_reader)
94    }
95
96    /// Loads a contract from a loaded JSON value.
97    pub fn load_contract_from_reader(&self, v: impl Read) -> Result<Contract, ArtifactError> {
98        self.load_contract(v, from_reader)
99    }
100
101    /// Loads a contract from bytes of JSON text.
102    pub fn load_contract_from_slice(&self, v: &[u8]) -> Result<Contract, ArtifactError> {
103        self.load_contract(v, from_slice)
104    }
105
106    /// Loads a contract from string of JSON text.
107    pub fn load_contract_from_str(&self, v: &str) -> Result<Contract, ArtifactError> {
108        self.load_contract(v, from_str)
109    }
110
111    /// Loads a contract from a loaded JSON value.
112    pub fn load_contract_from_value(&self, v: Value) -> Result<Contract, ArtifactError> {
113        self.load_contract(v, from_value)
114    }
115
116    /// Loads a contract from disk.
117    pub fn load_contract_from_file(&self, p: impl AsRef<Path>) -> Result<Contract, ArtifactError> {
118        let path = p.as_ref();
119        let file = File::open(path)?;
120        let reader = BufReader::new(file);
121        self.load_contract(reader, from_reader)
122    }
123
124    fn load_artifact<T>(
125        &self,
126        origin: impl ToString,
127        source: T,
128        loader: impl FnOnce(T) -> serde_json::Result<Contract>,
129    ) -> Result<Artifact, ArtifactError> {
130        let origin = self.origin.clone().unwrap_or_else(|| origin.to_string());
131        let mut artifact = Artifact::with_origin(origin);
132        artifact.insert(self.load_contract(source, loader)?);
133        Ok(artifact)
134    }
135
136    fn load_contract<T>(
137        &self,
138        source: T,
139        loader: impl FnOnce(T) -> serde_json::Result<Contract>,
140    ) -> Result<Contract, ArtifactError> {
141        let mut contract: Contract = loader(source)?;
142
143        if let Some(name) = &self.name {
144            contract.name.clone_from(name);
145        }
146
147        Ok(contract)
148    }
149
150    /// Serializes a single contract.
151    pub fn save_to_string(contract: &Contract) -> Result<String, ArtifactError> {
152        to_string(contract).map_err(Into::into)
153    }
154}
155
156impl Default for TruffleLoader {
157    fn default() -> Self {
158        TruffleLoader::new()
159    }
160}