1use anyhow::Result;
3use clap::Parser;
4use clio::Output;
5use hugr::envelope::{EnvelopeConfig, EnvelopeFormat, ZstdConfig};
6use std::io::{Read, Write};
7
8use crate::CliError;
9use crate::hugr_io::HugrInputArgs;
10
11#[derive(Parser, Debug)]
13#[clap(version = "1.0", long_about = None)]
14#[clap(about = "Convert a HUGR between different envelope formats.")]
15#[group(id = "hugr")]
16#[non_exhaustive]
17pub struct ConvertArgs {
18 #[command(flatten)]
20 pub input_args: HugrInputArgs,
21
22 #[clap(short, long, value_parser, default_value = "-")]
24 pub output: Output,
25
26 #[clap(short, long, value_name = "FORMAT")]
28 pub format: Option<String>,
29
30 #[clap(long, conflicts_with_all = ["format", "binary"])]
33 pub text: bool,
34
35 #[clap(long, conflicts_with_all = ["format", "text"])]
38 pub binary: bool,
39
40 #[clap(long)]
42 pub compress: bool,
43
44 #[clap(long, value_name = "LEVEL", requires = "compress")]
47 pub compression_level: Option<u8>,
48}
49
50impl ConvertArgs {
51 pub fn run_convert_with_io<R: Read, W: Write>(
58 &mut self,
59 input_override: Option<R>,
60 mut output_override: Option<W>,
61 ) -> Result<()> {
62 let (env_config, package) = self
63 .input_args
64 .get_described_package_with_reader(input_override)?;
65
66 let mut config = if self.text {
68 EnvelopeConfig::text()
69 } else if self.binary {
70 EnvelopeConfig::binary()
71 } else {
72 let format = match &self.format {
74 Some(fmt) => match fmt.as_str() {
75 #[expect(deprecated)]
76 "json" => EnvelopeFormat::PackageJson,
77 "model" => EnvelopeFormat::Model,
78 "model-exts" => EnvelopeFormat::ModelWithExtensions,
79 "model-text" | "s-expression" => EnvelopeFormat::SExpression,
80 "model-text-exts" | "s-expression-exts" => {
81 EnvelopeFormat::SExpressionWithExtensions
82 }
83 _ => Err(CliError::InvalidFormat(fmt.clone()))?,
84 },
85 None => env_config.header.config().format, };
87 EnvelopeConfig::new(format)
88 };
89
90 if let Some(level) = self.compress.then_some(self.compression_level).flatten() {
92 config = config.with_zstd(ZstdConfig::new(level));
93 }
94
95 if let Some(ref mut writer) = output_override {
97 hugr::envelope::write_envelope(writer, &package, config)?;
98 } else {
99 hugr::envelope::write_envelope(&mut self.output, &package, config)?;
100 }
101
102 Ok(())
103 }
104
105 pub fn run_convert(&mut self) -> Result<()> {
107 self.run_convert_with_io(None::<&[u8]>, None::<Vec<u8>>)
108 }
109}