Skip to content

Commit e74895b

Browse files
committed
WIT-based environment syntax
Signed-off-by: itowlson <[email protected]>
1 parent 3b25dad commit e74895b

File tree

4 files changed

+237
-112
lines changed

4 files changed

+237
-112
lines changed

Cargo.lock

+81
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/environments/Cargo.toml

+5
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,10 @@ edition = { workspace = true }
77
[dependencies]
88
anyhow = { workspace = true }
99
async-trait = "0.1"
10+
bytes = "1.1"
1011
futures = "0.3"
12+
futures-util = "0.3"
13+
id-arena = "2"
1114
indexmap = "2.2.6"
1215
miette = "7.2.0"
1316
oci-distribution = { git = "https://github.com/fermyon/oci-distribution", rev = "7e4ce9be9bcd22e78a28f06204931f10c44402ba" }
@@ -26,6 +29,8 @@ wac-parser = "0.6.0"
2629
wac-resolver = "0.6.0"
2730
wac-types = "0.6.0"
2831
wasm-pkg-loader = "0.4.1"
32+
wit-component = "0.217.0"
33+
wit-parser = "0.217.0"
2934

3035
[lints]
3136
workspace = true

crates/environments/src/environment_definition.rs

+101-26
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,112 @@
1-
use wasm_pkg_loader::PackageRef;
1+
use anyhow::Context;
22

3-
#[derive(Debug, serde::Deserialize)]
4-
pub struct TargetEnvironment {
5-
pub name: String,
6-
pub environments: std::collections::HashMap<TriggerType, TargetWorld>,
7-
}
3+
pub async fn load_environment(env_id: &str) -> anyhow::Result<TargetEnvironment> {
4+
use futures_util::TryStreamExt;
5+
6+
let (pkg_name, pkg_ver) = env_id.split_once('@').unwrap();
7+
8+
let mut client = wasm_pkg_loader::Client::with_global_defaults()?;
9+
10+
let package = pkg_name.to_owned().try_into().context("pkg ref parse")?;
11+
let version = wasm_pkg_loader::Version::parse(pkg_ver).context("pkg ver parse")?;
12+
13+
let release = client
14+
.get_release(&package, &version)
15+
.await
16+
.context("get release")?;
17+
let stm = client
18+
.stream_content(&package, &release)
19+
.await
20+
.context("stream content")?;
21+
let bytes = stm
22+
.try_collect::<bytes::BytesMut>()
23+
.await
24+
.context("collect stm")?
25+
.to_vec();
826

9-
#[derive(Debug, Eq, Hash, PartialEq, serde::Deserialize)]
10-
pub struct TargetWorld {
11-
wit_package: PackageRef,
12-
package_ver: String, // TODO: tidy to semver::Version
13-
world_name: WorldNames,
27+
TargetEnvironment::new(env_id.to_owned(), bytes)
1428
}
1529

16-
#[derive(Debug, Eq, Hash, PartialEq, serde::Deserialize)]
17-
#[serde(untagged)]
18-
enum WorldNames {
19-
Exactly(String),
20-
AnyOf(Vec<String>),
30+
pub struct TargetEnvironment {
31+
name: String,
32+
decoded: wit_parser::decoding::DecodedWasm,
33+
package: wit_parser::Package, // saves unwrapping it every time
34+
package_id: id_arena::Id<wit_parser::Package>,
35+
package_bytes: Vec<u8>,
2136
}
2237

23-
impl TargetWorld {
24-
fn versioned_name(&self, world_name: &str) -> String {
25-
format!("{}/{}@{}", self.wit_package, world_name, self.package_ver)
38+
impl TargetEnvironment {
39+
fn new(name: String, bytes: Vec<u8>) -> anyhow::Result<Self> {
40+
let decoded = wit_component::decode(&bytes).context("decode wasm")?;
41+
let package_id = decoded.package();
42+
let package = decoded
43+
.resolve()
44+
.packages
45+
.get(package_id)
46+
.context("should had a package")?
47+
.clone();
48+
49+
Ok(Self {
50+
name,
51+
decoded,
52+
package,
53+
package_id,
54+
package_bytes: bytes,
55+
})
56+
}
57+
58+
pub fn is_world_for(&self, trigger_type: &TriggerType, world: &wit_parser::World) -> bool {
59+
world.name.starts_with(&format!("trigger-{trigger_type}"))
60+
&& world.package.is_some_and(|p| p == self.package_id)
61+
}
62+
63+
pub fn supports_trigger_type(&self, trigger_type: &TriggerType) -> bool {
64+
self.decoded
65+
.resolve()
66+
.worlds
67+
.iter()
68+
.any(|(_, world)| self.is_world_for(trigger_type, world))
69+
}
70+
71+
pub fn worlds(&self, trigger_type: &TriggerType) -> Vec<String> {
72+
self.decoded
73+
.resolve()
74+
.worlds
75+
.iter()
76+
.filter(|(_, world)| self.is_world_for(trigger_type, world))
77+
.map(|(_, world)| self.world_qname(world))
78+
.collect()
79+
}
80+
81+
/// Fully qualified world name (e.g. fermyon:spin/[email protected])
82+
fn world_qname(&self, world: &wit_parser::World) -> String {
83+
let version_suffix = match self.package_version() {
84+
Some(version) => format!("@{version}"),
85+
None => "".to_owned(),
86+
};
87+
format!(
88+
"{}/{}{version_suffix}",
89+
self.package_namespaced_name(),
90+
world.name,
91+
)
92+
}
93+
94+
/// The environment name for UI purposes
95+
pub fn name(&self) -> &str {
96+
&self.name
97+
}
98+
99+
/// Namespaced but unversioned package name (e.g. spin:cli)
100+
pub fn package_namespaced_name(&self) -> String {
101+
format!("{}:{}", self.package.name.namespace, self.package.name.name)
102+
}
103+
104+
pub fn package_version(&self) -> Option<&semver::Version> {
105+
self.package.name.version.as_ref()
26106
}
27107

28-
pub fn versioned_names(&self) -> Vec<String> {
29-
match &self.world_name {
30-
WorldNames::Exactly(name) => vec![self.versioned_name(name)],
31-
WorldNames::AnyOf(names) => {
32-
names.iter().map(|name| self.versioned_name(name)).collect()
33-
}
34-
}
108+
pub fn package_bytes(&self) -> &[u8] {
109+
&self.package_bytes
35110
}
36111
}
37112

0 commit comments

Comments
 (0)