ethers_solc/artifacts/
output_selection.rs

1//! bindings for standard json output selection
2
3use serde::{ser::SerializeMap, Deserialize, Deserializer, Serialize, Serializer};
4use std::{collections::BTreeMap, fmt, str::FromStr};
5
6/// Represents the desired outputs based on a File `(file -> (contract -> [outputs]))`
7pub type FileOutputSelection = BTreeMap<String, Vec<String>>;
8
9/// Represents the selected output of files and contracts
10/// The first level key is the file name and the second level key is the
11/// contract name. An empty contract name is used for outputs that are
12/// not tied to a contract but to the whole source file like the AST.
13/// A star as contract name refers to all contracts in the file.
14/// Similarly, a star as a file name matches all files.
15/// To select all outputs the compiler can possibly generate, use
16/// "outputSelection: { "*": { "*": [ "*" ], "": [ "*" ] } }"
17/// but note that this might slow down the compilation process needlessly.
18///
19/// The available output types are as follows:
20///
21/// File level (needs empty string as contract name):
22///   ast - AST of all source files
23///
24/// Contract level (needs the contract name or "*"):
25///   abi - ABI
26///   devdoc - Developer documentation (natspec)
27///   userdoc - User documentation (natspec)
28///   metadata - Metadata
29///   ir - Yul intermediate representation of the code before optimization
30///   irOptimized - Intermediate representation after optimization
31///   storageLayout - Slots, offsets and types of the contract's state
32///     variables.
33///   evm.assembly - New assembly format
34///   evm.legacyAssembly - Old-style assembly format in JSON
35///   evm.bytecode.functionDebugData - Debugging information at function level
36///   evm.bytecode.object - Bytecode object
37///   evm.bytecode.opcodes - Opcodes list
38///   evm.bytecode.sourceMap - Source mapping (useful for debugging)
39///   evm.bytecode.linkReferences - Link references (if unlinked object)
40///   evm.bytecode.generatedSources - Sources generated by the compiler
41///   evm.deployedBytecode* - Deployed bytecode (has all the options that
42///     evm.bytecode has)
43///   evm.deployedBytecode.immutableReferences - Map from AST ids to
44///     bytecode ranges that reference immutables
45///   evm.methodIdentifiers - The list of function hashes
46///   evm.gasEstimates - Function gas estimates
47///   ewasm.wast - Ewasm in WebAssembly S-expressions format
48///   ewasm.wasm - Ewasm in WebAssembly binary format
49///
50/// Note that using a using `evm`, `evm.bytecode`, `ewasm`, etc. will select
51/// every target part of that output. Additionally, `*` can be used as a
52/// wildcard to request everything.
53///
54/// The default output selection is
55///
56/// ```json
57///   {
58///    "*": {
59///      "*": [
60///        "abi",
61///        "evm.bytecode",
62///        "evm.deployedBytecode",
63///        "evm.methodIdentifiers"
64///      ],
65///      "": [
66///        "ast"
67///      ]
68///    }
69///  }
70/// ```
71#[derive(Debug, Clone, Eq, PartialEq, Default, Deserialize)]
72#[serde(transparent)]
73pub struct OutputSelection(pub BTreeMap<String, FileOutputSelection>);
74
75impl OutputSelection {
76    /// select all outputs the compiler can possibly generate, use
77    /// `{ "*": { "*": [ "*" ], "": [ "*" ] } }`
78    /// but note that this might slow down the compilation process needlessly.
79    pub fn complete_output_selection() -> Self {
80        BTreeMap::from([(
81            "*".to_string(),
82            BTreeMap::from([
83                ("*".to_string(), vec!["*".to_string()]),
84                ("".to_string(), vec!["*".to_string()]),
85            ]),
86        )])
87        .into()
88    }
89
90    /// Default output selection for compiler output:
91    ///
92    /// `{ "*": { "*": [ "*" ], "": [
93    /// "abi","evm.bytecode","evm.deployedBytecode","evm.methodIdentifiers"] } }`
94    ///
95    /// Which enables it for all files and all their contracts ("*" wildcard)
96    pub fn default_output_selection() -> Self {
97        BTreeMap::from([("*".to_string(), Self::default_file_output_selection())]).into()
98    }
99
100    /// Default output selection for a single file:
101    ///
102    /// `{ "*": [ "*" ], "": [
103    /// "abi","evm.bytecode","evm.deployedBytecode","evm.methodIdentifiers"] }`
104    ///
105    /// Which enables it for all the contracts in the file ("*" wildcard)
106    pub fn default_file_output_selection() -> FileOutputSelection {
107        BTreeMap::from([(
108            "*".to_string(),
109            vec![
110                "abi".to_string(),
111                "evm.bytecode".to_string(),
112                "evm.deployedBytecode".to_string(),
113                "evm.methodIdentifiers".to_string(),
114            ],
115        )])
116    }
117
118    /// Returns an empty output selection which corresponds to an empty map `{}`
119    pub fn empty_file_output_select() -> FileOutputSelection {
120        Default::default()
121    }
122}
123
124// this will make sure that if the `FileOutputSelection` for a certain file is empty will be
125// serializes as `"*" : []` because
126// > Contract level (needs the contract name or "*") <https://docs.soliditylang.org/en/v0.8.13/using-the-compiler.html>
127impl Serialize for OutputSelection {
128    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
129    where
130        S: Serializer,
131    {
132        struct EmptyFileOutput;
133
134        impl Serialize for EmptyFileOutput {
135            fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
136            where
137                S: Serializer,
138            {
139                let mut map = serializer.serialize_map(Some(1))?;
140                map.serialize_entry("*", &[] as &[String])?;
141                map.end()
142            }
143        }
144
145        let mut map = serializer.serialize_map(Some(self.0.len()))?;
146        for (file, selection) in self.0.iter() {
147            if selection.is_empty() {
148                map.serialize_entry(file, &EmptyFileOutput {})?;
149            } else {
150                map.serialize_entry(file, selection)?;
151            }
152        }
153        map.end()
154    }
155}
156
157impl AsRef<BTreeMap<String, FileOutputSelection>> for OutputSelection {
158    fn as_ref(&self) -> &BTreeMap<String, FileOutputSelection> {
159        &self.0
160    }
161}
162
163impl AsMut<BTreeMap<String, FileOutputSelection>> for OutputSelection {
164    fn as_mut(&mut self) -> &mut BTreeMap<String, FileOutputSelection> {
165        &mut self.0
166    }
167}
168
169impl From<BTreeMap<String, FileOutputSelection>> for OutputSelection {
170    fn from(s: BTreeMap<String, FileOutputSelection>) -> Self {
171        OutputSelection(s)
172    }
173}
174
175/// Contract level output selection
176#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
177pub enum ContractOutputSelection {
178    Abi,
179    DevDoc,
180    UserDoc,
181    Metadata,
182    Ir,
183    IrOptimized,
184    StorageLayout,
185    Evm(EvmOutputSelection),
186    Ewasm(EwasmOutputSelection),
187}
188
189impl ContractOutputSelection {
190    /// Returns the basic set of contract level settings that should be included in the `Contract`
191    /// that solc emits:
192    ///    - "abi"
193    ///    - "evm.bytecode"
194    ///    - "evm.deployedBytecode"
195    ///    - "evm.methodIdentifiers"
196    pub fn basic() -> Vec<ContractOutputSelection> {
197        vec![
198            ContractOutputSelection::Abi,
199            BytecodeOutputSelection::All.into(),
200            DeployedBytecodeOutputSelection::All.into(),
201            EvmOutputSelection::MethodIdentifiers.into(),
202        ]
203    }
204}
205
206impl Serialize for ContractOutputSelection {
207    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
208    where
209        S: Serializer,
210    {
211        serializer.collect_str(self)
212    }
213}
214
215impl<'de> Deserialize<'de> for ContractOutputSelection {
216    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
217    where
218        D: Deserializer<'de>,
219    {
220        String::deserialize(deserializer)?.parse().map_err(serde::de::Error::custom)
221    }
222}
223
224impl fmt::Display for ContractOutputSelection {
225    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
226        match self {
227            ContractOutputSelection::Abi => f.write_str("abi"),
228            ContractOutputSelection::DevDoc => f.write_str("devdoc"),
229            ContractOutputSelection::UserDoc => f.write_str("userdoc"),
230            ContractOutputSelection::Metadata => f.write_str("metadata"),
231            ContractOutputSelection::Ir => f.write_str("ir"),
232            ContractOutputSelection::IrOptimized => f.write_str("irOptimized"),
233            ContractOutputSelection::StorageLayout => f.write_str("storageLayout"),
234            ContractOutputSelection::Evm(e) => e.fmt(f),
235            ContractOutputSelection::Ewasm(e) => e.fmt(f),
236        }
237    }
238}
239
240impl FromStr for ContractOutputSelection {
241    type Err = String;
242
243    fn from_str(s: &str) -> Result<Self, Self::Err> {
244        match s {
245            "abi" => Ok(ContractOutputSelection::Abi),
246            "devdoc" => Ok(ContractOutputSelection::DevDoc),
247            "userdoc" => Ok(ContractOutputSelection::UserDoc),
248            "metadata" => Ok(ContractOutputSelection::Metadata),
249            "ir" => Ok(ContractOutputSelection::Ir),
250            "ir-optimized" | "irOptimized" | "iroptimized" => {
251                Ok(ContractOutputSelection::IrOptimized)
252            }
253            "storage-layout" | "storagelayout" | "storageLayout" => {
254                Ok(ContractOutputSelection::StorageLayout)
255            }
256            s => EvmOutputSelection::from_str(s)
257                .map(ContractOutputSelection::Evm)
258                .or_else(|_| EwasmOutputSelection::from_str(s).map(ContractOutputSelection::Ewasm))
259                .map_err(|_| format!("Invalid contract output selection: {s}")),
260        }
261    }
262}
263
264impl<T: Into<EvmOutputSelection>> From<T> for ContractOutputSelection {
265    fn from(evm: T) -> Self {
266        ContractOutputSelection::Evm(evm.into())
267    }
268}
269
270impl From<EwasmOutputSelection> for ContractOutputSelection {
271    fn from(ewasm: EwasmOutputSelection) -> Self {
272        ContractOutputSelection::Ewasm(ewasm)
273    }
274}
275
276/// Contract level output selection for `evm`
277#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
278pub enum EvmOutputSelection {
279    All,
280    Assembly,
281    LegacyAssembly,
282    MethodIdentifiers,
283    GasEstimates,
284    ByteCode(BytecodeOutputSelection),
285    DeployedByteCode(DeployedBytecodeOutputSelection),
286}
287
288impl From<BytecodeOutputSelection> for EvmOutputSelection {
289    fn from(b: BytecodeOutputSelection) -> Self {
290        EvmOutputSelection::ByteCode(b)
291    }
292}
293
294impl From<DeployedBytecodeOutputSelection> for EvmOutputSelection {
295    fn from(b: DeployedBytecodeOutputSelection) -> Self {
296        EvmOutputSelection::DeployedByteCode(b)
297    }
298}
299
300impl Serialize for EvmOutputSelection {
301    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
302    where
303        S: Serializer,
304    {
305        serializer.collect_str(self)
306    }
307}
308
309impl<'de> Deserialize<'de> for EvmOutputSelection {
310    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
311    where
312        D: Deserializer<'de>,
313    {
314        String::deserialize(deserializer)?.parse().map_err(serde::de::Error::custom)
315    }
316}
317
318impl fmt::Display for EvmOutputSelection {
319    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
320        match self {
321            EvmOutputSelection::All => f.write_str("evm"),
322            EvmOutputSelection::Assembly => f.write_str("evm.assembly"),
323            EvmOutputSelection::LegacyAssembly => f.write_str("evm.legacyAssembly"),
324            EvmOutputSelection::MethodIdentifiers => f.write_str("evm.methodIdentifiers"),
325            EvmOutputSelection::GasEstimates => f.write_str("evm.gasEstimates"),
326            EvmOutputSelection::ByteCode(b) => b.fmt(f),
327            EvmOutputSelection::DeployedByteCode(b) => b.fmt(f),
328        }
329    }
330}
331
332impl FromStr for EvmOutputSelection {
333    type Err = String;
334
335    fn from_str(s: &str) -> Result<Self, Self::Err> {
336        match s {
337            "evm" => Ok(EvmOutputSelection::All),
338            "asm" | "evm.assembly" => Ok(EvmOutputSelection::Assembly),
339            "evm.legacyAssembly" => Ok(EvmOutputSelection::LegacyAssembly),
340            "methodidentifiers" | "evm.methodIdentifiers" | "evm.methodidentifiers" => {
341                Ok(EvmOutputSelection::MethodIdentifiers)
342            }
343            "gas" | "evm.gasEstimates" | "evm.gasestimates" => Ok(EvmOutputSelection::GasEstimates),
344            s => BytecodeOutputSelection::from_str(s)
345                .map(EvmOutputSelection::ByteCode)
346                .or_else(|_| {
347                    DeployedBytecodeOutputSelection::from_str(s)
348                        .map(EvmOutputSelection::DeployedByteCode)
349                })
350                .map_err(|_| format!("Invalid evm selection: {s}")),
351        }
352    }
353}
354
355/// Contract level output selection for `evm.bytecode`
356#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
357pub enum BytecodeOutputSelection {
358    All,
359    FunctionDebugData,
360    Object,
361    Opcodes,
362    SourceMap,
363    LinkReferences,
364    GeneratedSources,
365}
366
367impl Serialize for BytecodeOutputSelection {
368    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
369    where
370        S: Serializer,
371    {
372        serializer.collect_str(self)
373    }
374}
375
376impl<'de> Deserialize<'de> for BytecodeOutputSelection {
377    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
378    where
379        D: Deserializer<'de>,
380    {
381        String::deserialize(deserializer)?.parse().map_err(serde::de::Error::custom)
382    }
383}
384
385impl fmt::Display for BytecodeOutputSelection {
386    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
387        match self {
388            BytecodeOutputSelection::All => f.write_str("evm.bytecode"),
389            BytecodeOutputSelection::FunctionDebugData => {
390                f.write_str("evm.bytecode.functionDebugData")
391            }
392            BytecodeOutputSelection::Object => f.write_str("evm.bytecode.object"),
393            BytecodeOutputSelection::Opcodes => f.write_str("evm.bytecode.opcodes"),
394            BytecodeOutputSelection::SourceMap => f.write_str("evm.bytecode.sourceMap"),
395            BytecodeOutputSelection::LinkReferences => f.write_str("evm.bytecode.linkReferences"),
396            BytecodeOutputSelection::GeneratedSources => {
397                f.write_str("evm.bytecode.generatedSources")
398            }
399        }
400    }
401}
402
403impl FromStr for BytecodeOutputSelection {
404    type Err = String;
405
406    fn from_str(s: &str) -> Result<Self, Self::Err> {
407        match s {
408            "evm.bytecode" => Ok(BytecodeOutputSelection::All),
409            "evm.bytecode.functionDebugData" => Ok(BytecodeOutputSelection::FunctionDebugData),
410            "code" | "bin" | "evm.bytecode.object" => Ok(BytecodeOutputSelection::Object),
411            "evm.bytecode.opcodes" => Ok(BytecodeOutputSelection::Opcodes),
412            "evm.bytecode.sourceMap" => Ok(BytecodeOutputSelection::SourceMap),
413            "evm.bytecode.linkReferences" => Ok(BytecodeOutputSelection::LinkReferences),
414            "evm.bytecode.generatedSources" => Ok(BytecodeOutputSelection::GeneratedSources),
415            s => Err(format!("Invalid bytecode selection: {s}")),
416        }
417    }
418}
419
420/// Contract level output selection for `evm.deployedBytecode`
421#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
422pub enum DeployedBytecodeOutputSelection {
423    All,
424    FunctionDebugData,
425    Object,
426    Opcodes,
427    SourceMap,
428    LinkReferences,
429    GeneratedSources,
430    ImmutableReferences,
431}
432
433impl Serialize for DeployedBytecodeOutputSelection {
434    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
435    where
436        S: Serializer,
437    {
438        serializer.collect_str(self)
439    }
440}
441
442impl<'de> Deserialize<'de> for DeployedBytecodeOutputSelection {
443    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
444    where
445        D: Deserializer<'de>,
446    {
447        String::deserialize(deserializer)?.parse().map_err(serde::de::Error::custom)
448    }
449}
450
451impl fmt::Display for DeployedBytecodeOutputSelection {
452    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
453        match self {
454            DeployedBytecodeOutputSelection::All => f.write_str("evm.deployedBytecode"),
455            DeployedBytecodeOutputSelection::FunctionDebugData => {
456                f.write_str("evm.deployedBytecode.functionDebugData")
457            }
458            DeployedBytecodeOutputSelection::Object => f.write_str("evm.deployedBytecode.object"),
459            DeployedBytecodeOutputSelection::Opcodes => f.write_str("evm.deployedBytecode.opcodes"),
460            DeployedBytecodeOutputSelection::SourceMap => {
461                f.write_str("evm.deployedBytecode.sourceMap")
462            }
463            DeployedBytecodeOutputSelection::LinkReferences => {
464                f.write_str("evm.deployedBytecode.linkReferences")
465            }
466            DeployedBytecodeOutputSelection::GeneratedSources => {
467                f.write_str("evm.deployedBytecode.generatedSources")
468            }
469            DeployedBytecodeOutputSelection::ImmutableReferences => {
470                f.write_str("evm.deployedBytecode.immutableReferences")
471            }
472        }
473    }
474}
475
476impl FromStr for DeployedBytecodeOutputSelection {
477    type Err = String;
478
479    fn from_str(s: &str) -> Result<Self, Self::Err> {
480        match s {
481            "evm.deployedBytecode" => Ok(DeployedBytecodeOutputSelection::All),
482            "evm.deployedBytecode.functionDebugData" => {
483                Ok(DeployedBytecodeOutputSelection::FunctionDebugData)
484            }
485            "deployed-code" |
486            "deployed-bin" |
487            "runtime-code" |
488            "runtime-bin" |
489            "evm.deployedBytecode.object" => Ok(DeployedBytecodeOutputSelection::Object),
490            "evm.deployedBytecode.opcodes" => Ok(DeployedBytecodeOutputSelection::Opcodes),
491            "evm.deployedBytecode.sourceMap" => Ok(DeployedBytecodeOutputSelection::SourceMap),
492            "evm.deployedBytecode.linkReferences" => {
493                Ok(DeployedBytecodeOutputSelection::LinkReferences)
494            }
495            "evm.deployedBytecode.generatedSources" => {
496                Ok(DeployedBytecodeOutputSelection::GeneratedSources)
497            }
498            "evm.deployedBytecode.immutableReferences" => {
499                Ok(DeployedBytecodeOutputSelection::ImmutableReferences)
500            }
501            s => Err(format!("Invalid deployedBytecode selection: {s}")),
502        }
503    }
504}
505
506/// Contract level output selection for `evm.ewasm`
507#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
508pub enum EwasmOutputSelection {
509    All,
510    Wast,
511    Wasm,
512}
513
514impl Serialize for EwasmOutputSelection {
515    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
516    where
517        S: Serializer,
518    {
519        serializer.collect_str(self)
520    }
521}
522
523impl<'de> Deserialize<'de> for EwasmOutputSelection {
524    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
525    where
526        D: Deserializer<'de>,
527    {
528        String::deserialize(deserializer)?.parse().map_err(serde::de::Error::custom)
529    }
530}
531
532impl fmt::Display for EwasmOutputSelection {
533    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
534        match self {
535            EwasmOutputSelection::All => f.write_str("ewasm"),
536            EwasmOutputSelection::Wast => f.write_str("ewasm.wast"),
537            EwasmOutputSelection::Wasm => f.write_str("ewasm.wasm"),
538        }
539    }
540}
541
542impl FromStr for EwasmOutputSelection {
543    type Err = String;
544
545    fn from_str(s: &str) -> Result<Self, Self::Err> {
546        match s {
547            "ewasm" => Ok(EwasmOutputSelection::All),
548            "ewasm.wast" => Ok(EwasmOutputSelection::Wast),
549            "ewasm.wasm" => Ok(EwasmOutputSelection::Wasm),
550            s => Err(format!("Invalid ewasm selection: {s}")),
551        }
552    }
553}
554
555#[cfg(test)]
556mod tests {
557    use super::*;
558
559    #[test]
560    fn outputselection_serde_works() {
561        let mut output = BTreeMap::default();
562        output.insert(
563            "*".to_string(),
564            vec![
565                "abi".to_string(),
566                "evm.bytecode".to_string(),
567                "evm.deployedBytecode".to_string(),
568                "evm.methodIdentifiers".to_string(),
569            ],
570        );
571
572        let json = serde_json::to_string(&output).unwrap();
573        let deserde_selection: BTreeMap<String, Vec<ContractOutputSelection>> =
574            serde_json::from_str(&json).unwrap();
575
576        assert_eq!(json, serde_json::to_string(&deserde_selection).unwrap());
577    }
578
579    #[test]
580    fn empty_outputselection_serde_works() {
581        let mut empty = OutputSelection::default();
582        empty.0.insert("contract.sol".to_string(), OutputSelection::empty_file_output_select());
583        let s = serde_json::to_string(&empty).unwrap();
584        assert_eq!(s, r#"{"contract.sol":{"*":[]}}"#);
585    }
586
587    #[test]
588    fn deployed_bytecode_from_str() {
589        assert_eq!(
590            DeployedBytecodeOutputSelection::from_str("evm.deployedBytecode.immutableReferences")
591                .unwrap(),
592            DeployedBytecodeOutputSelection::ImmutableReferences
593        )
594    }
595}