oci_wasm/
component.rs

1use std::{collections::HashSet, path::Path};
2
3use anyhow::Context;
4use serde::{Deserialize, Serialize};
5use wit_parser::{PackageId, Resolve, WorldId};
6
7/// Information about the component in the manifest. This is generally synthesized from a
8/// component's world
9#[derive(Serialize, Deserialize, Debug)]
10#[serde(rename_all = "camelCase")]
11pub struct Component {
12    /// A list of all exports from the component
13    pub exports: Vec<String>,
14    /// A list of all imports from the component
15    pub imports: Vec<String>,
16    // This is optional metadata for indexing. Implementations MAY use this information to fetch
17    // other data to inspect the specified world
18    pub target: Option<String>,
19}
20
21impl Component {
22    /// Create a component from a parsed [`Resolve`] and [`WorldId`]. This is a lower level function
23    /// for when you've already parsed a resolve and have the world ID
24    ///
25    /// Returns an error only if the world doesn't exist in the resolve.
26    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    /// Create a component from a parsed [`Resolve`] and [`PackageId`]. This is a lower level
48    /// function for when you have already parsed a binary wit package and have the package ID and
49    /// resolve available. This outputs a component with all exports and an empty imports list.
50    ///
51    /// Returns an error only if the package doesn't exist in the resolve.
52    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    /// Create a component by loading the given component from the filesystem
84    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    /// Create a component from the raw bytes of the component
90    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}