Skip to main content

hugr_cli/
extensions.rs

1//! Dump standard extensions in serialized form.
2use anyhow::Result;
3use clap::Parser;
4use hugr::Extension;
5use hugr::extension::ExtensionRegistry;
6use std::{io::Write, path::PathBuf};
7
8/// Dump the standard extensions.
9#[derive(Parser, Debug)]
10#[clap(version = "1.0", long_about = None)]
11#[clap(about = "Write standard extensions.")]
12#[group(id = "hugr")]
13#[non_exhaustive]
14pub struct ExtArgs {
15    /// Output directory
16    #[arg(
17        default_value = ".",
18        short,
19        long,
20        value_name = "OUTPUT",
21        help = "Output directory."
22    )]
23    pub outdir: PathBuf,
24    /// Do not add the extension version to the output filename, e.g. "foo.json".
25    ///
26    /// Only writes the latest version of each extension.
27    ///
28    /// If this flag is not present, extensions are written with the version in the filename, e.g. "foo-1.2.3.json".
29    #[arg(
30        long,
31        help = "Do not add the extension version to the output filename.",
32        default_value_t = false
33    )]
34    pub unversioned: bool,
35}
36
37impl ExtArgs {
38    /// Write out the standard extensions in serialized form.
39    ///
40    /// Qualified names of extensions used to generate directories under the specified output directory.
41    /// The extension version is added to the json filename.
42    ///
43    /// E.g. extension "foo.bar.baz" will be written to "OUTPUT/foo/bar/baz-1.2.3.json".
44    pub fn run_dump(&self, registry: &ExtensionRegistry) -> Result<()> {
45        if !self.unversioned {
46            registry
47                .iter_all()
48                .try_for_each(|ext| self.dump_extension(ext))?;
49        } else {
50            // Only one version of each extension can be written out, so we take the latest version of each.
51            registry
52                .iter()
53                .map(|ext| ext.latest())
54                .try_for_each(|ext| self.dump_extension(ext))?;
55        }
56
57        Ok(())
58    }
59
60    fn dump_extension(&self, ext: &Extension) -> Result<()> {
61        let mut path = self.outdir.clone();
62        let mut parts = ext.name().split('.').peekable();
63        while let Some(part) = parts.next() {
64            if parts.peek().is_some() {
65                path.push(part);
66                continue;
67            }
68
69            // Choose whether to add the extension version to the output
70            // filename or leave it without.
71            if self.unversioned {
72                path.push(format!("{part}.json"));
73            } else {
74                path.push(format!("{part}-{}.json", ext.version()));
75            }
76        }
77
78        std::fs::create_dir_all(path.clone().parent().unwrap())?;
79        // file buffer
80        let mut file = std::fs::File::create(&path)?;
81
82        serde_json::to_writer_pretty(&mut file, &ext)?;
83
84        // write newline, for pre-commit end of file check that edits the file to
85        // add newlines if missing.
86        file.write_all(b"\n")?;
87
88        Ok(())
89    }
90}