diff --git a/Cargo.lock b/Cargo.lock index c78924b3e6..4dc9a09f23 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -291,6 +291,17 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" +[[package]] +name = "comfy-table" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24f165e7b643266ea80cb858aed492ad9280e3e05ce24d4a99d7d7b889b6a4d9" +dependencies = [ + "strum", + "strum_macros", + "unicode-width", +] + [[package]] name = "component" version = "0.0.0" @@ -1458,6 +1469,12 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "rustversion" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e819f2bc632f285be6d7cd36e25940d45b2391dd6d9b939e79de557f7014248" + [[package]] name = "ruzstd" version = "0.7.2" @@ -1600,6 +1617,25 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" +[[package]] +name = "strum" +version = "0.26.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" + +[[package]] +name = "strum_macros" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" +dependencies = [ + "heck 0.5.0", + "proc-macro2", + "quote", + "rustversion", + "syn", +] + [[package]] name = "symbolic_expressions" version = "5.0.3" @@ -1979,6 +2015,7 @@ dependencies = [ "bitflags", "clap", "clap_complete", + "comfy-table", "cpp_demangle", "env_logger", "gimli 0.30.0", diff --git a/Cargo.toml b/Cargo.toml index 239bf9add7..0a906a5d19 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -74,6 +74,7 @@ anyhow = "1.0.58" arbitrary = "1.1.0" clap = { version = "4.0.0", features = ["derive"] } clap_complete = "4.4.7" +comfy-table = { version = "7.1.3", default-features = false } criterion = { version = "0.5.1", default-features = false } env_logger = "0.11" indexmap = { version = "2.0.0", default-features = false } @@ -122,6 +123,7 @@ env_logger = { workspace = true } log = { workspace = true } clap = { workspace = true, features = ['wrap_help'] } clap_complete = { workspace = true, optional = true } +comfy-table = { workspace = true } tempfile = "3.2.0" wat = { workspace = true, features = ['dwarf', 'component-model'] } termcolor = { workspace = true } diff --git a/crates/wasm-metadata/src/payload.rs b/crates/wasm-metadata/src/payload.rs index 6acb22aaae..7aa5741711 100644 --- a/crates/wasm-metadata/src/payload.rs +++ b/crates/wasm-metadata/src/payload.rs @@ -1,4 +1,3 @@ -use std::fmt::{self, Display}; use std::ops::Range; use anyhow::Result; @@ -181,48 +180,4 @@ impl Payload { Self::Component { children, .. } => children.push(child), } } - - fn display(&self, f: &mut fmt::Formatter, indent: usize) -> fmt::Result { - let spaces = std::iter::repeat(" ").take(indent).collect::(); - match self { - Self::Module(Metadata { - name, producers, .. - }) => { - if let Some(name) = name { - writeln!(f, "{spaces}module {name}:")?; - } else { - writeln!(f, "{spaces}module:")?; - } - if let Some(producers) = producers { - producers.display(f, indent + 4)?; - } - Ok(()) - } - Self::Component { - children, - metadata: Metadata { - name, producers, .. - }, - } => { - if let Some(name) = name { - writeln!(f, "{spaces}component {name}:")?; - } else { - writeln!(f, "{spaces}component:")?; - } - if let Some(producers) = producers { - producers.display(f, indent + 4)?; - } - for c in children { - c.display(f, indent + 4)?; - } - Ok(()) - } - } - } -} - -impl Display for Payload { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - self.display(f, 0) - } } diff --git a/crates/wasm-metadata/src/producers.rs b/crates/wasm-metadata/src/producers.rs index 104e7498df..180f3adba9 100644 --- a/crates/wasm-metadata/src/producers.rs +++ b/crates/wasm-metadata/src/producers.rs @@ -1,7 +1,6 @@ use anyhow::Result; use indexmap::{map::Entry, IndexMap}; use serde_derive::Serialize; -use std::fmt; use wasm_encoder::Encode; use wasmparser::{BinaryReader, KnownCustom, Parser, ProducersSectionReader}; @@ -150,27 +149,6 @@ impl Producers { pub fn add_to_wasm(&self, input: &[u8]) -> Result> { rewrite_wasm(&None, self, &None, &None, &None, &None, &None, input) } - - pub(crate) fn display(&self, f: &mut fmt::Formatter, indent: usize) -> fmt::Result { - let indent = std::iter::repeat(" ").take(indent).collect::(); - for (fieldname, fieldvalues) in self.0.iter() { - writeln!(f, "{indent}{fieldname}:")?; - for (name, version) in fieldvalues { - if version.is_empty() { - writeln!(f, "{indent} {name}")?; - } else { - writeln!(f, "{indent} {name}: {version}")?; - } - } - } - Ok(()) - } -} - -impl fmt::Display for Producers { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - self.display(f, 0) - } } /// Contents of a producers field diff --git a/src/bin/wasm-tools/metadata.rs b/src/bin/wasm-tools/metadata.rs index 3e0ff6e13e..48d787de78 100644 --- a/src/bin/wasm-tools/metadata.rs +++ b/src/bin/wasm-tools/metadata.rs @@ -1,6 +1,12 @@ -use anyhow::Result; 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 termcolor::WriteColor; +use wasm_metadata::{Metadata, Payload}; + /// Manipulate metadata (module name, producers) to a WebAssembly file. #[derive(clap::Parser)] pub enum Opts { @@ -44,11 +50,11 @@ impl ShowOpts { let input = self.io.parse_input_wasm()?; let mut output = self.io.output_writer()?; - let metadata = wasm_metadata::Payload::from_binary(&input)?; + let payload = wasm_metadata::Payload::from_binary(&input)?; if self.json { - write!(output, "{}", serde_json::to_string(&metadata)?)?; + write!(output, "{}", serde_json::to_string(&payload)?)?; } else { - write!(output, "{metadata}")?; + fmt_payload(&payload, &mut output)?; } Ok(()) } @@ -82,3 +88,75 @@ impl AddOpts { Ok(()) } } + +fn fmt_payload(payload: &Payload, f: &mut Box) -> Result<()> { + 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", "VALUE"]); + let Metadata { + name, + author, + description, + producers, + licenses, + source, + homepage, + range, + } = payload.metadata(); + + // Print the basic information + let kind = match payload { + Payload::Component { .. } => "component", + Payload::Module(_) => "module", + }; + table.add_row(vec!["kind", &kind]); + let name = name.as_deref().unwrap_or(""); + table.add_row(vec!["name", &name]); + table.add_row(vec![ + "range", + &format!("0x{:x}..0x{:x}", range.start, range.end), + ]); + + // Print the OCI annotations + if let Some(description) = description { + table.add_row(vec!["description", &description.to_string()]); + } + if let Some(licenses) = licenses { + table.add_row(vec!["licenses", &licenses.to_string()]); + } + if let Some(author) = author { + table.add_row(vec!["author", &author.to_string()]); + } + if let Some(source) = source { + table.add_row(vec!["source", &source.to_string()]); + } + if let Some(homepage) = homepage { + table.add_row(vec!["homepage", &homepage.to_string()]); + } + + if let Some(producers) = producers { + for (name, pairs) in producers.iter() { + for (field, version) in pairs.iter() { + match version.len() { + 0 => table.add_row(vec![name, &format!("{field}")]), + _ => table.add_row(vec![name, &format!("{field} [{version}]")]), + }; + } + } + } + + // Write the table to the writer + writeln!(f, "{table}")?; + + if let Payload::Component { children, .. } = payload { + for payload in children { + fmt_payload(payload, f)?; + } + } + + Ok(()) +} diff --git a/tests/cli/add-metadata-merge-sections.wat.stdout b/tests/cli/add-metadata-merge-sections.wat.stdout index 7ce7a97bed..accb94fc95 100644 --- a/tests/cli/add-metadata-merge-sections.wat.stdout +++ b/tests/cli/add-metadata-merge-sections.wat.stdout @@ -1,6 +1,15 @@ -module: - language: - foo: 3 - bar: 1 - sdk: - foo: 2 +╭──────────┬───────────╮ +│ KIND ┆ VALUE │ +╞══════════╪═══════════╡ +│ kind ┆ module │ +├╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┤ +│ name ┆ │ +├╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┤ +│ range ┆ 0x0..0x36 │ +├╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┤ +│ language ┆ foo [3] │ +├╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┤ +│ language ┆ bar [1] │ +├╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┤ +│ sdk ┆ foo [2] │ +╰──────────┴───────────╯ \ No newline at end of file diff --git a/tests/cli/add-metadata-overwrite-name.wat.stdout b/tests/cli/add-metadata-overwrite-name.wat.stdout index 085a5600b2..e02f74d9b0 100644 --- a/tests/cli/add-metadata-overwrite-name.wat.stdout +++ b/tests/cli/add-metadata-overwrite-name.wat.stdout @@ -1 +1,9 @@ -module foo: +╭───────┬───────────╮ +│ KIND ┆ VALUE │ +╞═══════╪═══════════╡ +│ kind ┆ module │ +├╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┤ +│ name ┆ foo │ +├╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┤ +│ range ┆ 0x0..0x15 │ +╰───────┴───────────╯ \ No newline at end of file diff --git a/tests/cli/add-metadata.wat.stdout b/tests/cli/add-metadata.wat.stdout index 775d000171..3fa15ddc76 100644 --- a/tests/cli/add-metadata.wat.stdout +++ b/tests/cli/add-metadata.wat.stdout @@ -1,7 +1,15 @@ -module foo: - language: - bar: 1 - processed-by: - baz: 1 - sdk: - my-sdk: 2 +╭──────────────┬────────────╮ +│ KIND ┆ VALUE │ +╞══════════════╪════════════╡ +│ kind ┆ module │ +├╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌┤ +│ name ┆ foo │ +├╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌┤ +│ range ┆ 0x0..0x54 │ +├╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌┤ +│ language ┆ bar [1] │ +├╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌┤ +│ processed-by ┆ baz [1] │ +├╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌┤ +│ sdk ┆ my-sdk [2] │ +╰──────────────┴────────────╯ \ No newline at end of file diff --git a/tests/cli/metadata-add-component.wat.stdout b/tests/cli/metadata-add-component.wat.stdout index d099bd0b07..bc4c6875e7 100644 --- a/tests/cli/metadata-add-component.wat.stdout +++ b/tests/cli/metadata-add-component.wat.stdout @@ -1,4 +1,20 @@ -component foo: - language: - foo: 1 - module: +╭──────────┬───────────╮ +│ KIND ┆ VALUE │ +╞══════════╪═══════════╡ +│ kind ┆ component │ +├╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┤ +│ name ┆ foo │ +├╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┤ +│ range ┆ 0x0..0x65 │ +├╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┤ +│ language ┆ foo [1] │ +╰──────────┴───────────╯ +╭───────┬───────────╮ +│ KIND ┆ VALUE │ +╞═══════╪═══════════╡ +│ kind ┆ module │ +├╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┤ +│ name ┆ │ +├╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┤ +│ range ┆ 0xa..0x31 │ +╰───────┴───────────╯ \ No newline at end of file diff --git a/tests/cli/metadata-component.wat.stdout b/tests/cli/metadata-component.wat.stdout index b96562a443..dc109a55cf 100644 --- a/tests/cli/metadata-component.wat.stdout +++ b/tests/cli/metadata-component.wat.stdout @@ -1,3 +1,27 @@ -component my-name: - module submodule: - module another submodule: +╭───────┬───────────╮ +│ KIND ┆ VALUE │ +╞═══════╪═══════════╡ +│ kind ┆ component │ +├╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┤ +│ name ┆ my-name │ +├╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┤ +│ range ┆ 0x0..0x88 │ +╰───────┴───────────╯ +╭───────┬───────────╮ +│ KIND ┆ VALUE │ +╞═══════╪═══════════╡ +│ kind ┆ module │ +├╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┤ +│ name ┆ submodule │ +├╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┤ +│ range ┆ 0xa..0x25 │ +╰───────┴───────────╯ +╭───────┬───────────────────╮ +│ KIND ┆ VALUE │ +╞═══════╪═══════════════════╡ +│ kind ┆ module │ +├╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ +│ name ┆ another submodule │ +├╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ +│ range ┆ 0x27..0x4a │ +╰───────┴───────────────────╯ \ No newline at end of file diff --git a/tests/cli/metadata.wat.stdout b/tests/cli/metadata.wat.stdout index 71d91c1359..b255f9ff31 100644 --- a/tests/cli/metadata.wat.stdout +++ b/tests/cli/metadata.wat.stdout @@ -1 +1,9 @@ -module: +╭───────┬───────────╮ +│ KIND ┆ VALUE │ +╞═══════╪═══════════╡ +│ kind ┆ module │ +├╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┤ +│ name ┆ │ +├╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┤ +│ range ┆ 0x0..0x8 │ +╰───────┴───────────╯ \ No newline at end of file