interstice_cli/
example.rs1use 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}