forge/cmd/
flatten.rs

1use clap::{Parser, ValueHint};
2use eyre::Result;
3use foundry_cli::{
4    opts::{BuildOpts, ProjectPathOpts},
5    utils::LoadConfig,
6};
7use foundry_common::{compile::with_compilation_reporter, fs};
8use foundry_compilers::{
9    compilers::solc::SolcLanguage,
10    error::SolcError,
11    flatten::{Flattener, FlattenerError},
12};
13use std::path::PathBuf;
14
15/// CLI arguments for `forge flatten`.
16#[derive(Clone, Debug, Parser)]
17pub struct FlattenArgs {
18    /// The path to the contract to flatten.
19    #[arg(value_hint = ValueHint::FilePath, value_name = "PATH")]
20    pub target_path: PathBuf,
21
22    /// The path to output the flattened contract.
23    ///
24    /// If not specified, the flattened contract will be output to stdout.
25    #[arg(
26        long,
27        short,
28        value_hint = ValueHint::FilePath,
29        value_name = "PATH",
30    )]
31    pub output: Option<PathBuf>,
32
33    #[command(flatten)]
34    project_paths: ProjectPathOpts,
35}
36
37impl FlattenArgs {
38    pub fn run(self) -> Result<()> {
39        let Self { target_path, output, project_paths } = self;
40
41        // flatten is a subset of `BuildArgs` so we can reuse that to get the config
42        let build = BuildOpts { project_paths, ..Default::default() };
43        let config = build.load_config()?;
44        let project = config.ephemeral_project()?;
45
46        let target_path = dunce::canonicalize(target_path)?;
47
48        let flattener =
49            with_compilation_reporter(true, || Flattener::new(project.clone(), &target_path));
50
51        let flattened = match flattener {
52            Ok(flattener) => Ok(flattener.flatten()),
53            Err(FlattenerError::Compilation(_)) => {
54                // Fallback to the old flattening implementation if we couldn't compile the target
55                // successfully. This would be the case if the target has invalid
56                // syntax. (e.g. Solang)
57                project.paths.with_language::<SolcLanguage>().flatten(&target_path)
58            }
59            Err(FlattenerError::Other(err)) => Err(err),
60        }
61        .map_err(|err: SolcError| eyre::eyre!("Failed to flatten: {err}"))?;
62
63        match output {
64            Some(output) => {
65                fs::create_dir_all(output.parent().unwrap())?;
66                fs::write(&output, flattened)?;
67                sh_println!("Flattened file written at {}", output.display())?;
68            }
69            None => sh_println!("{flattened}")?,
70        };
71
72        Ok(())
73    }
74}