1use std::collections::HashSet;
2use std::error::Error;
3use std::fmt;
4
5use crate::object::{MaterializedCE, Value};
6
7#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
12pub struct ComponentHandle(u64);
13
14impl ComponentHandle {
15 pub fn from_raw(raw: u64) -> Self {
16 Self(raw)
17 }
18
19 pub fn into_raw(self) -> u64 {
20 self.0
21 }
22}
23
24#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
26pub struct CallbackHandle(u64);
27
28impl CallbackHandle {
29 pub fn from_raw(raw: u64) -> Self { Self(raw) }
30 pub fn into_raw(self) -> u64 { self.0 }
31}
32
33#[derive(Debug, Clone, PartialEq)]
36pub enum TransportValue {
37 Null,
38 Bool(bool),
39 Number(f64),
40 String(String),
41 Array(Vec<TransportValue>),
42 Table(Vec<(String, TransportValue)>),
43 Component(ComponentHandle),
44 Callback(CallbackHandle),
45}
46
47#[derive(Debug, Clone, Default, PartialEq, Eq)]
49pub struct HostCapabilities {
50 pub components: HashSet<String>,
51 pub component_operations: HashSet<String>,
52 pub api_ids: HashSet<String>,
53}
54
55impl HostCapabilities {
56 pub fn supports_component(mut self, name: impl Into<String>) -> Self {
57 self.components.insert(name.into().to_lowercase()); self
58 }
59 pub fn supports_operation(mut self, operation: impl Into<String>) -> Self {
60 self.component_operations.insert(operation.into()); self
61 }
62 pub fn supports_api(mut self, id: impl Into<String>) -> Self {
63 self.api_ids.insert(id.into()); self
64 }
65}
66
67#[derive(Debug)]
74pub struct HostContext {
75 session_tag: u32,
76 next_component: u32,
77 next_callback: u32,
78 components: HashSet<ComponentHandle>,
79 callbacks: HashSet<CallbackHandle>,
80}
81
82impl HostContext {
83 pub(crate) fn new(session_tag: u32) -> Self {
84 Self { session_tag, next_component: 1, next_callback: 1,
85 components: HashSet::new(), callbacks: HashSet::new() }
86 }
87 pub fn allocate_component(&mut self) -> ComponentHandle {
88 let handle = ComponentHandle::from_raw(
89 ((self.session_tag as u64) << 32) | self.next_component as u64);
90 self.next_component = self.next_component.checked_add(1).expect("component handle space exhausted");
91 self.components.insert(handle); handle
92 }
93 pub fn adopt_component(&mut self, handle: ComponentHandle) {
94 self.components.insert(handle);
95 }
96 pub fn allocate_callback(&mut self) -> CallbackHandle {
97 let handle = CallbackHandle::from_raw(
98 ((self.session_tag as u64) << 32) | self.next_callback as u64);
99 self.next_callback = self.next_callback.checked_add(1).expect("callback handle space exhausted");
100 self.callbacks.insert(handle); handle
101 }
102 pub fn owns_component(&self, handle: ComponentHandle) -> bool { self.components.contains(&handle) }
103 pub fn owns_callback(&self, handle: CallbackHandle) -> bool { self.callbacks.contains(&handle) }
104}
105
106impl fmt::Debug for ComponentHandle {
107 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
108 f.debug_tuple("ComponentHandle").field(&self.0).finish()
109 }
110}
111
112#[derive(Debug, Clone, PartialEq)]
113pub enum HostRequest {
114 RegisterComponent { tree: MaterializedCE },
117 Emit { tree: MaterializedCE },
120 Spawn {
121 tree: MaterializedCE,
122 },
123 Register {
124 tree: MaterializedCE,
125 },
126 Attach {
127 parent: Option<ComponentHandle>,
128 child: ComponentHandle,
129 },
130 Query {
131 selector: String,
132 scope: Option<ComponentHandle>,
133 multiple: bool,
134 },
135 RegisterHandler {
136 scope: ComponentHandle,
137 signal: String,
138 name: Option<String>,
139 handler: Value,
140 },
141 InvokeComponentMethod {
142 component: ComponentHandle,
143 component_type: String,
144 method: String,
145 args: Vec<Value>,
146 },
147 CallApi {
148 api_id: String,
149 args: Vec<TransportValue>,
150 },
151 AudioClipInstance {
152 source: ComponentHandle,
153 start_beat: Option<f64>,
154 stop_beat: Option<f64>,
155 },
156 AudioOperation {
157 operation: String,
158 target: Option<ComponentHandle>,
159 args: Vec<Value>,
160 },
161 ReplTree {
162 value: Value,
163 max_depth: Option<usize>,
164 },
165 ReplDump {
166 value: Value,
167 },
168 ReplHelp,
169 ReplClear,
170 EngineMutation {
173 operation: String,
174 targets: Vec<ComponentHandle>,
175 args: Vec<Value>,
176 },
177}
178
179impl HostRequest {
180 pub fn operation_name(&self) -> &str {
181 match self {
182 Self::RegisterComponent { .. } => "register_component",
183 Self::Emit { .. } => "emit",
184 Self::Spawn { .. } => "spawn",
185 Self::Register { .. } => "register",
186 Self::Attach { .. } => "attach",
187 Self::Query { .. } => "query",
188 Self::RegisterHandler { .. } => "register_handler",
189 Self::InvokeComponentMethod { .. } => "invoke_component_method",
190 Self::CallApi { api_id, .. } => api_id,
191 Self::AudioClipInstance { .. } => "audio_clip_instance",
192 Self::AudioOperation { operation, .. } | Self::EngineMutation { operation, .. } => {
193 operation
194 }
195 Self::ReplTree { .. } => "repl_tree",
196 Self::ReplDump { .. } => "repl_dump",
197 Self::ReplHelp => "repl_help",
198 Self::ReplClear => "repl_clear",
199 }
200 }
201}
202
203#[derive(Debug, Clone, PartialEq)]
204pub enum HostResponse {
205 Unit,
206 Value(Value),
207 Component {
208 handle: ComponentHandle,
209 component_type: String,
210 },
211 Components(Vec<(ComponentHandle, String)>),
212 Transport(TransportValue),
213}
214
215#[derive(Debug, Clone, PartialEq, Eq)]
216pub enum HostErrorKind {
217 UnsupportedHostOperation,
218 InvalidRequest,
219 HostFailure,
220 ForeignHandle,
221 Conversion,
222}
223
224#[derive(Debug, Clone, PartialEq, Eq)]
225pub struct HostError {
226 pub kind: HostErrorKind,
227 pub operation: String,
228 pub message: String,
229}
230
231impl HostError {
232 pub fn unsupported(operation: impl Into<String>) -> Self {
233 let operation = operation.into();
234 Self {
235 kind: HostErrorKind::UnsupportedHostOperation,
236 message: format!("host operation '{operation}' is unavailable"),
237 operation,
238 }
239 }
240
241 pub fn failure(operation: impl Into<String>, message: impl Into<String>) -> Self {
242 Self {
243 kind: HostErrorKind::HostFailure,
244 operation: operation.into(),
245 message: message.into(),
246 }
247 }
248}
249
250impl fmt::Display for HostError {
251 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
252 write!(f, "{}: {}", self.operation, self.message)
253 }
254}
255
256impl Error for HostError {}
257
258pub trait Host {
260 fn dispatch(&mut self, request: HostRequest) -> Result<HostResponse, HostError> {
263 Err(HostError::unsupported(request.operation_name()))
264 }
265
266 fn capabilities(&self) -> HostCapabilities { HostCapabilities::default() }
267
268 fn dispatch_with_context(
269 &mut self,
270 _context: &mut HostContext,
271 request: HostRequest,
272 ) -> Result<HostResponse, HostError> {
273 self.dispatch(request)
274 }
275}
276
277#[derive(Debug, Default, Clone, Copy)]
279pub struct Hostless;
280
281impl Host for Hostless {
282 fn dispatch(&mut self, request: HostRequest) -> Result<HostResponse, HostError> {
283 Err(HostError::unsupported(request.operation_name()))
284 }
285}