wasmer_pack_cli/
show.rs

1use std::{fmt::Display, io::Write, path::PathBuf, str::FromStr};
2
3use anyhow::{Context, Error};
4use clap::Parser;
5use wasmer_pack::{Metadata, Package};
6
7#[derive(Debug, Parser)]
8pub struct Show {
9    /// The format to use when emitting metadata.
10    #[clap(short, long, default_value_t = Format::Text)]
11    format: Format,
12    /// The Pirita file to read.
13    input: PathBuf,
14}
15
16impl Show {
17    pub fn run(self) -> Result<(), Error> {
18        let pkg = crate::pirita::load_from_disk(&self.input).with_context(|| {
19            format!("Unable to load a package from \"{}\"", self.input.display())
20        })?;
21
22        let summary: Summary = summarize(&pkg);
23
24        let mut stdout = std::io::stdout();
25        match self.format {
26            Format::Json => {
27                summary.write_json(stdout.lock())?;
28                writeln!(stdout)?;
29            }
30            Format::Text => {
31                summary.dump(stdout.lock())?;
32            }
33        }
34
35        Ok(())
36    }
37}
38
39fn summarize(pkg: &Package) -> Summary {
40    let Metadata {
41        description,
42        package_name,
43        version,
44        ..
45    } = pkg.metadata();
46
47    let bindings = pkg
48        .libraries()
49        .iter()
50        .map(|lib| Library {
51            interface_name: lib.interface_name().to_string(),
52            wasi: lib.requires_wasi(),
53        })
54        .collect();
55
56    let commands = pkg
57        .commands()
58        .iter()
59        .map(|cmd| Command {
60            name: cmd.name.clone(),
61        })
62        .collect();
63
64    Summary {
65        description: description.clone(),
66        name: package_name.to_string(),
67        version: version.clone(),
68        bindings,
69        commands,
70    }
71}
72
73#[derive(Debug, serde::Serialize)]
74struct Summary {
75    name: String,
76    version: String,
77    description: Option<String>,
78    bindings: Vec<Library>,
79    commands: Vec<Command>,
80}
81
82impl Summary {
83    fn write_json(&self, writer: impl Write) -> Result<(), Error> {
84        serde_json::to_writer_pretty(writer, self)?;
85        Ok(())
86    }
87
88    fn dump(&self, mut writer: impl Write) -> Result<(), Error> {
89        let Summary {
90            name,
91            version,
92            description,
93            commands,
94            bindings,
95        } = self;
96
97        writeln!(writer, "{name} {version}")?;
98
99        if let Some(description) = description {
100            writeln!(writer, "{description}")?;
101        }
102
103        if !commands.is_empty() {
104            writeln!(writer, "Commands:")?;
105            for command in commands {
106                command.dump(&mut writer)?;
107            }
108        }
109
110        if !bindings.is_empty() {
111            writeln!(writer, "Bindings:")?;
112            for lib in bindings {
113                lib.dump(&mut writer)?;
114            }
115        }
116
117        Ok(())
118    }
119}
120
121#[derive(Debug, serde::Serialize)]
122struct Command {
123    name: String,
124}
125
126impl Command {
127    fn dump(&self, mut writer: impl Write) -> Result<(), Error> {
128        let Command { name } = self;
129
130        writeln!(writer, "- {name}")?;
131
132        Ok(())
133    }
134}
135
136#[derive(Debug, serde::Serialize)]
137struct Library {
138    interface_name: String,
139    wasi: bool,
140}
141
142impl Library {
143    fn dump(&self, mut writer: impl Write) -> Result<(), Error> {
144        let Library {
145            interface_name,
146            wasi,
147        } = self;
148
149        write!(writer, "- {interface_name}")?;
150
151        if *wasi {
152            write!(writer, " (wasi)")?;
153        }
154
155        writeln!(writer)?;
156
157        Ok(())
158    }
159}
160
161#[derive(Debug, Copy, Clone, PartialEq, Eq, clap::ValueEnum)]
162pub enum Format {
163    Json,
164    Text,
165}
166
167impl Display for Format {
168    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
169        match self {
170            Format::Json => f.write_str("json"),
171            Format::Text => f.write_str("text"),
172        }
173    }
174}
175
176impl FromStr for Format {
177    type Err = Error;
178
179    fn from_str(s: &str) -> Result<Self, Self::Err> {
180        match s {
181            "json" => Ok(Format::Json),
182            "text" => Ok(Format::Text),
183            other => anyhow::bail!("Expected \"json\" or \"text\", found \"{other}\""),
184        }
185    }
186}