foundry_compilers/artifact_output/
configurable.rs

1//! A configurable artifacts handler implementation
2//!
3//! Configuring artifacts requires two pieces: the `ConfigurableArtifacts` handler, which contains
4//! the configuration of how to construct the `ConfigurableArtifact` type based on a `Contract`. The
5//! `ConfigurableArtifacts` populates a single `Artifact`, the `ConfigurableArtifact`, by default
6//! with essential entries only, such as `abi`, `bytecode`,..., but may include additional values
7//! based on its `ExtraOutputValues` that maps to various objects in the solc contract output, see
8//! also: [`OutputSelection`](foundry_compilers_artifacts::output_selection::OutputSelection). In
9//! addition to that some output values can also be emitted as standalone files.
10
11use crate::{
12    sources::VersionedSourceFile, Artifact, ArtifactFile, ArtifactOutput, SolcConfig, SolcError,
13    SourceFile,
14};
15use alloy_json_abi::JsonAbi;
16use alloy_primitives::hex;
17use foundry_compilers_artifacts::{
18    bytecode::{CompactBytecode, CompactDeployedBytecode},
19    contract::Contract,
20    output_selection::{
21        BytecodeOutputSelection, ContractOutputSelection, DeployedBytecodeOutputSelection,
22        EvmOutputSelection, EwasmOutputSelection,
23    },
24    BytecodeObject, ConfigurableContractArtifact, Evm, Ewasm, GeneratedSource, LosslessMetadata,
25    Metadata, Settings,
26};
27use foundry_compilers_core::utils;
28use std::{fs, path::Path};
29
30/// An `Artifact` implementation that can be configured to include additional content and emit
31/// additional files
32///
33/// Creates a single json artifact with
34/// ```json
35///  {
36///    "abi": [],
37///    "bytecode": {...},
38///    "deployedBytecode": {...},
39///    "methodIdentifiers": {...},
40///    // additional values
41///  }
42/// ```
43#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
44pub struct ConfigurableArtifacts {
45    /// A set of additional values to include in the contract's artifact file
46    pub additional_values: ExtraOutputValues,
47
48    /// A set of values that should be written to a separate file
49    pub additional_files: ExtraOutputFiles,
50
51    /// PRIVATE: This structure may grow, As such, constructing this structure should
52    /// _always_ be done using a public constructor or update syntax:
53    ///
54    /// ```
55    /// use foundry_compilers::{ConfigurableArtifacts, ExtraOutputFiles};
56    ///
57    /// let config = ConfigurableArtifacts {
58    ///     additional_files: ExtraOutputFiles { metadata: true, ..Default::default() },
59    ///     ..Default::default()
60    /// };
61    /// ```
62    #[doc(hidden)]
63    pub __non_exhaustive: (),
64}
65
66impl ConfigurableArtifacts {
67    pub fn new(
68        extra_values: impl IntoIterator<Item = ContractOutputSelection>,
69        extra_files: impl IntoIterator<Item = ContractOutputSelection>,
70    ) -> Self {
71        Self {
72            additional_values: ExtraOutputValues::from_output_selection(extra_values),
73            additional_files: ExtraOutputFiles::from_output_selection(extra_files),
74            ..Default::default()
75        }
76    }
77
78    /// Returns the `Settings` this configuration corresponds to
79    pub fn solc_settings(&self) -> Settings {
80        SolcConfig::builder()
81            .additional_outputs(self.output_selection())
82            .ast(self.additional_values.ast)
83            .build()
84    }
85
86    /// Returns the output selection corresponding to this configuration
87    pub fn output_selection(&self) -> Vec<ContractOutputSelection> {
88        let mut selection = ContractOutputSelection::basic();
89
90        let ExtraOutputValues {
91            // handled above
92            ast: _,
93            userdoc,
94            devdoc,
95            method_identifiers,
96            storage_layout,
97            transient_storage_layout,
98            assembly,
99            legacy_assembly,
100            gas_estimates,
101            metadata,
102            ir,
103            ir_optimized,
104            ir_optimized_ast,
105            ewasm,
106            function_debug_data,
107            generated_sources,
108            source_map,
109            opcodes,
110            __non_exhaustive,
111        } = self.additional_values;
112
113        if ir || self.additional_files.ir {
114            selection.push(ContractOutputSelection::Ir);
115        }
116        if ir_optimized || self.additional_files.ir_optimized {
117            selection.push(ContractOutputSelection::IrOptimized);
118        }
119        if metadata || self.additional_files.metadata {
120            selection.push(ContractOutputSelection::Metadata);
121        }
122        if storage_layout {
123            selection.push(ContractOutputSelection::StorageLayout);
124        }
125        if devdoc {
126            selection.push(ContractOutputSelection::DevDoc);
127        }
128        if userdoc {
129            selection.push(ContractOutputSelection::UserDoc);
130        }
131        if gas_estimates {
132            selection.push(EvmOutputSelection::GasEstimates.into());
133        }
134        if assembly || self.additional_files.assembly {
135            selection.push(EvmOutputSelection::Assembly.into());
136        }
137        if legacy_assembly || self.additional_files.legacy_assembly {
138            selection.push(EvmOutputSelection::LegacyAssembly.into());
139        }
140        if ewasm || self.additional_files.ewasm {
141            selection.push(EwasmOutputSelection::All.into());
142        }
143        if function_debug_data {
144            selection.push(BytecodeOutputSelection::FunctionDebugData.into());
145        }
146        if method_identifiers {
147            selection.push(EvmOutputSelection::MethodIdentifiers.into());
148        }
149        if generated_sources {
150            selection.push(
151                EvmOutputSelection::ByteCode(BytecodeOutputSelection::GeneratedSources).into(),
152            );
153        }
154        if source_map {
155            selection.push(EvmOutputSelection::ByteCode(BytecodeOutputSelection::SourceMap).into());
156        }
157        if ir_optimized_ast {
158            selection.push(ContractOutputSelection::IrOptimizedAst);
159        }
160        if opcodes {
161            selection.push(EvmOutputSelection::ByteCode(BytecodeOutputSelection::Opcodes).into());
162        }
163        if transient_storage_layout {
164            selection.push(ContractOutputSelection::TransientStorageLayout);
165        }
166        selection
167    }
168}
169
170impl ArtifactOutput for ConfigurableArtifacts {
171    type Artifact = ConfigurableContractArtifact;
172    type CompilerContract = Contract;
173
174    /// Writes extra files for compiled artifact based on [Self::additional_files]
175    fn handle_artifacts(
176        &self,
177        contracts: &crate::VersionedContracts<Contract>,
178        artifacts: &crate::Artifacts<Self::Artifact>,
179    ) -> Result<(), SolcError> {
180        for (file, contracts) in contracts.as_ref().iter() {
181            for (name, versioned_contracts) in contracts {
182                for contract in versioned_contracts {
183                    if let Some(artifact) = artifacts.find_artifact_with_profile(
184                        file,
185                        name,
186                        &contract.version,
187                        &contract.profile,
188                    ) {
189                        let file = &artifact.file;
190                        utils::create_parent_dir_all(file)?;
191                        self.additional_files.write_extras(&contract.contract, file)?;
192                    }
193                }
194            }
195        }
196        Ok(())
197    }
198
199    fn contract_to_artifact(
200        &self,
201        _file: &Path,
202        _name: &str,
203        contract: Contract,
204        source_file: Option<&SourceFile>,
205    ) -> Self::Artifact {
206        let mut artifact_userdoc = None;
207        let mut artifact_devdoc = None;
208        let mut artifact_raw_metadata = None;
209        let mut artifact_metadata = None;
210        let mut artifact_ir = None;
211        let mut artifact_ir_optimized = None;
212        let mut artifact_ir_optimized_ast = None;
213        let mut artifact_ewasm = None;
214        let mut artifact_bytecode = None;
215        let mut artifact_deployed_bytecode = None;
216        let mut artifact_gas_estimates = None;
217        let mut artifact_function_debug_data = None;
218        let mut artifact_method_identifiers = None;
219        let mut artifact_assembly = None;
220        let mut artifact_legacy_assembly = None;
221        let mut artifact_storage_layout = None;
222        let mut artifact_transient_storage_layout = None;
223        let mut generated_sources = None;
224        let mut opcodes = None;
225
226        let Contract {
227            abi,
228            metadata,
229            userdoc,
230            devdoc,
231            ir,
232            storage_layout,
233            transient_storage_layout,
234            evm,
235            ewasm,
236            ir_optimized,
237            ir_optimized_ast,
238        } = contract;
239
240        if self.additional_values.metadata || self.additional_files.metadata {
241            if let Some(LosslessMetadata { raw_metadata, metadata }) = metadata {
242                artifact_raw_metadata = Some(raw_metadata);
243                artifact_metadata = Some(metadata);
244            }
245        }
246        if self.additional_values.userdoc {
247            artifact_userdoc = Some(userdoc);
248        }
249        if self.additional_values.devdoc {
250            artifact_devdoc = Some(devdoc);
251        }
252        if self.additional_values.ewasm || self.additional_files.ewasm {
253            artifact_ewasm = ewasm;
254        }
255        if self.additional_values.ir || self.additional_files.ir {
256            artifact_ir = ir;
257        }
258        if self.additional_values.ir_optimized || self.additional_files.ir_optimized {
259            artifact_ir_optimized = ir_optimized;
260        }
261        if self.additional_values.ir_optimized_ast {
262            artifact_ir_optimized_ast = ir_optimized_ast;
263        }
264        if self.additional_values.storage_layout {
265            artifact_storage_layout = Some(storage_layout);
266        }
267        if self.additional_values.transient_storage_layout {
268            artifact_transient_storage_layout = Some(transient_storage_layout);
269        }
270
271        if let Some(evm) = evm {
272            let Evm {
273                assembly,
274                mut bytecode,
275                deployed_bytecode,
276                method_identifiers,
277                gas_estimates,
278                legacy_assembly,
279            } = evm;
280
281            if self.additional_values.function_debug_data {
282                artifact_function_debug_data =
283                    bytecode.as_mut().map(|code| std::mem::take(&mut code.function_debug_data));
284            }
285            if self.additional_values.generated_sources {
286                generated_sources =
287                    bytecode.as_mut().map(|code| std::mem::take(&mut code.generated_sources));
288            }
289
290            if self.additional_values.opcodes {
291                opcodes = bytecode.as_mut().and_then(|code| code.opcodes.take())
292            }
293
294            artifact_bytecode = bytecode.map(Into::into);
295            artifact_deployed_bytecode = deployed_bytecode.map(Into::into);
296            artifact_method_identifiers = Some(method_identifiers);
297
298            if self.additional_values.gas_estimates {
299                artifact_gas_estimates = gas_estimates;
300            }
301            if self.additional_values.assembly || self.additional_files.assembly {
302                artifact_assembly = assembly;
303            }
304
305            if self.additional_values.legacy_assembly || self.additional_files.legacy_assembly {
306                artifact_legacy_assembly = legacy_assembly;
307            }
308        }
309
310        ConfigurableContractArtifact {
311            abi,
312            bytecode: artifact_bytecode,
313            deployed_bytecode: artifact_deployed_bytecode,
314            assembly: artifact_assembly,
315            legacy_assembly: artifact_legacy_assembly,
316            opcodes,
317            function_debug_data: artifact_function_debug_data,
318            method_identifiers: artifact_method_identifiers,
319            gas_estimates: artifact_gas_estimates,
320            raw_metadata: artifact_raw_metadata,
321            metadata: artifact_metadata,
322            storage_layout: artifact_storage_layout,
323            transient_storage_layout: artifact_transient_storage_layout,
324            userdoc: artifact_userdoc,
325            devdoc: artifact_devdoc,
326            ir: artifact_ir,
327            ir_optimized: artifact_ir_optimized,
328            ir_optimized_ast: artifact_ir_optimized_ast,
329            ewasm: artifact_ewasm,
330            id: source_file.as_ref().map(|s| s.id),
331            ast: source_file.and_then(|s| s.ast.clone()),
332            generated_sources: generated_sources.unwrap_or_default(),
333        }
334    }
335
336    fn standalone_source_file_to_artifact(
337        &self,
338        _path: &Path,
339        file: &VersionedSourceFile,
340    ) -> Option<Self::Artifact> {
341        file.source_file.ast.clone().map(|ast| ConfigurableContractArtifact {
342            abi: Some(JsonAbi::default()),
343            id: Some(file.source_file.id),
344            ast: Some(ast),
345            bytecode: Some(CompactBytecode::empty()),
346            deployed_bytecode: Some(CompactDeployedBytecode::empty()),
347            ..Default::default()
348        })
349    }
350
351    /// We want to enforce recompilation if artifact is missing data we need for writing extra
352    /// files.
353    fn is_dirty(&self, artifact_file: &ArtifactFile<Self::Artifact>) -> Result<bool, SolcError> {
354        let artifact = &artifact_file.artifact;
355        let ExtraOutputFiles {
356            abi: _,
357            metadata,
358            ir,
359            ir_optimized,
360            ewasm,
361            assembly,
362            legacy_assembly,
363            source_map,
364            generated_sources,
365            bytecode: _,
366            deployed_bytecode: _,
367            __non_exhaustive: _,
368        } = self.additional_files;
369
370        if metadata && artifact.metadata.is_none() {
371            return Ok(true);
372        }
373        if ir && artifact.ir.is_none() {
374            return Ok(true);
375        }
376        if ir_optimized && artifact.ir_optimized.is_none() {
377            return Ok(true);
378        }
379        if ewasm && artifact.ewasm.is_none() {
380            return Ok(true);
381        }
382        if assembly && artifact.assembly.is_none() {
383            return Ok(true);
384        }
385        if legacy_assembly && artifact.legacy_assembly.is_none() {
386            return Ok(true);
387        }
388        if source_map && artifact.get_source_map_str().is_none() {
389            return Ok(true);
390        }
391        if generated_sources {
392            // We can't check if generated sources are missing or just empty.
393            return Ok(true);
394        }
395        Ok(false)
396    }
397
398    /// Writes extra files for cached artifacts based on [Self::additional_files].
399    fn handle_cached_artifacts(
400        &self,
401        artifacts: &crate::Artifacts<Self::Artifact>,
402    ) -> Result<(), SolcError> {
403        for artifacts in artifacts.values() {
404            for artifacts in artifacts.values() {
405                for artifact_file in artifacts {
406                    let file = &artifact_file.file;
407                    let artifact = &artifact_file.artifact;
408                    self.additional_files.process_abi(artifact.abi.as_ref(), file)?;
409                    self.additional_files.process_assembly(artifact.assembly.as_deref(), file)?;
410                    self.additional_files
411                        .process_legacy_assembly(artifact.legacy_assembly.clone(), file)?;
412                    self.additional_files
413                        .process_bytecode(artifact.bytecode.as_ref().map(|b| &b.object), file)?;
414                    self.additional_files.process_deployed_bytecode(
415                        artifact
416                            .deployed_bytecode
417                            .as_ref()
418                            .and_then(|d| d.bytecode.as_ref())
419                            .map(|b| &b.object),
420                        file,
421                    )?;
422                    self.additional_files
423                        .process_generated_sources(Some(&artifact.generated_sources), file)?;
424                    self.additional_files.process_ir(artifact.ir.as_deref(), file)?;
425                    self.additional_files
426                        .process_ir_optimized(artifact.ir_optimized.as_deref(), file)?;
427                    self.additional_files.process_ewasm(artifact.ewasm.as_ref(), file)?;
428                    self.additional_files.process_metadata(artifact.metadata.as_ref(), file)?;
429                    self.additional_files
430                        .process_source_map(artifact.get_source_map_str().as_deref(), file)?;
431                }
432            }
433        }
434
435        Ok(())
436    }
437}
438
439/// Determines the additional values to include in the contract's artifact file
440#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
441pub struct ExtraOutputValues {
442    pub ast: bool,
443    pub userdoc: bool,
444    pub devdoc: bool,
445    pub method_identifiers: bool,
446    pub storage_layout: bool,
447    pub transient_storage_layout: bool,
448    pub assembly: bool,
449    pub legacy_assembly: bool,
450    pub gas_estimates: bool,
451    pub metadata: bool,
452    pub ir: bool,
453    pub ir_optimized: bool,
454    pub ir_optimized_ast: bool,
455    pub ewasm: bool,
456    pub function_debug_data: bool,
457    pub generated_sources: bool,
458    pub source_map: bool,
459    pub opcodes: bool,
460
461    /// PRIVATE: This structure may grow, As such, constructing this structure should
462    /// _always_ be done using a public constructor or update syntax:
463    ///
464    /// ```
465    /// use foundry_compilers::ExtraOutputValues;
466    ///
467    /// let config = ExtraOutputValues { ir: true, ..Default::default() };
468    /// ```
469    #[doc(hidden)]
470    pub __non_exhaustive: (),
471}
472
473impl ExtraOutputValues {
474    /// Returns an instance where all values are set to `true`
475    pub fn all() -> Self {
476        Self {
477            ast: true,
478            userdoc: true,
479            devdoc: true,
480            method_identifiers: true,
481            storage_layout: true,
482            transient_storage_layout: true,
483            assembly: true,
484            legacy_assembly: true,
485            gas_estimates: true,
486            metadata: true,
487            ir: true,
488            ir_optimized: true,
489            ir_optimized_ast: true,
490            ewasm: true,
491            function_debug_data: true,
492            generated_sources: true,
493            source_map: true,
494            opcodes: true,
495            __non_exhaustive: (),
496        }
497    }
498
499    /// Sets the values based on a set of `ContractOutputSelection`
500    pub fn from_output_selection(
501        settings: impl IntoIterator<Item = ContractOutputSelection>,
502    ) -> Self {
503        let mut config = Self::default();
504        for value in settings.into_iter() {
505            match value {
506                ContractOutputSelection::DevDoc => {
507                    config.devdoc = true;
508                }
509                ContractOutputSelection::UserDoc => {
510                    config.userdoc = true;
511                }
512                ContractOutputSelection::Metadata => {
513                    config.metadata = true;
514                }
515                ContractOutputSelection::Ir => {
516                    config.ir = true;
517                }
518                ContractOutputSelection::IrOptimized => {
519                    config.ir_optimized = true;
520                }
521                ContractOutputSelection::StorageLayout => {
522                    config.storage_layout = true;
523                }
524                ContractOutputSelection::Evm(evm) => match evm {
525                    EvmOutputSelection::All => {
526                        config.assembly = true;
527                        config.legacy_assembly = true;
528                        config.gas_estimates = true;
529                        config.method_identifiers = true;
530                        config.generated_sources = true;
531                        config.source_map = true;
532                        config.opcodes = true;
533                    }
534                    EvmOutputSelection::Assembly => {
535                        config.assembly = true;
536                    }
537                    EvmOutputSelection::LegacyAssembly => {
538                        config.legacy_assembly = true;
539                    }
540                    EvmOutputSelection::MethodIdentifiers => {
541                        config.method_identifiers = true;
542                    }
543                    EvmOutputSelection::GasEstimates => {
544                        config.gas_estimates = true;
545                    }
546                    EvmOutputSelection::ByteCode(BytecodeOutputSelection::FunctionDebugData) => {
547                        config.function_debug_data = true;
548                    }
549                    EvmOutputSelection::ByteCode(BytecodeOutputSelection::Opcodes) => {
550                        config.opcodes = true;
551                    }
552                    EvmOutputSelection::ByteCode(BytecodeOutputSelection::GeneratedSources) => {
553                        config.generated_sources = true;
554                    }
555                    EvmOutputSelection::ByteCode(BytecodeOutputSelection::SourceMap) => {
556                        config.source_map = true;
557                    }
558                    _ => {}
559                },
560                ContractOutputSelection::Ewasm(_) => {
561                    config.ewasm = true;
562                }
563                ContractOutputSelection::IrOptimizedAst => {
564                    config.ir_optimized_ast = true;
565                }
566                ContractOutputSelection::TransientStorageLayout => {
567                    config.transient_storage_layout = true;
568                }
569                ContractOutputSelection::Abi => {}
570            }
571        }
572
573        config
574    }
575}
576
577/// Determines what to emit as an additional file
578#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
579pub struct ExtraOutputFiles {
580    pub abi: bool,
581    pub metadata: bool,
582    pub ir: bool,
583    pub ir_optimized: bool,
584    pub ewasm: bool,
585    pub assembly: bool,
586    pub legacy_assembly: bool,
587    pub source_map: bool,
588    pub generated_sources: bool,
589    pub bytecode: bool,
590    pub deployed_bytecode: bool,
591
592    /// PRIVATE: This structure may grow, As such, constructing this structure should
593    /// _always_ be done using a public constructor or update syntax:
594    ///
595    /// ```
596    /// use foundry_compilers::ExtraOutputFiles;
597    ///
598    /// let config = ExtraOutputFiles { metadata: true, ..Default::default() };
599    /// ```
600    #[doc(hidden)]
601    pub __non_exhaustive: (),
602}
603
604impl ExtraOutputFiles {
605    /// Returns an instance where all values are set to `true`
606    pub fn all() -> Self {
607        Self {
608            abi: true,
609            metadata: true,
610            ir: true,
611            ir_optimized: true,
612            ewasm: true,
613            assembly: true,
614            legacy_assembly: true,
615            source_map: true,
616            generated_sources: true,
617            bytecode: true,
618            deployed_bytecode: true,
619            __non_exhaustive: (),
620        }
621    }
622
623    /// Sets the values based on a set of `ContractOutputSelection`
624    pub fn from_output_selection(
625        settings: impl IntoIterator<Item = ContractOutputSelection>,
626    ) -> Self {
627        let mut config = Self::default();
628        for value in settings.into_iter() {
629            match value {
630                ContractOutputSelection::Abi => {
631                    config.abi = true;
632                }
633                ContractOutputSelection::Metadata => {
634                    config.metadata = true;
635                }
636                ContractOutputSelection::Ir => {
637                    config.ir = true;
638                }
639                ContractOutputSelection::IrOptimized => {
640                    config.ir_optimized = true;
641                }
642                ContractOutputSelection::Evm(evm) => match evm {
643                    EvmOutputSelection::All => {
644                        config.assembly = true;
645                        config.legacy_assembly = true;
646                        config.generated_sources = true;
647                        config.source_map = true;
648                        config.bytecode = true;
649                        config.deployed_bytecode = true;
650                    }
651                    EvmOutputSelection::Assembly => {
652                        config.assembly = true;
653                    }
654                    EvmOutputSelection::LegacyAssembly => {
655                        config.legacy_assembly = true;
656                    }
657                    EvmOutputSelection::ByteCode(BytecodeOutputSelection::GeneratedSources) => {
658                        config.generated_sources = true;
659                    }
660                    EvmOutputSelection::ByteCode(BytecodeOutputSelection::Object) => {
661                        config.bytecode = true;
662                    }
663                    EvmOutputSelection::ByteCode(BytecodeOutputSelection::SourceMap) => {
664                        config.source_map = true;
665                    }
666                    EvmOutputSelection::DeployedByteCode(DeployedBytecodeOutputSelection::All)
667                    | EvmOutputSelection::DeployedByteCode(
668                        DeployedBytecodeOutputSelection::Object,
669                    ) => {
670                        config.deployed_bytecode = true;
671                    }
672                    _ => {}
673                },
674                ContractOutputSelection::Ewasm(_) => {
675                    config.ewasm = true;
676                }
677                _ => {}
678            }
679        }
680        config
681    }
682
683    fn process_abi(&self, abi: Option<&JsonAbi>, file: &Path) -> Result<(), SolcError> {
684        if self.abi {
685            if let Some(abi) = abi {
686                let file = file.with_extension("abi.json");
687                fs::write(&file, serde_json::to_string_pretty(abi)?)
688                    .map_err(|err| SolcError::io(err, file))?
689            }
690        }
691        Ok(())
692    }
693
694    fn process_metadata(&self, metadata: Option<&Metadata>, file: &Path) -> Result<(), SolcError> {
695        if self.metadata {
696            if let Some(metadata) = metadata {
697                let file = file.with_extension("metadata.json");
698                fs::write(&file, serde_json::to_string_pretty(metadata)?)
699                    .map_err(|err| SolcError::io(err, file))?
700            }
701        }
702        Ok(())
703    }
704
705    fn process_ir(&self, ir: Option<&str>, file: &Path) -> Result<(), SolcError> {
706        if self.ir {
707            if let Some(ir) = ir {
708                let file = file.with_extension("ir");
709                fs::write(&file, ir).map_err(|err| SolcError::io(err, file))?
710            }
711        }
712        Ok(())
713    }
714
715    fn process_ir_optimized(
716        &self,
717        ir_optimized: Option<&str>,
718        file: &Path,
719    ) -> Result<(), SolcError> {
720        if self.ir_optimized {
721            if let Some(ir_optimized) = ir_optimized {
722                let file = file.with_extension("iropt");
723                fs::write(&file, ir_optimized).map_err(|err| SolcError::io(err, file))?
724            }
725        }
726        Ok(())
727    }
728
729    fn process_ewasm(&self, ewasm: Option<&Ewasm>, file: &Path) -> Result<(), SolcError> {
730        if self.ewasm {
731            if let Some(ewasm) = ewasm {
732                let file = file.with_extension("ewasm");
733                fs::write(&file, serde_json::to_vec_pretty(ewasm)?)
734                    .map_err(|err| SolcError::io(err, file))?;
735            }
736        }
737        Ok(())
738    }
739
740    fn process_assembly(&self, asm: Option<&str>, file: &Path) -> Result<(), SolcError> {
741        if self.assembly {
742            if let Some(asm) = asm {
743                let file = file.with_extension("asm");
744                fs::write(&file, asm).map_err(|err| SolcError::io(err, file))?
745            }
746        }
747        Ok(())
748    }
749
750    fn process_legacy_assembly(
751        &self,
752        asm: Option<serde_json::Value>,
753        file: &Path,
754    ) -> Result<(), SolcError> {
755        if self.legacy_assembly {
756            if let Some(legacy_asm) = asm {
757                let file = file.with_extension("legacyAssembly.json");
758                fs::write(&file, format!("{legacy_asm}")).map_err(|err| SolcError::io(err, file))?
759            }
760        }
761        Ok(())
762    }
763
764    fn process_generated_sources(
765        &self,
766        generated_sources: Option<&Vec<GeneratedSource>>,
767        file: &Path,
768    ) -> Result<(), SolcError> {
769        if self.generated_sources {
770            if let Some(generated_sources) = generated_sources {
771                let file = file.with_extension("gensources");
772                fs::write(&file, serde_json::to_vec_pretty(generated_sources)?)
773                    .map_err(|err| SolcError::io(err, file))?;
774            }
775        }
776        Ok(())
777    }
778
779    fn process_source_map(&self, source_map: Option<&str>, file: &Path) -> Result<(), SolcError> {
780        if self.source_map {
781            if let Some(source_map) = source_map {
782                let file = file.with_extension("sourcemap");
783                fs::write(&file, source_map).map_err(|err| SolcError::io(err, file))?
784            }
785        }
786        Ok(())
787    }
788
789    fn process_bytecode(
790        &self,
791        bytecode: Option<&BytecodeObject>,
792        file: &Path,
793    ) -> Result<(), SolcError> {
794        if self.bytecode {
795            if let Some(bytecode) = bytecode {
796                let code = hex::encode(bytecode.as_ref());
797                let file = file.with_extension("bin");
798                fs::write(&file, code).map_err(|err| SolcError::io(err, file))?
799            }
800        }
801        Ok(())
802    }
803
804    fn process_deployed_bytecode(
805        &self,
806        deployed: Option<&BytecodeObject>,
807        file: &Path,
808    ) -> Result<(), SolcError> {
809        if self.deployed_bytecode {
810            if let Some(deployed) = deployed {
811                let code = hex::encode(deployed.as_ref());
812                let file = file.with_extension("deployed-bin");
813                fs::write(&file, code).map_err(|err| SolcError::io(err, file))?
814            }
815        }
816        Ok(())
817    }
818
819    /// Write the set values as separate files
820    pub fn write_extras(&self, contract: &Contract, file: &Path) -> Result<(), SolcError> {
821        self.process_abi(contract.abi.as_ref(), file)?;
822        self.process_metadata(contract.metadata.as_ref().map(|m| &m.metadata), file)?;
823        self.process_ir(contract.ir.as_deref(), file)?;
824        self.process_ir_optimized(contract.ir_optimized.as_deref(), file)?;
825        self.process_ewasm(contract.ewasm.as_ref(), file)?;
826
827        let evm = contract.evm.as_ref();
828        self.process_assembly(evm.and_then(|evm| evm.assembly.as_deref()), file)?;
829        self.process_legacy_assembly(evm.and_then(|evm| evm.legacy_assembly.clone()), file)?;
830
831        let bytecode = evm.and_then(|evm| evm.bytecode.as_ref());
832        self.process_generated_sources(bytecode.map(|b| &b.generated_sources), file)?;
833
834        let deployed_bytecode = evm.and_then(|evm| evm.deployed_bytecode.as_ref());
835        self.process_source_map(bytecode.and_then(|b| b.source_map.as_deref()), file)?;
836        self.process_bytecode(bytecode.map(|b| &b.object), file)?;
837        self.process_deployed_bytecode(
838            deployed_bytecode.and_then(|d| d.bytecode.as_ref()).map(|b| &b.object),
839            file,
840        )?;
841
842        Ok(())
843    }
844}