Skip to main content

standard_plugin_cli/
world.rs

1//! The worlds of `standard:plugin@2.0.0`, read from the SDK's WIT package,
2//! as sets of import and export names a component can be checked
3//! against.
4
5use std::collections::BTreeSet;
6
7use standard_plugin_manifest::World;
8use wit_parser::{Resolve, WorldItem, WorldKey};
9
10/// The WIT package this CLI checks against: the one the SDK version it
11/// pins builds its bindings from.
12pub const WIT: &str = standard_plugin::sources::WIT;
13
14/// What a world lets a component import and requires it to export.
15#[derive(Clone, Debug, PartialEq, Eq)]
16pub struct WorldShape {
17    /// Interface names (`standard:plugin/values@2.0.0`) and the types the
18    /// world `use`s (`event`, `power`).
19    pub imports: BTreeSet<String>,
20    /// Exported function names (`activate`, `frame`, ...) and interface
21    /// names (`standard:plugin/companion@2.0.0`).
22    pub exports: BTreeSet<String>,
23}
24
25/// The shape of `world`, with the implicit imports its `use`s pull in.
26pub fn shape(world: World) -> WorldShape {
27    let mut resolve = Resolve::default();
28    let package = resolve
29        .push_source("plugin.wit", WIT)
30        .expect("the embedded WIT package parses");
31    let id = resolve
32        .select_world(&[package], Some(world.wit_name()))
33        .expect("the embedded WIT package has both worlds");
34    let world = &resolve.worlds[id];
35    let name = |key: &WorldKey, item: &WorldItem| match (key, item) {
36        (_, WorldItem::Interface { id, .. }) => resolve.id_of(*id),
37        (WorldKey::Name(name), WorldItem::Function(_) | WorldItem::Type { .. }) => {
38            Some(name.clone())
39        }
40        _ => None,
41    };
42    WorldShape {
43        imports: world
44            .imports
45            .iter()
46            .filter_map(|(key, item)| name(key, item))
47            .collect(),
48        exports: world
49            .exports
50            .iter()
51            .filter_map(|(key, item)| name(key, item))
52            .collect(),
53    }
54}
55
56/// `standard:plugin/<name>@2.0.0` for an interface of this package.
57pub fn interface(name: &str) -> String {
58    format!("standard:plugin/{name}@2.0.0")
59}
60
61#[cfg(test)]
62mod tests {
63    use super::*;
64
65    #[test]
66    fn the_worlds_list_their_interfaces_and_exports() {
67        let ui = shape(World::Ui);
68        assert!(ui.imports.contains(&interface("surface")));
69        assert!(ui.imports.contains(&interface("claims")));
70        assert!(!ui.imports.contains(&interface("daemon-process")));
71        assert_eq!(
72            ui.exports,
73            ["activate", "deactivate", "event", "frame"]
74                .map(String::from)
75                .into()
76        );
77        let daemon = shape(World::Daemon);
78        assert!(daemon.imports.contains(&interface("daemon-panes")));
79        assert!(daemon.exports.contains("drive"));
80        assert!(daemon.exports.contains(&interface("companion")));
81        assert!(!daemon.exports.contains("handle-call"));
82    }
83}