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