1use std::{collections::HashSet, path::Path};
2
3use anyhow::Context;
4use serde::{Deserialize, Serialize};
5use wit_parser::{PackageId, Resolve, WorldId};
6
7#[derive(Serialize, Deserialize, Debug)]
10#[serde(rename_all = "camelCase")]
11pub struct Component {
12 pub exports: Vec<String>,
14 pub imports: Vec<String>,
16 pub target: Option<String>,
19}
20
21impl Component {
22 pub fn from_world(resolve: &Resolve, world_id: WorldId) -> anyhow::Result<Self> {
27 let world = resolve
28 .worlds
29 .iter()
30 .find_map(|(id, w)| (id == world_id).then_some(w))
31 .context("component world not found")?;
32 Ok(Component {
33 exports: world
34 .exports
35 .keys()
36 .map(|key| resolve.name_world_key(key))
37 .collect(),
38 imports: world
39 .imports
40 .keys()
41 .map(|key| resolve.name_world_key(key))
42 .collect(),
43 target: None,
44 })
45 }
46
47 pub fn from_package(resolve: &Resolve, pkg_id: PackageId) -> anyhow::Result<Self> {
53 let pkg = resolve.packages.get(pkg_id).context("package not found")?;
54 let mut exports = pkg
55 .worlds
56 .iter()
57 .filter_map(|(_name, world_id)| {
58 let world = resolve.worlds.get(*world_id)?;
59 let mut exports = world
60 .exports
61 .keys()
62 .map(|key| resolve.name_world_key(key))
63 .collect::<Vec<_>>();
64 let mut fully_qualified_world =
65 format!("{}:{}/{}", pkg.name.namespace, pkg.name.name, world.name);
66 if let Some(ver) = pkg.name.version.as_ref() {
67 fully_qualified_world.push('@');
68 fully_qualified_world.push_str(&ver.to_string());
69 }
70 exports.push(fully_qualified_world);
71 Some(exports)
72 })
73 .flatten()
74 .collect::<HashSet<_>>();
75 exports.extend(pkg.interfaces.values().filter_map(|id| resolve.id_of(*id)));
76 Ok(Component {
77 exports: exports.into_iter().collect(),
78 imports: vec![],
79 target: None,
80 })
81 }
82
83 pub async fn from_component(path: impl AsRef<Path>) -> anyhow::Result<Self> {
85 let data = tokio::fs::read(path).await.context("Unable to read file")?;
86 Self::from_raw_component(data)
87 }
88
89 pub fn from_raw_component(raw: impl AsRef<[u8]>) -> anyhow::Result<Self> {
91 match wit_component::decode(raw.as_ref()).context("failed to decode WIT component")? {
92 wit_component::DecodedWasm::Component(resolve, world) => {
93 Self::from_world(&resolve, world)
94 }
95 wit_component::DecodedWasm::WitPackage(resolve, pkg_id) => {
96 Self::from_package(&resolve, pkg_id)
97 }
98 }
99 }
100}