@@ -47,12 +47,13 @@ pub mod serde_with;
47
47
pub use header:: { EnvelopeConfig , EnvelopeFormat , MAGIC_NUMBERS , ZstdConfig } ;
48
48
pub use package_json:: PackageEncodingError ;
49
49
50
- use crate :: Hugr ;
50
+ use crate :: { Hugr , HugrView } ;
51
51
use crate :: {
52
52
extension:: { ExtensionRegistry , Version } ,
53
53
package:: Package ,
54
54
} ;
55
55
use header:: EnvelopeHeader ;
56
+ use std:: collections:: HashSet ;
56
57
use std:: io:: BufRead ;
57
58
use std:: io:: Write ;
58
59
use std:: str:: FromStr ;
@@ -64,6 +65,49 @@ use itertools::Itertools as _;
64
65
use crate :: import:: ImportError ;
65
66
use crate :: { Extension , import:: import_package} ;
66
67
68
+ /// Key used to store the name of the generator that produced the envelope.
69
+ pub const GENERATOR_KEY : & str = "__generator" ;
70
+
71
+ /// Get the name of the generator from the metadata of the HUGR modules.
72
+ /// If multiple modules have different generators, only the first one is returned.
73
+ fn get_generator < H : HugrView > ( modules : & [ H ] ) -> Option < String > {
74
+ let generators: HashSet < String > = modules
75
+ . iter ( )
76
+ . filter_map ( |hugr| hugr. get_metadata ( hugr. module_root ( ) , GENERATOR_KEY ) )
77
+ . map ( |v| v. to_string ( ) )
78
+ . collect ( ) ;
79
+ debug_assert ! (
80
+ generators. len( ) <= 1 ,
81
+ "Multiple generators found in the package metadata: {generators:?}"
82
+ ) ;
83
+ generators. into_iter ( ) . next ( )
84
+ }
85
+
86
+ fn gen_str ( generator : & Option < String > ) -> String {
87
+ match generator {
88
+ Some ( g) => format ! ( "\n generated by {g}" ) ,
89
+ None => String :: new ( ) ,
90
+ }
91
+ }
92
+
93
+ /// Wrap an error with a generator string.
94
+ #[ derive( Error , Debug ) ]
95
+ #[ error( "{inner}{}" , gen_str( & self . generator) ) ]
96
+ pub struct WithGenerator < E : std:: fmt:: Display > {
97
+ inner : E ,
98
+ /// The name of the generator that produced the envelope, if any.
99
+ generator : Option < String > ,
100
+ }
101
+
102
+ impl < E : std:: fmt:: Display > WithGenerator < E > {
103
+ fn new ( err : E , modules : & [ impl HugrView ] ) -> Self {
104
+ Self {
105
+ inner : err,
106
+ generator : get_generator ( modules) ,
107
+ }
108
+ }
109
+ }
110
+
67
111
/// Read a HUGR envelope from a reader.
68
112
///
69
113
/// Returns the deserialized package and the configuration used to encode it.
@@ -216,6 +260,7 @@ pub enum EnvelopeError {
216
260
/// The source error.
217
261
#[ from]
218
262
source : ImportError ,
263
+ // TODO add generator to model import errors
219
264
} ,
220
265
/// Error reading a HUGR model payload.
221
266
#[ error( transparent) ]
@@ -454,7 +499,7 @@ fn check_breaking_extensions(
454
499
hugr : impl crate :: HugrView ,
455
500
registry : & ExtensionRegistry ,
456
501
) -> Result < ( ) , ExtensionBreakingError > {
457
- let Some ( exts) = hugr. get_metadata ( hugr. module_root ( ) , & USED_EXTENSIONS_KEY ) else {
502
+ let Some ( exts) = hugr. get_metadata ( hugr. module_root ( ) , USED_EXTENSIONS_KEY ) else {
458
503
return Ok ( ( ) ) ; // No used extensions metadata, nothing to check
459
504
} ;
460
505
let used_exts: Vec < UsedExtension > = serde_json:: from_value ( exts. clone ( ) ) ?; // TODO handle errors properly
@@ -690,4 +735,28 @@ pub(crate) mod test {
690
735
Err ( ExtensionBreakingError :: Deserialization ( _) )
691
736
) ;
692
737
}
738
+
739
+ #[ test]
740
+ fn test_with_generator_error_message ( ) {
741
+ let test_ext = Extension :: new ( ExtensionId :: new_unchecked ( "test" ) , Version :: new ( 1 , 0 , 0 ) ) ;
742
+ let registry = ExtensionRegistry :: new ( [ Arc :: new ( test_ext) ] ) ;
743
+
744
+ let mut hugr = simple_package ( ) . modules . remove ( 0 ) ;
745
+
746
+ // Set a generator name in the metadata
747
+ let generator_name = json ! ( { "name" : "TestGenerator" , "version" : "1.2.3" } ) ;
748
+ hugr. set_metadata ( hugr. module_root ( ) , GENERATOR_KEY , generator_name. clone ( ) ) ;
749
+
750
+ // Set incompatible extension version in metadata
751
+ let used_exts = json ! ( [ { "name" : "test" , "version" : "2.0.0" } ] ) ;
752
+ hugr. set_metadata ( hugr. module_root ( ) , USED_EXTENSIONS_KEY , used_exts) ;
753
+
754
+ // Create the error and wrap it with WithGenerator
755
+ let err = check_breaking_extensions ( & hugr, & registry) . unwrap_err ( ) ;
756
+ let with_gen = WithGenerator :: new ( err, & [ & hugr] ) ;
757
+
758
+ let err_msg = with_gen. to_string ( ) ;
759
+ assert ! ( err_msg. contains( "Extension 'test' version mismatch" ) ) ;
760
+ assert ! ( err_msg. contains( generator_name. to_string( ) . as_str( ) ) ) ;
761
+ }
693
762
}
0 commit comments