Skip to content

Commit 7dbbf26

Browse files
committed
refactor!: move envelope writing to writer.rs
New WriteError + deprecate EnvelopeError
1 parent 2a89f28 commit 7dbbf26

File tree

6 files changed

+230
-184
lines changed

6 files changed

+230
-184
lines changed

hugr-cli/src/lib.rs

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ use clap::{Parser, crate_version};
2626
#[cfg(feature = "tracing")]
2727
use clap_verbosity_flag::VerbosityFilter;
2828
use clap_verbosity_flag::{InfoLevel, Verbosity};
29-
use hugr::envelope::EnvelopeError;
3029
use hugr::package::PackageValidationError;
3130
use thiserror::Error;
3231
#[cfg(feature = "tracing")]
@@ -90,9 +89,6 @@ pub enum CliError {
9089
#[error("Error validating HUGR.")]
9190
/// Errors produced by the `validate` subcommand.
9291
Validate(#[from] PackageValidationError),
93-
#[error("Error decoding HUGR envelope.")]
94-
/// Errors produced by the `validate` subcommand.
95-
Envelope(#[from] EnvelopeError),
9692
/// Pretty error when the user passes a non-envelope file.
9793
#[error(
9894
"Input file is not a HUGR envelope. Invalid magic number.\n\nUse `--hugr-json` to read a raw HUGR JSON file instead."

hugr-core/src/envelope.rs

Lines changed: 11 additions & 155 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
//! envelopes from/to readers and writers, or call [`Package::load`] and
99
//! [`Package::store`] directly.
1010
//!
11+
//!
1112
//! ## Payload formats
1213
//!
1314
//! The envelope may encode the HUGR in different formats, listed in
@@ -38,8 +39,10 @@ pub mod description;
3839
mod header;
3940
mod package_json;
4041
mod reader;
42+
mod writer;
4143
use reader::EnvelopeReader;
4244
pub use reader::PayloadError;
45+
pub use writer::WriteError;
4346

4447
pub mod serde_with;
4548

@@ -158,7 +161,7 @@ pub fn write_envelope(
158161
writer: impl Write,
159162
package: &Package,
160163
config: EnvelopeConfig,
161-
) -> Result<(), EnvelopeError> {
164+
) -> Result<(), WriteError> {
162165
write_envelope_impl(writer, &package.modules, &package.extensions, config)
163166
}
164167

@@ -167,95 +170,12 @@ pub fn write_envelope(
167170
/// It is recommended to use a buffered writer for better performance.
168171
/// See [`std::io::BufWriter`] for more information.
169172
pub(crate) fn write_envelope_impl<'h>(
170-
mut writer: impl Write,
173+
writer: impl Write,
171174
hugrs: impl IntoIterator<Item = &'h Hugr>,
172175
extensions: &ExtensionRegistry,
173176
config: EnvelopeConfig,
174-
) -> Result<(), EnvelopeError> {
175-
let header = config.make_header();
176-
header.write(&mut writer)?;
177-
178-
match config.zstd {
179-
#[cfg(feature = "zstd")]
180-
Some(zstd) => {
181-
let writer = zstd::Encoder::new(writer, zstd.level())?.auto_finish();
182-
write_impl(writer, hugrs, extensions, config)?;
183-
}
184-
#[cfg(not(feature = "zstd"))]
185-
Some(_) => return Err(EnvelopeError::ZstdUnsupported),
186-
None => write_impl(writer, hugrs, extensions, config)?,
187-
}
188-
189-
Ok(())
190-
}
191-
/// Error type for envelope operations.
192-
#[derive(Debug, Error)]
193-
#[non_exhaustive]
194-
pub enum EnvelopeError {
195-
/// The specified payload format is not supported.
196-
#[error("Payload format {format} is not supported.{}",
197-
match feature {
198-
Some(f) => format!(" This requires the '{f}' feature for `hugr`."),
199-
None => String::new()
200-
},
201-
)]
202-
FormatUnsupported {
203-
/// The unsupported format.
204-
format: EnvelopeFormat,
205-
/// Optionally, the feature required to support this format.
206-
feature: Option<&'static str>,
207-
},
208-
/// Not all envelope formats can be represented as ASCII.
209-
///
210-
/// This error is used when trying to store the envelope into a string.
211-
#[error("Envelope format {format} cannot be represented as ASCII.")]
212-
NonASCIIFormat {
213-
/// The unsupported format.
214-
format: EnvelopeFormat,
215-
},
216-
/// Envelope encoding required zstd compression, but the feature is not enabled.
217-
#[error("Zstd compression is not supported. This requires the 'zstd' feature for `hugr`.")]
218-
ZstdUnsupported,
219-
220-
/// JSON serialization error.
221-
#[error(transparent)]
222-
SerdeError {
223-
/// The source error.
224-
#[from]
225-
source: serde_json::Error,
226-
},
227-
/// IO read/write error.
228-
#[error(transparent)]
229-
IO {
230-
/// The source error.
231-
#[from]
232-
source: std::io::Error,
233-
},
234-
/// Error writing a json package to the payload.
235-
#[error(transparent)]
236-
PackageEncoding {
237-
/// The source error.
238-
#[from]
239-
source: PackageEncodingError,
240-
},
241-
242-
/// Error writing a HUGR model payload.
243-
#[error(transparent)]
244-
ModelWrite {
245-
/// The source error.
246-
#[from]
247-
source: hugr_model::v0::binary::WriteError,
248-
},
249-
250-
/// The specified payload format is not supported.
251-
#[error(
252-
"The envelope configuration has unknown {}. Please update your HUGR version.",
253-
if flag_ids.len() == 1 {format!("flag #{}", flag_ids[0])} else {format!("flags {}", flag_ids.iter().join(", "))}
254-
)]
255-
FlagUnsupported {
256-
/// The unrecognized flag bits.
257-
flag_ids: Vec<usize>,
258-
},
177+
) -> Result<(), WriteError> {
178+
writer::write_envelope(writer, hugrs, extensions, config)
259179
}
260180

261181
#[derive(Debug, Error)]
@@ -283,69 +203,6 @@ fn check_model_version(format: EnvelopeFormat) -> Result<(), FormatUnsupportedEr
283203
Ok(())
284204
}
285205

286-
/// Internal implementation of [`write_envelope`] to call with/without the zstd compression wrapper.
287-
fn write_impl<'h>(
288-
writer: impl Write,
289-
hugrs: impl IntoIterator<Item = &'h Hugr>,
290-
extensions: &ExtensionRegistry,
291-
config: EnvelopeConfig,
292-
) -> Result<(), EnvelopeError> {
293-
match config.format {
294-
EnvelopeFormat::PackageJson => package_json::to_json_writer(hugrs, extensions, writer)?,
295-
EnvelopeFormat::Model
296-
| EnvelopeFormat::ModelWithExtensions
297-
| EnvelopeFormat::ModelText
298-
| EnvelopeFormat::ModelTextWithExtensions => {
299-
encode_model(writer, hugrs, extensions, config.format)?;
300-
}
301-
}
302-
Ok(())
303-
}
304-
305-
fn encode_model<'h>(
306-
mut writer: impl Write,
307-
hugrs: impl IntoIterator<Item = &'h Hugr>,
308-
extensions: &ExtensionRegistry,
309-
format: EnvelopeFormat,
310-
) -> Result<(), EnvelopeError> {
311-
use hugr_model::v0::{binary::write_to_writer, bumpalo::Bump};
312-
313-
use crate::export::export_package;
314-
315-
if format.model_version() != Some(0) {
316-
return Err(EnvelopeError::FormatUnsupported {
317-
format,
318-
feature: None,
319-
});
320-
}
321-
322-
// Prepend extensions for binary model.
323-
if format == EnvelopeFormat::ModelTextWithExtensions {
324-
serde_json::to_writer(&mut writer, &extensions.iter().collect_vec())?;
325-
}
326-
327-
let bump = Bump::default();
328-
let model_package = export_package(hugrs, extensions, &bump);
329-
330-
match format {
331-
EnvelopeFormat::Model | EnvelopeFormat::ModelWithExtensions => {
332-
write_to_writer(&model_package, &mut writer)?;
333-
}
334-
EnvelopeFormat::ModelText | EnvelopeFormat::ModelTextWithExtensions => {
335-
let model_package = model_package.as_ast().unwrap();
336-
writeln!(writer, "{model_package}")?;
337-
}
338-
_ => unreachable!(),
339-
}
340-
341-
// Append extensions for binary model.
342-
if format == EnvelopeFormat::ModelWithExtensions {
343-
serde_json::to_writer(writer, &extensions.iter().collect_vec())?;
344-
}
345-
346-
Ok(())
347-
}
348-
349206
#[derive(Debug, Error)]
350207
#[error(
351208
"Extension '{name}' version mismatch: registered version is {registered}, but used version is {used}"
@@ -475,10 +332,8 @@ pub(crate) mod test {
475332
#[rstest]
476333
fn errors() {
477334
let package = simple_package();
478-
assert_matches!(
479-
package.store_str(EnvelopeConfig::binary()),
480-
Err(EnvelopeError::NonASCIIFormat { .. })
481-
);
335+
// The binary format is not ASCII-printable, so store_str should fail
336+
assert!(package.store_str(EnvelopeConfig::binary()).is_err());
482337
}
483338

484339
#[rstest]
@@ -507,7 +362,8 @@ pub(crate) mod test {
507362
match cfg!(feature = "zstd") {
508363
true => res.unwrap(),
509364
false => {
510-
assert_matches!(res, Err(EnvelopeError::ZstdUnsupported));
365+
// ZstdUnsupported error should be raised
366+
assert!(res.is_err());
511367
return;
512368
}
513369
}

hugr-core/src/envelope/header.rs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,6 @@ use std::num::NonZeroU8;
66
use itertools::Itertools;
77
use thiserror::Error;
88

9-
use super::EnvelopeError;
10-
119
/// Magic number identifying the start of an envelope.
1210
///
1311
/// In ascii, this is "`HUGRiHJv`". The second half is a randomly generated string
@@ -280,7 +278,7 @@ impl EnvelopeHeader {
280278
/// Write an envelope header to a writer.
281279
///
282280
/// See the [`crate::envelope`] module documentation for the binary format.
283-
pub fn write(&self, writer: &mut impl Write) -> Result<(), EnvelopeError> {
281+
pub fn write(&self, writer: &mut impl Write) -> Result<(), HeaderError> {
284282
// The first 8 bytes are the magic number in little-endian.
285283
writer.write_all(MAGIC_NUMBERS)?;
286284
// Next is the format descriptor.

0 commit comments

Comments
 (0)