hugr_core/envelope/
package_json.rs

1//! Encoding / decoding of Package json, used in the `PackageJson` envelope format.
2use derive_more::{Display, Error, From};
3use itertools::Itertools;
4use std::io;
5
6use crate::extension::resolution::ExtensionResolutionError;
7use crate::extension::{ExtensionRegistry, PRELUDE_REGISTRY};
8use crate::hugr::ExtensionError;
9use crate::package::Package;
10use crate::{Extension, Hugr, HugrView};
11
12/// Read a Package in json format from an io reader.
13pub(super) fn from_json_reader(
14    reader: impl io::Read,
15    extension_registry: &ExtensionRegistry,
16) -> Result<Package, PackageEncodingError> {
17    let val: serde_json::Value = serde_json::from_reader(reader)?;
18
19    let PackageDeser {
20        modules,
21        extensions: pkg_extensions,
22    } = serde_json::from_value::<PackageDeser>(val.clone())?;
23    let mut modules = modules.into_iter().map(|h| h.0).collect_vec();
24
25    // TODO: We don't currently store transitive extension dependencies in the
26    // package's extensions. For example, if we use a `collections.list` const
27    // value but don't use anything in `prelude` we would not include `prelude` in
28    // the package's extensions. But this would then fail when loading the
29    // extensions, as we _need_ the prelude to load the `collections.list` op
30    // definitions here.
31    //
32    // The current fix is to always include the prelude when decoding, but this
33    // only works for transitive `prelude` dependencies.
34    //
35    // Chains of custom extensions will cause this to fail.
36    let extension_registry = if PRELUDE_REGISTRY
37        .iter()
38        .any(|e| !extension_registry.contains(&e.name))
39    {
40        let mut reg_with_prelude = extension_registry.clone();
41        reg_with_prelude.extend(PRELUDE_REGISTRY.iter().cloned());
42        reg_with_prelude
43    } else {
44        extension_registry.clone()
45    };
46
47    let mut pkg_extensions = ExtensionRegistry::new_with_extension_resolution(
48        pkg_extensions,
49        &(&extension_registry).into(),
50    )?;
51
52    // Resolve the operations in the modules using the defined registries.
53    let mut combined_registry = extension_registry.clone();
54    combined_registry.extend(&pkg_extensions);
55
56    for module in &mut modules {
57        module.resolve_extension_defs(&combined_registry)?;
58        pkg_extensions.extend(module.extensions());
59    }
60
61    Ok(Package {
62        modules,
63        extensions: pkg_extensions,
64    })
65}
66
67/// Write the Package in json format into an io writer.
68pub(super) fn to_json_writer<'h>(
69    hugrs: impl IntoIterator<Item = &'h Hugr>,
70    extensions: &ExtensionRegistry,
71    writer: impl io::Write,
72) -> Result<(), PackageEncodingError> {
73    let pkg_ser = PackageSer {
74        modules: hugrs.into_iter().map(HugrSer).collect(),
75        extensions: extensions.iter().map(|e| e.as_ref()).collect(),
76    };
77    serde_json::to_writer(writer, &pkg_ser)?;
78    Ok(())
79}
80
81/// Error raised while loading a package.
82#[derive(Debug, Display, Error, From)]
83#[non_exhaustive]
84pub enum PackageEncodingError {
85    /// Error raised while parsing the package json.
86    JsonEncoding(serde_json::Error),
87    /// Error raised while reading from a file.
88    IOError(io::Error),
89    /// Could not resolve the extension needed to encode the hugr.
90    ExtensionResolution(ExtensionResolutionError),
91    /// Could not resolve the runtime extensions for the hugr.
92    RuntimeExtensionResolution(ExtensionError),
93}
94
95/// A private package structure implementing the serde traits.
96///
97/// We use this to avoid exposing a public implementation of Serialize/Deserialize,
98/// as the json definition is not stable, and should always be wrapped in an Envelope.
99#[derive(Debug, serde::Serialize)]
100struct PackageSer<'h> {
101    pub modules: Vec<HugrSer<'h>>,
102    pub extensions: Vec<&'h Extension>,
103}
104#[derive(Debug, serde::Serialize)]
105#[serde(transparent)]
106struct HugrSer<'h>(#[serde(serialize_with = "Hugr::serde_serialize")] pub &'h Hugr);
107
108/// A private package structure implementing the serde traits.
109///
110/// We use this to avoid exposing a public implementation of Serialize/Deserialize,
111/// as the json definition is not stable, and should always be wrapped in an Envelope.
112#[derive(Debug, serde::Deserialize)]
113struct PackageDeser {
114    pub modules: Vec<HugrDeser>,
115    pub extensions: Vec<Extension>,
116}
117#[derive(Debug, serde::Deserialize)]
118#[serde(transparent)]
119struct HugrDeser(#[serde(deserialize_with = "Hugr::serde_deserialize")] pub Hugr);