@@ -163,61 +163,61 @@ async fn validate_wasm_against_world(
163
163
target_world : & CandidateWorld ,
164
164
component : & ComponentToValidate < ' _ > ,
165
165
) -> anyhow:: Result < ( ) > {
166
- // Because we are abusing a composition tool to do validation, we have to
167
- // provide a name by which to refer to the component in the dummy composition.
168
- let component_name = "root:component" ;
169
- let component_key = wac_types:: BorrowedPackageKey :: from_name_and_version ( component_name, None ) ;
170
-
171
- // wac is going to get the world from the environment package bytes.
172
- // This constructs a key for that mapping.
173
- let env_pkg_name = target_world. package_namespaced_name ( ) ;
174
- let env_pkg_key = wac_types:: BorrowedPackageKey :: from_name_and_version (
175
- & env_pkg_name,
176
- target_world. package_version ( ) ,
177
- ) ;
166
+ use wac_types:: { validate_target, ItemKind , Package as WacPackage , Types as WacTypes , WorldId } ;
178
167
179
- let env_name = env. name ( ) ;
168
+ // Gets the selected world from the component encoded WIT package
169
+ // TODO: make this an export on `wac_types::Types`.
170
+ fn get_wit_world (
171
+ types : & WacTypes ,
172
+ top_level_world : WorldId ,
173
+ world_name : & str ,
174
+ ) -> anyhow:: Result < WorldId > {
175
+ let top_level_world = & types[ top_level_world] ;
176
+ let world = top_level_world
177
+ . exports
178
+ . get ( world_name)
179
+ . with_context ( || format ! ( "wit package did not contain a world named '{world_name}'" ) ) ?;
180
180
181
- let wac_text = format ! (
182
- r#"
183
- package validate:[email protected] targets {target_world};
184
- let c = new {component_name} {{ ... }};
185
- export c...;
186
- "#
187
- ) ;
181
+ let ItemKind :: Type ( wac_types:: Type :: World ( world_id) ) = world else {
182
+ // We expect the top-level world to export a world type
183
+ anyhow:: bail!( "wit package was not encoded properly" )
184
+ } ;
185
+ let wit_world = & types[ * world_id] ;
186
+ let world = wit_world. exports . values ( ) . next ( ) ;
187
+ let Some ( ItemKind :: Component ( w) ) = world else {
188
+ // We expect the nested world type to export a component
189
+ anyhow:: bail!( "wit package was not encoded properly" )
190
+ } ;
191
+ Ok ( * w)
192
+ }
193
+
194
+ let mut types = WacTypes :: default ( ) ;
195
+
196
+ let target_world_package = WacPackage :: from_bytes (
197
+ & target_world. package_namespaced_name ( ) ,
198
+ target_world. package_version ( ) ,
199
+ target_world. package_bytes ( ) ,
200
+ & mut types,
201
+ ) ?;
188
202
189
- let doc = wac_parser :: Document :: parse ( & wac_text )
190
- . context ( "Internal error constructing WAC document for target checking" ) ?;
203
+ let target_world_id =
204
+ get_wit_world ( & types , target_world_package . ty ( ) , target_world . world_name ( ) ) ?;
191
205
192
- let mut packages : indexmap :: IndexMap < wac_types :: BorrowedPackageKey , Vec < u8 > > =
193
- Default :: default ( ) ;
206
+ let component_package =
207
+ WacPackage :: from_bytes ( component . id ( ) , None , component . wasm_bytes ( ) , & mut types ) ? ;
194
208
195
- packages. insert ( env_pkg_key, target_world. package_bytes ( ) . to_vec ( ) ) ;
196
- packages. insert ( component_key, component. wasm_bytes ( ) . to_vec ( ) ) ;
209
+ let target_result = validate_target ( & types, target_world_id, component_package. ty ( ) ) ;
197
210
198
- match doc . resolve ( packages ) {
211
+ match target_result {
199
212
Ok ( _) => Ok ( ( ) ) ,
200
- Err ( wac_parser:: resolution:: Error :: TargetMismatch { kind, name, world, .. } ) => {
201
- // This one doesn't seem to get hit at the moment - we get MissingTargetExport or ImportNotInTarget instead
202
- Err ( anyhow ! ( "Component {} ({}) can't run in environment {env_name} because world {world} expects an {} named {name}" , component. id( ) , component. source_description( ) , kind. to_string( ) . to_lowercase( ) ) )
203
- }
204
- Err ( wac_parser:: resolution:: Error :: MissingTargetExport { name, world, .. } ) => {
205
- Err ( anyhow ! ( "Component {} ({}) can't run in environment {env_name} because world {world} requires an export named {name}, which the component does not provide" , component. id( ) , component. source_description( ) ) )
206
- }
207
- Err ( wac_parser:: resolution:: Error :: PackageMissingExport { export, .. } ) => {
208
- // TODO: The export here seems wrong - it seems to contain the world name rather than the interface name
209
- Err ( anyhow ! ( "Component {} ({}) can't run in environment {env_name} because world {target_world} requires an export named {export}, which the component does not provide" , component. id( ) , component. source_description( ) ) )
210
- }
211
- Err ( wac_parser:: resolution:: Error :: ImportNotInTarget { name, world, .. } ) => {
212
- Err ( anyhow ! ( "Component {} ({}) can't run in environment {env_name} because world {world} does not provide an import named {name}, which the component requires" , component. id( ) , component. source_description( ) ) )
213
- }
214
- Err ( wac_parser:: resolution:: Error :: SpreadExportNoEffect { .. } ) => {
215
- // We don't have any name info in this case, but it *may* indicate that the component doesn't provide any export at all
216
- Err ( anyhow ! ( "Component {} ({}) can't run in environment {env_name} because it requires an export which the component does not provide" , component. id( ) , component. source_description( ) ) )
217
- }
218
- Err ( e) => {
219
- Err ( anyhow ! ( e) )
220
- } ,
213
+ Err ( report) => Err ( format_target_result_error (
214
+ & types,
215
+ env. name ( ) ,
216
+ target_world. to_string ( ) ,
217
+ component. id ( ) ,
218
+ component. source_description ( ) ,
219
+ & report,
220
+ ) ) ,
221
221
}
222
222
}
223
223
@@ -242,3 +242,46 @@ fn validate_host_reqs(
242
242
fn satisfies ( host_caps : & [ String ] , host_req : & String ) -> bool {
243
243
host_caps. contains ( host_req)
244
244
}
245
+
246
+ fn format_target_result_error (
247
+ types : & wac_types:: Types ,
248
+ env_name : & str ,
249
+ target_world_name : String ,
250
+ component_id : & str ,
251
+ source_description : & str ,
252
+ report : & wac_types:: TargetValidationReport ,
253
+ ) -> anyhow:: Error {
254
+ let mut error_string = format ! (
255
+ "Component {} ({}) can't run in environment {} because world {} ...\n " ,
256
+ component_id, source_description, env_name, target_world_name
257
+ ) ;
258
+
259
+ for ( idx, import) in report. imports_not_in_target ( ) . enumerate ( ) {
260
+ if idx == 0 {
261
+ error_string. push_str ( "... requires imports named\n - " ) ;
262
+ } else {
263
+ error_string. push_str ( " - " ) ;
264
+ }
265
+ error_string. push_str ( import) ;
266
+ error_string. push ( '\n' ) ;
267
+ }
268
+
269
+ for ( idx, ( export, export_kind) ) in report. missing_exports ( ) . enumerate ( ) {
270
+ if idx == 0 {
271
+ error_string. push_str ( "... requires exports named\n - " ) ;
272
+ } else {
273
+ error_string. push_str ( " - " ) ;
274
+ }
275
+ error_string. push_str ( export) ;
276
+ error_string. push_str ( " (" ) ;
277
+ error_string. push_str ( export_kind. desc ( types) ) ;
278
+ error_string. push_str ( ")\n " ) ;
279
+ }
280
+
281
+ for ( name, extern_kind, error) in report. mismatched_types ( ) {
282
+ error_string. push_str ( "... found a type mismatch for " ) ;
283
+ error_string. push_str ( & format ! ( "{extern_kind} {name}: {error}" ) ) ;
284
+ }
285
+
286
+ anyhow ! ( error_string)
287
+ }
0 commit comments