Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ ahash = { version = "0.8.11", default-features = false }
anyhow = "1.0.58"
arbitrary = "1.1.0"
bitflags = "2.5.0"
bytesize = "2.0.0"
clap = { version = "4.0.0", features = ["derive"] }
clap_complete = "4.4.7"
comfy-table = { version = "7.1.3", default-features = false }
Expand Down Expand Up @@ -168,6 +169,7 @@ wit-parser = { workspace = true, optional = true, features = ['decoding', 'wat',

# Dependencies of `metadata`
wasm-metadata = { workspace = true, features = ["clap"], optional = true }
bytesize = { workspace = true }

# Dependencies of `wit-smith`
wit-smith = { workspace = true, features = ["clap"], optional = true }
Expand Down
126 changes: 121 additions & 5 deletions src/bin/wasm-tools/metadata.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
use bytesize::ByteSize;
use std::io::Write;

use anyhow::Result;
use comfy_table::modifiers::UTF8_ROUND_CORNERS;
use comfy_table::presets::UTF8_FULL;
use comfy_table::{ContentArrangement, Table};
use comfy_table::{CellAlignment, ContentArrangement, Table};
use termcolor::WriteColor;
use wasm_metadata::{Metadata, Payload};

Expand Down Expand Up @@ -54,7 +55,8 @@ impl ShowOpts {
if self.json {
write!(output, "{}", serde_json::to_string(&payload)?)?;
} else {
write_table(&payload, &mut output)?;
write_summary_table(&payload, &mut output)?;
write_details_table(&payload, &mut output)?;
}
Ok(())
}
Expand Down Expand Up @@ -89,8 +91,122 @@ impl AddOpts {
}
}

/// Write a table containing a wasm binary's metadata to a writer
fn write_table(payload: &Payload, f: &mut Box<dyn WriteColor>) -> Result<()> {
/// Write a table containing a summarized overview of a wasm binary's metadata to
/// a writer.
fn write_summary_table(payload: &Payload, f: &mut Box<dyn WriteColor>) -> Result<()> {
// Prepare a table and get the individual metadata
let mut table = Table::new();
table
.load_preset(UTF8_FULL)
.apply_modifier(UTF8_ROUND_CORNERS)
.set_content_arrangement(ContentArrangement::Dynamic)
.set_width(80)
.set_header(vec!["KIND", "NAME", "SIZE", "SIZE%", "LANGUAGE", "PARENT"]);

table
.column_mut(2)
.expect("This should be the SIZE column")
.set_cell_alignment(CellAlignment::Right);

table
.column_mut(3)
.expect("This should be the SIZE% column")
.set_cell_alignment(CellAlignment::Right);

// Get the max value of the `range` field. This is the upper memory bound.
fn find_range_max(max: &mut usize, payload: &Payload) {
if let Payload::Component { children, .. } = payload {
for child in children {
let range = &child.metadata().range;
if range.end > *max {
*max = range.end;
}
find_range_max(max, child);
}
}
}

let mut range_max = 0;
find_range_max(&mut range_max, payload);

// Recursively add all children to the table
write_summary_table_inner(&payload, "<root>", &mut 0, range_max, f, &mut table)?;

// Write the table to the writer
writeln!(f, "{table}")?;

Ok(())
}

// The recursing inner function of `write_summary_table`
fn write_summary_table_inner(
payload: &Payload,
parent: &str,
unknown_id: &mut u16,
range_max: usize,
f: &mut Box<dyn WriteColor>,
table: &mut Table,
) -> Result<()> {
let Metadata {
name,
range,
producers,
..
} = payload.metadata();

let name = match name.as_deref() {
Some(name) => name.to_owned(),
None => {
let name = format!("unknown({unknown_id})");
*unknown_id += 1;
name
}
};
let size = ByteSize::b((range.end - range.start) as u64)
.display()
.si_short()
.to_string();

let usep = match ((range.end - range.start) as f64 / range_max as f64 * 100.0).round() {
// If the item was truly empty, it wouldn't be part of the binary
0.0..=1.0 => "<1%".to_string(),
// We're hedging against the low-ends, this hedges against the high-ends.
// Makes sure we don't see a mix of <1% and 100% in the same table, unless
// the item is actually 100% of the binary.
100.0 if range.end != range_max => ">99%".to_string(),
usep => format!("{}%", usep),
};
let kind = match payload {
Payload::Component { .. } => "component",
Payload::Module(_) => "module",
};
let languages = match producers {
Some(producers) => match producers.iter().find(|(name, _)| *name == "language") {
Some((_, pairs)) => pairs
.iter()
.map(|(lang, _)| lang.to_owned())
.collect::<Vec<_>>()
.join(", "),
None => "-".to_string(),
},
None => "-".to_string(),
};

table.add_row(vec![&kind, &*name, &*size, &usep, &languages, &parent]);

// Recursively print any children
if let Payload::Component { children, .. } = payload {
for payload in children {
write_summary_table_inner(payload, &name, unknown_id, range_max, f, table)?;
}
}

Ok(())
}

/// Write a table containing a detailed overview of a wasm binary's metadata to
/// a writer.
fn write_details_table(payload: &Payload, f: &mut Box<dyn WriteColor>) -> Result<()> {
// Prepare a table and get the individual metadata
let mut table = Table::new();
table
Expand Down Expand Up @@ -194,7 +310,7 @@ fn write_table(payload: &Payload, f: &mut Box<dyn WriteColor>) -> Result<()> {
// Recursively print any children
if let Payload::Component { children, .. } = payload {
for payload in children {
write_table(payload, f)?;
write_details_table(payload, f)?;
}
}

Expand Down
Loading