Skip to content

Commit

Permalink
WIT-based environment syntax
Browse files Browse the repository at this point in the history
Signed-off-by: itowlson <[email protected]>
  • Loading branch information
itowlson committed Sep 13, 2024
1 parent 3b25dad commit e74895b
Show file tree
Hide file tree
Showing 4 changed files with 237 additions and 112 deletions.
81 changes: 81 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions crates/environments/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@ edition = { workspace = true }
[dependencies]
anyhow = { workspace = true }
async-trait = "0.1"
bytes = "1.1"
futures = "0.3"
futures-util = "0.3"
id-arena = "2"
indexmap = "2.2.6"
miette = "7.2.0"
oci-distribution = { git = "https://github.com/fermyon/oci-distribution", rev = "7e4ce9be9bcd22e78a28f06204931f10c44402ba" }
Expand All @@ -26,6 +29,8 @@ wac-parser = "0.6.0"
wac-resolver = "0.6.0"
wac-types = "0.6.0"
wasm-pkg-loader = "0.4.1"
wit-component = "0.217.0"
wit-parser = "0.217.0"

[lints]
workspace = true
127 changes: 101 additions & 26 deletions crates/environments/src/environment_definition.rs
Original file line number Diff line number Diff line change
@@ -1,37 +1,112 @@
use wasm_pkg_loader::PackageRef;
use anyhow::Context;

#[derive(Debug, serde::Deserialize)]
pub struct TargetEnvironment {
pub name: String,
pub environments: std::collections::HashMap<TriggerType, TargetWorld>,
}
pub async fn load_environment(env_id: &str) -> anyhow::Result<TargetEnvironment> {
use futures_util::TryStreamExt;

let (pkg_name, pkg_ver) = env_id.split_once('@').unwrap();

let mut client = wasm_pkg_loader::Client::with_global_defaults()?;

let package = pkg_name.to_owned().try_into().context("pkg ref parse")?;
let version = wasm_pkg_loader::Version::parse(pkg_ver).context("pkg ver parse")?;

let release = client
.get_release(&package, &version)
.await
.context("get release")?;
let stm = client
.stream_content(&package, &release)
.await
.context("stream content")?;
let bytes = stm
.try_collect::<bytes::BytesMut>()
.await
.context("collect stm")?
.to_vec();

#[derive(Debug, Eq, Hash, PartialEq, serde::Deserialize)]
pub struct TargetWorld {
wit_package: PackageRef,
package_ver: String, // TODO: tidy to semver::Version
world_name: WorldNames,
TargetEnvironment::new(env_id.to_owned(), bytes)
}

#[derive(Debug, Eq, Hash, PartialEq, serde::Deserialize)]
#[serde(untagged)]
enum WorldNames {
Exactly(String),
AnyOf(Vec<String>),
pub struct TargetEnvironment {
name: String,
decoded: wit_parser::decoding::DecodedWasm,
package: wit_parser::Package, // saves unwrapping it every time
package_id: id_arena::Id<wit_parser::Package>,
package_bytes: Vec<u8>,
}

impl TargetWorld {
fn versioned_name(&self, world_name: &str) -> String {
format!("{}/{}@{}", self.wit_package, world_name, self.package_ver)
impl TargetEnvironment {
fn new(name: String, bytes: Vec<u8>) -> anyhow::Result<Self> {
let decoded = wit_component::decode(&bytes).context("decode wasm")?;
let package_id = decoded.package();
let package = decoded
.resolve()
.packages
.get(package_id)
.context("should had a package")?
.clone();

Ok(Self {
name,
decoded,
package,
package_id,
package_bytes: bytes,
})
}

pub fn is_world_for(&self, trigger_type: &TriggerType, world: &wit_parser::World) -> bool {
world.name.starts_with(&format!("trigger-{trigger_type}"))
&& world.package.is_some_and(|p| p == self.package_id)
}

pub fn supports_trigger_type(&self, trigger_type: &TriggerType) -> bool {
self.decoded
.resolve()
.worlds
.iter()
.any(|(_, world)| self.is_world_for(trigger_type, world))
}

pub fn worlds(&self, trigger_type: &TriggerType) -> Vec<String> {
self.decoded
.resolve()
.worlds
.iter()
.filter(|(_, world)| self.is_world_for(trigger_type, world))
.map(|(_, world)| self.world_qname(world))
.collect()
}

/// Fully qualified world name (e.g. fermyon:spin/[email protected])
fn world_qname(&self, world: &wit_parser::World) -> String {
let version_suffix = match self.package_version() {
Some(version) => format!("@{version}"),
None => "".to_owned(),
};
format!(
"{}/{}{version_suffix}",
self.package_namespaced_name(),
world.name,
)
}

/// The environment name for UI purposes
pub fn name(&self) -> &str {
&self.name
}

/// Namespaced but unversioned package name (e.g. spin:cli)
pub fn package_namespaced_name(&self) -> String {
format!("{}:{}", self.package.name.namespace, self.package.name.name)
}

pub fn package_version(&self) -> Option<&semver::Version> {
self.package.name.version.as_ref()
}

pub fn versioned_names(&self) -> Vec<String> {
match &self.world_name {
WorldNames::Exactly(name) => vec![self.versioned_name(name)],
WorldNames::AnyOf(names) => {
names.iter().map(|name| self.versioned_name(name)).collect()
}
}
pub fn package_bytes(&self) -> &[u8] {
&self.package_bytes
}
}

Expand Down
Loading

0 comments on commit e74895b

Please sign in to comment.