Skip to main content

interstice_cli/
example.rs

1use crate::{
2    data_directory::nodes_dir,
3    node_registry::{NodeRecord, NodeRegistry},
4};
5use interstice_core::{IntersticeError, Node};
6
7const HELLO_BYTES: &[u8] = include_bytes!(concat!(
8    env!("CARGO_MANIFEST_DIR"),
9    "/module_examples/hello.wasm"
10));
11const CALLER_BYTES: &[u8] = include_bytes!(concat!(
12    env!("CARGO_MANIFEST_DIR"),
13    "/module_examples/caller.wasm"
14));
15const GRAPHICS_BYTES: &[u8] = include_bytes!(concat!(
16    env!("CARGO_MANIFEST_DIR"),
17    "/module_examples/graphics.wasm"
18));
19const AUDIO_BYTES: &[u8] = include_bytes!(concat!(
20    env!("CARGO_MANIFEST_DIR"),
21    "/module_examples/audio.wasm"
22));
23
24struct ExampleModule {
25    bytes: &'static [u8],
26    public: bool,
27}
28
29struct ExampleConfig {
30    name: &'static str,
31    port: u32,
32    modules: Vec<ExampleModule>,
33}
34
35fn example_config(example_name: &str) -> Result<ExampleConfig, IntersticeError> {
36    match example_name {
37        "hello" => Ok(ExampleConfig {
38            name: "hello",
39            port: 8080,
40            modules: vec![ExampleModule {
41                bytes: HELLO_BYTES,
42                public: false,
43            }],
44        }),
45        "caller" => Ok(ExampleConfig {
46            name: "caller",
47            port: 8081,
48            modules: vec![ExampleModule {
49                bytes: CALLER_BYTES,
50                public: true,
51            }],
52        }),
53        "graphics" => Ok(ExampleConfig {
54            name: "graphics",
55            port: 8082,
56            modules: vec![ExampleModule {
57                bytes: GRAPHICS_BYTES,
58                public: true,
59            }],
60        }),
61        "audio" => Ok(ExampleConfig {
62            name: "audio",
63            port: 8083,
64            modules: vec![ExampleModule {
65                bytes: AUDIO_BYTES,
66                public: false,
67            }],
68        }),
69        _ => Err(IntersticeError::Internal(format!(
70            "Unknown example '{example_name}'. Expected hello, caller, graphics, or audio."
71        ))),
72    }
73}
74
75pub async fn example(example_name: &str) -> Result<(), IntersticeError> {
76    let config = example_config(example_name)?;
77    let mut registry = NodeRegistry::load()?;
78    let name = format!("example-{}", config.name);
79    let (node, is_new) = if let Some(entry) = registry.get(&name) {
80        let node_id = entry
81            .node_id
82            .as_ref()
83            .ok_or_else(|| IntersticeError::Internal("Missing node id".into()))?;
84        let node_port = entry
85            .address
86            .split(':')
87            .last()
88            .ok_or_else(|| IntersticeError::Internal("Invalid address".into()))?
89            .parse()
90            .map_err(|_| IntersticeError::Internal("Invalid port".into()))?;
91        if node_port != config.port {
92            return Err(IntersticeError::Internal(format!(
93                "Example '{}' expects port {}, but registry has {}. Remove the existing entry to continue.",
94                config.name, config.port, node_port
95            )));
96        }
97        (
98            Node::load(&nodes_dir(), node_id.parse().unwrap(), node_port).await?,
99            false,
100        )
101    } else {
102        let node = Node::new(&nodes_dir(), config.port)?;
103        registry.add(NodeRecord {
104            name,
105            address: format!("127.0.0.1:{}", config.port),
106            node_id: Some(node.id.to_string()),
107            local: true,
108            last_seen: None,
109        })?;
110        (node, true)
111    };
112
113    let modules_path = nodes_dir().join(node.id.to_string()).join("modules");
114    let has_modules = std::fs::read_dir(&modules_path)
115        .map(|entries| {
116            entries.filter_map(|entry| entry.ok()).any(|entry| {
117                let path = entry.path();
118                path.is_dir() && path.join("module.wasm").exists()
119            })
120        })
121        .unwrap_or(false);
122
123    if is_new || !has_modules {
124        for module in &config.modules {
125            let schema = node.load_module_from_bytes(module.bytes).await?;
126            if module.public {
127                let _ = schema.to_public();
128            }
129        }
130    }
131
132    let _node_schema = node.schema("MyNode".into()).await.to_public();
133
134    node.start().await?;
135    Ok(())
136}