|
1 |
| -use wasm_pkg_loader::PackageRef; |
| 1 | +use anyhow::Context; |
2 | 2 |
|
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(); |
8 | 26 |
|
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) |
14 | 28 | }
|
15 | 29 |
|
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>, |
21 | 36 | }
|
22 | 37 |
|
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() |
26 | 106 | }
|
27 | 107 |
|
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 |
35 | 110 | }
|
36 | 111 | }
|
37 | 112 |
|
|
0 commit comments