oci_wasm/
component.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
use std::{collections::HashSet, path::Path};

use anyhow::Context;
use serde::{Deserialize, Serialize};
use wit_parser::{PackageId, Resolve, WorldId};

/// Information about the component in the manifest. This is generally synthesized from a
/// component's world
#[derive(Serialize, Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
pub struct Component {
    /// A list of all exports from the component
    pub exports: Vec<String>,
    /// A list of all imports from the component
    pub imports: Vec<String>,
    // This is optional metadata for indexing. Implementations MAY use this information to fetch
    // other data to inspect the specified world
    pub target: Option<String>,
}

impl Component {
    /// Create a component from a parsed [`Resolve`] and [`WorldId`]. This is a lower level function
    /// for when you've already parsed a resolve and have the world ID
    ///
    /// Returns an error only if the world doesn't exist in the resolve.
    pub fn from_world(resolve: &Resolve, world_id: WorldId) -> anyhow::Result<Self> {
        let world = resolve
            .worlds
            .iter()
            .find_map(|(id, w)| (id == world_id).then_some(w))
            .context("component world not found")?;
        Ok(Component {
            exports: world
                .exports
                .keys()
                .map(|key| resolve.name_world_key(key))
                .collect(),
            imports: world
                .imports
                .keys()
                .map(|key| resolve.name_world_key(key))
                .collect(),
            target: None,
        })
    }

    /// Create a component from a parsed [`Resolve`] and [`PackageId`]. This is a lower level
    /// function for when you have already parsed a binary wit package and have the package ID and
    /// resolve available. This outputs a component with all exports and an empty imports list.
    ///
    /// Returns an error only if the package doesn't exist in the resolve.
    pub fn from_package(resolve: &Resolve, pkg_id: PackageId) -> anyhow::Result<Self> {
        let pkg = resolve.packages.get(pkg_id).context("package not found")?;
        let mut exports = pkg
            .worlds
            .iter()
            .filter_map(|(_name, world_id)| {
                let world = resolve.worlds.get(*world_id)?;
                let mut exports = world
                    .exports
                    .keys()
                    .map(|key| resolve.name_world_key(key))
                    .collect::<Vec<_>>();
                let mut fully_qualified_world =
                    format!("{}:{}/{}", pkg.name.namespace, pkg.name.name, world.name);
                if let Some(ver) = pkg.name.version.as_ref() {
                    fully_qualified_world.push('@');
                    fully_qualified_world.push_str(&ver.to_string());
                }
                exports.push(fully_qualified_world);
                Some(exports)
            })
            .flatten()
            .collect::<HashSet<_>>();
        exports.extend(pkg.interfaces.values().filter_map(|id| resolve.id_of(*id)));
        Ok(Component {
            exports: exports.into_iter().collect(),
            imports: vec![],
            target: None,
        })
    }

    /// Create a component by loading the given component from the filesystem
    pub async fn from_component(path: impl AsRef<Path>) -> anyhow::Result<Self> {
        let data = tokio::fs::read(path).await.context("Unable to read file")?;
        Self::from_raw_component(data)
    }

    /// Create a component from the raw bytes of the component
    pub fn from_raw_component(raw: impl AsRef<[u8]>) -> anyhow::Result<Self> {
        match wit_component::decode(raw.as_ref()).context("failed to decode WIT component")? {
            wit_component::DecodedWasm::Component(resolve, world) => {
                Self::from_world(&resolve, world)
            }
            wit_component::DecodedWasm::WitPackage(resolve, pkg_id) => {
                Self::from_package(&resolve, pkg_id)
            }
        }
    }
}