Repository: bytecodealliance/wasm-tools
Pull request #1659 (2024-07-09 23:19:03)

feat: support basic use syntax in interfaces and worlds
Adds support for building up simple `use` statements for wit-encoder.


Patch:
diff --git a/crates/wit-encoder/src/interface.rs b/crates/wit-encoder/src/interface.rs
index 5641426c..bf082716 100644
--- a/crates/wit-encoder/src/interface.rs
+++ b/crates/wit-encoder/src/interface.rs
@@ -1,6 +1,6 @@
 use std::fmt;
 
-use crate::{Docs, Ident, Render, RenderOpts, StandaloneFunc, TypeDef};
+use crate::{Docs, Ident, Render, RenderOpts, StandaloneFunc, TypeDef, Use};
 
 #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
 pub struct Interface {
@@ -34,6 +34,11 @@ impl Interface {
         self.items.push(InterfaceItem::Function(function));
     }
 
+    /// Add a `Use` to the interface
+    pub fn use_(&mut self, use_: Use) {
+        self.items.push(InterfaceItem::Use(use_));
+    }
+
     pub fn items(&self) -> &[InterfaceItem] {
         &self.items
     }
@@ -51,6 +56,7 @@ impl Interface {
 #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
 pub enum InterfaceItem {
     TypeDef(TypeDef),
+    Use(Use),
     Function(StandaloneFunc),
 }
 
@@ -73,6 +79,9 @@ impl Render for InterfaceItems {
                     }
                     write!(f, ";\n")?;
                 }
+                InterfaceItem::Use(use_) => {
+                    use_.render(f, opts)?;
+                }
             }
         }
         Ok(())
diff --git a/crates/wit-encoder/src/lib.rs b/crates/wit-encoder/src/lib.rs
index c4e8eb4f..902ffc00 100644
--- a/crates/wit-encoder/src/lib.rs
+++ b/crates/wit-encoder/src/lib.rs
@@ -17,6 +17,7 @@ mod resource;
 mod result;
 mod tuple;
 mod ty;
+mod use_;
 mod variant;
 mod world;
 
@@ -34,5 +35,6 @@ pub use resource::*;
 pub use result::*;
 pub use tuple::*;
 pub use ty::*;
+pub use use_::*;
 pub use variant::*;
 pub use world::*;
diff --git a/crates/wit-encoder/src/use_.rs b/crates/wit-encoder/src/use_.rs
new file mode 100644
index 00000000..9322c15d
--- /dev/null
+++ b/crates/wit-encoder/src/use_.rs
@@ -0,0 +1,44 @@
+use std::fmt;
+
+use crate::{Ident, Render};
+
+/// Enable the union of a world with another world
+#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
+pub struct Use {
+    target: Ident,
+    use_names_list: Vec<(String, Option<String>)>,
+}
+
+impl Use {
+    pub fn new(use_target: impl Into<Ident>) -> Self {
+        Self {
+            target: use_target.into(),
+            use_names_list: vec![],
+        }
+    }
+
+    pub fn item(&mut self, id: &str, alias: Option<&str>) {
+        self.use_names_list
+            .push((id.to_string(), alias.map(|s| s.to_string())));
+    }
+}
+
+impl Render for Use {
+    fn render(&self, f: &mut fmt::Formatter<'_>, opts: &crate::RenderOpts) -> fmt::Result {
+        let len = self.use_names_list.len();
+
+        write!(f, "{}use {}.{{ ", opts.spaces(), self.target)?;
+        for (i, (id, alias)) in self.use_names_list.iter().enumerate() {
+            if let Some(alias) = alias {
+                write!(f, "{id} as {alias}")?;
+            } else {
+                write!(f, "{id}")?;
+            }
+            if i < len - 1 {
+                write!(f, ", ")?;
+            }
+        }
+        write!(f, " }};\n")?;
+        Ok(())
+    }
+}
diff --git a/crates/wit-encoder/src/world.rs b/crates/wit-encoder/src/world.rs
index 7203524b..5f933209 100644
--- a/crates/wit-encoder/src/world.rs
+++ b/crates/wit-encoder/src/world.rs
@@ -1,6 +1,6 @@
 use std::fmt;
 
-use crate::{ident::Ident, Docs, Include, Interface, Render, RenderOpts, StandaloneFunc};
+use crate::{ident::Ident, Docs, Include, Interface, Render, RenderOpts, StandaloneFunc, Use};
 
 #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
 pub struct World {
@@ -55,6 +55,9 @@ impl World {
     pub fn include(&mut self, include: Include) {
         self.item(WorldItem::Include(include));
     }
+    pub fn use_(&mut self, use_: Use) {
+        self.item(WorldItem::Use(use_));
+    }
 
     /// Set the documentation
     pub fn docs(&mut self, docs: Option<impl Into<Docs>>) {
@@ -143,6 +146,7 @@ impl Render for World {
                     render_function(f, opts, function)?;
                 }
                 WorldItem::Include(include) => include.render(f, opts)?,
+                WorldItem::Use(use_) => use_.render(f, opts)?,
             }
         }
         let opts = &opts.outdent();
@@ -173,6 +177,9 @@ pub enum WorldItem {
 
     /// Include type
     Include(Include),
+
+    /// Use
+    Use(Use),
 }
 
 impl WorldItem {


Test patch:
diff --git a/crates/wit-encoder/tests/use.rs b/crates/wit-encoder/tests/use.rs
new file mode 100644
index 00000000..c3d891c6
--- /dev/null
+++ b/crates/wit-encoder/tests/use.rs
@@ -0,0 +1,45 @@
+use pretty_assertions::assert_eq;
+use wit_encoder::{Interface, Package, PackageName, ResourceFunc, TypeDef, Use, World};
+
+const PACKAGE: &str = indoc::indoc! {"
+    package foo:foo;
+
+    interface foo {
+      resource bar {
+      }
+    }
+
+    interface bar {
+      use foo.{ bar as foobar };
+      resource baz {
+      }
+    }
+
+    world baz {
+      use bar.{ baz };
+    }
+"};
+
+#[test]
+fn concrete_types() {
+    let mut package = Package::new(PackageName::new("foo", "foo", None));
+
+    let mut interface = Interface::new("foo");
+    interface.type_def(TypeDef::resource("bar", Vec::<ResourceFunc>::new()));
+    package.interface(interface);
+
+    let mut interface = Interface::new("bar");
+    let mut use_ = Use::new("foo");
+    use_.item("bar", Some("foobar"));
+    interface.use_(use_);
+    interface.type_def(TypeDef::resource("baz", Vec::<ResourceFunc>::new()));
+    package.interface(interface);
+
+    let mut world = World::new("baz");
+    let mut use_ = Use::new("bar");
+    use_.item("baz", None);
+    world.use_(use_);
+    package.world(world);
+
+    assert_eq!(package.to_string(), PACKAGE);
+}