Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a function to extract a world from a document #928

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 2 additions & 6 deletions crates/wit-component/tests/components.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use anyhow::{anyhow, Result};
use anyhow::Result;
use pretty_assertions::assert_eq;
use std::{fs, path::Path};
use wasm_encoder::{Encode, Section};
Expand Down Expand Up @@ -99,11 +99,7 @@ fn read_core_module(path: &Path) -> Result<Vec<u8>> {
UnresolvedPackage::parse_file(&interface)?,
&Default::default(),
)?;
let doc = *resolve.packages[pkg].documents.iter().next().unwrap().1;
let doc = &resolve.documents[doc];
let world = doc
.default_world
.ok_or_else(|| anyhow!("no default world specified"))?;
let world = resolve.select_world(pkg, None)?;
let encoded = wit_component::metadata::encode(&resolve, world, StringEncoding::UTF8)?;

let section = wasm_encoder::CustomSection {
Expand Down
61 changes: 60 additions & 1 deletion crates/wit-parser/src/resolve.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use crate::{
Document, DocumentId, Error, Function, Interface, InterfaceId, Results, Type, TypeDef,
TypeDefKind, TypeId, TypeOwner, UnresolvedPackage, World, WorldId, WorldItem,
};
use anyhow::{bail, Context, Result};
use anyhow::{anyhow, bail, Context, Result};
use id_arena::{Arena, Id};
use indexmap::{IndexMap, IndexSet};
use std::collections::{HashMap, HashSet};
Expand Down Expand Up @@ -376,6 +376,65 @@ impl Resolve {
.push(interface.name.as_ref()?);
Some(base.to_string())
}

/// Attempts to locate a default world for the `pkg` specified within this
/// [`Resolve`]. Optionally takes a string-based `world` "specifier" to
/// resolve the world.
///
/// This is intended for use by bindings generators and such as the default
/// logic for locating a world within a package used for binding. The
/// `world` argument is typically a user-specified argument (which again is
/// optional and not required) where the `pkg` is determined ambiently by
/// the integration.
///
/// If `world` is `None` (e.g. not specified by a user) then the package
/// must have exactly one `default world` within its documents, otherwise an
/// error will be returned. If `world` is `Some` then it's a `.`-separated
/// name where the first element is the name of the document and the second,
/// optional, element is the name of the `world`. For example the name `foo`
/// would mean the `default world` of the `foo` document. The name `foo.bar`
/// would mean the world named `bar` in the `foo` document.
pub fn select_world(&self, pkg: PackageId, world: Option<&str>) -> Result<WorldId> {
match world {
Some(world) => {
let mut parts = world.splitn(2, '.');
let doc = parts.next().unwrap();
let world = parts.next();
let doc = *self.packages[pkg]
.documents
.get(doc)
.ok_or_else(|| anyhow!("no document named `{doc}` in package"))?;
match world {
Some(name) => self.documents[doc]
.worlds
.get(name)
.copied()
.ok_or_else(|| anyhow!("no world named `{name}` in document")),
None => self.documents[doc]
.default_world
.ok_or_else(|| anyhow!("no default world in document")),
}
}
None => {
if self.packages[pkg].documents.is_empty() {
bail!("no documents found in package")
}

let mut unique_default_world = None;
for (_name, doc) in &self.documents {
if let Some(default_world) = doc.default_world {
if unique_default_world.is_some() {
bail!("multiple default worlds found in package, one must be specified")
} else {
unique_default_world = Some(default_world);
}
}
}

unique_default_world.ok_or_else(|| anyhow!("no default world in package"))
}
}
}
}

/// Structure returned by [`Resolve::merge`] which contains mappings from
Expand Down
22 changes: 2 additions & 20 deletions src/bin/wasm-tools/component.rs
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ pub struct EmbedOpts {
/// world`, or it can be a `foo/bar` name where `foo` names a document and
/// `bar` names a world within that document.
#[clap(short, long)]
world: String,
world: Option<String>,

/// Don't read a core wasm module as input, instead generating a "dummy"
/// module as a placeholder.
Expand All @@ -180,25 +180,7 @@ impl EmbedOpts {
Some(self.io.parse_input_wasm()?)
};
let (resolve, id) = parse_wit(&self.wit)?;

let mut parts = self.world.split('/');
let doc = match parts.next() {
Some(name) => match resolve.packages[id].documents.get(name) {
Some(doc) => *doc,
None => bail!("no document named `{name}` in package"),
},
None => bail!("invalid `--world` argument"),
};
let world = match parts.next() {
Some(name) => match resolve.documents[doc].worlds.get(name) {
Some(world) => *world,
None => bail!("no world named `{name}` in document"),
},
None => match resolve.documents[doc].default_world {
Some(world) => world,
None => bail!("no default world found in document"),
},
};
let world = resolve.select_world(id, self.world.as_deref())?;

let encoded = wit_component::metadata::encode(
&resolve,
Expand Down