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 super::WithGenerator;
7use crate::extension::ExtensionRegistry;
8use crate::extension::resolution::ExtensionResolutionError;
9use crate::package::Package;
10use crate::{Extension, Hugr};
11
12/// Read a Package in json format from an io reader.
13/// Returns package and the combined extension registry
14/// of the provided registry and the package extensions.
15pub(super) fn from_json_reader(
16    reader: impl io::Read,
17    extension_registry: &ExtensionRegistry,
18) -> Result<Package, PackageEncodingError> {
19    let val: serde_json::Value = serde_json::from_reader(reader)?;
20
21    let PackageDeser {
22        modules,
23        extensions: pkg_extensions,
24    } = serde_json::from_value::<PackageDeser>(val.clone())?;
25    let mut modules = modules.into_iter().map(|h| h.0).collect_vec();
26    let pkg_extensions = ExtensionRegistry::new_with_extension_resolution(
27        pkg_extensions,
28        &extension_registry.into(),
29    )
30    .map_err(|err| WithGenerator::new(err, &modules))?;
31
32    // Resolve the operations in the modules using the defined registries.
33    let mut combined_registry = extension_registry.clone();
34    combined_registry.extend(&pkg_extensions);
35
36    modules
37        .iter_mut()
38        .try_for_each(|module| module.resolve_extension_defs(&combined_registry))
39        .map_err(|err| WithGenerator::new(err, &modules))?;
40
41    Ok(Package {
42        modules,
43        extensions: pkg_extensions,
44    })
45}
46
47/// Write the Package in json format into an io writer.
48pub(super) fn to_json_writer<'h>(
49    hugrs: impl IntoIterator<Item = &'h Hugr>,
50    extensions: &ExtensionRegistry,
51    writer: impl io::Write,
52) -> Result<(), PackageEncodingError> {
53    let pkg_ser = PackageSer {
54        modules: hugrs.into_iter().map(HugrSer).collect(),
55        extensions: extensions.iter().map(std::convert::AsRef::as_ref).collect(),
56    };
57
58    // Validate the hugr serializations against the schema.
59    //
60    // NOTE: The schema definition is currently broken, so this check always succeeds.
61    // See <https://github.com/CQCL/hugr/issues/2401>
62    #[cfg(all(test, not(miri)))]
63    if std::env::var("HUGR_TEST_SCHEMA").is_ok_and(|x| !x.is_empty()) {
64        use crate::hugr::serialize::test::check_hugr_serialization_schema;
65
66        for hugr in &pkg_ser.modules {
67            check_hugr_serialization_schema(hugr.0);
68        }
69    }
70
71    serde_json::to_writer(writer, &pkg_ser)?;
72    Ok(())
73}
74
75/// Error raised while loading a package.
76#[derive(Debug, Display, Error, From)]
77#[non_exhaustive]
78#[display("Error reading or writing a package in JSON format.")]
79pub enum PackageEncodingError {
80    /// Error raised while parsing the package json.
81    JsonEncoding(#[from] serde_json::Error),
82    /// Error raised while reading from a file.
83    IOError(#[from] io::Error),
84    /// Could not resolve the extension needed to encode the hugr.
85    ExtensionResolution(#[from] WithGenerator<ExtensionResolutionError>),
86}
87
88/// A private package structure implementing the serde traits.
89///
90/// We use this to avoid exposing a public implementation of Serialize/Deserialize,
91/// as the json definition is not stable, and should always be wrapped in an Envelope.
92#[derive(Debug, serde::Serialize)]
93struct PackageSer<'h> {
94    pub modules: Vec<HugrSer<'h>>,
95    pub extensions: Vec<&'h Extension>,
96}
97#[derive(Debug, serde::Serialize)]
98#[serde(transparent)]
99struct HugrSer<'h>(#[serde(serialize_with = "Hugr::serde_serialize")] pub &'h Hugr);
100
101/// A private package structure implementing the serde traits.
102///
103/// We use this to avoid exposing a public implementation of Serialize/Deserialize,
104/// as the json definition is not stable, and should always be wrapped in an Envelope.
105#[derive(Debug, serde::Deserialize)]
106struct PackageDeser {
107    pub modules: Vec<HugrDeser>,
108    pub extensions: Vec<Extension>,
109}
110#[derive(Debug, serde::Deserialize)]
111#[serde(transparent)]
112struct HugrDeser(#[serde(deserialize_with = "Hugr::serde_deserialize")] pub Hugr);