From af8879c03ce8feaf38b40f63ca8024534496d4dc Mon Sep 17 00:00:00 2001 From: Daniel Macovei Date: Thu, 13 Jun 2024 13:50:21 -0500 Subject: [PATCH 1/4] init --- crates/wit-component/src/printing.rs | 2 +- crates/wit-parser/src/ast.rs | 50 +++++++++ crates/wit-parser/src/ast/lex.rs | 3 + crates/wit-parser/src/ast/resolve.rs | 156 +++++++++++++++++++++++++-- crates/wit-parser/src/decoding.rs | 2 + crates/wit-parser/src/lib.rs | 3 + crates/wit-parser/src/resolve.rs | 2 +- 7 files changed, 210 insertions(+), 8 deletions(-) diff --git a/crates/wit-component/src/printing.rs b/crates/wit-component/src/printing.rs index 28406a5023..d641446aee 100644 --- a/crates/wit-component/src/printing.rs +++ b/crates/wit-component/src/printing.rs @@ -69,7 +69,7 @@ impl WitPrinter { } if has_multiple_packages { - self.output.push_str("{"); + self.output.push_str("{\n"); self.output.indent += 1 } else { self.print_semicolon(); diff --git a/crates/wit-parser/src/ast.rs b/crates/wit-parser/src/ast.rs index d65d5fed4f..9a75126654 100644 --- a/crates/wit-parser/src/ast.rs +++ b/crates/wit-parser/src/ast.rs @@ -220,6 +220,19 @@ impl<'a> DeclList<'a> { } Ok(()) } + + fn for_each_nest<'b>(&'b self, mut f: impl FnMut(&'b Nest<'a>) -> Result<()>) -> Result<()> { + for item in self.items.iter() { + if let AstItem::Interface(i) = item { + for item in i.items.iter() { + if let InterfaceItem::Nest(n) = item { + f(n)?; + } + } + } + } + Ok(()) + } } enum AstItem<'a> { @@ -545,6 +558,42 @@ enum InterfaceItem<'a> { TypeDef(TypeDef<'a>), Func(NamedFunc<'a>), Use(Use<'a>), + Nest(Nest<'a>), +} + +struct Nest<'a> { + id: PackageName<'a>, + name: Id<'a>, + attributes: Vec>, +} + +impl<'a> Nest<'a> { + fn parse(tokens: &mut Tokenizer<'a>, attributes: Vec>) -> Result { + tokens.eat(Token::Nest)?; + let id = parse_id(tokens)?; + tokens.expect(Token::Colon)?; + // `foo:bar/baz@1.0` + let namespace = id; + let pkg_name = parse_id(tokens)?; + tokens.expect(Token::Slash)?; + let name = parse_id(tokens)?; + let version = parse_opt_version(tokens)?; + tokens.expect_semicolon()?; + Ok(Self { + id: PackageName { + docs: Default::default(), + span: Span { + start: namespace.span.start, + end: pkg_name.span.end, + }, + namespace, + name: pkg_name, + version, + }, + name, + attributes, + }) + } } struct Use<'a> { @@ -983,6 +1032,7 @@ impl<'a> InterfaceItem<'a> { NamedFunc::parse(tokens, docs, attributes).map(InterfaceItem::Func) } Some((_span, Token::Use)) => Use::parse(tokens, attributes).map(InterfaceItem::Use), + Some((_span, Token::Nest)) => Nest::parse(tokens, attributes).map(InterfaceItem::Nest), other => Err(err_expected(tokens, "`type`, `resource` or `func`", other).into()), } } diff --git a/crates/wit-parser/src/ast/lex.rs b/crates/wit-parser/src/ast/lex.rs index 93ad600872..0428cfe269 100644 --- a/crates/wit-parser/src/ast/lex.rs +++ b/crates/wit-parser/src/ast/lex.rs @@ -84,6 +84,7 @@ pub enum Token { As, From_, Static, + Nest, Interface, Tuple, Import, @@ -315,6 +316,7 @@ impl<'a> Tokenizer<'a> { "as" => As, "from" => From_, "static" => Static, + "nest" => Nest, "interface" => Interface, "tuple" => Tuple, "world" => World, @@ -576,6 +578,7 @@ impl Token { As => "keyword `as`", From_ => "keyword `from`", Static => "keyword `static`", + Nest => "keyword `nest`", Interface => "keyword `interface`", Tuple => "keyword `tuple`", Import => "keyword `import`", diff --git a/crates/wit-parser/src/ast/resolve.rs b/crates/wit-parser/src/ast/resolve.rs index 0d3465a602..0403e004c6 100644 --- a/crates/wit-parser/src/ast/resolve.rs +++ b/crates/wit-parser/src/ast/resolve.rs @@ -1,5 +1,12 @@ -use super::{ParamList, ResultList, WorldOrInterface}; +use super::{ + // AstItem as RootAstItem, Interface as RootInterface, + Nest, + ParamList, + ResultList, + WorldOrInterface, +}; use crate::ast::toposort::toposort; +use crate::ast::InterfaceItem; use crate::*; use anyhow::bail; use std::collections::{HashMap, HashSet}; @@ -100,9 +107,11 @@ enum Key { enum TypeItem<'a, 'b> { Use(&'b ast::Use<'a>), + Nest(&'b ast::Nest<'a>), Def(&'b ast::TypeDef<'a>), } +#[derive(Debug)] enum TypeOrItem { Type(TypeId), Item(&'static str), @@ -174,6 +183,15 @@ impl<'a> Resolver<'a> { for item in decl_list.items.iter() { match item { ast::AstItem::Interface(iface) => { + for item in &iface.items { + if let InterfaceItem::Nest(n) = item { + let id = match self.ast_items[i][n.name.name] { + AstItem::Interface(id) => id, + AstItem::World(_) => unreachable!(), + }; + iface_id_to_ast.insert(id, (iface, i)); + } + } let id = match self.ast_items[i][iface.name.name] { AstItem::Interface(id) => id, AstItem::World(_) => unreachable!(), @@ -292,6 +310,30 @@ impl<'a> Resolver<'a> { Ok(()) }) .unwrap(); + decl_list + .for_each_nest(|nest: &Nest| { + let deps = foreign_deps + .entry(nest.id.package_name()) + .or_insert_with(|| { + self.foreign_dep_spans.push(nest.id.span); + IndexMap::new() + }); + let id = *deps.entry(nest.name.name).or_insert_with(|| { + log::trace!( + "creating an interface for foreign dep: {}/{}", + nest.id.package_name(), + nest.name.name + ); + + AstItem::Interface(self.alloc_interface(nest.name.span)) + }); + match id { + AstItem::Interface(id) => foreign_interfaces.insert(id), + AstItem::World(_) => unreachable!(), + }; + Ok(()) + }) + .unwrap(); } self.foreign_deps = foreign_deps; self.foreign_interfaces = foreign_interfaces; @@ -306,6 +348,7 @@ impl<'a> Resolver<'a> { }); self.interfaces.alloc(Interface { name: None, + nested: IndexMap::new(), types: IndexMap::new(), docs: Docs::default(), stability: Default::default(), @@ -364,6 +407,28 @@ impl<'a> Resolver<'a> { assert!(prev.is_none()); let prev = names.insert(i.name.name, item); assert!(prev.is_none()); + for nestitem in i.items.iter() { + if let InterfaceItem::Nest(n) = nestitem { + if package_items.insert(n.name.name, n.name.span).is_some() { + bail!(Error::new( + n.name.span, + format!("duplicate item named `{}`", n.name.name), + )) + } + let prev = decl_list_ns.insert(n.name.name, ()); + assert!(prev.is_none()); + let prev = order.insert(n.name.name, Vec::new()); + assert!(prev.is_none()); + // let i = RootAstItem::Interface(RootInterface { + // docs: Default::default(), + // attributes: vec![], + // name: n.name.clone(), + // items: vec![], + // }); + // let prev = names.insert(n.name.name, &i); + // assert!(prev.is_none()); + } + } } ast::AstItem::World(w) => { if package_items.insert(w.name.name, w.name.span).is_some() { @@ -380,7 +445,7 @@ impl<'a> Resolver<'a> { assert!(prev.is_none()); } // These are processed down below. - ast::AstItem::Use(_) => {} + ast::AstItem::Use(_) => {} // ast::AstItem::Nest(_) => todo!(), } } decl_list_namespaces.push(decl_list_ns); @@ -458,6 +523,29 @@ impl<'a> Resolver<'a> { } Ok(()) })?; + decl_list.for_each_nest(|nest: &Nest| { + match decl_list_ns.get(nest.name.name) { + Some((_, ItemSource::Foreign)) => return Ok(()), + Some((_, ItemSource::Local(id))) => { + order[nest.name.name].push(id.clone()); + } + None => match package_items.get(nest.name.name) { + Some(_) => { + // order[nest.name.name].push(nest.name.clone()); + } + None => { + bail!(Error::new( + nest.name.span, + format!( + "interface or world `{name}` not found in package", + name = nest.name.name + ), + )) + } + }, + } + Ok(()) + })?; } let order = toposort("interface or world", &order)?; @@ -470,22 +558,32 @@ impl<'a> Resolver<'a> { let mut iface_id_order = Vec::new(); let mut world_id_order = Vec::new(); for name in order { - match names.get(name).unwrap() { - ast::AstItem::Interface(_) => { + match names.get(name) { + Some(ast::AstItem::Interface(i)) => { + for item in &i.items { + if let InterfaceItem::Nest(n) = item { + let id = self.alloc_interface(package_items[name]); + self.interfaces[id].name = Some(name.to_string()); + let prev = ids.insert(n.name.name, AstItem::Interface(id)); + assert!(prev.is_none()); + iface_id_order.push(id); + } + } let id = self.alloc_interface(package_items[name]); self.interfaces[id].name = Some(name.to_string()); let prev = ids.insert(name, AstItem::Interface(id)); assert!(prev.is_none()); iface_id_order.push(id); } - ast::AstItem::World(_) => { + Some(ast::AstItem::World(_)) => { let id = self.alloc_world(package_items[name]); self.worlds[id].name = name.to_string(); let prev = ids.insert(name, AstItem::World(id)); assert!(prev.is_none()); world_id_order.push(id); } - ast::AstItem::Use(_) => unreachable!(), + Some(ast::AstItem::Use(_)) => unreachable!(), + None => {} }; } for decl_list in decl_lists { @@ -517,6 +615,15 @@ impl<'a> Resolver<'a> { (name.name, item) } ast::AstItem::Interface(i) => { + for item in &i.items { + if let InterfaceItem::Nest(n) = item { + let iface_item = ids[n.name.name]; + let prev = items.insert(n.name.name, iface_item); + assert!(prev.is_none()); + let prev = self.package_items.insert(n.name.name, iface_item); + assert!(prev.is_none()); + } + } let iface_item = ids[i.name.name]; assert!(matches!(iface_item, AstItem::Interface(_))); (i.name.name, iface_item) @@ -587,6 +694,12 @@ impl<'a> Resolver<'a> { Ok(()) })?; + decl_list.for_each_nest(|nest| { + let (item, name, span) = self.resolve_ast_item_nest(nest)?; + let iface = self.extract_iface_from_item(&item, &name, span)?; + let lookup = &mut self.interface_types[iface.index()]; + Ok(()) + })?; } Ok(()) } @@ -784,6 +897,7 @@ impl<'a> Resolver<'a> { ast::InterfaceItem::Use(u) => Some(TypeItem::Use(u)), ast::InterfaceItem::TypeDef(t) => Some(TypeItem::Def(t)), ast::InterfaceItem::Func(_) => None, + ast::InterfaceItem::Nest(n) => Some(TypeItem::Nest(n)), }), )?; @@ -830,6 +944,7 @@ impl<'a> Resolver<'a> { } } ast::InterfaceItem::TypeDef(_) => {} + ast::InterfaceItem::Nest(_) => {} } } for func in funcs { @@ -861,6 +976,9 @@ impl<'a> Resolver<'a> { TypeItem::Use(u) => { self.resolve_use(owner, u)?; } + TypeItem::Nest(n) => { + self.resolve_nest(owner, n)?; + } TypeItem::Def(_) => {} } } @@ -892,6 +1010,7 @@ impl<'a> Resolver<'a> { type_defs.insert(name.name, None); } } + TypeItem::Nest(n) => {} } } let order = toposort("type", &type_deps)?; @@ -950,6 +1069,16 @@ impl<'a> Resolver<'a> { Ok(()) } + fn resolve_nest(&mut self, owner: TypeOwner, n: &ast::Nest<'a>) -> Result<()> { + let (item, name, span) = self.resolve_ast_item_nest(&n)?; + let use_from = self.extract_iface_from_item(&item, &name, span)?; + let extracted = &self.interfaces[use_from]; + + let stability = self.stability(&n.attributes)?; + let lookup = &self.interface_types[use_from.index()]; + Ok(()) + } + /// For each name in the `include`, resolve the path of the include, add it to the self.includes fn resolve_include(&mut self, world_id: WorldId, i: &ast::Include<'a>) -> Result<()> { let stability = self.stability(&i.attributes)?; @@ -1051,6 +1180,21 @@ impl<'a> Resolver<'a> { } } + fn resolve_ast_item_nest(&self, nest: &ast::Nest<'a>) -> Result<(AstItem, String, Span)> { + let item: Option<&AstItem> = self.ast_items[self.cur_ast_index] + .get(nest.name.name) + .or_else(|| self.package_items.get(nest.name.name)); + match item { + Some(item) => Ok((*item, nest.name.name.to_string(), nest.id.span)), + None => { + bail!(Error::new( + nest.id.span, + format!("interface or world `{}` does not exist", nest.name.name), + )) + } + } + } + fn extract_iface_from_item( &self, item: &AstItem, diff --git a/crates/wit-parser/src/decoding.rs b/crates/wit-parser/src/decoding.rs index 1c61ecfd48..5d30063aa1 100644 --- a/crates/wit-parser/src/decoding.rs +++ b/crates/wit-parser/src/decoding.rs @@ -897,6 +897,7 @@ impl WitPackageDecoder<'_> { self.resolve.interfaces.alloc(Interface { name: Some(name.interface().to_string()), docs: Default::default(), + nested: IndexMap::new(), types: IndexMap::default(), functions: IndexMap::new(), package: None, @@ -952,6 +953,7 @@ impl WitPackageDecoder<'_> { let mut interface = Interface { name: interface_name.clone(), docs: Default::default(), + nested: IndexMap::new(), types: IndexMap::default(), functions: IndexMap::new(), package: None, diff --git a/crates/wit-parser/src/lib.rs b/crates/wit-parser/src/lib.rs index 72e0857bce..bf21132b41 100644 --- a/crates/wit-parser/src/lib.rs +++ b/crates/wit-parser/src/lib.rs @@ -411,6 +411,9 @@ pub struct Interface { /// This is `None` for inline interfaces in worlds. pub name: Option, + /// The nested interfaces in this interface. + #[cfg_attr(feature = "serde", serde(serialize_with = "serialize_id_map"))] + pub nested: IndexMap, /// Exported types from this interface. /// /// Export names are listed within the types themselves. Note that the diff --git a/crates/wit-parser/src/resolve.rs b/crates/wit-parser/src/resolve.rs index 8a34a8e667..3742e3dbba 100644 --- a/crates/wit-parser/src/resolve.rs +++ b/crates/wit-parser/src/resolve.rs @@ -1357,7 +1357,7 @@ impl Remap { iface.package = Some(pkgid); if let Some(name) = &iface.name { let prev = resolve.packages[pkgid].interfaces.insert(name.clone(), id); - assert!(prev.is_none()); + // assert!(prev.is_none()); } } for id in self.worlds.iter().skip(foreign_worlds) { From 403d1a3ac0a9a8b899e63995371f7fd1d8cc0ad8 Mon Sep 17 00:00:00 2001 From: Daniel Macovei Date: Tue, 18 Jun 2024 10:58:53 -0500 Subject: [PATCH 2/4] bulk --- crates/wit-component/src/encoding/wit/v1.rs | 7 ++ crates/wit-component/src/encoding/wit/v2.rs | 52 ++++++++- crates/wit-component/src/printing.rs | 6 + crates/wit-component/tests/interfaces.rs | 2 - .../wit-component/tests/interfaces/nested.wat | 109 ++++++++++++++++++ .../tests/interfaces/nested/deps/nestee.wit | 20 ++++ .../tests/interfaces/nested/deps/nestnest.wit | 7 ++ .../tests/interfaces/nested/nested.wit | 24 ++++ .../tests/interfaces/nested/thing.wit.print | 27 +++++ crates/wit-parser/src/ast.rs | 6 +- crates/wit-parser/src/ast/resolve.rs | 83 +++++-------- crates/wit-parser/src/decoding.rs | 99 +++++++++++++++- crates/wit-parser/src/resolve.rs | 24 +++- crates/wit-parser/tests/ui/comments.wit.json | 1 + .../tests/ui/complex-include.wit.json | 6 + .../tests/ui/cross-package-resource.wit.json | 2 + crates/wit-parser/tests/ui/diamond1.wit.json | 2 + .../tests/ui/disambiguate-diamond.wit.json | 4 + .../tests/ui/feature-gates.wit.json | 6 + .../tests/ui/foreign-deps-union.wit.json | 13 +++ .../wit-parser/tests/ui/foreign-deps.wit.json | 13 +++ crates/wit-parser/tests/ui/functions.wit.json | 1 + .../tests/ui/ignore-files-deps.wit.json | 1 + .../tests/ui/import-export-overlap2.wit.json | 1 + .../wit-parser/tests/ui/include-reps.wit.json | 2 + .../tests/ui/kinds-of-deps.wit.json | 4 + .../wit-parser/tests/ui/many-names.wit.json | 2 + .../ui/multi-file-multi-package.wit.json | 8 ++ .../wit-parser/tests/ui/multi-file.wit.json | 10 ++ .../ui/multi-package-shared-deps.wit.json | 2 + .../ui/multi-package-transitive-deps.wit.json | 2 + .../ui/name-both-resource-and-type.wit.json | 2 + ...ges-explicit-colliding-decl-names.wit.json | 4 + ...ages-explicit-internal-references.wit.json | 2 + .../ui/packages-explicit-with-semver.wit.json | 4 + .../ui/packages-multiple-explicit.wit.json | 4 + .../ui/packages-single-explicit.wit.json | 2 + crates/wit-parser/tests/ui/random.wit.json | 1 + .../tests/ui/resources-empty.wit.json | 1 + .../resources-multiple-returns-own.wit.json | 1 + .../tests/ui/resources-multiple.wit.json | 1 + .../tests/ui/resources-return-own.wit.json | 1 + crates/wit-parser/tests/ui/resources.wit.json | 2 + .../wit-parser/tests/ui/resources1.wit.json | 1 + .../wit-parser/tests/ui/shared-types.wit.json | 2 + .../tests/ui/simple-wasm-text.wit.json | 1 + .../tests/ui/since-and-unstable.wit.json | 7 ++ .../tests/ui/stress-export-elaborate.wit.json | 10 ++ .../tests/ui/type-then-eof.wit.json | 1 + crates/wit-parser/tests/ui/types.wit.json | 1 + crates/wit-parser/tests/ui/use-chain.wit.json | 2 + crates/wit-parser/tests/ui/use.wit.json | 7 ++ crates/wit-parser/tests/ui/versions.wit.json | 3 + crates/wit-parser/tests/ui/wasi.wit.json | 1 + .../tests/ui/world-diamond.wit.json | 3 + .../tests/ui/world-iface-no-collide.wit.json | 1 + .../tests/ui/world-implicit-import1.wit.json | 3 + .../tests/ui/world-implicit-import2.wit.json | 1 + .../tests/ui/world-implicit-import3.wit.json | 1 + .../tests/ui/world-same-fields4.wit.json | 3 + .../ui/world-top-level-resources.wit.json | 2 + .../tests/ui/worlds-union-dedup.wit.json | 6 + .../tests/ui/worlds-with-types.wit.json | 1 + 63 files changed, 564 insertions(+), 64 deletions(-) create mode 100644 crates/wit-component/tests/interfaces/nested.wat create mode 100644 crates/wit-component/tests/interfaces/nested/deps/nestee.wit create mode 100644 crates/wit-component/tests/interfaces/nested/deps/nestnest.wit create mode 100644 crates/wit-component/tests/interfaces/nested/nested.wit create mode 100644 crates/wit-component/tests/interfaces/nested/thing.wit.print diff --git a/crates/wit-component/src/encoding/wit/v1.rs b/crates/wit-component/src/encoding/wit/v1.rs index ed6c39a35f..10d3ab5915 100644 --- a/crates/wit-component/src/encoding/wit/v1.rs +++ b/crates/wit-component/src/encoding/wit/v1.rs @@ -170,6 +170,13 @@ impl InterfaceEncoder<'_> { let iface = &self.resolve.interfaces[interface]; let mut type_order = IndexSet::new(); for (_, id) in iface.types.iter() { + let ty = &self.resolve.types[*id]; + match ty.owner { + TypeOwner::Interface(iface_id) => { + self.interface = Some(iface_id); + } + _ => unreachable!(), + } self.encode_valtype(self.resolve, &Type::Id(*id))?; type_order.insert(*id); } diff --git a/crates/wit-component/src/encoding/wit/v2.rs b/crates/wit-component/src/encoding/wit/v2.rs index 7024c6ccf6..54b6f7b23c 100644 --- a/crates/wit-component/src/encoding/wit/v2.rs +++ b/crates/wit-component/src/encoding/wit/v2.rs @@ -1,5 +1,5 @@ use crate::encoding::types::{FunctionKey, ValtypeEncoder}; -use anyhow::Result; +use anyhow::{bail, Result}; use indexmap::IndexSet; use std::collections::HashMap; use std::mem; @@ -184,6 +184,13 @@ impl InterfaceEncoder<'_> { let iface = &self.resolve.interfaces[interface]; let mut type_order = IndexSet::new(); for (_, id) in iface.types.iter() { + let ty = &self.resolve.types[*id]; + match ty.owner { + TypeOwner::Interface(iface_id) => { + self.interface = Some(iface_id); + } + _ => unreachable!(), + } self.encode_valtype(self.resolve, &Type::Id(*id))?; type_order.insert(*id); } @@ -218,7 +225,48 @@ impl InterfaceEncoder<'_> { .unwrap() .export(name, ComponentTypeRef::Func(ty)); } - let instance = self.pop_instance(); + let mut instance = self.pop_instance(); + for (orig_name, _) in &iface.nested { + let mut pkg_parts = orig_name.split("/"); + let pkg = pkg_parts.next().expect("expected projection"); + let iface_name = pkg_parts.next().expect("expected projection"); + let mut parts = pkg.split(":"); + let namespace = parts.next().expect("expected :"); + let name = parts.next().expect("expected :"); + let name = PackageName { + namespace: namespace.to_string(), + name: name.to_string(), + version: None, + }; + + let package_id = self.resolve.package_names.get(&name).unwrap(); + let package = &self.resolve.packages[*package_id]; + let nested = package.interfaces.get(iface_name).unwrap(); + let nested_iface = &self.resolve.interfaces[*nested]; + let mut inst = InterfaceEncoder::new(&self.resolve); + inst.push_instance(); + for (_, id) in &nested_iface.types { + let ty = &self.resolve.types[*id]; + match ty.owner { + TypeOwner::Interface(iface_id) => { + inst.interface = Some(iface_id); + } + _ => unreachable!(), + } + inst.encode_valtype(self.resolve, &Type::Id(*id))?; + } + for (_, _) in &nested_iface.nested { + bail!("Using `nest` in a nested interface is not yet supported"); + } + let ty = instance.ty(); + let nested = inst.pop_instance(); + ty.instance(&nested); + instance.export( + orig_name, + ComponentTypeRef::Instance(instance.type_count() - 1), + ); + } + let idx = self.outer.type_count(); self.outer.ty().instance(&instance); self.import_map.insert(interface, self.instances); diff --git a/crates/wit-component/src/printing.rs b/crates/wit-component/src/printing.rs index d641446aee..d35785c82e 100644 --- a/crates/wit-component/src/printing.rs +++ b/crates/wit-component/src/printing.rs @@ -82,6 +82,12 @@ impl WitPrinter { self.output.push_str("interface "); self.print_name(name); self.output.push_str(" {\n"); + let nested = &resolve.interfaces[*id].nested; + for item in nested { + self.output.push_str("nest "); + self.print_name(item.0); + self.output.push_str(";\n") + } self.print_interface(resolve, *id)?; writeln!(&mut self.output, "}}\n")?; } diff --git a/crates/wit-component/tests/interfaces.rs b/crates/wit-component/tests/interfaces.rs index 926499db9c..96ea8f8f0c 100644 --- a/crates/wit-component/tests/interfaces.rs +++ b/crates/wit-component/tests/interfaces.rs @@ -53,7 +53,6 @@ fn run_test(path: &Path, is_dir: bool) -> Result<()> { } else { resolve.append(UnresolvedPackageGroup::parse_file(path)?)? }; - for package in packages { assert_print(&resolve, &[package], path, is_dir)?; @@ -77,7 +76,6 @@ fn run_test(path: &Path, is_dir: bool) -> Result<()> { decoded.packages().len(), "Each input WIT package should produce WASM that contains only one package" ); - let decoded_package = decoded.packages()[0]; let resolve = decoded.resolve(); diff --git a/crates/wit-component/tests/interfaces/nested.wat b/crates/wit-component/tests/interfaces/nested.wat new file mode 100644 index 0000000000..9196085738 --- /dev/null +++ b/crates/wit-component/tests/interfaces/nested.wat @@ -0,0 +1,109 @@ +(component + (type (;0;) + (component + (type (;0;) + (instance + (type (;0;) (record (field "foo" string))) + (export (;1;) "usable-record" (type (eq 0))) + ) + ) + (import "foo:nestee/usable" (instance (;0;) (type 0))) + (alias export 0 "usable-record" (type (;1;))) + (type (;2;) + (instance + (alias outer 1 1 (type (;0;))) + (export (;1;) "usable-record" (type (eq 0))) + (type (;2;) (record (field "foo" string))) + (export (;3;) "my-record" (type (eq 2))) + (type (;4;) (func (result string))) + (export (;0;) "hello" (func (type 4))) + (type (;5;) + (instance + (type (;0;) (record (field "foo" string))) + (export (;1;) "nestrecord" (type (eq 0))) + ) + ) + (export (;0;) "foo:nestee/things" (instance (type 5))) + (type (;6;) + (instance + (export (;0;) "foo" (type (sub resource))) + ) + ) + (export (;1;) "foo:nestee/more" (instance (type 6))) + ) + ) + (export (;1;) "foo:thing/something" (instance (type 2))) + ) + ) + (export (;1;) "something" (type 0)) + (type (;2;) + (component + (type (;0;) + (instance + (type (;0;) (list u8)) + (export (;1;) "random" (type (eq 0))) + ) + ) + (export (;0;) "foo:thing/flat" (instance (type 0))) + ) + ) + (export (;3;) "flat" (type 2)) + (type (;4;) + (component + (type (;0;) + (component + (type (;0;) + (instance + (type (;0;) (record (field "foo" string))) + (export (;1;) "usable-record" (type (eq 0))) + ) + ) + (import "foo:nestee/usable" (instance (;0;) (type 0))) + (alias export 0 "usable-record" (type (;1;))) + (type (;2;) + (instance + (alias outer 1 1 (type (;0;))) + (export (;1;) "usable-record" (type (eq 0))) + (type (;2;) (record (field "foo" string))) + (export (;3;) "my-record" (type (eq 2))) + (type (;4;) (func (result string))) + (export (;0;) "hello" (func (type 4))) + ) + ) + (import "foo:thing/something" (instance (;1;) (type 2))) + (type (;3;) + (instance + (type (;0;) (list u8)) + (export (;1;) "random" (type (eq 0))) + ) + ) + (import "foo:thing/flat" (instance (;2;) (type 3))) + (type (;4;) + (instance + (alias outer 1 1 (type (;0;))) + (export (;1;) "usable-record" (type (eq 0))) + (type (;2;) (record (field "foo" string))) + (export (;3;) "my-record" (type (eq 2))) + (type (;4;) (func (result string))) + (export (;0;) "hello" (func (type 4))) + ) + ) + (export (;3;) "foo:thing/something" (instance (type 4))) + (type (;5;) + (instance + (type (;0;) (list u8)) + (export (;1;) "random" (type (eq 0))) + ) + ) + (export (;4;) "foo:thing/flat" (instance (type 5))) + ) + ) + (export (;0;) "foo:thing/my-world" (component (type 0))) + ) + ) + (export (;5;) "my-world" (type 4)) + (@custom "package-docs" "\01{\22interfaces\22:{\22something\22:{\22types\22:{\22my-record\22:{\22stability\22:{\22stable\22:{\22since\22:\221.0.0\22}}}}}}}") + (@producers + (processed-by "wit-component" "$CARGO_PKG_VERSION") + ) +) diff --git a/crates/wit-component/tests/interfaces/nested/deps/nestee.wit b/crates/wit-component/tests/interfaces/nested/deps/nestee.wit new file mode 100644 index 0000000000..79031fca65 --- /dev/null +++ b/crates/wit-component/tests/interfaces/nested/deps/nestee.wit @@ -0,0 +1,20 @@ +package foo:nestee; + +interface things { + record nestrecord { + foo: string + } + hello: func() -> string; +} + +interface more { + resource foo { + bar: func() -> option; + } +} + +interface usable { + record usable-record { + foo: string + } +} \ No newline at end of file diff --git a/crates/wit-component/tests/interfaces/nested/deps/nestnest.wit b/crates/wit-component/tests/interfaces/nested/deps/nestnest.wit new file mode 100644 index 0000000000..22ef29c97c --- /dev/null +++ b/crates/wit-component/tests/interfaces/nested/deps/nestnest.wit @@ -0,0 +1,7 @@ +package foo:nestnest; + +interface deep { + record deep-record { + foo: string + } +} \ No newline at end of file diff --git a/crates/wit-component/tests/interfaces/nested/nested.wit b/crates/wit-component/tests/interfaces/nested/nested.wit new file mode 100644 index 0000000000..710fb34111 --- /dev/null +++ b/crates/wit-component/tests/interfaces/nested/nested.wit @@ -0,0 +1,24 @@ +package foo:thing; + +interface something { + use foo:nestee/usable.{usable-record}; + nest foo:nestee/things; + nest foo:nestee/more; + @since(version = 1.0.0) + record my-record { + foo: string + } + + hello: func() -> string; +} + +interface flat { + type random = list; +} + +world my-world { + import something; + export something; + import flat; + export flat; +} \ No newline at end of file diff --git a/crates/wit-component/tests/interfaces/nested/thing.wit.print b/crates/wit-component/tests/interfaces/nested/thing.wit.print new file mode 100644 index 0000000000..e6a4c01a98 --- /dev/null +++ b/crates/wit-component/tests/interfaces/nested/thing.wit.print @@ -0,0 +1,27 @@ +package foo:thing; + +interface something { + nest foo:nestee/things; + nest foo:nestee/more; + use foo:nestee/usable.{usable-record}; + + @since(version = 1.0.0) + record my-record { + foo: string, + } + + hello: func() -> string; +} + +interface flat { + type random = list; +} + +world my-world { + import foo:nestee/usable; + import something; + import flat; + + export something; + export flat; +} diff --git a/crates/wit-parser/src/ast.rs b/crates/wit-parser/src/ast.rs index 9a75126654..5e7bdfdd5d 100644 --- a/crates/wit-parser/src/ast.rs +++ b/crates/wit-parser/src/ast.rs @@ -564,11 +564,11 @@ enum InterfaceItem<'a> { struct Nest<'a> { id: PackageName<'a>, name: Id<'a>, - attributes: Vec>, + // attributes: Vec>, } impl<'a> Nest<'a> { - fn parse(tokens: &mut Tokenizer<'a>, attributes: Vec>) -> Result { + fn parse(tokens: &mut Tokenizer<'a>, _attributes: Vec>) -> Result { tokens.eat(Token::Nest)?; let id = parse_id(tokens)?; tokens.expect(Token::Colon)?; @@ -591,7 +591,7 @@ impl<'a> Nest<'a> { version, }, name, - attributes, + // attributes, }) } } diff --git a/crates/wit-parser/src/ast/resolve.rs b/crates/wit-parser/src/ast/resolve.rs index 0403e004c6..f6c496ae66 100644 --- a/crates/wit-parser/src/ast/resolve.rs +++ b/crates/wit-parser/src/ast/resolve.rs @@ -107,7 +107,7 @@ enum Key { enum TypeItem<'a, 'b> { Use(&'b ast::Use<'a>), - Nest(&'b ast::Nest<'a>), + Nest, Def(&'b ast::TypeDef<'a>), } @@ -419,14 +419,6 @@ impl<'a> Resolver<'a> { assert!(prev.is_none()); let prev = order.insert(n.name.name, Vec::new()); assert!(prev.is_none()); - // let i = RootAstItem::Interface(RootInterface { - // docs: Default::default(), - // attributes: vec![], - // name: n.name.clone(), - // items: vec![], - // }); - // let prev = names.insert(n.name.name, &i); - // assert!(prev.is_none()); } } } @@ -445,7 +437,7 @@ impl<'a> Resolver<'a> { assert!(prev.is_none()); } // These are processed down below. - ast::AstItem::Use(_) => {} // ast::AstItem::Nest(_) => todo!(), + ast::AstItem::Use(_) => {} } } decl_list_namespaces.push(decl_list_ns); @@ -530,9 +522,7 @@ impl<'a> Resolver<'a> { order[nest.name.name].push(id.clone()); } None => match package_items.get(nest.name.name) { - Some(_) => { - // order[nest.name.name].push(nest.name.clone()); - } + Some(_) => {} None => { bail!(Error::new( nest.name.span, @@ -560,17 +550,33 @@ impl<'a> Resolver<'a> { for name in order { match names.get(name) { Some(ast::AstItem::Interface(i)) => { + let id = self.alloc_interface(package_items[name]); + self.interfaces[id].name = Some(name.to_string()); for item in &i.items { if let InterfaceItem::Nest(n) = item { - let id = self.alloc_interface(package_items[name]); - self.interfaces[id].name = Some(name.to_string()); + let nest = self + .foreign_deps + .get(&PackageName { + namespace: n.id.namespace.name.to_string(), + name: n.id.name.name.to_string(), + version: n.id.clone().version.map(|v| v.1), + }) + .unwrap() + .get(&n.name.name) + .unwrap(); + let nested_id = if let AstItem::Interface(id) = nest { + id + } else { + bail!("Expected interface item") + }; + self.interfaces[id].nested.insert( + format!("{}/{}", n.id.package_name(), n.name.name.to_string()), + *nested_id, + ); let prev = ids.insert(n.name.name, AstItem::Interface(id)); assert!(prev.is_none()); - iface_id_order.push(id); } } - let id = self.alloc_interface(package_items[name]); - self.interfaces[id].name = Some(name.to_string()); let prev = ids.insert(name, AstItem::Interface(id)); assert!(prev.is_none()); iface_id_order.push(id); @@ -694,12 +700,7 @@ impl<'a> Resolver<'a> { Ok(()) })?; - decl_list.for_each_nest(|nest| { - let (item, name, span) = self.resolve_ast_item_nest(nest)?; - let iface = self.extract_iface_from_item(&item, &name, span)?; - let lookup = &mut self.interface_types[iface.index()]; - Ok(()) - })?; + decl_list.for_each_nest(|_nest| Ok(()))?; } Ok(()) } @@ -897,7 +898,7 @@ impl<'a> Resolver<'a> { ast::InterfaceItem::Use(u) => Some(TypeItem::Use(u)), ast::InterfaceItem::TypeDef(t) => Some(TypeItem::Def(t)), ast::InterfaceItem::Func(_) => None, - ast::InterfaceItem::Nest(n) => Some(TypeItem::Nest(n)), + ast::InterfaceItem::Nest(_) => Some(TypeItem::Nest), }), )?; @@ -976,10 +977,7 @@ impl<'a> Resolver<'a> { TypeItem::Use(u) => { self.resolve_use(owner, u)?; } - TypeItem::Nest(n) => { - self.resolve_nest(owner, n)?; - } - TypeItem::Def(_) => {} + _ => {} } } @@ -1010,7 +1008,7 @@ impl<'a> Resolver<'a> { type_defs.insert(name.name, None); } } - TypeItem::Nest(n) => {} + TypeItem::Nest => {} } } let order = toposort("type", &type_deps)?; @@ -1069,16 +1067,6 @@ impl<'a> Resolver<'a> { Ok(()) } - fn resolve_nest(&mut self, owner: TypeOwner, n: &ast::Nest<'a>) -> Result<()> { - let (item, name, span) = self.resolve_ast_item_nest(&n)?; - let use_from = self.extract_iface_from_item(&item, &name, span)?; - let extracted = &self.interfaces[use_from]; - - let stability = self.stability(&n.attributes)?; - let lookup = &self.interface_types[use_from.index()]; - Ok(()) - } - /// For each name in the `include`, resolve the path of the include, add it to the self.includes fn resolve_include(&mut self, world_id: WorldId, i: &ast::Include<'a>) -> Result<()> { let stability = self.stability(&i.attributes)?; @@ -1180,21 +1168,6 @@ impl<'a> Resolver<'a> { } } - fn resolve_ast_item_nest(&self, nest: &ast::Nest<'a>) -> Result<(AstItem, String, Span)> { - let item: Option<&AstItem> = self.ast_items[self.cur_ast_index] - .get(nest.name.name) - .or_else(|| self.package_items.get(nest.name.name)); - match item { - Some(item) => Ok((*item, nest.name.name.to_string(), nest.id.span)), - None => { - bail!(Error::new( - nest.id.span, - format!("interface or world `{}` does not exist", nest.name.name), - )) - } - } - } - fn extract_iface_from_item( &self, item: &AstItem, diff --git a/crates/wit-parser/src/decoding.rs b/crates/wit-parser/src/decoding.rs index 5d30063aa1..de8efa52a4 100644 --- a/crates/wit-parser/src/decoding.rs +++ b/crates/wit-parser/src/decoding.rs @@ -730,6 +730,97 @@ impl WitPackageDecoder<'_> { Ok(()) } + fn register_export( + &mut self, + name: &str, + ty: &types::ComponentInstanceType, + ) -> Result { + let (is_local, interface) = match self.named_interfaces.get(name) { + Some(id) => (true, *id), + None => (false, self.extract_dep_interface(name)?), + }; + let owner = TypeOwner::Interface(interface); + for (name, ty) in ty.exports.iter() { + match *ty { + types::ComponentEntityType::Module(_) => todo!(), + types::ComponentEntityType::Func(_) => todo!(), + types::ComponentEntityType::Value(_) => todo!(), + types::ComponentEntityType::Type { + referenced, + created, + } => { + match self.resolve.interfaces[interface] + .types + .get(name.as_str()) + .copied() + { + // If this name is already defined as a type in the + // specified interface then that's ok. For package-local + // interfaces that's expected since the interface was + // fully defined. For remote interfaces it means we're + // using something that was already used elsewhere. In + // both cases continue along. + // + // Notably for the remotely defined case this will also + // walk over the structure of the type and register + // internal wasmparser ids with wit-parser ids. This is + // necessary to ensure that anonymous types like + // `list` defined in original definitions are + // unified with anonymous types when duplicated inside + // of worlds. Overall this prevents, for example, extra + // `list` types from popping up when decoding. This + // is not strictly necessary but assists with + // roundtripping assertions during fuzzing. + Some(id) => { + log::debug!("type already exist"); + match referenced { + types::ComponentAnyTypeId::Defined(ty) => { + self.register_defined(id, &self.types[ty])?; + } + types::ComponentAnyTypeId::Resource(_) => {} + _ => unreachable!(), + } + let prev = self.type_map.insert(created, id); + assert!(prev.is_none()); + } + + // If the name is not defined, however, then there's two + // possibilities: + // + // * For package-local interfaces this is an error + // because the package-local interface defined + // everything already and this is referencing + // something that isn't defined. + // + // * For remote interfaces they're never fully declared + // so it's lazily filled in here. This means that the + // view of remote interfaces ends up being the minimal + // slice needed for this resolve, which is what's + // intended. + None => { + if is_local { + bail!("instance type export `{name}` not defined in interface"); + } + let id = self.register_type_export( + name.as_str(), + owner, + referenced, + created, + )?; + let prev = self.resolve.interfaces[interface] + .types + .insert(name.to_string(), id); + assert!(prev.is_none()); + } + } + } + types::ComponentEntityType::Instance(_) => todo!(), + types::ComponentEntityType::Component(_) => todo!(), + } + } + Ok(interface) + } + /// Registers that the `name` provided is either imported interface from a /// foreign package or referencing a previously defined interface in this /// package. @@ -960,7 +1051,8 @@ impl WitPackageDecoder<'_> { stability: Default::default(), }; - let owner = TypeOwner::Interface(self.resolve.interfaces.next_id()); + let next_id = self.resolve.interfaces.next_id(); + let owner = TypeOwner::Interface(next_id); for (name, ty) in ty.exports.iter() { match *ty { types::ComponentEntityType::Type { @@ -982,6 +1074,11 @@ impl WitPackageDecoder<'_> { let prev = interface.functions.insert(name.to_string(), func); assert!(prev.is_none()); } + types::ComponentEntityType::Instance(inst) => { + let ty = &self.types[inst]; + let iface = self.register_export(&name, &ty)?; + interface.nested.insert(name.to_string(), iface); + } _ => bail!("instance type export `{name}` is not a type or function"), }; } diff --git a/crates/wit-parser/src/resolve.rs b/crates/wit-parser/src/resolve.rs index 3742e3dbba..4f13143451 100644 --- a/crates/wit-parser/src/resolve.rs +++ b/crates/wit-parser/src/resolve.rs @@ -899,6 +899,23 @@ impl Resolve { } } + // pub fn type_interface_nest(&self, id: TypeId) -> Option { + // let ty = &self.types[id]; + // let dep = match ty.kind { + // TypeDefKind::Type(Type::Id(id)) => id, + // _ => return None, + // }; + // let other = &self.types[dep]; + // if ty.owner == other.owner { + // None + // } else { + // match other.owner { + // TypeOwner::Interface(id) => Some(id), + // _ => unreachable!(), + // } + // } + // } + /// Returns an iterator of all interfaces that the interface `id` depends /// on. /// @@ -915,6 +932,11 @@ impl Resolve { .filter_map(move |(_name, ty)| self.type_interface_dep(*ty)) } + pub fn interface_nests(&self, id: InterfaceId) -> impl Iterator + '_ { + self.interfaces[id].nested.iter().map(|n| n.1.clone()) + // .filter_map(move |(_name, ty)| self.type_interface_dep(*ty)) + } + /// Returns an iterator of all packages that the package `id` depends /// on. /// @@ -1357,7 +1379,7 @@ impl Remap { iface.package = Some(pkgid); if let Some(name) = &iface.name { let prev = resolve.packages[pkgid].interfaces.insert(name.clone(), id); - // assert!(prev.is_none()); + assert!(prev.is_none()); } } for id in self.worlds.iter().skip(foreign_worlds) { diff --git a/crates/wit-parser/tests/ui/comments.wit.json b/crates/wit-parser/tests/ui/comments.wit.json index f2a04f9d39..b882e6e1ae 100644 --- a/crates/wit-parser/tests/ui/comments.wit.json +++ b/crates/wit-parser/tests/ui/comments.wit.json @@ -3,6 +3,7 @@ "interfaces": [ { "name": "foo", + "nested": {}, "types": { "x": 0, "bar": 1 diff --git a/crates/wit-parser/tests/ui/complex-include.wit.json b/crates/wit-parser/tests/ui/complex-include.wit.json index f9b17f2792..ba6623ed1c 100644 --- a/crates/wit-parser/tests/ui/complex-include.wit.json +++ b/crates/wit-parser/tests/ui/complex-include.wit.json @@ -126,36 +126,42 @@ "interfaces": [ { "name": "a", + "nested": {}, "types": {}, "functions": {}, "package": 0 }, { "name": "b", + "nested": {}, "types": {}, "functions": {}, "package": 0 }, { "name": "a", + "nested": {}, "types": {}, "functions": {}, "package": 1 }, { "name": "b", + "nested": {}, "types": {}, "functions": {}, "package": 1 }, { "name": "ai", + "nested": {}, "types": {}, "functions": {}, "package": 2 }, { "name": "bi", + "nested": {}, "types": {}, "functions": {}, "package": 2 diff --git a/crates/wit-parser/tests/ui/cross-package-resource.wit.json b/crates/wit-parser/tests/ui/cross-package-resource.wit.json index 51ab92795d..e97bdfd924 100644 --- a/crates/wit-parser/tests/ui/cross-package-resource.wit.json +++ b/crates/wit-parser/tests/ui/cross-package-resource.wit.json @@ -3,6 +3,7 @@ "interfaces": [ { "name": "foo", + "nested": {}, "types": { "r": 0 }, @@ -11,6 +12,7 @@ }, { "name": "foo", + "nested": {}, "types": { "r": 1, "t": 2 diff --git a/crates/wit-parser/tests/ui/diamond1.wit.json b/crates/wit-parser/tests/ui/diamond1.wit.json index 54e87e7b04..33abd1e63e 100644 --- a/crates/wit-parser/tests/ui/diamond1.wit.json +++ b/crates/wit-parser/tests/ui/diamond1.wit.json @@ -21,12 +21,14 @@ "interfaces": [ { "name": "types", + "nested": {}, "types": {}, "functions": {}, "package": 0 }, { "name": "types", + "nested": {}, "types": {}, "functions": {}, "package": 1 diff --git a/crates/wit-parser/tests/ui/disambiguate-diamond.wit.json b/crates/wit-parser/tests/ui/disambiguate-diamond.wit.json index 34f675da51..f2eb1746e6 100644 --- a/crates/wit-parser/tests/ui/disambiguate-diamond.wit.json +++ b/crates/wit-parser/tests/ui/disambiguate-diamond.wit.json @@ -31,6 +31,7 @@ "interfaces": [ { "name": "shared1", + "nested": {}, "types": { "the-type": 0 }, @@ -39,6 +40,7 @@ }, { "name": "shared2", + "nested": {}, "types": { "the-type": 1 }, @@ -47,6 +49,7 @@ }, { "name": null, + "nested": {}, "types": { "the-type": 2 }, @@ -55,6 +58,7 @@ }, { "name": null, + "nested": {}, "types": { "the-type": 3 }, diff --git a/crates/wit-parser/tests/ui/feature-gates.wit.json b/crates/wit-parser/tests/ui/feature-gates.wit.json index 9cdb44c582..a90509ef27 100644 --- a/crates/wit-parser/tests/ui/feature-gates.wit.json +++ b/crates/wit-parser/tests/ui/feature-gates.wit.json @@ -65,6 +65,7 @@ "interfaces": [ { "name": "ungated", + "nested": {}, "types": {}, "functions": { "ungated": { @@ -88,6 +89,7 @@ }, { "name": "ungated2", + "nested": {}, "types": { "ungated": 0, "ungated2": 1 @@ -102,6 +104,7 @@ }, { "name": "ungated-use-target", + "nested": {}, "types": { "t": 2 }, @@ -115,6 +118,7 @@ }, { "name": "ungated-use", + "nested": {}, "types": { "t": 3 }, @@ -128,6 +132,7 @@ }, { "name": "ungated-for-world", + "nested": {}, "types": {}, "functions": {}, "stability": { @@ -139,6 +144,7 @@ }, { "name": "with-resources", + "nested": {}, "types": { "ungated": 4 }, diff --git a/crates/wit-parser/tests/ui/foreign-deps-union.wit.json b/crates/wit-parser/tests/ui/foreign-deps-union.wit.json index 419d62e7ce..77f27971fb 100644 --- a/crates/wit-parser/tests/ui/foreign-deps-union.wit.json +++ b/crates/wit-parser/tests/ui/foreign-deps-union.wit.json @@ -117,24 +117,28 @@ "interfaces": [ { "name": "other-interface", + "nested": {}, "types": {}, "functions": {}, "package": 0 }, { "name": "saas", + "nested": {}, "types": {}, "functions": {}, "package": 1 }, { "name": "i", + "nested": {}, "types": {}, "functions": {}, "package": 2 }, { "name": "the-default", + "nested": {}, "types": { "some-type": 0 }, @@ -143,6 +147,7 @@ }, { "name": "the-default", + "nested": {}, "types": { "from-default": 1 }, @@ -151,6 +156,7 @@ }, { "name": "some-interface", + "nested": {}, "types": { "another-type": 2 }, @@ -159,6 +165,7 @@ }, { "name": "another-interface", + "nested": {}, "types": { "yet-another-type": 3 }, @@ -167,6 +174,7 @@ }, { "name": "clocks", + "nested": {}, "types": { "timestamp": 4 }, @@ -175,6 +183,7 @@ }, { "name": "filesystem", + "nested": {}, "types": { "stat": 5 }, @@ -183,6 +192,7 @@ }, { "name": "foo", + "nested": {}, "types": { "timestamp": 6, "stat": 7 @@ -192,6 +202,7 @@ }, { "name": "bar", + "nested": {}, "types": { "from-default": 8, "another-type": 9, @@ -202,6 +213,7 @@ }, { "name": "use1", + "nested": {}, "types": { "some-type": 11 }, @@ -210,6 +222,7 @@ }, { "name": "use2", + "nested": {}, "types": { "some-type": 12 }, diff --git a/crates/wit-parser/tests/ui/foreign-deps.wit.json b/crates/wit-parser/tests/ui/foreign-deps.wit.json index 503c1b32c9..b41cc2fc23 100644 --- a/crates/wit-parser/tests/ui/foreign-deps.wit.json +++ b/crates/wit-parser/tests/ui/foreign-deps.wit.json @@ -72,24 +72,28 @@ "interfaces": [ { "name": "other-interface", + "nested": {}, "types": {}, "functions": {}, "package": 0 }, { "name": "saas", + "nested": {}, "types": {}, "functions": {}, "package": 1 }, { "name": "i", + "nested": {}, "types": {}, "functions": {}, "package": 2 }, { "name": "the-default", + "nested": {}, "types": { "some-type": 0 }, @@ -98,6 +102,7 @@ }, { "name": "the-default", + "nested": {}, "types": { "from-default": 1 }, @@ -106,6 +111,7 @@ }, { "name": "some-interface", + "nested": {}, "types": { "another-type": 2 }, @@ -114,6 +120,7 @@ }, { "name": "another-interface", + "nested": {}, "types": { "yet-another-type": 3 }, @@ -122,6 +129,7 @@ }, { "name": "clocks", + "nested": {}, "types": { "timestamp": 4 }, @@ -130,6 +138,7 @@ }, { "name": "filesystem", + "nested": {}, "types": { "stat": 5 }, @@ -138,6 +147,7 @@ }, { "name": "foo", + "nested": {}, "types": { "timestamp": 6, "stat": 7 @@ -147,6 +157,7 @@ }, { "name": "bar", + "nested": {}, "types": { "from-default": 8, "another-type": 9, @@ -157,6 +168,7 @@ }, { "name": "use1", + "nested": {}, "types": { "some-type": 11 }, @@ -165,6 +177,7 @@ }, { "name": "use2", + "nested": {}, "types": { "some-type": 12 }, diff --git a/crates/wit-parser/tests/ui/functions.wit.json b/crates/wit-parser/tests/ui/functions.wit.json index 902fe35709..9fb97218df 100644 --- a/crates/wit-parser/tests/ui/functions.wit.json +++ b/crates/wit-parser/tests/ui/functions.wit.json @@ -3,6 +3,7 @@ "interfaces": [ { "name": "functions", + "nested": {}, "types": {}, "functions": { "f1": { diff --git a/crates/wit-parser/tests/ui/ignore-files-deps.wit.json b/crates/wit-parser/tests/ui/ignore-files-deps.wit.json index 7a812dbecf..31c9fa20da 100644 --- a/crates/wit-parser/tests/ui/ignore-files-deps.wit.json +++ b/crates/wit-parser/tests/ui/ignore-files-deps.wit.json @@ -16,6 +16,7 @@ "interfaces": [ { "name": "types", + "nested": {}, "types": {}, "functions": {}, "package": 0 diff --git a/crates/wit-parser/tests/ui/import-export-overlap2.wit.json b/crates/wit-parser/tests/ui/import-export-overlap2.wit.json index 0c991269c5..df8bf284b3 100644 --- a/crates/wit-parser/tests/ui/import-export-overlap2.wit.json +++ b/crates/wit-parser/tests/ui/import-export-overlap2.wit.json @@ -25,6 +25,7 @@ "interfaces": [ { "name": null, + "nested": {}, "types": {}, "functions": {}, "package": 0 diff --git a/crates/wit-parser/tests/ui/include-reps.wit.json b/crates/wit-parser/tests/ui/include-reps.wit.json index 21debef2fe..c6d44fb7c6 100644 --- a/crates/wit-parser/tests/ui/include-reps.wit.json +++ b/crates/wit-parser/tests/ui/include-reps.wit.json @@ -40,12 +40,14 @@ "interfaces": [ { "name": "a", + "nested": {}, "types": {}, "functions": {}, "package": 0 }, { "name": "b", + "nested": {}, "types": {}, "functions": {}, "package": 0 diff --git a/crates/wit-parser/tests/ui/kinds-of-deps.wit.json b/crates/wit-parser/tests/ui/kinds-of-deps.wit.json index f13afe6301..5a931fb139 100644 --- a/crates/wit-parser/tests/ui/kinds-of-deps.wit.json +++ b/crates/wit-parser/tests/ui/kinds-of-deps.wit.json @@ -31,24 +31,28 @@ "interfaces": [ { "name": "d", + "nested": {}, "types": {}, "functions": {}, "package": 0 }, { "name": "e", + "nested": {}, "types": {}, "functions": {}, "package": 1 }, { "name": "b", + "nested": {}, "types": {}, "functions": {}, "package": 2 }, { "name": "c", + "nested": {}, "types": {}, "functions": {}, "package": 3 diff --git a/crates/wit-parser/tests/ui/many-names.wit.json b/crates/wit-parser/tests/ui/many-names.wit.json index 590c68471a..9e05b5146d 100644 --- a/crates/wit-parser/tests/ui/many-names.wit.json +++ b/crates/wit-parser/tests/ui/many-names.wit.json @@ -16,12 +16,14 @@ "interfaces": [ { "name": "x", + "nested": {}, "types": {}, "functions": {}, "package": 0 }, { "name": null, + "nested": {}, "types": {}, "functions": {}, "package": 0 diff --git a/crates/wit-parser/tests/ui/multi-file-multi-package.wit.json b/crates/wit-parser/tests/ui/multi-file-multi-package.wit.json index a2dea31719..73e7c2d38c 100644 --- a/crates/wit-parser/tests/ui/multi-file-multi-package.wit.json +++ b/crates/wit-parser/tests/ui/multi-file-multi-package.wit.json @@ -72,6 +72,7 @@ "interfaces": [ { "name": "i2", + "nested": {}, "types": { "b": 0 }, @@ -80,6 +81,7 @@ }, { "name": null, + "nested": {}, "types": { "b": 1 }, @@ -88,6 +90,7 @@ }, { "name": "i3", + "nested": {}, "types": { "a": 2 }, @@ -96,6 +99,7 @@ }, { "name": null, + "nested": {}, "types": { "a": 3 }, @@ -104,6 +108,7 @@ }, { "name": "i1", + "nested": {}, "types": { "a": 4 }, @@ -112,6 +117,7 @@ }, { "name": null, + "nested": {}, "types": { "a": 5 }, @@ -120,6 +126,7 @@ }, { "name": "i4", + "nested": {}, "types": { "b": 6 }, @@ -128,6 +135,7 @@ }, { "name": null, + "nested": {}, "types": { "b": 7 }, diff --git a/crates/wit-parser/tests/ui/multi-file.wit.json b/crates/wit-parser/tests/ui/multi-file.wit.json index 45af0f5d32..67fcdda733 100644 --- a/crates/wit-parser/tests/ui/multi-file.wit.json +++ b/crates/wit-parser/tests/ui/multi-file.wit.json @@ -49,6 +49,7 @@ "interfaces": [ { "name": "irrelevant-name", + "nested": {}, "types": { "a-name": 0 }, @@ -57,6 +58,7 @@ }, { "name": "depend-on-me", + "nested": {}, "types": { "x": 1 }, @@ -65,6 +67,7 @@ }, { "name": "depends-on-later-item", + "nested": {}, "types": { "x": 2 }, @@ -73,12 +76,14 @@ }, { "name": "later-interface", + "nested": {}, "types": {}, "functions": {}, "package": 0 }, { "name": "cycle1", + "nested": {}, "types": { "t": 3 }, @@ -87,6 +92,7 @@ }, { "name": "cycle2", + "nested": {}, "types": { "t": 4 }, @@ -95,6 +101,7 @@ }, { "name": "cycle3", + "nested": {}, "types": { "t": 5 }, @@ -103,6 +110,7 @@ }, { "name": "foo", + "nested": {}, "types": { "x": 6 }, @@ -111,6 +119,7 @@ }, { "name": "something-else", + "nested": {}, "types": { "y": 7 }, @@ -119,6 +128,7 @@ }, { "name": "bar", + "nested": {}, "types": { "x": 8, "x2": 9, diff --git a/crates/wit-parser/tests/ui/multi-package-shared-deps.wit.json b/crates/wit-parser/tests/ui/multi-package-shared-deps.wit.json index d3434da0c5..6d31f7f3ee 100644 --- a/crates/wit-parser/tests/ui/multi-package-shared-deps.wit.json +++ b/crates/wit-parser/tests/ui/multi-package-shared-deps.wit.json @@ -38,12 +38,14 @@ "interfaces": [ { "name": "types", + "nested": {}, "types": {}, "functions": {}, "package": 0 }, { "name": "types", + "nested": {}, "types": {}, "functions": {}, "package": 1 diff --git a/crates/wit-parser/tests/ui/multi-package-transitive-deps.wit.json b/crates/wit-parser/tests/ui/multi-package-transitive-deps.wit.json index ece4cdade9..4aaaab4bd5 100644 --- a/crates/wit-parser/tests/ui/multi-package-transitive-deps.wit.json +++ b/crates/wit-parser/tests/ui/multi-package-transitive-deps.wit.json @@ -33,6 +33,7 @@ "interfaces": [ { "name": "types", + "nested": {}, "types": { "a": 0 }, @@ -41,6 +42,7 @@ }, { "name": "types", + "nested": {}, "types": { "a": 1, "r": 2 diff --git a/crates/wit-parser/tests/ui/name-both-resource-and-type.wit.json b/crates/wit-parser/tests/ui/name-both-resource-and-type.wit.json index 1dad5120b5..b230152086 100644 --- a/crates/wit-parser/tests/ui/name-both-resource-and-type.wit.json +++ b/crates/wit-parser/tests/ui/name-both-resource-and-type.wit.json @@ -3,6 +3,7 @@ "interfaces": [ { "name": "foo", + "nested": {}, "types": { "a": 0 }, @@ -11,6 +12,7 @@ }, { "name": "foo", + "nested": {}, "types": { "a": 1, "t1": 2, diff --git a/crates/wit-parser/tests/ui/packages-explicit-colliding-decl-names.wit.json b/crates/wit-parser/tests/ui/packages-explicit-colliding-decl-names.wit.json index f4c9e7055c..9de71aadb4 100644 --- a/crates/wit-parser/tests/ui/packages-explicit-colliding-decl-names.wit.json +++ b/crates/wit-parser/tests/ui/packages-explicit-colliding-decl-names.wit.json @@ -38,6 +38,7 @@ "interfaces": [ { "name": "i", + "nested": {}, "types": { "a": 0 }, @@ -46,6 +47,7 @@ }, { "name": null, + "nested": {}, "types": { "a": 1 }, @@ -54,6 +56,7 @@ }, { "name": "i", + "nested": {}, "types": { "a": 2 }, @@ -62,6 +65,7 @@ }, { "name": null, + "nested": {}, "types": { "a": 3 }, diff --git a/crates/wit-parser/tests/ui/packages-explicit-internal-references.wit.json b/crates/wit-parser/tests/ui/packages-explicit-internal-references.wit.json index 1d59a07e3a..df9d0cf4e7 100644 --- a/crates/wit-parser/tests/ui/packages-explicit-internal-references.wit.json +++ b/crates/wit-parser/tests/ui/packages-explicit-internal-references.wit.json @@ -21,6 +21,7 @@ "interfaces": [ { "name": "i1", + "nested": {}, "types": { "a": 0 }, @@ -29,6 +30,7 @@ }, { "name": null, + "nested": {}, "types": { "a": 1 }, diff --git a/crates/wit-parser/tests/ui/packages-explicit-with-semver.wit.json b/crates/wit-parser/tests/ui/packages-explicit-with-semver.wit.json index 23a10462aa..c0125847cd 100644 --- a/crates/wit-parser/tests/ui/packages-explicit-with-semver.wit.json +++ b/crates/wit-parser/tests/ui/packages-explicit-with-semver.wit.json @@ -38,6 +38,7 @@ "interfaces": [ { "name": "i1", + "nested": {}, "types": { "a": 0 }, @@ -46,6 +47,7 @@ }, { "name": null, + "nested": {}, "types": { "a": 1 }, @@ -54,6 +56,7 @@ }, { "name": "i1", + "nested": {}, "types": { "a": 2 }, @@ -62,6 +65,7 @@ }, { "name": null, + "nested": {}, "types": { "a": 3 }, diff --git a/crates/wit-parser/tests/ui/packages-multiple-explicit.wit.json b/crates/wit-parser/tests/ui/packages-multiple-explicit.wit.json index de73c51de3..d41ae95ce8 100644 --- a/crates/wit-parser/tests/ui/packages-multiple-explicit.wit.json +++ b/crates/wit-parser/tests/ui/packages-multiple-explicit.wit.json @@ -38,6 +38,7 @@ "interfaces": [ { "name": "i2", + "nested": {}, "types": { "b": 0 }, @@ -46,6 +47,7 @@ }, { "name": null, + "nested": {}, "types": { "b": 1 }, @@ -54,6 +56,7 @@ }, { "name": "i1", + "nested": {}, "types": { "a": 2 }, @@ -62,6 +65,7 @@ }, { "name": null, + "nested": {}, "types": { "a": 3 }, diff --git a/crates/wit-parser/tests/ui/packages-single-explicit.wit.json b/crates/wit-parser/tests/ui/packages-single-explicit.wit.json index 3eefde2810..4da0f368dd 100644 --- a/crates/wit-parser/tests/ui/packages-single-explicit.wit.json +++ b/crates/wit-parser/tests/ui/packages-single-explicit.wit.json @@ -21,6 +21,7 @@ "interfaces": [ { "name": "i1", + "nested": {}, "types": { "a": 0 }, @@ -29,6 +30,7 @@ }, { "name": null, + "nested": {}, "types": { "a": 1 }, diff --git a/crates/wit-parser/tests/ui/random.wit.json b/crates/wit-parser/tests/ui/random.wit.json index 5d46664671..d3d58b360f 100644 --- a/crates/wit-parser/tests/ui/random.wit.json +++ b/crates/wit-parser/tests/ui/random.wit.json @@ -3,6 +3,7 @@ "interfaces": [ { "name": "random", + "nested": {}, "types": {}, "functions": { "get-random-bytes": { diff --git a/crates/wit-parser/tests/ui/resources-empty.wit.json b/crates/wit-parser/tests/ui/resources-empty.wit.json index 6c40c61dfc..beb71d632d 100644 --- a/crates/wit-parser/tests/ui/resources-empty.wit.json +++ b/crates/wit-parser/tests/ui/resources-empty.wit.json @@ -3,6 +3,7 @@ "interfaces": [ { "name": "resources-empty", + "nested": {}, "types": { "r1": 0 }, diff --git a/crates/wit-parser/tests/ui/resources-multiple-returns-own.wit.json b/crates/wit-parser/tests/ui/resources-multiple-returns-own.wit.json index e7234f7ed8..0340ad8b88 100644 --- a/crates/wit-parser/tests/ui/resources-multiple-returns-own.wit.json +++ b/crates/wit-parser/tests/ui/resources-multiple-returns-own.wit.json @@ -3,6 +3,7 @@ "interfaces": [ { "name": "resources1", + "nested": {}, "types": { "r1": 0 }, diff --git a/crates/wit-parser/tests/ui/resources-multiple.wit.json b/crates/wit-parser/tests/ui/resources-multiple.wit.json index 2914ab99d9..a90c95c00b 100644 --- a/crates/wit-parser/tests/ui/resources-multiple.wit.json +++ b/crates/wit-parser/tests/ui/resources-multiple.wit.json @@ -3,6 +3,7 @@ "interfaces": [ { "name": "resources-multiple", + "nested": {}, "types": { "r1": 0 }, diff --git a/crates/wit-parser/tests/ui/resources-return-own.wit.json b/crates/wit-parser/tests/ui/resources-return-own.wit.json index 3e44fc106c..f816e574e2 100644 --- a/crates/wit-parser/tests/ui/resources-return-own.wit.json +++ b/crates/wit-parser/tests/ui/resources-return-own.wit.json @@ -3,6 +3,7 @@ "interfaces": [ { "name": "resources1", + "nested": {}, "types": { "r1": 0 }, diff --git a/crates/wit-parser/tests/ui/resources.wit.json b/crates/wit-parser/tests/ui/resources.wit.json index 169fb02110..312743afb1 100644 --- a/crates/wit-parser/tests/ui/resources.wit.json +++ b/crates/wit-parser/tests/ui/resources.wit.json @@ -34,6 +34,7 @@ "interfaces": [ { "name": "foo", + "nested": {}, "types": { "a": 0, "b": 1, @@ -156,6 +157,7 @@ }, { "name": "i", + "nested": {}, "types": { "a": 7, "t1": 8, diff --git a/crates/wit-parser/tests/ui/resources1.wit.json b/crates/wit-parser/tests/ui/resources1.wit.json index 447e5ec2b9..ac1305231d 100644 --- a/crates/wit-parser/tests/ui/resources1.wit.json +++ b/crates/wit-parser/tests/ui/resources1.wit.json @@ -3,6 +3,7 @@ "interfaces": [ { "name": "resources1", + "nested": {}, "types": { "r1": 0 }, diff --git a/crates/wit-parser/tests/ui/shared-types.wit.json b/crates/wit-parser/tests/ui/shared-types.wit.json index 569f6d3861..ea417d64f1 100644 --- a/crates/wit-parser/tests/ui/shared-types.wit.json +++ b/crates/wit-parser/tests/ui/shared-types.wit.json @@ -22,6 +22,7 @@ "interfaces": [ { "name": null, + "nested": {}, "types": {}, "functions": { "a": { @@ -39,6 +40,7 @@ }, { "name": null, + "nested": {}, "types": {}, "functions": { "a": { diff --git a/crates/wit-parser/tests/ui/simple-wasm-text.wit.json b/crates/wit-parser/tests/ui/simple-wasm-text.wit.json index 969c13a506..647c578fe9 100644 --- a/crates/wit-parser/tests/ui/simple-wasm-text.wit.json +++ b/crates/wit-parser/tests/ui/simple-wasm-text.wit.json @@ -3,6 +3,7 @@ "interfaces": [ { "name": "x", + "nested": {}, "types": {}, "functions": {}, "package": 0 diff --git a/crates/wit-parser/tests/ui/since-and-unstable.wit.json b/crates/wit-parser/tests/ui/since-and-unstable.wit.json index 31c4fa0c4b..d76c020721 100644 --- a/crates/wit-parser/tests/ui/since-and-unstable.wit.json +++ b/crates/wit-parser/tests/ui/since-and-unstable.wit.json @@ -140,6 +140,7 @@ "interfaces": [ { "name": "foo1", + "nested": {}, "types": {}, "functions": {}, "stability": { @@ -151,6 +152,7 @@ }, { "name": "foo2", + "nested": {}, "types": {}, "functions": {}, "stability": { @@ -163,6 +165,7 @@ }, { "name": "foo3", + "nested": {}, "types": {}, "functions": {}, "stability": { @@ -175,6 +178,7 @@ }, { "name": "in-an-interface", + "nested": {}, "types": { "r1": 0, "r2": 1, @@ -250,12 +254,14 @@ }, { "name": "z", + "nested": {}, "types": {}, "functions": {}, "package": 0 }, { "name": null, + "nested": {}, "types": {}, "functions": {}, "stability": { @@ -267,6 +273,7 @@ }, { "name": null, + "nested": {}, "types": {}, "functions": {}, "stability": { diff --git a/crates/wit-parser/tests/ui/stress-export-elaborate.wit.json b/crates/wit-parser/tests/ui/stress-export-elaborate.wit.json index d77812776b..3b6e2414b0 100644 --- a/crates/wit-parser/tests/ui/stress-export-elaborate.wit.json +++ b/crates/wit-parser/tests/ui/stress-export-elaborate.wit.json @@ -62,6 +62,7 @@ "interfaces": [ { "name": "i1", + "nested": {}, "types": { "t1": 0, "t2": 1, @@ -79,6 +80,7 @@ }, { "name": "i2", + "nested": {}, "types": { "t1": 10, "t2": 11, @@ -96,6 +98,7 @@ }, { "name": "i3", + "nested": {}, "types": { "t1": 20, "t2": 21, @@ -113,6 +116,7 @@ }, { "name": "i4", + "nested": {}, "types": { "t1": 30, "t2": 31, @@ -130,6 +134,7 @@ }, { "name": "i5", + "nested": {}, "types": { "t1": 40, "t2": 41, @@ -147,6 +152,7 @@ }, { "name": "i6", + "nested": {}, "types": { "t1": 50, "t2": 51, @@ -164,6 +170,7 @@ }, { "name": "i7", + "nested": {}, "types": { "t1": 60, "t2": 61, @@ -181,6 +188,7 @@ }, { "name": "i8", + "nested": {}, "types": { "t1": 70, "t2": 71, @@ -198,6 +206,7 @@ }, { "name": "i9", + "nested": {}, "types": { "t1": 80, "t2": 81, @@ -215,6 +224,7 @@ }, { "name": "i10", + "nested": {}, "types": { "t1": 90, "t2": 91, diff --git a/crates/wit-parser/tests/ui/type-then-eof.wit.json b/crates/wit-parser/tests/ui/type-then-eof.wit.json index beb0a7adca..354036986e 100644 --- a/crates/wit-parser/tests/ui/type-then-eof.wit.json +++ b/crates/wit-parser/tests/ui/type-then-eof.wit.json @@ -3,6 +3,7 @@ "interfaces": [ { "name": "foo", + "nested": {}, "types": {}, "functions": { "foo": { diff --git a/crates/wit-parser/tests/ui/types.wit.json b/crates/wit-parser/tests/ui/types.wit.json index 8874e3541d..d456ea6fd3 100644 --- a/crates/wit-parser/tests/ui/types.wit.json +++ b/crates/wit-parser/tests/ui/types.wit.json @@ -3,6 +3,7 @@ "interfaces": [ { "name": "types", + "nested": {}, "types": { "t1": 0, "t2": 1, diff --git a/crates/wit-parser/tests/ui/use-chain.wit.json b/crates/wit-parser/tests/ui/use-chain.wit.json index bd942bb8cd..8697663de5 100644 --- a/crates/wit-parser/tests/ui/use-chain.wit.json +++ b/crates/wit-parser/tests/ui/use-chain.wit.json @@ -3,6 +3,7 @@ "interfaces": [ { "name": "foo", + "nested": {}, "types": { "foo": 0 }, @@ -11,6 +12,7 @@ }, { "name": "name", + "nested": {}, "types": { "foo": 1 }, diff --git a/crates/wit-parser/tests/ui/use.wit.json b/crates/wit-parser/tests/ui/use.wit.json index 43cc16b4a7..d08dc02d94 100644 --- a/crates/wit-parser/tests/ui/use.wit.json +++ b/crates/wit-parser/tests/ui/use.wit.json @@ -3,6 +3,7 @@ "interfaces": [ { "name": "bar", + "nested": {}, "types": { "the-type": 0 }, @@ -11,6 +12,7 @@ }, { "name": "foo", + "nested": {}, "types": { "the-type": 1 }, @@ -19,6 +21,7 @@ }, { "name": "baz", + "nested": {}, "types": { "the-type": 2, "test": 3 @@ -28,18 +31,21 @@ }, { "name": "empty", + "nested": {}, "types": {}, "functions": {}, "package": 0 }, { "name": "use-from-empty", + "nested": {}, "types": {}, "functions": {}, "package": 0 }, { "name": "use-multiple", + "nested": {}, "types": { "the-type": 4, "test": 5 @@ -65,6 +71,7 @@ }, { "name": "trailing-comma", + "nested": {}, "types": { "the-type": 6, "the-foo": 7 diff --git a/crates/wit-parser/tests/ui/versions.wit.json b/crates/wit-parser/tests/ui/versions.wit.json index febd55be51..e68c12779f 100644 --- a/crates/wit-parser/tests/ui/versions.wit.json +++ b/crates/wit-parser/tests/ui/versions.wit.json @@ -3,6 +3,7 @@ "interfaces": [ { "name": "foo", + "nested": {}, "types": { "t": 0 }, @@ -11,6 +12,7 @@ }, { "name": "foo", + "nested": {}, "types": { "t": 1 }, @@ -19,6 +21,7 @@ }, { "name": "foo", + "nested": {}, "types": { "t": 2, "t2": 3 diff --git a/crates/wit-parser/tests/ui/wasi.wit.json b/crates/wit-parser/tests/ui/wasi.wit.json index e784fbd207..c539523086 100644 --- a/crates/wit-parser/tests/ui/wasi.wit.json +++ b/crates/wit-parser/tests/ui/wasi.wit.json @@ -3,6 +3,7 @@ "interfaces": [ { "name": "wasi", + "nested": {}, "types": { "clockid": 0, "timestamp": 1, diff --git a/crates/wit-parser/tests/ui/world-diamond.wit.json b/crates/wit-parser/tests/ui/world-diamond.wit.json index 6dba45edb9..1ba4eb540b 100644 --- a/crates/wit-parser/tests/ui/world-diamond.wit.json +++ b/crates/wit-parser/tests/ui/world-diamond.wit.json @@ -34,6 +34,7 @@ "interfaces": [ { "name": "shared-items", + "nested": {}, "types": { "foo": 0 }, @@ -42,6 +43,7 @@ }, { "name": "i1", + "nested": {}, "types": { "foo": 1 }, @@ -61,6 +63,7 @@ }, { "name": "i2", + "nested": {}, "types": { "foo": 2 }, diff --git a/crates/wit-parser/tests/ui/world-iface-no-collide.wit.json b/crates/wit-parser/tests/ui/world-iface-no-collide.wit.json index fffc11557e..43e3e945c5 100644 --- a/crates/wit-parser/tests/ui/world-iface-no-collide.wit.json +++ b/crates/wit-parser/tests/ui/world-iface-no-collide.wit.json @@ -27,6 +27,7 @@ "interfaces": [ { "name": "foo", + "nested": {}, "types": { "t": 0 }, diff --git a/crates/wit-parser/tests/ui/world-implicit-import1.wit.json b/crates/wit-parser/tests/ui/world-implicit-import1.wit.json index 708c339b40..1c0473b683 100644 --- a/crates/wit-parser/tests/ui/world-implicit-import1.wit.json +++ b/crates/wit-parser/tests/ui/world-implicit-import1.wit.json @@ -26,6 +26,7 @@ "interfaces": [ { "name": "foo", + "nested": {}, "types": { "a": 0 }, @@ -34,6 +35,7 @@ }, { "name": null, + "nested": {}, "types": { "a": 1 }, @@ -42,6 +44,7 @@ }, { "name": null, + "nested": {}, "types": {}, "functions": {}, "package": 0 diff --git a/crates/wit-parser/tests/ui/world-implicit-import2.wit.json b/crates/wit-parser/tests/ui/world-implicit-import2.wit.json index 97049842a4..c7ff241171 100644 --- a/crates/wit-parser/tests/ui/world-implicit-import2.wit.json +++ b/crates/wit-parser/tests/ui/world-implicit-import2.wit.json @@ -31,6 +31,7 @@ "interfaces": [ { "name": "foo", + "nested": {}, "types": { "g": 0 }, diff --git a/crates/wit-parser/tests/ui/world-implicit-import3.wit.json b/crates/wit-parser/tests/ui/world-implicit-import3.wit.json index 159bee7738..9dfc4bf223 100644 --- a/crates/wit-parser/tests/ui/world-implicit-import3.wit.json +++ b/crates/wit-parser/tests/ui/world-implicit-import3.wit.json @@ -32,6 +32,7 @@ "interfaces": [ { "name": "foo", + "nested": {}, "types": { "g": 0 }, diff --git a/crates/wit-parser/tests/ui/world-same-fields4.wit.json b/crates/wit-parser/tests/ui/world-same-fields4.wit.json index 3be16acf9b..dbe420b3f4 100644 --- a/crates/wit-parser/tests/ui/world-same-fields4.wit.json +++ b/crates/wit-parser/tests/ui/world-same-fields4.wit.json @@ -27,6 +27,7 @@ "interfaces": [ { "name": "shared-items", + "nested": {}, "types": { "a": 0 }, @@ -35,12 +36,14 @@ }, { "name": null, + "nested": {}, "types": {}, "functions": {}, "package": 0 }, { "name": null, + "nested": {}, "types": { "a": 1 }, diff --git a/crates/wit-parser/tests/ui/world-top-level-resources.wit.json b/crates/wit-parser/tests/ui/world-top-level-resources.wit.json index 794e699f45..23541f192d 100644 --- a/crates/wit-parser/tests/ui/world-top-level-resources.wit.json +++ b/crates/wit-parser/tests/ui/world-top-level-resources.wit.json @@ -27,6 +27,7 @@ "interfaces": [ { "name": "types", + "nested": {}, "types": { "request": 0, "response": 1 @@ -97,6 +98,7 @@ }, { "name": "handler", + "nested": {}, "types": { "request": 5, "response": 6 diff --git a/crates/wit-parser/tests/ui/worlds-union-dedup.wit.json b/crates/wit-parser/tests/ui/worlds-union-dedup.wit.json index d1e24e97b6..74f110386a 100644 --- a/crates/wit-parser/tests/ui/worlds-union-dedup.wit.json +++ b/crates/wit-parser/tests/ui/worlds-union-dedup.wit.json @@ -55,36 +55,42 @@ "interfaces": [ { "name": "a1", + "nested": {}, "types": {}, "functions": {}, "package": 0 }, { "name": "a2", + "nested": {}, "types": {}, "functions": {}, "package": 0 }, { "name": "b1", + "nested": {}, "types": {}, "functions": {}, "package": 0 }, { "name": "b2", + "nested": {}, "types": {}, "functions": {}, "package": 0 }, { "name": "c", + "nested": {}, "types": {}, "functions": {}, "package": 0 }, { "name": "d", + "nested": {}, "types": {}, "functions": {}, "package": 0 diff --git a/crates/wit-parser/tests/ui/worlds-with-types.wit.json b/crates/wit-parser/tests/ui/worlds-with-types.wit.json index 70e857d68b..65f5a2a0b9 100644 --- a/crates/wit-parser/tests/ui/worlds-with-types.wit.json +++ b/crates/wit-parser/tests/ui/worlds-with-types.wit.json @@ -111,6 +111,7 @@ "interfaces": [ { "name": "disambiguate", + "nested": {}, "types": { "t": 0 }, From e91ccd535f1351e2b3d771c1cb0a2aecebc8b308 Mon Sep 17 00:00:00 2001 From: Daniel Macovei Date: Tue, 18 Jun 2024 15:31:25 -0500 Subject: [PATCH 3/4] field gates, nesting nest statements, doc comments --- crates/wit-component/src/encoding/wit/v2.rs | 55 +++++++++++++++---- crates/wit-component/src/printing.rs | 2 + .../tests/interfaces/doc-comments.wat | 2 +- .../wit-component/tests/interfaces/nested.wat | 9 ++- .../tests/interfaces/nested/deps/nestee.wit | 3 + .../tests/interfaces/nested/nested.wit | 2 + .../tests/interfaces/nested/thing.wit.print | 2 + .../tests/interfaces/resources.wat | 2 +- .../tests/interfaces/wasi-http.wat | 2 +- crates/wit-parser/src/ast.rs | 16 ++++-- crates/wit-parser/src/ast/resolve.rs | 20 +++---- crates/wit-parser/src/decoding.rs | 23 +++++++- crates/wit-parser/src/lib.rs | 20 +++++-- crates/wit-parser/src/metadata.rs | 35 +++++++++++- crates/wit-parser/src/resolve.rs | 22 -------- 15 files changed, 156 insertions(+), 59 deletions(-) diff --git a/crates/wit-component/src/encoding/wit/v2.rs b/crates/wit-component/src/encoding/wit/v2.rs index 54b6f7b23c..3907cba9fb 100644 --- a/crates/wit-component/src/encoding/wit/v2.rs +++ b/crates/wit-component/src/encoding/wit/v2.rs @@ -1,5 +1,5 @@ use crate::encoding::types::{FunctionKey, ValtypeEncoder}; -use anyhow::{bail, Result}; +use anyhow::Result; use indexmap::IndexSet; use std::collections::HashMap; use std::mem; @@ -226,6 +226,20 @@ impl InterfaceEncoder<'_> { .export(name, ComponentTypeRef::Func(ty)); } let mut instance = self.pop_instance(); + self.encode_nested(iface, &mut instance)?; + + let idx = self.outer.type_count(); + self.outer.ty().instance(&instance); + self.import_map.insert(interface, self.instances); + self.instances += 1; + Ok(idx) + } + + fn encode_nested<'a>( + &'a mut self, + iface: &Interface, + instance: &'a mut InstanceType, + ) -> Result<&mut InstanceType> { for (orig_name, _) in &iface.nested { let mut pkg_parts = orig_name.split("/"); let pkg = pkg_parts.next().expect("expected projection"); @@ -255,23 +269,40 @@ impl InterfaceEncoder<'_> { } inst.encode_valtype(self.resolve, &Type::Id(*id))?; } - for (_, _) in &nested_iface.nested { - bail!("Using `nest` in a nested interface is not yet supported"); - } let ty = instance.ty(); - let nested = inst.pop_instance(); - ty.instance(&nested); + let nested_instance = &mut inst.pop_instance(); + for (orig_name, _) in &nested_iface.nested { + let mut nest_package_parts = orig_name.split("/"); + let nest_pkg = nest_package_parts.next().unwrap(); + let nest_iface = nest_package_parts.next().unwrap(); + let mut parts = nest_pkg.split(":"); + let ns = parts.next().unwrap(); + let name = parts.next().unwrap(); + let name = PackageName { + namespace: ns.to_string(), + name: name.to_string(), + version: None, + }; + let nest_pkg_id = self.resolve.package_names.get(&name).unwrap(); + let nested_package = &self.resolve.packages[*nest_pkg_id]; + let myguy = nested_package.interfaces.get(nest_iface).unwrap(); + let nest = &self.resolve.interfaces[*myguy]; + let mut clone = nested_instance.clone(); + let deep_instance = self.encode_nested(nest, &mut clone)?; + let deep_ty = nested_instance.ty(); + deep_ty.instance(&deep_instance); + nested_instance.export( + orig_name, + ComponentTypeRef::Instance(deep_instance.type_count()), + ); + } + ty.instance(&nested_instance); instance.export( orig_name, ComponentTypeRef::Instance(instance.type_count() - 1), ); } - - let idx = self.outer.type_count(); - self.outer.ty().instance(&instance); - self.import_map.insert(interface, self.instances); - self.instances += 1; - Ok(idx) + Ok(instance) } fn push_instance(&mut self) { diff --git a/crates/wit-component/src/printing.rs b/crates/wit-component/src/printing.rs index d35785c82e..6bdb52665d 100644 --- a/crates/wit-component/src/printing.rs +++ b/crates/wit-component/src/printing.rs @@ -84,6 +84,8 @@ impl WitPrinter { self.output.push_str(" {\n"); let nested = &resolve.interfaces[*id].nested; for item in nested { + self.print_stability(&item.1.stability); + self.print_docs(&item.1.docs); self.output.push_str("nest "); self.print_name(item.0); self.output.push_str(";\n") diff --git a/crates/wit-component/tests/interfaces/doc-comments.wat b/crates/wit-component/tests/interfaces/doc-comments.wat index 6d61c7f477..028bde18b1 100644 --- a/crates/wit-component/tests/interfaces/doc-comments.wat +++ b/crates/wit-component/tests/interfaces/doc-comments.wat @@ -66,7 +66,7 @@ ) ) (export (;5;) "coverage-world" (type 4)) - (@custom "package-docs" "\00{\22docs\22:\22package docs;\22,\22worlds\22:{\22coverage-world\22:{\22docs\22:\22world docs\22,\22interfaces\22:{\22i\22:{\22docs\22:\22world inline interface docs\22,\22funcs\22:{\22f\22:\22inline interface func docs\22},\22types\22:{\22t\22:{\22docs\22:\22inline interface typedef docs\22}}}},\22types\22:{\22t\22:{\22docs\22:\22world typedef docs\22}},\22funcs\22:{\22imp\22:\22world func import docs\22,\22exp\22:\22world func export docs\22}}},\22interfaces\22:{\22coverage-iface\22:{\22docs\22:\22interface docs\22,\22funcs\22:{\22[constructor]res\22:\22constructor docs\22,\22[method]res.m\22:\22method docs\22,\22[static]res.s\22:\22static func docs\22,\22f\22:\22interface func docs\22},\22types\22:{\22t\22:{\22docs\22:\22basic typedef docs\22},\22r\22:{\22docs\22:\22record typedef docs\22,\22items\22:{\22f1\22:\22record field docs\22}},\22fl\22:{\22items\22:{\22f1\22:\22flag docs\22}},\22v\22:{\22items\22:{\22c1\22:\22variant case docs\22}},\22e\22:{\22items\22:{\22c1\22:\22enum case docs\22}}}},\22other-comment-forms\22:{\22docs\22:\22other comment forms\5cn multi-line block\22,\22funcs\22:{\22multiple-lines-split\22:\22one doc line\5cnnon-doc in the middle\5cnanother doc line\22,\22mixed-forms\22:\22mixed forms; line doc\5cnplus block doc\5cn multi-line\22}}}}") + (@custom "package-docs" "\00{\22docs\22:\22package docs;\22,\22worlds\22:{\22coverage-world\22:{\22docs\22:\22world docs\22,\22interfaces\22:{\22i\22:{\22docs\22:\22world inline interface docs\22,\22funcs\22:{\22f\22:\22inline interface func docs\22},\22types\22:{\22t\22:{\22docs\22:\22inline interface typedef docs\22}},\22nested\22:{}}},\22types\22:{\22t\22:{\22docs\22:\22world typedef docs\22}},\22funcs\22:{\22imp\22:\22world func import docs\22,\22exp\22:\22world func export docs\22}}},\22interfaces\22:{\22coverage-iface\22:{\22docs\22:\22interface docs\22,\22funcs\22:{\22[constructor]res\22:\22constructor docs\22,\22[method]res.m\22:\22method docs\22,\22[static]res.s\22:\22static func docs\22,\22f\22:\22interface func docs\22},\22types\22:{\22t\22:{\22docs\22:\22basic typedef docs\22},\22r\22:{\22docs\22:\22record typedef docs\22,\22items\22:{\22f1\22:\22record field docs\22}},\22fl\22:{\22items\22:{\22f1\22:\22flag docs\22}},\22v\22:{\22items\22:{\22c1\22:\22variant case docs\22}},\22e\22:{\22items\22:{\22c1\22:\22enum case docs\22}}},\22nested\22:{}},\22other-comment-forms\22:{\22docs\22:\22other comment forms\5cn multi-line block\22,\22funcs\22:{\22multiple-lines-split\22:\22one doc line\5cnnon-doc in the middle\5cnanother doc line\22,\22mixed-forms\22:\22mixed forms; line doc\5cnplus block doc\5cn multi-line\22},\22nested\22:{}}}}") (@producers (processed-by "wit-component" "$CARGO_PKG_VERSION") ) diff --git a/crates/wit-component/tests/interfaces/nested.wat b/crates/wit-component/tests/interfaces/nested.wat index 9196085738..57cdcef6e9 100644 --- a/crates/wit-component/tests/interfaces/nested.wat +++ b/crates/wit-component/tests/interfaces/nested.wat @@ -21,6 +21,13 @@ (instance (type (;0;) (record (field "foo" string))) (export (;1;) "nestrecord" (type (eq 0))) + (type (;2;) + (instance + (type (;0;) (record (field "foo" string))) + (export (;1;) "nestrecord" (type (eq 0))) + ) + ) + (export (;0;) "foo:nestnest/deep" (instance (type 2))) ) ) (export (;0;) "foo:nestee/things" (instance (type 5))) @@ -102,7 +109,7 @@ ) ) (export (;5;) "my-world" (type 4)) - (@custom "package-docs" "\01{\22interfaces\22:{\22something\22:{\22types\22:{\22my-record\22:{\22stability\22:{\22stable\22:{\22since\22:\221.0.0\22}}}}}}}") + (@custom "package-docs" "\01{\22interfaces\22:{\22something\22:{\22types\22:{\22my-record\22:{\22stability\22:{\22stable\22:{\22since\22:\221.0.0\22}}}},\22nested\22:{\22foo:nestee/things\22:{\22docs\22:{\22contents\22:\22nesting can be documented\22},\22stability\22:\22unknown\22},\22foo:nestee/more\22:{\22docs\22:{\22contents\22:null},\22stability\22:{\22stable\22:{\22since\22:\221.0.0\22}}}}}}}") (@producers (processed-by "wit-component" "$CARGO_PKG_VERSION") ) diff --git a/crates/wit-component/tests/interfaces/nested/deps/nestee.wit b/crates/wit-component/tests/interfaces/nested/deps/nestee.wit index 79031fca65..c3f8da4ccf 100644 --- a/crates/wit-component/tests/interfaces/nested/deps/nestee.wit +++ b/crates/wit-component/tests/interfaces/nested/deps/nestee.wit @@ -1,6 +1,9 @@ package foo:nestee; interface things { + //nesting can be documented + @since(version = 1.0.0) + nest foo:nestnest/deep; record nestrecord { foo: string } diff --git a/crates/wit-component/tests/interfaces/nested/nested.wit b/crates/wit-component/tests/interfaces/nested/nested.wit index 710fb34111..04c32a880f 100644 --- a/crates/wit-component/tests/interfaces/nested/nested.wit +++ b/crates/wit-component/tests/interfaces/nested/nested.wit @@ -2,7 +2,9 @@ package foo:thing; interface something { use foo:nestee/usable.{usable-record}; + //nesting can be documented nest foo:nestee/things; + @since(version = 1.0.0) nest foo:nestee/more; @since(version = 1.0.0) record my-record { diff --git a/crates/wit-component/tests/interfaces/nested/thing.wit.print b/crates/wit-component/tests/interfaces/nested/thing.wit.print index e6a4c01a98..f38346b419 100644 --- a/crates/wit-component/tests/interfaces/nested/thing.wit.print +++ b/crates/wit-component/tests/interfaces/nested/thing.wit.print @@ -1,7 +1,9 @@ package foo:thing; interface something { + /// nesting can be documented nest foo:nestee/things; + @since(version = 1.0.0) nest foo:nestee/more; use foo:nestee/usable.{usable-record}; diff --git a/crates/wit-component/tests/interfaces/resources.wat b/crates/wit-component/tests/interfaces/resources.wat index c4a8a9d38d..cb8fa8de23 100644 --- a/crates/wit-component/tests/interfaces/resources.wat +++ b/crates/wit-component/tests/interfaces/resources.wat @@ -180,7 +180,7 @@ ) ) (export (;11;) "implicit-own-handles3" (type 10)) - (@custom "package-docs" "\00{\22worlds\22:{\22implicit-own-handles3\22:{\22types\22:{\22a\22:{\22docs\22:\22there should only be one `list` type despite there looking like two\5cnlist types here\22}}}},\22interfaces\22:{\22implicit-own-handles2\22:{\22types\22:{\22a\22:{\22docs\22:\22the `own` return and list param should be the same `own`\22},\22b\22:{\22docs\22:\22same as above, even when the `list` implicitly-defined `own` comes\5cnbefore an explicitly defined `own`\22},\22c\22:{\22docs\22:\22same as the above, the `own` argument should have the same type as the\5cnreturn value\22}}}}}") + (@custom "package-docs" "\00{\22worlds\22:{\22implicit-own-handles3\22:{\22types\22:{\22a\22:{\22docs\22:\22there should only be one `list` type despite there looking like two\5cnlist types here\22}}}},\22interfaces\22:{\22implicit-own-handles2\22:{\22types\22:{\22a\22:{\22docs\22:\22the `own` return and list param should be the same `own`\22},\22b\22:{\22docs\22:\22same as above, even when the `list` implicitly-defined `own` comes\5cnbefore an explicitly defined `own`\22},\22c\22:{\22docs\22:\22same as the above, the `own` argument should have the same type as the\5cnreturn value\22}},\22nested\22:{}}}}") (@producers (processed-by "wit-component" "$CARGO_PKG_VERSION") ) diff --git a/crates/wit-component/tests/interfaces/wasi-http.wat b/crates/wit-component/tests/interfaces/wasi-http.wat index e75781c785..a96f5acd7c 100644 --- a/crates/wit-component/tests/interfaces/wasi-http.wat +++ b/crates/wit-component/tests/interfaces/wasi-http.wat @@ -874,7 +874,7 @@ ) ) (export (;7;) "proxy" (type 6)) - (@custom "package-docs" "\00{\22worlds\22:{\22proxy\22:{\22docs\22:\22The `wasi:http/proxy` world captures a widely-implementable intersection of\5cnhosts that includes HTTP forward and reverse proxies. Components targeting\5cnthis world may concurrently stream in and out any number of incoming and\5cnoutgoing HTTP requests.\22}},\22interfaces\22:{\22types\22:{\22docs\22:\22This interface defines all of the types and methods for implementing\5cnHTTP Requests and Responses, both incoming and outgoing, as well as\5cntheir headers, trailers, and bodies.\22,\22funcs\22:{\22http-error-code\22:\22Attempts to extract a http-related `error` from the wasi:io `error`\5cnprovided.\5cn\5cnStream operations which return\5cn`wasi:io/stream/stream-error::last-operation-failed` have a payload of\5cntype `wasi:io/error/error` with more information about the operation\5cnthat failed. This payload can be passed through to this function to see\5cnif there's http-related information about the error to return.\5cn\5cnNote that this function is fallible because not all io-errors are\5cnhttp-related errors.\22,\22[constructor]fields\22:\22Construct an empty HTTP Fields.\5cn\5cnThe resulting `fields` is mutable.\22,\22[static]fields.from-list\22:\22Construct an HTTP Fields.\5cn\5cnThe resulting `fields` is mutable.\5cn\5cnThe list represents each key-value pair in the Fields. Keys\5cnwhich have multiple values are represented by multiple entries in this\5cnlist with the same key.\5cn\5cnThe tuple is a pair of the field key, represented as a string, and\5cnValue, represented as a list of bytes. In a valid Fields, all keys\5cnand values are valid UTF-8 strings. However, values are not always\5cnwell-formed, so they are represented as a raw list of bytes.\5cn\5cnAn error result will be returned if any header or value was\5cnsyntactically invalid, or if a header was forbidden.\22,\22[method]fields.get\22:\22Get all of the values corresponding to a key. If the key is not present\5cnin this `fields`, an empty list is returned. However, if the key is\5cnpresent but empty, this is represented by a list with one or more\5cnempty field-values present.\22,\22[method]fields.has\22:\22Returns `true` when the key is present in this `fields`. If the key is\5cnsyntactically invalid, `false` is returned.\22,\22[method]fields.set\22:\22Set all of the values for a key. Clears any existing values for that\5cnkey, if they have been set.\5cn\5cnFails with `header-error.immutable` if the `fields` are immutable.\22,\22[method]fields.delete\22:\22Delete all values for a key. Does nothing if no values for the key\5cnexist.\5cn\5cnFails with `header-error.immutable` if the `fields` are immutable.\22,\22[method]fields.append\22:\22Append a value for a key. Does not change or delete any existing\5cnvalues for that key.\5cn\5cnFails with `header-error.immutable` if the `fields` are immutable.\22,\22[method]fields.entries\22:\22Retrieve the full set of keys and values in the Fields. Like the\5cnconstructor, the list represents each key-value pair.\5cn\5cnThe outer list represents each key-value pair in the Fields. Keys\5cnwhich have multiple values are represented by multiple entries in this\5cnlist with the same key.\22,\22[method]fields.clone\22:\22Make a deep copy of the Fields. Equivelant in behavior to calling the\5cn`fields` constructor on the return value of `entries`. The resulting\5cn`fields` is mutable.\22,\22[method]incoming-request.method\22:\22Returns the method of the incoming request.\22,\22[method]incoming-request.path-with-query\22:\22Returns the path with query parameters from the request, as a string.\22,\22[method]incoming-request.scheme\22:\22Returns the protocol scheme from the request.\22,\22[method]incoming-request.authority\22:\22Returns the authority from the request, if it was present.\22,\22[method]incoming-request.headers\22:\22Get the `headers` associated with the request.\5cn\5cnThe returned `headers` resource is immutable: `set`, `append`, and\5cn`delete` operations will fail with `header-error.immutable`.\5cn\5cnThe `headers` returned are a child resource: it must be dropped before\5cnthe parent `incoming-request` is dropped. Dropping this\5cn`incoming-request` before all children are dropped will trap.\22,\22[method]incoming-request.consume\22:\22Gives the `incoming-body` associated with this request. Will only\5cnreturn success at most once, and subsequent calls will return error.\22,\22[constructor]outgoing-request\22:\22Construct a new `outgoing-request` with a default `method` of `GET`, and\5cn`none` values for `path-with-query`, `scheme`, and `authority`.\5cn\5cn* `headers` is the HTTP Headers for the Request.\5cn\5cnIt is possible to construct, or manipulate with the accessor functions\5cnbelow, an `outgoing-request` with an invalid combination of `scheme`\5cnand `authority`, or `headers` which are not permitted to be sent.\5cnIt is the obligation of the `outgoing-handler.handle` implementation\5cnto reject invalid constructions of `outgoing-request`.\22,\22[method]outgoing-request.body\22:\22Returns the resource corresponding to the outgoing Body for this\5cnRequest.\5cn\5cnReturns success on the first call: the `outgoing-body` resource for\5cnthis `outgoing-request` can be retrieved at most once. Subsequent\5cncalls will return error.\22,\22[method]outgoing-request.method\22:\22Get the Method for the Request.\22,\22[method]outgoing-request.set-method\22:\22Set the Method for the Request. Fails if the string present in a\5cn`method.other` argument is not a syntactically valid method.\22,\22[method]outgoing-request.path-with-query\22:\22Get the combination of the HTTP Path and Query for the Request.\5cnWhen `none`, this represents an empty Path and empty Query.\22,\22[method]outgoing-request.set-path-with-query\22:\22Set the combination of the HTTP Path and Query for the Request.\5cnWhen `none`, this represents an empty Path and empty Query. Fails is the\5cnstring given is not a syntactically valid path and query uri component.\22,\22[method]outgoing-request.scheme\22:\22Get the HTTP Related Scheme for the Request. When `none`, the\5cnimplementation may choose an appropriate default scheme.\22,\22[method]outgoing-request.set-scheme\22:\22Set the HTTP Related Scheme for the Request. When `none`, the\5cnimplementation may choose an appropriate default scheme. Fails if the\5cnstring given is not a syntactically valid uri scheme.\22,\22[method]outgoing-request.authority\22:\22Get the HTTP Authority for the Request. A value of `none` may be used\5cnwith Related Schemes which do not require an Authority. The HTTP and\5cnHTTPS schemes always require an authority.\22,\22[method]outgoing-request.set-authority\22:\22Set the HTTP Authority for the Request. A value of `none` may be used\5cnwith Related Schemes which do not require an Authority. The HTTP and\5cnHTTPS schemes always require an authority. Fails if the string given is\5cnnot a syntactically valid uri authority.\22,\22[method]outgoing-request.headers\22:\22Get the headers associated with the Request.\5cn\5cnThe returned `headers` resource is immutable: `set`, `append`, and\5cn`delete` operations will fail with `header-error.immutable`.\5cn\5cnThis headers resource is a child: it must be dropped before the parent\5cn`outgoing-request` is dropped, or its ownership is transfered to\5cnanother component by e.g. `outgoing-handler.handle`.\22,\22[constructor]request-options\22:\22Construct a default `request-options` value.\22,\22[method]request-options.connect-timeout\22:\22The timeout for the initial connect to the HTTP Server.\22,\22[method]request-options.set-connect-timeout\22:\22Set the timeout for the initial connect to the HTTP Server. An error\5cnreturn value indicates that this timeout is not supported.\22,\22[method]request-options.first-byte-timeout\22:\22The timeout for receiving the first byte of the Response body.\22,\22[method]request-options.set-first-byte-timeout\22:\22Set the timeout for receiving the first byte of the Response body. An\5cnerror return value indicates that this timeout is not supported.\22,\22[method]request-options.between-bytes-timeout\22:\22The timeout for receiving subsequent chunks of bytes in the Response\5cnbody stream.\22,\22[method]request-options.set-between-bytes-timeout\22:\22Set the timeout for receiving subsequent chunks of bytes in the Response\5cnbody stream. An error return value indicates that this timeout is not\5cnsupported.\22,\22[static]response-outparam.set\22:\22Set the value of the `response-outparam` to either send a response,\5cnor indicate an error.\5cn\5cnThis method consumes the `response-outparam` to ensure that it is\5cncalled at most once. If it is never called, the implementation\5cnwill respond with an error.\5cn\5cnThe user may provide an `error` to `response` to allow the\5cnimplementation determine how to respond with an HTTP error response.\22,\22[method]incoming-response.status\22:\22Returns the status code from the incoming response.\22,\22[method]incoming-response.headers\22:\22Returns the headers from the incoming response.\5cn\5cnThe returned `headers` resource is immutable: `set`, `append`, and\5cn`delete` operations will fail with `header-error.immutable`.\5cn\5cnThis headers resource is a child: it must be dropped before the parent\5cn`incoming-response` is dropped.\22,\22[method]incoming-response.consume\22:\22Returns the incoming body. May be called at most once. Returns error\5cnif called additional times.\22,\22[method]incoming-body.stream\22:\22Returns the contents of the body, as a stream of bytes.\5cn\5cnReturns success on first call: the stream representing the contents\5cncan be retrieved at most once. Subsequent calls will return error.\5cn\5cnThe returned `input-stream` resource is a child: it must be dropped\5cnbefore the parent `incoming-body` is dropped, or consumed by\5cn`incoming-body.finish`.\5cn\5cnThis invariant ensures that the implementation can determine whether\5cnthe user is consuming the contents of the body, waiting on the\5cn`future-trailers` to be ready, or neither. This allows for network\5cnbackpressure is to be applied when the user is consuming the body,\5cnand for that backpressure to not inhibit delivery of the trailers if\5cnthe user does not read the entire body.\22,\22[static]incoming-body.finish\22:\22Takes ownership of `incoming-body`, and returns a `future-trailers`.\5cnThis function will trap if the `input-stream` child is still alive.\22,\22[method]future-trailers.subscribe\22:\22Returns a pollable which becomes ready when either the trailers have\5cnbeen received, or an error has occured. When this pollable is ready,\5cnthe `get` method will return `some`.\22,\22[method]future-trailers.get\22:\22Returns the contents of the trailers, or an error which occured,\5cnonce the future is ready.\5cn\5cnThe outer `option` represents future readiness. Users can wait on this\5cn`option` to become `some` using the `subscribe` method.\5cn\5cnThe outer `result` is used to retrieve the trailers or error at most\5cnonce. It will be success on the first call in which the outer option\5cnis `some`, and error on subsequent calls.\5cn\5cnThe inner `result` represents that either the HTTP Request or Response\5cnbody, as well as any trailers, were received successfully, or that an\5cnerror occured receiving them. The optional `trailers` indicates whether\5cnor not trailers were present in the body.\5cn\5cnWhen some `trailers` are returned by this method, the `trailers`\5cnresource is immutable, and a child. Use of the `set`, `append`, or\5cn`delete` methods will return an error, and the resource must be\5cndropped before the parent `future-trailers` is dropped.\22,\22[constructor]outgoing-response\22:\22Construct an `outgoing-response`, with a default `status-code` of `200`.\5cnIf a different `status-code` is needed, it must be set via the\5cn`set-status-code` method.\5cn\5cn* `headers` is the HTTP Headers for the Response.\22,\22[method]outgoing-response.status-code\22:\22Get the HTTP Status Code for the Response.\22,\22[method]outgoing-response.set-status-code\22:\22Set the HTTP Status Code for the Response. Fails if the status-code\5cngiven is not a valid http status code.\22,\22[method]outgoing-response.headers\22:\22Get the headers associated with the Request.\5cn\5cnThe returned `headers` resource is immutable: `set`, `append`, and\5cn`delete` operations will fail with `header-error.immutable`.\5cn\5cnThis headers resource is a child: it must be dropped before the parent\5cn`outgoing-request` is dropped, or its ownership is transfered to\5cnanother component by e.g. `outgoing-handler.handle`.\22,\22[method]outgoing-response.body\22:\22Returns the resource corresponding to the outgoing Body for this Response.\5cn\5cnReturns success on the first call: the `outgoing-body` resource for\5cnthis `outgoing-response` can be retrieved at most once. Subsequent\5cncalls will return error.\22,\22[method]outgoing-body.write\22:\22Returns a stream for writing the body contents.\5cn\5cnThe returned `output-stream` is a child resource: it must be dropped\5cnbefore the parent `outgoing-body` resource is dropped (or finished),\5cnotherwise the `outgoing-body` drop or `finish` will trap.\5cn\5cnReturns success on the first call: the `output-stream` resource for\5cnthis `outgoing-body` may be retrieved at most once. Subsequent calls\5cnwill return error.\22,\22[static]outgoing-body.finish\22:\22Finalize an outgoing body, optionally providing trailers. This must be\5cncalled to signal that the response is complete. If the `outgoing-body`\5cnis dropped without calling `outgoing-body.finalize`, the implementation\5cnshould treat the body as corrupted.\5cn\5cnFails if the body's `outgoing-request` or `outgoing-response` was\5cnconstructed with a Content-Length header, and the contents written\5cnto the body (via `write`) does not match the value given in the\5cnContent-Length.\22,\22[method]future-incoming-response.subscribe\22:\22Returns a pollable which becomes ready when either the Response has\5cnbeen received, or an error has occured. When this pollable is ready,\5cnthe `get` method will return `some`.\22,\22[method]future-incoming-response.get\22:\22Returns the incoming HTTP Response, or an error, once one is ready.\5cn\5cnThe outer `option` represents future readiness. Users can wait on this\5cn`option` to become `some` using the `subscribe` method.\5cn\5cnThe outer `result` is used to retrieve the response or error at most\5cnonce. It will be success on the first call in which the outer option\5cnis `some`, and error on subsequent calls.\5cn\5cnThe inner `result` represents that either the incoming HTTP Response\5cnstatus and headers have recieved successfully, or that an error\5cnoccured. Errors may also occur while consuming the response body,\5cnbut those will be reported by the `incoming-body` and its\5cn`output-stream` child.\22},\22types\22:{\22method\22:{\22docs\22:\22This type corresponds to HTTP standard Methods.\22},\22scheme\22:{\22docs\22:\22This type corresponds to HTTP standard Related Schemes.\22},\22DNS-error-payload\22:{\22docs\22:\22Defines the case payload type for `DNS-error` above:\22},\22TLS-alert-received-payload\22:{\22docs\22:\22Defines the case payload type for `TLS-alert-received` above:\22},\22field-size-payload\22:{\22docs\22:\22Defines the case payload type for `HTTP-response-{header,trailer}-size` above:\22},\22error-code\22:{\22docs\22:\22These cases are inspired by the IANA HTTP Proxy Error Types:\5cnhttps://www.iana.org/assignments/http-proxy-status/http-proxy-status.xhtml#table-http-proxy-error-types\22,\22items\22:{\22internal-error\22:\22This is a catch-all error for anything that doesn't fit cleanly into a\5cnmore specific case. It also includes an optional string for an\5cnunstructured description of the error. Users should not depend on the\5cnstring for diagnosing errors, as it's not required to be consistent\5cnbetween implementations.\22}},\22header-error\22:{\22docs\22:\22This type enumerates the different kinds of errors that may occur when\5cnsetting or appending to a `fields` resource.\22,\22items\22:{\22invalid-syntax\22:\22This error indicates that a `field-key` or `field-value` was\5cnsyntactically invalid when used with an operation that sets headers in a\5cn`fields`.\22,\22forbidden\22:\22This error indicates that a forbidden `field-key` was used when trying\5cnto set a header in a `fields`.\22,\22immutable\22:\22This error indicates that the operation on the `fields` was not\5cnpermitted because the fields are immutable.\22}},\22field-key\22:{\22docs\22:\22Field keys are always strings.\22},\22field-value\22:{\22docs\22:\22Field values should always be ASCII strings. However, in\5cnreality, HTTP implementations often have to interpret malformed values,\5cnso they are provided as a list of bytes.\22},\22fields\22:{\22docs\22:\22This following block defines the `fields` resource which corresponds to\5cnHTTP standard Fields. Fields are a common representation used for both\5cnHeaders and Trailers.\5cn\5cnA `fields` may be mutable or immutable. A `fields` created using the\5cnconstructor, `from-list`, or `clone` will be mutable, but a `fields`\5cnresource given by other means (including, but not limited to,\5cn`incoming-request.headers`, `outgoing-request.headers`) might be be\5cnimmutable. In an immutable fields, the `set`, `append`, and `delete`\5cnoperations will fail with `header-error.immutable`.\22},\22headers\22:{\22docs\22:\22Headers is an alias for Fields.\22},\22trailers\22:{\22docs\22:\22Trailers is an alias for Fields.\22},\22incoming-request\22:{\22docs\22:\22Represents an incoming HTTP Request.\22},\22outgoing-request\22:{\22docs\22:\22Represents an outgoing HTTP Request.\22},\22request-options\22:{\22docs\22:\22Parameters for making an HTTP Request. Each of these parameters is\5cncurrently an optional timeout applicable to the transport layer of the\5cnHTTP protocol.\5cn\5cnThese timeouts are separate from any the user may use to bound a\5cnblocking call to `wasi:io/poll.poll`.\22},\22response-outparam\22:{\22docs\22:\22Represents the ability to send an HTTP Response.\5cn\5cnThis resource is used by the `wasi:http/incoming-handler` interface to\5cnallow a Response to be sent corresponding to the Request provided as the\5cnother argument to `incoming-handler.handle`.\22},\22status-code\22:{\22docs\22:\22This type corresponds to the HTTP standard Status Code.\22},\22incoming-response\22:{\22docs\22:\22Represents an incoming HTTP Response.\22},\22incoming-body\22:{\22docs\22:\22Represents an incoming HTTP Request or Response's Body.\5cn\5cnA body has both its contents - a stream of bytes - and a (possibly\5cnempty) set of trailers, indicating that the full contents of the\5cnbody have been received. This resource represents the contents as\5cnan `input-stream` and the delivery of trailers as a `future-trailers`,\5cnand ensures that the user of this interface may only be consuming either\5cnthe body contents or waiting on trailers at any given time.\22},\22future-trailers\22:{\22docs\22:\22Represents a future which may eventaully return trailers, or an error.\5cn\5cnIn the case that the incoming HTTP Request or Response did not have any\5cntrailers, this future will resolve to the empty set of trailers once the\5cncomplete Request or Response body has been received.\22},\22outgoing-response\22:{\22docs\22:\22Represents an outgoing HTTP Response.\22},\22outgoing-body\22:{\22docs\22:\22Represents an outgoing HTTP Request or Response's Body.\5cn\5cnA body has both its contents - a stream of bytes - and a (possibly\5cnempty) set of trailers, inducating the full contents of the body\5cnhave been sent. This resource represents the contents as an\5cn`output-stream` child resource, and the completion of the body (with\5cnoptional trailers) with a static function that consumes the\5cn`outgoing-body` resource, and ensures that the user of this interface\5cnmay not write to the body contents after the body has been finished.\5cn\5cnIf the user code drops this resource, as opposed to calling the static\5cnmethod `finish`, the implementation should treat the body as incomplete,\5cnand that an error has occured. The implementation should propogate this\5cnerror to the HTTP protocol by whatever means it has available,\5cnincluding: corrupting the body on the wire, aborting the associated\5cnRequest, or sending a late status code for the Response.\22},\22future-incoming-response\22:{\22docs\22:\22Represents a future which may eventaully return an incoming HTTP\5cnResponse, or an error.\5cn\5cnThis resource is returned by the `wasi:http/outgoing-handler` interface to\5cnprovide the HTTP Response corresponding to the sent Request.\22}}},\22incoming-handler\22:{\22docs\22:\22This interface defines a handler of incoming HTTP Requests. It should\5cnbe exported by components which can respond to HTTP Requests.\22,\22funcs\22:{\22handle\22:\22This function is invoked with an incoming HTTP Request, and a resource\5cn`response-outparam` which provides the capability to reply with an HTTP\5cnResponse. The response is sent by calling the `response-outparam.set`\5cnmethod, which allows execution to continue after the response has been\5cnsent. This enables both streaming to the response body, and performing other\5cnwork.\5cn\5cnThe implementor of this function must write a response to the\5cn`response-outparam` before returning, or else the caller will respond\5cnwith an error on its behalf.\22}},\22outgoing-handler\22:{\22docs\22:\22This interface defines a handler of outgoing HTTP Requests. It should be\5cnimported by components which wish to make HTTP Requests.\22,\22funcs\22:{\22handle\22:\22This function is invoked with an outgoing HTTP Request, and it returns\5cna resource `future-incoming-response` which represents an HTTP Response\5cnwhich may arrive in the future.\5cn\5cnThe `options` argument accepts optional parameters for the HTTP\5cnprotocol's transport layer.\5cn\5cnThis function may return an error if the `outgoing-request` is invalid\5cnor not allowed to be made. Otherwise, protocol errors are reported\5cnthrough the `future-incoming-response`.\22}}}}") + (@custom "package-docs" "\00{\22worlds\22:{\22proxy\22:{\22docs\22:\22The `wasi:http/proxy` world captures a widely-implementable intersection of\5cnhosts that includes HTTP forward and reverse proxies. Components targeting\5cnthis world may concurrently stream in and out any number of incoming and\5cnoutgoing HTTP requests.\22}},\22interfaces\22:{\22types\22:{\22docs\22:\22This interface defines all of the types and methods for implementing\5cnHTTP Requests and Responses, both incoming and outgoing, as well as\5cntheir headers, trailers, and bodies.\22,\22funcs\22:{\22http-error-code\22:\22Attempts to extract a http-related `error` from the wasi:io `error`\5cnprovided.\5cn\5cnStream operations which return\5cn`wasi:io/stream/stream-error::last-operation-failed` have a payload of\5cntype `wasi:io/error/error` with more information about the operation\5cnthat failed. This payload can be passed through to this function to see\5cnif there's http-related information about the error to return.\5cn\5cnNote that this function is fallible because not all io-errors are\5cnhttp-related errors.\22,\22[constructor]fields\22:\22Construct an empty HTTP Fields.\5cn\5cnThe resulting `fields` is mutable.\22,\22[static]fields.from-list\22:\22Construct an HTTP Fields.\5cn\5cnThe resulting `fields` is mutable.\5cn\5cnThe list represents each key-value pair in the Fields. Keys\5cnwhich have multiple values are represented by multiple entries in this\5cnlist with the same key.\5cn\5cnThe tuple is a pair of the field key, represented as a string, and\5cnValue, represented as a list of bytes. In a valid Fields, all keys\5cnand values are valid UTF-8 strings. However, values are not always\5cnwell-formed, so they are represented as a raw list of bytes.\5cn\5cnAn error result will be returned if any header or value was\5cnsyntactically invalid, or if a header was forbidden.\22,\22[method]fields.get\22:\22Get all of the values corresponding to a key. If the key is not present\5cnin this `fields`, an empty list is returned. However, if the key is\5cnpresent but empty, this is represented by a list with one or more\5cnempty field-values present.\22,\22[method]fields.has\22:\22Returns `true` when the key is present in this `fields`. If the key is\5cnsyntactically invalid, `false` is returned.\22,\22[method]fields.set\22:\22Set all of the values for a key. Clears any existing values for that\5cnkey, if they have been set.\5cn\5cnFails with `header-error.immutable` if the `fields` are immutable.\22,\22[method]fields.delete\22:\22Delete all values for a key. Does nothing if no values for the key\5cnexist.\5cn\5cnFails with `header-error.immutable` if the `fields` are immutable.\22,\22[method]fields.append\22:\22Append a value for a key. Does not change or delete any existing\5cnvalues for that key.\5cn\5cnFails with `header-error.immutable` if the `fields` are immutable.\22,\22[method]fields.entries\22:\22Retrieve the full set of keys and values in the Fields. Like the\5cnconstructor, the list represents each key-value pair.\5cn\5cnThe outer list represents each key-value pair in the Fields. Keys\5cnwhich have multiple values are represented by multiple entries in this\5cnlist with the same key.\22,\22[method]fields.clone\22:\22Make a deep copy of the Fields. Equivelant in behavior to calling the\5cn`fields` constructor on the return value of `entries`. The resulting\5cn`fields` is mutable.\22,\22[method]incoming-request.method\22:\22Returns the method of the incoming request.\22,\22[method]incoming-request.path-with-query\22:\22Returns the path with query parameters from the request, as a string.\22,\22[method]incoming-request.scheme\22:\22Returns the protocol scheme from the request.\22,\22[method]incoming-request.authority\22:\22Returns the authority from the request, if it was present.\22,\22[method]incoming-request.headers\22:\22Get the `headers` associated with the request.\5cn\5cnThe returned `headers` resource is immutable: `set`, `append`, and\5cn`delete` operations will fail with `header-error.immutable`.\5cn\5cnThe `headers` returned are a child resource: it must be dropped before\5cnthe parent `incoming-request` is dropped. Dropping this\5cn`incoming-request` before all children are dropped will trap.\22,\22[method]incoming-request.consume\22:\22Gives the `incoming-body` associated with this request. Will only\5cnreturn success at most once, and subsequent calls will return error.\22,\22[constructor]outgoing-request\22:\22Construct a new `outgoing-request` with a default `method` of `GET`, and\5cn`none` values for `path-with-query`, `scheme`, and `authority`.\5cn\5cn* `headers` is the HTTP Headers for the Request.\5cn\5cnIt is possible to construct, or manipulate with the accessor functions\5cnbelow, an `outgoing-request` with an invalid combination of `scheme`\5cnand `authority`, or `headers` which are not permitted to be sent.\5cnIt is the obligation of the `outgoing-handler.handle` implementation\5cnto reject invalid constructions of `outgoing-request`.\22,\22[method]outgoing-request.body\22:\22Returns the resource corresponding to the outgoing Body for this\5cnRequest.\5cn\5cnReturns success on the first call: the `outgoing-body` resource for\5cnthis `outgoing-request` can be retrieved at most once. Subsequent\5cncalls will return error.\22,\22[method]outgoing-request.method\22:\22Get the Method for the Request.\22,\22[method]outgoing-request.set-method\22:\22Set the Method for the Request. Fails if the string present in a\5cn`method.other` argument is not a syntactically valid method.\22,\22[method]outgoing-request.path-with-query\22:\22Get the combination of the HTTP Path and Query for the Request.\5cnWhen `none`, this represents an empty Path and empty Query.\22,\22[method]outgoing-request.set-path-with-query\22:\22Set the combination of the HTTP Path and Query for the Request.\5cnWhen `none`, this represents an empty Path and empty Query. Fails is the\5cnstring given is not a syntactically valid path and query uri component.\22,\22[method]outgoing-request.scheme\22:\22Get the HTTP Related Scheme for the Request. When `none`, the\5cnimplementation may choose an appropriate default scheme.\22,\22[method]outgoing-request.set-scheme\22:\22Set the HTTP Related Scheme for the Request. When `none`, the\5cnimplementation may choose an appropriate default scheme. Fails if the\5cnstring given is not a syntactically valid uri scheme.\22,\22[method]outgoing-request.authority\22:\22Get the HTTP Authority for the Request. A value of `none` may be used\5cnwith Related Schemes which do not require an Authority. The HTTP and\5cnHTTPS schemes always require an authority.\22,\22[method]outgoing-request.set-authority\22:\22Set the HTTP Authority for the Request. A value of `none` may be used\5cnwith Related Schemes which do not require an Authority. The HTTP and\5cnHTTPS schemes always require an authority. Fails if the string given is\5cnnot a syntactically valid uri authority.\22,\22[method]outgoing-request.headers\22:\22Get the headers associated with the Request.\5cn\5cnThe returned `headers` resource is immutable: `set`, `append`, and\5cn`delete` operations will fail with `header-error.immutable`.\5cn\5cnThis headers resource is a child: it must be dropped before the parent\5cn`outgoing-request` is dropped, or its ownership is transfered to\5cnanother component by e.g. `outgoing-handler.handle`.\22,\22[constructor]request-options\22:\22Construct a default `request-options` value.\22,\22[method]request-options.connect-timeout\22:\22The timeout for the initial connect to the HTTP Server.\22,\22[method]request-options.set-connect-timeout\22:\22Set the timeout for the initial connect to the HTTP Server. An error\5cnreturn value indicates that this timeout is not supported.\22,\22[method]request-options.first-byte-timeout\22:\22The timeout for receiving the first byte of the Response body.\22,\22[method]request-options.set-first-byte-timeout\22:\22Set the timeout for receiving the first byte of the Response body. An\5cnerror return value indicates that this timeout is not supported.\22,\22[method]request-options.between-bytes-timeout\22:\22The timeout for receiving subsequent chunks of bytes in the Response\5cnbody stream.\22,\22[method]request-options.set-between-bytes-timeout\22:\22Set the timeout for receiving subsequent chunks of bytes in the Response\5cnbody stream. An error return value indicates that this timeout is not\5cnsupported.\22,\22[static]response-outparam.set\22:\22Set the value of the `response-outparam` to either send a response,\5cnor indicate an error.\5cn\5cnThis method consumes the `response-outparam` to ensure that it is\5cncalled at most once. If it is never called, the implementation\5cnwill respond with an error.\5cn\5cnThe user may provide an `error` to `response` to allow the\5cnimplementation determine how to respond with an HTTP error response.\22,\22[method]incoming-response.status\22:\22Returns the status code from the incoming response.\22,\22[method]incoming-response.headers\22:\22Returns the headers from the incoming response.\5cn\5cnThe returned `headers` resource is immutable: `set`, `append`, and\5cn`delete` operations will fail with `header-error.immutable`.\5cn\5cnThis headers resource is a child: it must be dropped before the parent\5cn`incoming-response` is dropped.\22,\22[method]incoming-response.consume\22:\22Returns the incoming body. May be called at most once. Returns error\5cnif called additional times.\22,\22[method]incoming-body.stream\22:\22Returns the contents of the body, as a stream of bytes.\5cn\5cnReturns success on first call: the stream representing the contents\5cncan be retrieved at most once. Subsequent calls will return error.\5cn\5cnThe returned `input-stream` resource is a child: it must be dropped\5cnbefore the parent `incoming-body` is dropped, or consumed by\5cn`incoming-body.finish`.\5cn\5cnThis invariant ensures that the implementation can determine whether\5cnthe user is consuming the contents of the body, waiting on the\5cn`future-trailers` to be ready, or neither. This allows for network\5cnbackpressure is to be applied when the user is consuming the body,\5cnand for that backpressure to not inhibit delivery of the trailers if\5cnthe user does not read the entire body.\22,\22[static]incoming-body.finish\22:\22Takes ownership of `incoming-body`, and returns a `future-trailers`.\5cnThis function will trap if the `input-stream` child is still alive.\22,\22[method]future-trailers.subscribe\22:\22Returns a pollable which becomes ready when either the trailers have\5cnbeen received, or an error has occured. When this pollable is ready,\5cnthe `get` method will return `some`.\22,\22[method]future-trailers.get\22:\22Returns the contents of the trailers, or an error which occured,\5cnonce the future is ready.\5cn\5cnThe outer `option` represents future readiness. Users can wait on this\5cn`option` to become `some` using the `subscribe` method.\5cn\5cnThe outer `result` is used to retrieve the trailers or error at most\5cnonce. It will be success on the first call in which the outer option\5cnis `some`, and error on subsequent calls.\5cn\5cnThe inner `result` represents that either the HTTP Request or Response\5cnbody, as well as any trailers, were received successfully, or that an\5cnerror occured receiving them. The optional `trailers` indicates whether\5cnor not trailers were present in the body.\5cn\5cnWhen some `trailers` are returned by this method, the `trailers`\5cnresource is immutable, and a child. Use of the `set`, `append`, or\5cn`delete` methods will return an error, and the resource must be\5cndropped before the parent `future-trailers` is dropped.\22,\22[constructor]outgoing-response\22:\22Construct an `outgoing-response`, with a default `status-code` of `200`.\5cnIf a different `status-code` is needed, it must be set via the\5cn`set-status-code` method.\5cn\5cn* `headers` is the HTTP Headers for the Response.\22,\22[method]outgoing-response.status-code\22:\22Get the HTTP Status Code for the Response.\22,\22[method]outgoing-response.set-status-code\22:\22Set the HTTP Status Code for the Response. Fails if the status-code\5cngiven is not a valid http status code.\22,\22[method]outgoing-response.headers\22:\22Get the headers associated with the Request.\5cn\5cnThe returned `headers` resource is immutable: `set`, `append`, and\5cn`delete` operations will fail with `header-error.immutable`.\5cn\5cnThis headers resource is a child: it must be dropped before the parent\5cn`outgoing-request` is dropped, or its ownership is transfered to\5cnanother component by e.g. `outgoing-handler.handle`.\22,\22[method]outgoing-response.body\22:\22Returns the resource corresponding to the outgoing Body for this Response.\5cn\5cnReturns success on the first call: the `outgoing-body` resource for\5cnthis `outgoing-response` can be retrieved at most once. Subsequent\5cncalls will return error.\22,\22[method]outgoing-body.write\22:\22Returns a stream for writing the body contents.\5cn\5cnThe returned `output-stream` is a child resource: it must be dropped\5cnbefore the parent `outgoing-body` resource is dropped (or finished),\5cnotherwise the `outgoing-body` drop or `finish` will trap.\5cn\5cnReturns success on the first call: the `output-stream` resource for\5cnthis `outgoing-body` may be retrieved at most once. Subsequent calls\5cnwill return error.\22,\22[static]outgoing-body.finish\22:\22Finalize an outgoing body, optionally providing trailers. This must be\5cncalled to signal that the response is complete. If the `outgoing-body`\5cnis dropped without calling `outgoing-body.finalize`, the implementation\5cnshould treat the body as corrupted.\5cn\5cnFails if the body's `outgoing-request` or `outgoing-response` was\5cnconstructed with a Content-Length header, and the contents written\5cnto the body (via `write`) does not match the value given in the\5cnContent-Length.\22,\22[method]future-incoming-response.subscribe\22:\22Returns a pollable which becomes ready when either the Response has\5cnbeen received, or an error has occured. When this pollable is ready,\5cnthe `get` method will return `some`.\22,\22[method]future-incoming-response.get\22:\22Returns the incoming HTTP Response, or an error, once one is ready.\5cn\5cnThe outer `option` represents future readiness. Users can wait on this\5cn`option` to become `some` using the `subscribe` method.\5cn\5cnThe outer `result` is used to retrieve the response or error at most\5cnonce. It will be success on the first call in which the outer option\5cnis `some`, and error on subsequent calls.\5cn\5cnThe inner `result` represents that either the incoming HTTP Response\5cnstatus and headers have recieved successfully, or that an error\5cnoccured. Errors may also occur while consuming the response body,\5cnbut those will be reported by the `incoming-body` and its\5cn`output-stream` child.\22},\22types\22:{\22method\22:{\22docs\22:\22This type corresponds to HTTP standard Methods.\22},\22scheme\22:{\22docs\22:\22This type corresponds to HTTP standard Related Schemes.\22},\22DNS-error-payload\22:{\22docs\22:\22Defines the case payload type for `DNS-error` above:\22},\22TLS-alert-received-payload\22:{\22docs\22:\22Defines the case payload type for `TLS-alert-received` above:\22},\22field-size-payload\22:{\22docs\22:\22Defines the case payload type for `HTTP-response-{header,trailer}-size` above:\22},\22error-code\22:{\22docs\22:\22These cases are inspired by the IANA HTTP Proxy Error Types:\5cnhttps://www.iana.org/assignments/http-proxy-status/http-proxy-status.xhtml#table-http-proxy-error-types\22,\22items\22:{\22internal-error\22:\22This is a catch-all error for anything that doesn't fit cleanly into a\5cnmore specific case. It also includes an optional string for an\5cnunstructured description of the error. Users should not depend on the\5cnstring for diagnosing errors, as it's not required to be consistent\5cnbetween implementations.\22}},\22header-error\22:{\22docs\22:\22This type enumerates the different kinds of errors that may occur when\5cnsetting or appending to a `fields` resource.\22,\22items\22:{\22invalid-syntax\22:\22This error indicates that a `field-key` or `field-value` was\5cnsyntactically invalid when used with an operation that sets headers in a\5cn`fields`.\22,\22forbidden\22:\22This error indicates that a forbidden `field-key` was used when trying\5cnto set a header in a `fields`.\22,\22immutable\22:\22This error indicates that the operation on the `fields` was not\5cnpermitted because the fields are immutable.\22}},\22field-key\22:{\22docs\22:\22Field keys are always strings.\22},\22field-value\22:{\22docs\22:\22Field values should always be ASCII strings. However, in\5cnreality, HTTP implementations often have to interpret malformed values,\5cnso they are provided as a list of bytes.\22},\22fields\22:{\22docs\22:\22This following block defines the `fields` resource which corresponds to\5cnHTTP standard Fields. Fields are a common representation used for both\5cnHeaders and Trailers.\5cn\5cnA `fields` may be mutable or immutable. A `fields` created using the\5cnconstructor, `from-list`, or `clone` will be mutable, but a `fields`\5cnresource given by other means (including, but not limited to,\5cn`incoming-request.headers`, `outgoing-request.headers`) might be be\5cnimmutable. In an immutable fields, the `set`, `append`, and `delete`\5cnoperations will fail with `header-error.immutable`.\22},\22headers\22:{\22docs\22:\22Headers is an alias for Fields.\22},\22trailers\22:{\22docs\22:\22Trailers is an alias for Fields.\22},\22incoming-request\22:{\22docs\22:\22Represents an incoming HTTP Request.\22},\22outgoing-request\22:{\22docs\22:\22Represents an outgoing HTTP Request.\22},\22request-options\22:{\22docs\22:\22Parameters for making an HTTP Request. Each of these parameters is\5cncurrently an optional timeout applicable to the transport layer of the\5cnHTTP protocol.\5cn\5cnThese timeouts are separate from any the user may use to bound a\5cnblocking call to `wasi:io/poll.poll`.\22},\22response-outparam\22:{\22docs\22:\22Represents the ability to send an HTTP Response.\5cn\5cnThis resource is used by the `wasi:http/incoming-handler` interface to\5cnallow a Response to be sent corresponding to the Request provided as the\5cnother argument to `incoming-handler.handle`.\22},\22status-code\22:{\22docs\22:\22This type corresponds to the HTTP standard Status Code.\22},\22incoming-response\22:{\22docs\22:\22Represents an incoming HTTP Response.\22},\22incoming-body\22:{\22docs\22:\22Represents an incoming HTTP Request or Response's Body.\5cn\5cnA body has both its contents - a stream of bytes - and a (possibly\5cnempty) set of trailers, indicating that the full contents of the\5cnbody have been received. This resource represents the contents as\5cnan `input-stream` and the delivery of trailers as a `future-trailers`,\5cnand ensures that the user of this interface may only be consuming either\5cnthe body contents or waiting on trailers at any given time.\22},\22future-trailers\22:{\22docs\22:\22Represents a future which may eventaully return trailers, or an error.\5cn\5cnIn the case that the incoming HTTP Request or Response did not have any\5cntrailers, this future will resolve to the empty set of trailers once the\5cncomplete Request or Response body has been received.\22},\22outgoing-response\22:{\22docs\22:\22Represents an outgoing HTTP Response.\22},\22outgoing-body\22:{\22docs\22:\22Represents an outgoing HTTP Request or Response's Body.\5cn\5cnA body has both its contents - a stream of bytes - and a (possibly\5cnempty) set of trailers, inducating the full contents of the body\5cnhave been sent. This resource represents the contents as an\5cn`output-stream` child resource, and the completion of the body (with\5cnoptional trailers) with a static function that consumes the\5cn`outgoing-body` resource, and ensures that the user of this interface\5cnmay not write to the body contents after the body has been finished.\5cn\5cnIf the user code drops this resource, as opposed to calling the static\5cnmethod `finish`, the implementation should treat the body as incomplete,\5cnand that an error has occured. The implementation should propogate this\5cnerror to the HTTP protocol by whatever means it has available,\5cnincluding: corrupting the body on the wire, aborting the associated\5cnRequest, or sending a late status code for the Response.\22},\22future-incoming-response\22:{\22docs\22:\22Represents a future which may eventaully return an incoming HTTP\5cnResponse, or an error.\5cn\5cnThis resource is returned by the `wasi:http/outgoing-handler` interface to\5cnprovide the HTTP Response corresponding to the sent Request.\22}},\22nested\22:{}},\22incoming-handler\22:{\22docs\22:\22This interface defines a handler of incoming HTTP Requests. It should\5cnbe exported by components which can respond to HTTP Requests.\22,\22funcs\22:{\22handle\22:\22This function is invoked with an incoming HTTP Request, and a resource\5cn`response-outparam` which provides the capability to reply with an HTTP\5cnResponse. The response is sent by calling the `response-outparam.set`\5cnmethod, which allows execution to continue after the response has been\5cnsent. This enables both streaming to the response body, and performing other\5cnwork.\5cn\5cnThe implementor of this function must write a response to the\5cn`response-outparam` before returning, or else the caller will respond\5cnwith an error on its behalf.\22},\22nested\22:{}},\22outgoing-handler\22:{\22docs\22:\22This interface defines a handler of outgoing HTTP Requests. It should be\5cnimported by components which wish to make HTTP Requests.\22,\22funcs\22:{\22handle\22:\22This function is invoked with an outgoing HTTP Request, and it returns\5cna resource `future-incoming-response` which represents an HTTP Response\5cnwhich may arrive in the future.\5cn\5cnThe `options` argument accepts optional parameters for the HTTP\5cnprotocol's transport layer.\5cn\5cnThis function may return an error if the `outgoing-request` is invalid\5cnor not allowed to be made. Otherwise, protocol errors are reported\5cnthrough the `future-incoming-response`.\22},\22nested\22:{}}}}") (@producers (processed-by "wit-component" "$CARGO_PKG_VERSION") ) diff --git a/crates/wit-parser/src/ast.rs b/crates/wit-parser/src/ast.rs index 5e7bdfdd5d..c6ee2ce2dd 100644 --- a/crates/wit-parser/src/ast.rs +++ b/crates/wit-parser/src/ast.rs @@ -562,13 +562,18 @@ enum InterfaceItem<'a> { } struct Nest<'a> { + docs: Docs<'a>, id: PackageName<'a>, name: Id<'a>, - // attributes: Vec>, + attributes: Vec>, } impl<'a> Nest<'a> { - fn parse(tokens: &mut Tokenizer<'a>, _attributes: Vec>) -> Result { + fn parse( + tokens: &mut Tokenizer<'a>, + docs: Docs<'a>, + attributes: Vec>, + ) -> Result { tokens.eat(Token::Nest)?; let id = parse_id(tokens)?; tokens.expect(Token::Colon)?; @@ -590,8 +595,9 @@ impl<'a> Nest<'a> { name: pkg_name, version, }, + docs, name, - // attributes, + attributes, }) } } @@ -1032,7 +1038,9 @@ impl<'a> InterfaceItem<'a> { NamedFunc::parse(tokens, docs, attributes).map(InterfaceItem::Func) } Some((_span, Token::Use)) => Use::parse(tokens, attributes).map(InterfaceItem::Use), - Some((_span, Token::Nest)) => Nest::parse(tokens, attributes).map(InterfaceItem::Nest), + Some((_span, Token::Nest)) => { + Nest::parse(tokens, docs, attributes).map(InterfaceItem::Nest) + } other => Err(err_expected(tokens, "`type`, `resource` or `func`", other).into()), } } diff --git a/crates/wit-parser/src/ast/resolve.rs b/crates/wit-parser/src/ast/resolve.rs index f6c496ae66..4e587e91ad 100644 --- a/crates/wit-parser/src/ast/resolve.rs +++ b/crates/wit-parser/src/ast/resolve.rs @@ -1,10 +1,4 @@ -use super::{ - // AstItem as RootAstItem, Interface as RootInterface, - Nest, - ParamList, - ResultList, - WorldOrInterface, -}; +use super::{Nest, ParamList, ResultList, WorldOrInterface}; use crate::ast::toposort::toposort; use crate::ast::InterfaceItem; use crate::*; @@ -569,9 +563,15 @@ impl<'a> Resolver<'a> { } else { bail!("Expected interface item") }; + let stability = self.stability(&n.attributes)?; + let docs = self.docs(&n.docs); self.interfaces[id].nested.insert( format!("{}/{}", n.id.package_name(), n.name.name.to_string()), - *nested_id, + crate::Nest { + id: *nested_id, + docs, + stability, + }, ); let prev = ids.insert(n.name.name, AstItem::Interface(id)); assert!(prev.is_none()); @@ -1457,7 +1457,7 @@ impl<'a> Resolver<'a> { Type::Id(*id) } - fn docs(&mut self, doc: &super::Docs<'_>) -> Docs { + fn docs(&self, doc: &super::Docs<'_>) -> Docs { let mut docs = vec![]; for doc in doc.docs.iter() { if let Some(doc) = doc.strip_prefix("/**") { @@ -1490,7 +1490,7 @@ impl<'a> Resolver<'a> { Docs { contents } } - fn stability(&mut self, attrs: &[ast::Attribute<'_>]) -> Result { + fn stability(&self, attrs: &[ast::Attribute<'_>]) -> Result { match attrs { [] => Ok(Stability::Unknown), [ast::Attribute::Since { diff --git a/crates/wit-parser/src/decoding.rs b/crates/wit-parser/src/decoding.rs index de8efa52a4..71bca9af30 100644 --- a/crates/wit-parser/src/decoding.rs +++ b/crates/wit-parser/src/decoding.rs @@ -814,7 +814,19 @@ impl WitPackageDecoder<'_> { } } } - types::ComponentEntityType::Instance(_) => todo!(), + types::ComponentEntityType::Instance(i) => { + let ty = &self.types[i]; + let deep = self.register_export(name, ty)?; + let iface = &mut self.resolve.interfaces[interface]; + iface.nested.insert( + name.to_string(), + Nest { + id: deep, + docs: Default::default(), + stability: Default::default(), + }, + ); + } types::ComponentEntityType::Component(_) => todo!(), } } @@ -1077,7 +1089,14 @@ impl WitPackageDecoder<'_> { types::ComponentEntityType::Instance(inst) => { let ty = &self.types[inst]; let iface = self.register_export(&name, &ty)?; - interface.nested.insert(name.to_string(), iface); + interface.nested.insert( + name.to_string(), + Nest { + id: iface, + docs: Default::default(), + stability: Default::default(), + }, + ); } _ => bail!("instance type export `{name}` is not a type or function"), }; diff --git a/crates/wit-parser/src/lib.rs b/crates/wit-parser/src/lib.rs index bf21132b41..9b422a7ef7 100644 --- a/crates/wit-parser/src/lib.rs +++ b/crates/wit-parser/src/lib.rs @@ -26,7 +26,7 @@ mod live; pub use live::LiveTypes; #[cfg(feature = "serde")] -use serde_derive::Serialize; +use serde_derive::{Deserialize, Serialize}; #[cfg(feature = "serde")] mod serde_; #[cfg(feature = "serde")] @@ -403,6 +403,19 @@ impl WorldItem { } } +#[cfg_attr(feature = "serde", derive(Serialize))] +#[derive(Debug, Clone)] +pub struct Nest { + #[cfg_attr(feature = "serde", serde(serialize_with = "serialize_id"))] + pub id: InterfaceId, + pub docs: Docs, + #[cfg_attr( + feature = "serde", + serde(skip_serializing_if = "Stability::is_unknown") + )] + pub stability: Stability, +} + #[derive(Debug, Clone)] #[cfg_attr(feature = "serde", derive(Serialize))] pub struct Interface { @@ -412,8 +425,7 @@ pub struct Interface { pub name: Option, /// The nested interfaces in this interface. - #[cfg_attr(feature = "serde", serde(serialize_with = "serialize_id_map"))] - pub nested: IndexMap, + pub nested: IndexMap, /// Exported types from this interface. /// /// Export names are listed within the types themselves. Note that the @@ -690,7 +702,7 @@ pub struct Stream { } #[derive(Clone, Default, Debug, PartialEq, Eq)] -#[cfg_attr(feature = "serde", derive(Serialize))] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct Docs { pub contents: Option, } diff --git a/crates/wit-parser/src/metadata.rs b/crates/wit-parser/src/metadata.rs index 103e3d03af..b954d42950 100644 --- a/crates/wit-parser/src/metadata.rs +++ b/crates/wit-parser/src/metadata.rs @@ -15,7 +15,7 @@ //! format to store this information inline. use crate::{ - Docs, Function, InterfaceId, PackageId, Resolve, Stability, TypeDefKind, TypeId, WorldId, + Docs, Function, InterfaceId, Nest, PackageId, Resolve, Stability, TypeDefKind, TypeId, WorldId, WorldItem, WorldKey, }; use anyhow::{bail, Result}; @@ -521,6 +521,7 @@ struct InterfaceMetadata { serde(default, skip_serializing_if = "StringMap::is_empty") )] types: StringMap, + nested: StringMap, } impl InterfaceMetadata { @@ -539,12 +540,18 @@ impl InterfaceMetadata { .map(|(name, id)| (name.to_string(), TypeMetadata::extract(resolve, *id))) .filter(|(_, item)| !item.is_empty()) .collect(); + let nested: IndexMap = interface + .nested + .iter() + .map(|n| (n.0.clone(), NestMetadata::extract(n.1.clone()))) + .collect(); Self { docs: interface.docs.contents.clone(), stability: interface.stability.clone(), funcs, types, + nested, } } @@ -566,6 +573,12 @@ impl InterfaceMetadata { interface.docs.contents = Some(docs.to_string()); } interface.stability = self.stability.clone(); + for (name, data) in &self.nested { + let Some(f) = interface.nested.get_mut(name) else { + bail!("missing func {name:?}"); + }; + data.inject(f)?; + } Ok(()) } @@ -654,6 +667,26 @@ impl FunctionMetadata { } } +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "serde", serde(deny_unknown_fields))] +struct NestMetadata { + docs: Docs, + stability: Stability, +} + +impl NestMetadata { + fn extract(nest: Nest) -> Self { + Self { + docs: nest.docs, + stability: nest.stability, + } + } + fn inject(&self, nest: &mut Nest) -> Result<()> { + nest.stability = self.stability.clone(); + nest.docs = self.docs.clone(); + Ok(()) + } +} #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", serde(deny_unknown_fields))] struct TypeMetadata { diff --git a/crates/wit-parser/src/resolve.rs b/crates/wit-parser/src/resolve.rs index 4f13143451..8a34a8e667 100644 --- a/crates/wit-parser/src/resolve.rs +++ b/crates/wit-parser/src/resolve.rs @@ -899,23 +899,6 @@ impl Resolve { } } - // pub fn type_interface_nest(&self, id: TypeId) -> Option { - // let ty = &self.types[id]; - // let dep = match ty.kind { - // TypeDefKind::Type(Type::Id(id)) => id, - // _ => return None, - // }; - // let other = &self.types[dep]; - // if ty.owner == other.owner { - // None - // } else { - // match other.owner { - // TypeOwner::Interface(id) => Some(id), - // _ => unreachable!(), - // } - // } - // } - /// Returns an iterator of all interfaces that the interface `id` depends /// on. /// @@ -932,11 +915,6 @@ impl Resolve { .filter_map(move |(_name, ty)| self.type_interface_dep(*ty)) } - pub fn interface_nests(&self, id: InterfaceId) -> impl Iterator + '_ { - self.interfaces[id].nested.iter().map(|n| n.1.clone()) - // .filter_map(move |(_name, ty)| self.type_interface_dep(*ty)) - } - /// Returns an iterator of all packages that the package `id` depends /// on. /// From c9ee248f03262cc57d3df6db8f7ed99629e9c62d Mon Sep 17 00:00:00 2001 From: Daniel Macovei Date: Wed, 19 Jun 2024 12:30:27 -0500 Subject: [PATCH 4/4] versioned nest package names and general tidying --- crates/wit-component/src/encoding/wit/v1.rs | 7 +- crates/wit-component/src/encoding/wit/v2.rs | 67 +++++-------- crates/wit-component/tests/interfaces.rs | 2 + .../wit-component/tests/interfaces/nested.wat | 98 +++---------------- .../tests/interfaces/nested/deps/nestee.wit | 10 +- .../tests/interfaces/nested/nested.wit | 16 +-- .../tests/interfaces/nested/thing.wit.print | 18 +--- crates/wit-parser/src/ast/resolve.rs | 33 +++++-- crates/wit-parser/src/decoding.rs | 88 ++++++++++------- crates/wit-parser/src/lib.rs | 2 + crates/wit-parser/src/metadata.rs | 6 +- 11 files changed, 125 insertions(+), 222 deletions(-) diff --git a/crates/wit-component/src/encoding/wit/v1.rs b/crates/wit-component/src/encoding/wit/v1.rs index 10d3ab5915..41d21ddae8 100644 --- a/crates/wit-component/src/encoding/wit/v1.rs +++ b/crates/wit-component/src/encoding/wit/v1.rs @@ -171,11 +171,8 @@ impl InterfaceEncoder<'_> { let mut type_order = IndexSet::new(); for (_, id) in iface.types.iter() { let ty = &self.resolve.types[*id]; - match ty.owner { - TypeOwner::Interface(iface_id) => { - self.interface = Some(iface_id); - } - _ => unreachable!(), + if let TypeOwner::Interface(iface_id) = ty.owner { + self.interface = Some(iface_id); } self.encode_valtype(self.resolve, &Type::Id(*id))?; type_order.insert(*id); diff --git a/crates/wit-component/src/encoding/wit/v2.rs b/crates/wit-component/src/encoding/wit/v2.rs index 3907cba9fb..a2ca15c5b0 100644 --- a/crates/wit-component/src/encoding/wit/v2.rs +++ b/crates/wit-component/src/encoding/wit/v2.rs @@ -185,11 +185,8 @@ impl InterfaceEncoder<'_> { let mut type_order = IndexSet::new(); for (_, id) in iface.types.iter() { let ty = &self.resolve.types[*id]; - match ty.owner { - TypeOwner::Interface(iface_id) => { - self.interface = Some(iface_id); - } - _ => unreachable!(), + if let TypeOwner::Interface(iface_id) = ty.owner { + self.interface = Some(iface_id); } self.encode_valtype(self.resolve, &Type::Id(*id))?; type_order.insert(*id); @@ -240,65 +237,47 @@ impl InterfaceEncoder<'_> { iface: &Interface, instance: &'a mut InstanceType, ) -> Result<&mut InstanceType> { - for (orig_name, _) in &iface.nested { - let mut pkg_parts = orig_name.split("/"); - let pkg = pkg_parts.next().expect("expected projection"); - let iface_name = pkg_parts.next().expect("expected projection"); - let mut parts = pkg.split(":"); - let namespace = parts.next().expect("expected :"); - let name = parts.next().expect("expected :"); - let name = PackageName { - namespace: namespace.to_string(), - name: name.to_string(), - version: None, - }; - - let package_id = self.resolve.package_names.get(&name).unwrap(); + for (nest_name, nest_item) in &iface.nested { + let package_id = self + .resolve + .package_names + .get(&nest_item.package_name) + .unwrap(); let package = &self.resolve.packages[*package_id]; - let nested = package.interfaces.get(iface_name).unwrap(); + let nested = package.interfaces.get(&nest_item.iface_name).unwrap(); let nested_iface = &self.resolve.interfaces[*nested]; let mut inst = InterfaceEncoder::new(&self.resolve); inst.push_instance(); for (_, id) in &nested_iface.types { let ty = &self.resolve.types[*id]; - match ty.owner { - TypeOwner::Interface(iface_id) => { - inst.interface = Some(iface_id); - } - _ => unreachable!(), + if let TypeOwner::Interface(iface_id) = ty.owner { + inst.interface = Some(iface_id); } inst.encode_valtype(self.resolve, &Type::Id(*id))?; } let ty = instance.ty(); let nested_instance = &mut inst.pop_instance(); - for (orig_name, _) in &nested_iface.nested { - let mut nest_package_parts = orig_name.split("/"); - let nest_pkg = nest_package_parts.next().unwrap(); - let nest_iface = nest_package_parts.next().unwrap(); - let mut parts = nest_pkg.split(":"); - let ns = parts.next().unwrap(); - let name = parts.next().unwrap(); - let name = PackageName { - namespace: ns.to_string(), - name: name.to_string(), - version: None, - }; - let nest_pkg_id = self.resolve.package_names.get(&name).unwrap(); - let nested_package = &self.resolve.packages[*nest_pkg_id]; - let myguy = nested_package.interfaces.get(nest_iface).unwrap(); - let nest = &self.resolve.interfaces[*myguy]; + for (nest_name, deep_nest) in &nested_iface.nested { + let deep_pkg_id = self + .resolve + .package_names + .get(&deep_nest.package_name) + .unwrap(); + let deep_package = &self.resolve.packages[*deep_pkg_id]; + let deep_iface_id = deep_package.interfaces.get(&deep_nest.iface_name).unwrap(); + let deep_nest = &self.resolve.interfaces[*deep_iface_id]; let mut clone = nested_instance.clone(); - let deep_instance = self.encode_nested(nest, &mut clone)?; + let deep_instance = self.encode_nested(deep_nest, &mut clone)?; let deep_ty = nested_instance.ty(); deep_ty.instance(&deep_instance); nested_instance.export( - orig_name, + nest_name, ComponentTypeRef::Instance(deep_instance.type_count()), ); } ty.instance(&nested_instance); instance.export( - orig_name, + nest_name, ComponentTypeRef::Instance(instance.type_count() - 1), ); } diff --git a/crates/wit-component/tests/interfaces.rs b/crates/wit-component/tests/interfaces.rs index 96ea8f8f0c..926499db9c 100644 --- a/crates/wit-component/tests/interfaces.rs +++ b/crates/wit-component/tests/interfaces.rs @@ -53,6 +53,7 @@ fn run_test(path: &Path, is_dir: bool) -> Result<()> { } else { resolve.append(UnresolvedPackageGroup::parse_file(path)?)? }; + for package in packages { assert_print(&resolve, &[package], path, is_dir)?; @@ -76,6 +77,7 @@ fn run_test(path: &Path, is_dir: bool) -> Result<()> { decoded.packages().len(), "Each input WIT package should produce WASM that contains only one package" ); + let decoded_package = decoded.packages()[0]; let resolve = decoded.resolve(); diff --git a/crates/wit-component/tests/interfaces/nested.wat b/crates/wit-component/tests/interfaces/nested.wat index 57cdcef6e9..d1a9444cca 100644 --- a/crates/wit-component/tests/interfaces/nested.wat +++ b/crates/wit-component/tests/interfaces/nested.wat @@ -4,112 +4,36 @@ (type (;0;) (instance (type (;0;) (record (field "foo" string))) - (export (;1;) "usable-record" (type (eq 0))) - ) - ) - (import "foo:nestee/usable" (instance (;0;) (type 0))) - (alias export 0 "usable-record" (type (;1;))) - (type (;2;) - (instance - (alias outer 1 1 (type (;0;))) - (export (;1;) "usable-record" (type (eq 0))) - (type (;2;) (record (field "foo" string))) - (export (;3;) "my-record" (type (eq 2))) - (type (;4;) (func (result string))) - (export (;0;) "hello" (func (type 4))) - (type (;5;) + (export (;1;) "my-record" (type (eq 0))) + (type (;2;) (func (result string))) + (export (;0;) "hello" (func (type 2))) + (type (;3;) (instance (type (;0;) (record (field "foo" string))) - (export (;1;) "nestrecord" (type (eq 0))) + (export (;1;) "nest-record" (type (eq 0))) (type (;2;) (instance (type (;0;) (record (field "foo" string))) - (export (;1;) "nestrecord" (type (eq 0))) + (export (;1;) "nest-record" (type (eq 0))) ) ) (export (;0;) "foo:nestnest/deep" (instance (type 2))) ) ) - (export (;0;) "foo:nestee/things" (instance (type 5))) - (type (;6;) + (export (;0;) "foo:nestee/things@1.0.0" (instance (type 3))) + (type (;4;) (instance (export (;0;) "foo" (type (sub resource))) ) ) - (export (;1;) "foo:nestee/more" (instance (type 6))) + (export (;1;) "foo:nestee/more@1.0.0" (instance (type 4))) ) ) - (export (;1;) "foo:thing/something" (instance (type 2))) + (export (;0;) "foo:thing/something" (instance (type 0))) ) ) (export (;1;) "something" (type 0)) - (type (;2;) - (component - (type (;0;) - (instance - (type (;0;) (list u8)) - (export (;1;) "random" (type (eq 0))) - ) - ) - (export (;0;) "foo:thing/flat" (instance (type 0))) - ) - ) - (export (;3;) "flat" (type 2)) - (type (;4;) - (component - (type (;0;) - (component - (type (;0;) - (instance - (type (;0;) (record (field "foo" string))) - (export (;1;) "usable-record" (type (eq 0))) - ) - ) - (import "foo:nestee/usable" (instance (;0;) (type 0))) - (alias export 0 "usable-record" (type (;1;))) - (type (;2;) - (instance - (alias outer 1 1 (type (;0;))) - (export (;1;) "usable-record" (type (eq 0))) - (type (;2;) (record (field "foo" string))) - (export (;3;) "my-record" (type (eq 2))) - (type (;4;) (func (result string))) - (export (;0;) "hello" (func (type 4))) - ) - ) - (import "foo:thing/something" (instance (;1;) (type 2))) - (type (;3;) - (instance - (type (;0;) (list u8)) - (export (;1;) "random" (type (eq 0))) - ) - ) - (import "foo:thing/flat" (instance (;2;) (type 3))) - (type (;4;) - (instance - (alias outer 1 1 (type (;0;))) - (export (;1;) "usable-record" (type (eq 0))) - (type (;2;) (record (field "foo" string))) - (export (;3;) "my-record" (type (eq 2))) - (type (;4;) (func (result string))) - (export (;0;) "hello" (func (type 4))) - ) - ) - (export (;3;) "foo:thing/something" (instance (type 4))) - (type (;5;) - (instance - (type (;0;) (list u8)) - (export (;1;) "random" (type (eq 0))) - ) - ) - (export (;4;) "foo:thing/flat" (instance (type 5))) - ) - ) - (export (;0;) "foo:thing/my-world" (component (type 0))) - ) - ) - (export (;5;) "my-world" (type 4)) - (@custom "package-docs" "\01{\22interfaces\22:{\22something\22:{\22types\22:{\22my-record\22:{\22stability\22:{\22stable\22:{\22since\22:\221.0.0\22}}}},\22nested\22:{\22foo:nestee/things\22:{\22docs\22:{\22contents\22:\22nesting can be documented\22},\22stability\22:\22unknown\22},\22foo:nestee/more\22:{\22docs\22:{\22contents\22:null},\22stability\22:{\22stable\22:{\22since\22:\221.0.0\22}}}}}}}") + (@custom "package-docs" "\01{\22interfaces\22:{\22something\22:{\22types\22:{\22my-record\22:{\22stability\22:{\22stable\22:{\22since\22:\221.0.0\22}}}},\22nested\22:{\22foo:nestee/things@1.0.0\22:{\22docs\22:{\22contents\22:\22nesting can be documented\22},\22stability\22:\22unknown\22},\22foo:nestee/more@1.0.0\22:{\22docs\22:{\22contents\22:null},\22stability\22:{\22stable\22:{\22since\22:\221.0.0\22}}}}}}}") (@producers (processed-by "wit-component" "$CARGO_PKG_VERSION") ) diff --git a/crates/wit-component/tests/interfaces/nested/deps/nestee.wit b/crates/wit-component/tests/interfaces/nested/deps/nestee.wit index c3f8da4ccf..962ce1ddd7 100644 --- a/crates/wit-component/tests/interfaces/nested/deps/nestee.wit +++ b/crates/wit-component/tests/interfaces/nested/deps/nestee.wit @@ -1,10 +1,10 @@ -package foo:nestee; +package foo:nestee@1.0.0; interface things { //nesting can be documented @since(version = 1.0.0) nest foo:nestnest/deep; - record nestrecord { + record nest-record { foo: string } hello: func() -> string; @@ -14,10 +14,4 @@ interface more { resource foo { bar: func() -> option; } -} - -interface usable { - record usable-record { - foo: string - } } \ No newline at end of file diff --git a/crates/wit-component/tests/interfaces/nested/nested.wit b/crates/wit-component/tests/interfaces/nested/nested.wit index 04c32a880f..6913f4158f 100644 --- a/crates/wit-component/tests/interfaces/nested/nested.wit +++ b/crates/wit-component/tests/interfaces/nested/nested.wit @@ -1,11 +1,10 @@ package foo:thing; interface something { - use foo:nestee/usable.{usable-record}; //nesting can be documented - nest foo:nestee/things; + nest foo:nestee/things@1.0.0; @since(version = 1.0.0) - nest foo:nestee/more; + nest foo:nestee/more@1.0.0; @since(version = 1.0.0) record my-record { foo: string @@ -13,14 +12,3 @@ interface something { hello: func() -> string; } - -interface flat { - type random = list; -} - -world my-world { - import something; - export something; - import flat; - export flat; -} \ No newline at end of file diff --git a/crates/wit-component/tests/interfaces/nested/thing.wit.print b/crates/wit-component/tests/interfaces/nested/thing.wit.print index f38346b419..661601c50f 100644 --- a/crates/wit-component/tests/interfaces/nested/thing.wit.print +++ b/crates/wit-component/tests/interfaces/nested/thing.wit.print @@ -2,11 +2,9 @@ package foo:thing; interface something { /// nesting can be documented - nest foo:nestee/things; + nest foo:nestee/things@1.0.0; @since(version = 1.0.0) - nest foo:nestee/more; - use foo:nestee/usable.{usable-record}; - + nest foo:nestee/more@1.0.0; @since(version = 1.0.0) record my-record { foo: string, @@ -15,15 +13,3 @@ interface something { hello: func() -> string; } -interface flat { - type random = list; -} - -world my-world { - import foo:nestee/usable; - import something; - import flat; - - export something; - export flat; -} diff --git a/crates/wit-parser/src/ast/resolve.rs b/crates/wit-parser/src/ast/resolve.rs index 4e587e91ad..a23f36792f 100644 --- a/crates/wit-parser/src/ast/resolve.rs +++ b/crates/wit-parser/src/ast/resolve.rs @@ -101,11 +101,9 @@ enum Key { enum TypeItem<'a, 'b> { Use(&'b ast::Use<'a>), - Nest, Def(&'b ast::TypeDef<'a>), } -#[derive(Debug)] enum TypeOrItem { Type(TypeId), Item(&'static str), @@ -401,8 +399,8 @@ impl<'a> Resolver<'a> { assert!(prev.is_none()); let prev = names.insert(i.name.name, item); assert!(prev.is_none()); - for nestitem in i.items.iter() { - if let InterfaceItem::Nest(n) = nestitem { + for nest_item in i.items.iter() { + if let InterfaceItem::Nest(n) = nest_item { if package_items.insert(n.name.name, n.name.span).is_some() { bail!(Error::new( n.name.span, @@ -565,12 +563,30 @@ impl<'a> Resolver<'a> { }; let stability = self.stability(&n.attributes)?; let docs = self.docs(&n.docs); + let name = if let Some(v) = &n.id.version { + format!( + "{}:{}/{}@{}", + n.id.namespace.name, + n.id.name.name, + n.name.name, + v.1.to_string() + ) + } else { + format!("{}/{}", n.id.package_name(), n.name.name) + }; self.interfaces[id].nested.insert( - format!("{}/{}", n.id.package_name(), n.name.name.to_string()), + // format!("{}/{}", n.id.package_name(), n.name.name.to_string()), + name, crate::Nest { id: *nested_id, docs, stability, + package_name: PackageName { + namespace: n.id.namespace.name.to_string(), + name: n.id.name.name.to_string(), + version: n.id.version.as_ref().map(|v| v.1.clone()), + }, + iface_name: n.name.name.to_string(), }, ); let prev = ids.insert(n.name.name, AstItem::Interface(id)); @@ -897,8 +913,7 @@ impl<'a> Resolver<'a> { fields.iter().filter_map(|i| match i { ast::InterfaceItem::Use(u) => Some(TypeItem::Use(u)), ast::InterfaceItem::TypeDef(t) => Some(TypeItem::Def(t)), - ast::InterfaceItem::Func(_) => None, - ast::InterfaceItem::Nest(_) => Some(TypeItem::Nest), + ast::InterfaceItem::Func(_) | ast::InterfaceItem::Nest(_) => None, }), )?; @@ -944,8 +959,7 @@ impl<'a> Resolver<'a> { .push(func.named_func().name.span); } } - ast::InterfaceItem::TypeDef(_) => {} - ast::InterfaceItem::Nest(_) => {} + ast::InterfaceItem::TypeDef(_) | ast::InterfaceItem::Nest(_) => {} } } for func in funcs { @@ -1008,7 +1022,6 @@ impl<'a> Resolver<'a> { type_defs.insert(name.name, None); } } - TypeItem::Nest => {} } } let order = toposort("type", &type_deps)?; diff --git a/crates/wit-parser/src/decoding.rs b/crates/wit-parser/src/decoding.rs index 71bca9af30..ecbd3fbddd 100644 --- a/crates/wit-parser/src/decoding.rs +++ b/crates/wit-parser/src/decoding.rs @@ -742,9 +742,6 @@ impl WitPackageDecoder<'_> { let owner = TypeOwner::Interface(interface); for (name, ty) in ty.exports.iter() { match *ty { - types::ComponentEntityType::Module(_) => todo!(), - types::ComponentEntityType::Func(_) => todo!(), - types::ComponentEntityType::Value(_) => todo!(), types::ComponentEntityType::Type { referenced, created, @@ -815,19 +812,30 @@ impl WitPackageDecoder<'_> { } } types::ComponentEntityType::Instance(i) => { - let ty = &self.types[i]; - let deep = self.register_export(name, ty)?; - let iface = &mut self.resolve.interfaces[interface]; - iface.nested.insert( - name.to_string(), - Nest { - id: deep, - docs: Default::default(), - stability: Default::default(), - }, - ); + match self.parse_component_name(name)?.kind() { + ComponentNameKind::Interface(iface_name) => { + let ty = &self.types[i]; + let deep = self.register_export(name, ty)?; + let iface = &mut self.resolve.interfaces[interface]; + iface.nested.insert( + name.to_string(), + Nest { + id: deep, + docs: Default::default(), + stability: Default::default(), + package_name: PackageName { + namespace: iface_name.namespace().to_string(), + name: iface_name.package().to_string(), + version: iface_name.version(), + }, + iface_name: iface_name.projection().to_string(), + }, + ); + } + _ => bail!("expected interface name"), + } } - types::ComponentEntityType::Component(_) => todo!(), + _ => bail!("instance type export `{name}` is not a type or instance"), } } Ok(interface) @@ -1063,42 +1071,52 @@ impl WitPackageDecoder<'_> { stability: Default::default(), }; - let next_id = self.resolve.interfaces.next_id(); - let owner = TypeOwner::Interface(next_id); - for (name, ty) in ty.exports.iter() { + let owner = TypeOwner::Interface(self.resolve.interfaces.next_id()); + for (exp_name, ty) in ty.exports.iter() { match *ty { types::ComponentEntityType::Type { referenced, created, } => { let ty = self - .register_type_export(name.as_str(), owner, referenced, created) - .with_context(|| format!("failed to register type export '{name}'"))?; - let prev = interface.types.insert(name.to_string(), ty); + .register_type_export(exp_name.as_str(), owner, referenced, created) + .with_context(|| format!("failed to register type export '{exp_name}'"))?; + let prev = interface.types.insert(exp_name.to_string(), ty); assert!(prev.is_none()); } types::ComponentEntityType::Func(ty) => { let ty = &self.types[ty]; let func = self - .convert_function(name.as_str(), ty, owner) - .with_context(|| format!("failed to convert function '{name}'"))?; - let prev = interface.functions.insert(name.to_string(), func); + .convert_function(exp_name.as_str(), ty, owner) + .with_context(|| format!("failed to convert function '{exp_name}'"))?; + let prev = interface.functions.insert(exp_name.to_string(), func); assert!(prev.is_none()); } types::ComponentEntityType::Instance(inst) => { - let ty = &self.types[inst]; - let iface = self.register_export(&name, &ty)?; - interface.nested.insert( - name.to_string(), - Nest { - id: iface, - docs: Default::default(), - stability: Default::default(), - }, - ); + match self.parse_component_name(exp_name)?.kind() { + ComponentNameKind::Interface(iface_name) => { + let ty = &self.types[inst]; + let deep = self.register_export(&exp_name, &ty)?; + interface.nested.insert( + exp_name.to_string(), + Nest { + id: deep, + docs: Default::default(), + stability: Default::default(), + package_name: PackageName { + namespace: iface_name.namespace().to_string(), + name: iface_name.package().to_string(), + version: iface_name.version(), + }, + iface_name: iface_name.projection().to_string(), + }, + ); + } + _ => bail!("expected interface name"), + } } - _ => bail!("instance type export `{name}` is not a type or function"), + _ => bail!("instance type export `{name}` is not a type, function or instance"), }; } let id = self.resolve.interfaces.alloc(interface); diff --git a/crates/wit-parser/src/lib.rs b/crates/wit-parser/src/lib.rs index 9b422a7ef7..bb4f40e3fc 100644 --- a/crates/wit-parser/src/lib.rs +++ b/crates/wit-parser/src/lib.rs @@ -414,6 +414,8 @@ pub struct Nest { serde(skip_serializing_if = "Stability::is_unknown") )] pub stability: Stability, + pub package_name: PackageName, + pub iface_name: String, } #[derive(Debug, Clone)] diff --git a/crates/wit-parser/src/metadata.rs b/crates/wit-parser/src/metadata.rs index b954d42950..9dd49da276 100644 --- a/crates/wit-parser/src/metadata.rs +++ b/crates/wit-parser/src/metadata.rs @@ -574,10 +574,10 @@ impl InterfaceMetadata { } interface.stability = self.stability.clone(); for (name, data) in &self.nested { - let Some(f) = interface.nested.get_mut(name) else { - bail!("missing func {name:?}"); + let Some(n) = interface.nested.get_mut(name) else { + bail!("missing nested item {name:?}"); }; - data.inject(f)?; + data.inject(n)?; } Ok(()) }