Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(forge): inspect - default to pretty output #9705

Merged
merged 18 commits into from
Jan 23, 2025
92 changes: 69 additions & 23 deletions crates/forge/bin/cmd/inspect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ use foundry_compilers::{
utils::canonicalize,
};
use regex::Regex;
use std::{fmt, sync::LazyLock};
use serde_json::{Map, Value};
use std::{collections::BTreeMap, fmt, sync::LazyLock};

/// CLI arguments for `forge inspect`.
#[derive(Clone, Debug, Parser)]
Expand All @@ -30,7 +31,7 @@ pub struct InspectArgs {
pub field: ContractArtifactField,

/// Pretty print the selected field, if supported.
#[arg(long)]
#[arg(long, default_value = "true")]
pub pretty: bool,
yash-atreya marked this conversation as resolved.
Show resolved Hide resolved
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should keep this with a warning or document as a breaking change

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, will be marked in release notes as breaking change given the breaking label


/// All build arguments are supported
Expand All @@ -40,7 +41,7 @@ pub struct InspectArgs {

impl InspectArgs {
pub fn run(self) -> Result<()> {
let Self { contract, field, build, pretty } = self;
let Self { contract, field, build, pretty: _ } = self;

trace!(target: "forge", ?field, ?contract, "running forge inspect");

Expand Down Expand Up @@ -85,7 +86,7 @@ impl InspectArgs {
.abi
.as_ref()
.ok_or_else(|| eyre::eyre!("Failed to fetch lossless ABI"))?;
if pretty {
if !shell::is_json() {
let source = foundry_cli::utils::abi_to_solidity(abi, &contract.name)?;
sh_println!("{source}")?;
} else {
Expand All @@ -105,7 +106,7 @@ impl InspectArgs {
print_json_str(&artifact.legacy_assembly, None)?;
}
ContractArtifactField::MethodIdentifiers => {
print_json(&artifact.method_identifiers)?;
print_method_identifiers(&artifact.method_identifiers)?;
}
ContractArtifactField::GasEstimates => {
print_json(&artifact.gas_estimates)?;
Expand Down Expand Up @@ -146,7 +147,7 @@ impl InspectArgs {
);
}
}
print_json(&out)?;
print_errors_events(&out, true)?;
}
ContractArtifactField::Events => {
let mut out = serde_json::Map::new();
Expand All @@ -162,7 +163,7 @@ impl InspectArgs {
);
}
}
print_json(&out)?;
print_errors_events(&out, false)?;
}
ContractArtifactField::Eof => {
print_eof(artifact.deployed_bytecode.and_then(|b| b.bytecode))?;
Expand All @@ -185,30 +186,75 @@ pub fn print_storage_layout(storage_layout: Option<&StorageLayout>) -> Result<()
return print_json(&storage_layout)
}

let mut table = Table::new();
table.apply_modifier(UTF8_ROUND_CORNERS);

table.set_header(vec![
let headers = vec![
Cell::new("Name"),
Cell::new("Type"),
Cell::new("Slot"),
Cell::new("Offset"),
Cell::new("Bytes"),
Cell::new("Contract"),
]);

for slot in &storage_layout.storage {
let storage_type = storage_layout.types.get(&slot.storage_type);
table.add_row([
slot.label.as_str(),
storage_type.map_or("?", |t| &t.label),
&slot.slot,
&slot.offset.to_string(),
storage_type.map_or("?", |t| &t.number_of_bytes),
&slot.contract,
]);
];

print_table(headers, |mut table| {
for slot in &storage_layout.storage {
let storage_type = storage_layout.types.get(&slot.storage_type);
table.add_row([
slot.label.as_str(),
storage_type.map_or("?", |t| &t.label),
&slot.slot,
&slot.offset.to_string(),
storage_type.map_or("?", |t| &t.number_of_bytes),
&slot.contract,
]);
}
table
})
}

fn print_method_identifiers(method_identifiers: &Option<BTreeMap<String, String>>) -> Result<()> {
let Some(method_identifiers) = method_identifiers else {
eyre::bail!("Could not get method identifiers");
};

if shell::is_json() {
return print_json(method_identifiers)
}

let headers = vec![Cell::new("Method"), Cell::new("Identifier")];

print_table(headers, |mut table| {
for (method, identifier) in method_identifiers {
table.add_row([method, identifier]);
}
table
})
}

fn print_errors_events(map: &Map<String, Value>, is_err: bool) -> Result<()> {
if shell::is_json() {
return print_json(map);
}

let headers = if is_err {
vec![Cell::new("Error"), Cell::new("Selector")]
} else {
vec![Cell::new("Event"), Cell::new("Topic")]
};
print_table(headers, |mut table| {
for (method, selector) in map {
table.add_row([method, selector.as_str().unwrap()]);
}
table
})
}

fn print_table(headers: Vec<Cell>, add_rows: impl Fn(Table) -> Table) -> Result<()> {
yash-atreya marked this conversation as resolved.
Show resolved Hide resolved
let mut table = Table::new();
table.apply_modifier(UTF8_ROUND_CORNERS);
table.set_header(headers);

let table = add_rows(table);

sh_println!("\n{table}\n")?;
Ok(())
}
Expand Down
Loading