use std::{
fs,
path::{Path, PathBuf},
};
use bulloak_foundry::{constants::DEFAULT_SOL_VERSION, scaffold::scaffold};
use clap::Parser;
use owo_colors::OwoColorize;
use serde::{Deserialize, Serialize};
use solang_forge_fmt::format;
use crate::{cli::Cli, glob::expand_glob};
#[doc(hidden)]
#[derive(Parser, Debug, Clone, Serialize, Deserialize)]
pub struct Scaffold {
pub files: Vec<PathBuf>,
#[arg(short = 'w', long, group = "file-handling", default_value_t = false)]
pub write_files: bool,
#[arg(
short = 'f',
long,
requires = "file-handling",
default_value_t = false
)]
pub force_write: bool,
#[arg(short = 's', long, default_value = DEFAULT_SOL_VERSION)]
pub solidity_version: String,
#[arg(short = 'S', long = "vm-skip", default_value_t = false)]
pub with_vm_skip: bool,
#[arg(short = 'm', long, default_value_t = false)]
pub skip_modifiers: bool,
#[arg(short = 'F', long = "format-descriptions", default_value_t = false)]
pub format_descriptions: bool,
}
impl Default for Scaffold {
fn default() -> Self {
Scaffold::parse_from(Vec::<String>::new())
}
}
impl Scaffold {
pub(crate) fn run(&self, cfg: &Cli) {
let mut files = Vec::with_capacity(self.files.len());
for pattern in &self.files {
match expand_glob(pattern.clone()) {
Ok(iter) => files.extend(iter),
Err(e) => {
eprintln!(
"{}: could not expand {}: {}",
"warn".yellow(),
pattern.display(),
e
);
}
}
}
let errors = files
.iter()
.filter_map(|file| {
self.process_file(file, cfg)
.map_err(|e| (file.as_path(), e))
.err()
})
.collect::<Vec<_>>();
if !errors.is_empty() {
Scaffold::report_errors(&errors);
std::process::exit(1);
}
}
fn process_file(&self, file: &Path, cfg: &Cli) -> anyhow::Result<()> {
let text = fs::read_to_string(file)?;
let emitted = scaffold(&text, &cfg.into())?;
let formatted = format(&emitted).unwrap_or_else(|err| {
eprintln!("{}: {}", "WARN".yellow(), err);
emitted
});
if self.write_files {
let file = file.with_extension("t.sol");
self.write_file(&formatted, &file);
} else {
println!("{formatted}");
}
Ok(())
}
fn write_file(&self, text: &str, file: &PathBuf) {
if file.exists() && !self.force_write {
eprintln!(
"{}: Skipped emitting {:?}",
"warn".yellow(),
file.as_path().blue()
);
eprintln!(
" {} The corresponding `.t.sol` file already exists",
"=".blue()
);
return;
}
if let Err(err) = fs::write(file, text) {
eprintln!("{}: {err}", "error".red());
};
}
fn report_errors(errors: &[(&Path, anyhow::Error)]) {
for (file, err) in errors {
eprintln!("{err}");
eprintln!("file: {}", file.display());
}
eprintln!(
"\n{}: Could not scaffold {} files. Check the output above or run {}, which might prove helpful.",
"warn".yellow(),
errors.len().yellow(),
"bulloak check".blue()
);
}
}