1use interstice_abi::{
2 Authority, ModuleDependency, ModuleVisibility, NodeDependency, encode, pack_ptr_len,
3};
4
5pub const fn validate_replicated_table_literal(value: &str) {
6 let bytes = value.as_bytes();
7 if bytes.is_empty() {
8 panic!("Replicated table path cannot be empty");
9 }
10
11 let mut dot_count = 0;
12 let mut segment_len = 0;
13 let mut index = 0;
14
15 while index < bytes.len() {
16 let byte = bytes[index];
17
18 if byte == b'.' {
19 if segment_len == 0 {
20 panic!("Replicated table path must not contain empty segments");
21 }
22 dot_count += 1;
23 segment_len = 0;
24 } else {
25 let is_valid_char = (byte >= b'a' && byte <= b'z')
26 || (byte >= b'A' && byte <= b'Z')
27 || (byte >= b'0' && byte <= b'9')
28 || byte == b'_'
29 || byte == b'-';
30
31 if !is_valid_char {
32 panic!("Replicated table path contains unsupported characters");
33 }
34 segment_len += 1;
35 }
36
37 index += 1;
38 }
39
40 if dot_count != 2 || segment_len == 0 {
41 panic!("Replicated table path must use 'node.module.table'");
42 }
43}
44
45#[macro_export]
46macro_rules! interstice_module {
47 () => {
48 interstice_module!(visibility: Private, authorities: [], replicated_tables: []);
49 };
50
51 (visibility: $vis:ident) => {
52 interstice_module!(visibility: $vis, authorities: [], replicated_tables: []);
53 };
54
55 (authorities: [$($auth:ident),* $(,)?]) => {
56 interstice_module!(visibility: Private, authorities: [$($auth),*], replicated_tables: []);
57 };
58
59 (replicated_tables: [$($rep:literal),* $(,)?]) => {
60 interstice_module!(visibility: Private, authorities: [], replicated_tables: [$($rep),*]);
61 };
62
63 (visibility: $vis:ident, authorities: [$($auth:ident),* $(,)?]) => {
64 interstice_module!(visibility: $vis, authorities: [$($auth),*], replicated_tables: []);
65 };
66
67 (visibility: $vis:ident, replicated_tables: [$($rep:literal),* $(,)?]) => {
68 interstice_module!(visibility: $vis, authorities: [], replicated_tables: [$($rep),*]);
69 };
70
71 (authorities: [$($auth:ident),* $(,)?], replicated_tables: [$($rep:literal),* $(,)?]) => {
72 interstice_module!(visibility: Private, authorities: [$($auth),*], replicated_tables: [$($rep),*]);
73 };
74
75 (visibility: $vis:ident, authorities: [$($auth:ident),* $(,)?], replicated_tables: [$($rep:literal),* $(,)?]) => {
76 $(
77 interstice_module!(@impl_authority $auth);
78 )*
79
80 use std::str::FromStr;
82 #[global_allocator]
85 static ALLOC: interstice_sdk::wee_alloc::WeeAlloc =
86 interstice_sdk::wee_alloc::WeeAlloc::INIT;
87
88 #[unsafe(no_mangle)]
89 pub extern "C" fn alloc(size: i32) -> i32 {
90 let layout = std::alloc::Layout::from_size_align(size as usize, 8).unwrap();
91 unsafe { std::alloc::alloc(layout) as i32 }
92 }
93
94 #[unsafe(no_mangle)]
95 pub extern "C" fn dealloc(ptr: i32, size: i32) {
96 let layout = std::alloc::Layout::from_size_align(size as usize, 8).unwrap();
97 unsafe { std::alloc::dealloc(ptr as *mut u8, layout) }
98 }
99
100 #[$crate::init]
102 fn interstice_init() {
103 std::panic::set_hook(Box::new(|info| {
104 let msg = if let Some(s) = info.payload().downcast_ref::<&str>() {
105 *s
106 } else if let Some(s) = info.payload().downcast_ref::<String>() {
107 s.as_str()
108 } else {
109 "panic occurred"
110 };
111
112 interstice_sdk::host_calls::log(&format!("Panic Error: {}", msg));
114 }));
115 }
116
117 pub mod bindings {
119 include!(concat!(env!("OUT_DIR"), "/interstice_bindings.rs"));
120 }
121
122 const __INTERSTICE_MODULE_NAME: &str = env!("CARGO_PKG_NAME");
125 const __INTERSTICE_MODULE_VERSION: &str = env!("CARGO_PKG_VERSION");
126 const __INTERSTICE_VISIBILITY: ModuleVisibility = ModuleVisibility::$vis;
127 const __INTERSTICE_AUTHORITIES: &[interstice_abi::Authority] = &[
128 $(interstice_abi::Authority::$auth),*
129 ];
130
131 #[unsafe(no_mangle)]
132 pub extern "C" fn interstice_describe() -> i64 {
133 let __interstice_replicated_tables: Vec<interstice_sdk::ReplicatedTableSchema> = vec![
134 $(
135 {
136 const _: () = interstice_sdk::macros::validate_replicated_table_literal($rep);
137 let parts: Vec<&str> = $rep.split('.').collect();
138 interstice_sdk::ReplicatedTableSchema {
139 node_name: parts[0].to_string(),
140 module_name: parts[1].to_string(),
141 table_name: parts[2].to_string(),
142 }
143 }
144 ),*
145 ];
146
147 let __interstice_node_dependencies = bindings::__GET_INTERSTICE_NODE_DEPENDENCIES();
148 for table in &__interstice_replicated_tables {
149 if let Err(error) = bindings::__INTERSTICE_VALIDATE_REPLICATED_TABLE(
150 &table.node_name,
151 &table.module_name,
152 &table.table_name,
153 &__interstice_node_dependencies,
154 ) {
155 panic!("{}", error);
156 }
157 }
158
159 interstice_sdk::macros::describe_module(
160 __INTERSTICE_MODULE_NAME,
161 __INTERSTICE_MODULE_VERSION,
162 __INTERSTICE_VISIBILITY,
163 __INTERSTICE_AUTHORITIES,
164 bindings::__GET_INTERSTICE_MODULE_DEPENDENCIES(),
165 __interstice_node_dependencies,
166 __interstice_replicated_tables,
167 )
168 }
169
170 };
171 (@impl_authority Input) => {
174 };
175
176 (@impl_authority Audio) => {
177 pub trait AudioExt {
178 fn audio(&self) -> Audio;
179 }
180
181 impl AudioExt for interstice_sdk::ReducerContext {
182 fn audio(&self) -> interstice_sdk::Audio {
183 interstice_sdk::Audio
184 }
185 }
186 };
187
188 (@impl_authority Gpu) => {
189 pub trait GpuExt {
190 fn gpu(&self) -> Gpu;
191 }
192
193 impl GpuExt for interstice_sdk::ReducerContext {
194 fn gpu(&self) -> interstice_sdk::Gpu {
195 interstice_sdk::Gpu
196 }
197 }
198 };
199
200 (@impl_authority File) => {
201 pub trait FileExt {
202 fn file(&self) -> File;
203 }
204
205 impl FileExt for interstice_sdk::ReducerContext {
206 fn file(&self) -> interstice_sdk::File {
207 interstice_sdk::File
208 }
209 }
210 };
211
212 (@impl_authority Module) => {
213 pub trait ModuleExt {
214 fn module(&self) -> ModuleAuthority;
215 }
216
217 impl ModuleExt for interstice_sdk::ReducerContext {
218 fn module(&self) -> interstice_sdk::ModuleAuthority {
219 interstice_sdk::ModuleAuthority
220 }
221 }
222 };
223}
224
225pub fn describe_module(
226 name: &str,
227 version: &str,
228 visibility: ModuleVisibility,
229 authorities: &'static [Authority],
230 module_dependencies: Vec<ModuleDependency>,
231 node_dependencies: Vec<NodeDependency>,
232 replicated_tables: Vec<interstice_abi::ReplicatedTableSchema>,
233) -> i64 {
234 let reducers = interstice_sdk_core::registry::collect_reducers();
235 let queries = interstice_sdk_core::registry::collect_queries();
236 let tables = interstice_sdk_core::registry::collect_tables();
237 let subscriptions = interstice_sdk_core::registry::collect_subscriptions();
238 let type_definitions = interstice_sdk_core::registry::collect_type_definitions();
239
240 let schema = interstice_abi::ModuleSchema {
241 abi_version: interstice_abi::ABI_VERSION,
242 name: name.to_string(),
243 version: version.into(),
244 visibility,
245 reducers,
246 queries,
247 tables,
248 subscriptions,
249 type_definitions,
250 authorities: authorities.to_vec(),
251 module_dependencies,
252 node_dependencies,
253 replicated_tables,
254 };
255
256 let bytes = encode(&schema).unwrap();
257 let len = bytes.len() as i32;
258 let ptr = Box::into_raw(bytes.into_boxed_slice()) as *mut u8 as i32;
259 return pack_ptr_len(ptr, len);
260}