1use crate::Dest;
12use actr_protocol::prost::Message as ProstMessage;
13use actr_protocol::{ActrError, ActrId, ActrType, DataChunk, PayloadType};
14
15pub mod code {
17 pub const SUCCESS: i32 = 0;
19 pub const GENERIC_ERROR: i32 = -1;
21 pub const INIT_FAILED: i32 = -2;
23 pub const HANDLE_FAILED: i32 = -3;
25 pub const ALLOC_FAILED: i32 = -4;
27 pub const PROTOCOL_ERROR: i32 = -5;
29 pub const BUFFER_TOO_SMALL: i32 = -6;
31 pub const UNSUPPORTED_OP: i32 = -7;
33}
34
35pub mod version {
37 pub const V1: u32 = 1;
39}
40
41pub mod op {
43 pub const HOST_CALL: u32 = 1;
44 pub const HOST_TELL: u32 = 2;
45 pub const HOST_CALL_RAW: u32 = 3;
46 pub const HOST_DISCOVER: u32 = 4;
47 pub const HOST_REGISTER_STREAM: u32 = 5;
48 pub const HOST_UNREGISTER_STREAM: u32 = 6;
49 pub const HOST_SEND_DATA_CHUNK: u32 = 7;
50 pub const GUEST_HANDLE: u32 = 101;
51 pub const GUEST_DATA_CHUNK: u32 = 102;
52 pub const GUEST_LIFECYCLE: u32 = 103;
53 pub const GUEST_HOOK: u32 = 104;
54}
55
56pub mod lifecycle_hook {
58 pub const ON_START: u32 = 1;
59 pub const ON_READY: u32 = 2;
60 pub const ON_STOP: u32 = 3;
61}
62
63pub mod webrtc_peer_status {
68 pub const IDLE: u32 = 0;
69 pub const CONNECTING: u32 = 1;
70 pub const CONNECTED: u32 = 2;
71 pub const RECOVERING: u32 = 3;
72}
73
74pub mod runtime_hook {
76 pub const ON_SIGNALING_CONNECTING: u32 = 1;
77 pub const ON_SIGNALING_CONNECTED: u32 = 2;
78 pub const ON_SIGNALING_DISCONNECTED: u32 = 3;
79 pub const ON_WEBSOCKET_CONNECTING: u32 = 4;
80 pub const ON_WEBSOCKET_CONNECTED: u32 = 5;
81 pub const ON_WEBSOCKET_DISCONNECTED: u32 = 6;
82 pub const ON_WEBRTC_CONNECTING: u32 = 7;
83 pub const ON_WEBRTC_CONNECTED: u32 = 8;
84 pub const ON_WEBRTC_DISCONNECTED: u32 = 9;
85 pub const ON_CREDENTIAL_RENEWED: u32 = 10;
86 pub const ON_CREDENTIAL_EXPIRING: u32 = 11;
87 pub const ON_MAILBOX_BACKPRESSURE: u32 = 12;
88}
89
90#[derive(Clone, PartialEq, prost::Message)]
92pub struct InitPayloadV1 {
93 #[prost(uint32, tag = "1")]
94 pub version: u32,
95 #[prost(string, tag = "2")]
96 pub actr_type: String,
97 #[prost(bytes = "vec", tag = "3")]
98 pub credential: Vec<u8>,
99 #[prost(bytes = "vec", tag = "4")]
100 pub actor_id: Vec<u8>,
101 #[prost(uint32, tag = "5")]
102 pub realm_id: u32,
103}
104
105#[derive(Clone, PartialEq, prost::Message)]
113pub struct AbiFrame {
114 #[prost(uint32, tag = "1")]
115 pub abi_version: u32,
116 #[prost(uint32, tag = "2")]
117 pub op: u32,
118 #[prost(bytes = "vec", tag = "3")]
119 pub payload: Vec<u8>,
120}
121
122#[derive(Clone, PartialEq, prost::Message)]
128pub struct AbiReply {
129 #[prost(uint32, tag = "1")]
130 pub abi_version: u32,
131 #[prost(int32, tag = "2")]
132 pub status: i32,
133 #[prost(bytes = "vec", tag = "3")]
134 pub payload: Vec<u8>,
135}
136
137#[derive(Clone, PartialEq, prost::Message)]
139pub struct InvocationContextV1 {
140 #[prost(message, required, tag = "1")]
141 pub self_id: ActrId,
142 #[prost(message, optional, tag = "2")]
143 pub caller_id: Option<ActrId>,
144 #[prost(string, tag = "3")]
145 pub request_id: String,
146}
147
148#[derive(Clone, PartialEq, prost::Message)]
150pub struct GuestHandleV1 {
151 #[prost(message, required, tag = "1")]
152 pub ctx: InvocationContextV1,
153 #[prost(bytes = "vec", tag = "2")]
154 pub rpc_envelope: Vec<u8>,
155 #[prost(uint64, tag = "3")]
156 pub bridge_token: u64,
157}
158
159#[derive(Clone, PartialEq, prost::Message)]
161pub struct GuestDataChunkV1 {
162 #[prost(message, required, tag = "1")]
163 pub chunk: DataChunk,
164 #[prost(message, required, tag = "2")]
165 pub sender: ActrId,
166 #[prost(uint64, tag = "3")]
167 pub bridge_token: u64,
168}
169
170#[derive(Clone, PartialEq, prost::Message)]
172pub struct GuestLifecycleV1 {
173 #[prost(message, required, tag = "1")]
174 pub ctx: InvocationContextV1,
175 #[prost(uint32, tag = "2")]
176 pub hook: u32,
177 #[prost(uint64, tag = "3")]
178 pub bridge_token: u64,
179}
180
181#[derive(Clone, PartialEq, prost::Message)]
183pub struct TimestampV1 {
184 #[prost(uint64, tag = "1")]
185 pub seconds: u64,
186 #[prost(uint32, tag = "2")]
187 pub nanoseconds: u32,
188}
189
190#[derive(Clone, PartialEq, prost::Message)]
192pub struct PeerEventV1 {
193 #[prost(message, required, tag = "1")]
194 pub peer: ActrId,
195 #[prost(bool, optional, tag = "2")]
196 pub relayed: Option<bool>,
197 #[prost(uint32, optional, tag = "3")]
200 pub status: Option<u32>,
201}
202
203#[derive(Clone, PartialEq, prost::Message)]
205pub struct CredentialEventV1 {
206 #[prost(message, required, tag = "1")]
207 pub new_expiry: TimestampV1,
208}
209
210#[derive(Clone, PartialEq, prost::Message)]
212pub struct BackpressureEventV1 {
213 #[prost(uint64, tag = "1")]
214 pub queue_len: u64,
215 #[prost(uint64, tag = "2")]
216 pub threshold: u64,
217}
218
219#[derive(Clone, PartialEq, prost::Message)]
221pub struct GuestHookV1 {
222 #[prost(message, required, tag = "1")]
223 pub ctx: InvocationContextV1,
224 #[prost(uint32, tag = "2")]
225 pub hook: u32,
226 #[prost(message, optional, tag = "3")]
227 pub peer: Option<PeerEventV1>,
228 #[prost(message, optional, tag = "4")]
229 pub credential: Option<CredentialEventV1>,
230 #[prost(message, optional, tag = "5")]
231 pub backpressure: Option<BackpressureEventV1>,
232 #[prost(uint64, tag = "6")]
233 pub bridge_token: u64,
234}
235
236#[derive(Clone, PartialEq, prost::Message)]
238pub struct DestV1 {
239 #[prost(oneof = "DestKind", tags = "1, 2, 3")]
240 pub kind: Option<DestKind>,
241}
242
243#[derive(Clone, PartialEq, prost::Oneof)]
245pub enum DestKind {
246 #[prost(bool, tag = "1")]
247 Host(bool),
248 #[prost(bool, tag = "2")]
249 Workload(bool),
250 #[prost(message, tag = "3")]
251 Peer(ActrId),
252}
253
254impl DestV1 {
255 pub fn host() -> Self {
257 Self {
258 kind: Some(DestKind::Host(true)),
259 }
260 }
261
262 pub fn workload() -> Self {
264 Self {
265 kind: Some(DestKind::Workload(true)),
266 }
267 }
268
269 pub fn peer(id: ActrId) -> Self {
271 Self {
272 kind: Some(DestKind::Peer(id)),
273 }
274 }
275
276 pub fn try_into_dest(self) -> Result<Dest, ActrError> {
278 match self.kind {
279 Some(DestKind::Host(_)) => Ok(Dest::Host),
280 Some(DestKind::Workload(_)) => Ok(Dest::Workload),
281 Some(DestKind::Peer(id)) => Ok(Dest::Peer(id)),
282 None => Err(ActrError::DecodeFailure(
283 "destination kind is missing".into(),
284 )),
285 }
286 }
287}
288
289#[derive(Clone, PartialEq, prost::Message)]
291pub struct HostCallV1 {
292 #[prost(string, tag = "1")]
293 pub route_key: String,
294 #[prost(message, required, tag = "2")]
295 pub dest: DestV1,
296 #[prost(bytes = "vec", tag = "3")]
297 pub payload: Vec<u8>,
298}
299
300#[derive(Clone, PartialEq, prost::Message)]
302pub struct HostTellV1 {
303 #[prost(string, tag = "1")]
304 pub route_key: String,
305 #[prost(message, required, tag = "2")]
306 pub dest: DestV1,
307 #[prost(bytes = "vec", tag = "3")]
308 pub payload: Vec<u8>,
309}
310
311#[derive(Clone, PartialEq, prost::Message)]
313pub struct HostCallRawV1 {
314 #[prost(string, tag = "1")]
315 pub route_key: String,
316 #[prost(message, required, tag = "2")]
317 pub target: ActrId,
318 #[prost(bytes = "vec", tag = "3")]
319 pub payload: Vec<u8>,
320}
321
322#[derive(Clone, PartialEq, prost::Message)]
324pub struct HostDiscoverV1 {
325 #[prost(message, required, tag = "1")]
326 pub target_type: ActrType,
327}
328
329#[derive(Clone, PartialEq, prost::Message)]
331pub struct HostRegisterStreamV1 {
332 #[prost(string, tag = "1")]
333 pub stream_id: String,
334}
335
336#[derive(Clone, PartialEq, prost::Message)]
338pub struct HostUnregisterStreamV1 {
339 #[prost(string, tag = "1")]
340 pub stream_id: String,
341}
342
343#[derive(Clone, PartialEq, prost::Message)]
345pub struct HostSendDataChunkV1 {
346 #[prost(message, required, tag = "1")]
347 pub dest: DestV1,
348 #[prost(message, required, tag = "2")]
349 pub chunk: DataChunk,
350 #[prost(enumeration = "PayloadType", tag = "3")]
351 pub payload_type: i32,
352}
353
354pub trait AbiPayload: ProstMessage + Default + Sized {
356 const ABI_VERSION: u32;
357 const OP: u32;
358
359 fn to_frame(&self) -> Result<AbiFrame, i32> {
360 let mut payload = Vec::new();
361 self.encode(&mut payload)
362 .map_err(|_| code::PROTOCOL_ERROR)?;
363
364 Ok(AbiFrame {
365 abi_version: Self::ABI_VERSION,
366 op: Self::OP,
367 payload,
368 })
369 }
370
371 fn decode_payload(bytes: &[u8]) -> Result<Self, i32> {
372 Self::decode(bytes).map_err(|_| code::PROTOCOL_ERROR)
373 }
374}
375
376impl AbiPayload for HostCallV1 {
377 const ABI_VERSION: u32 = version::V1;
378 const OP: u32 = op::HOST_CALL;
379}
380
381impl AbiPayload for HostTellV1 {
382 const ABI_VERSION: u32 = version::V1;
383 const OP: u32 = op::HOST_TELL;
384}
385
386impl AbiPayload for HostCallRawV1 {
387 const ABI_VERSION: u32 = version::V1;
388 const OP: u32 = op::HOST_CALL_RAW;
389}
390
391impl AbiPayload for HostDiscoverV1 {
392 const ABI_VERSION: u32 = version::V1;
393 const OP: u32 = op::HOST_DISCOVER;
394}
395
396impl AbiPayload for HostRegisterStreamV1 {
397 const ABI_VERSION: u32 = version::V1;
398 const OP: u32 = op::HOST_REGISTER_STREAM;
399}
400
401impl AbiPayload for HostUnregisterStreamV1 {
402 const ABI_VERSION: u32 = version::V1;
403 const OP: u32 = op::HOST_UNREGISTER_STREAM;
404}
405
406impl AbiPayload for HostSendDataChunkV1 {
407 const ABI_VERSION: u32 = version::V1;
408 const OP: u32 = op::HOST_SEND_DATA_CHUNK;
409}
410
411impl AbiPayload for GuestHandleV1 {
412 const ABI_VERSION: u32 = version::V1;
413 const OP: u32 = op::GUEST_HANDLE;
414}
415
416impl AbiPayload for GuestDataChunkV1 {
417 const ABI_VERSION: u32 = version::V1;
418 const OP: u32 = op::GUEST_DATA_CHUNK;
419}
420
421impl AbiPayload for GuestLifecycleV1 {
422 const ABI_VERSION: u32 = version::V1;
423 const OP: u32 = op::GUEST_LIFECYCLE;
424}
425
426impl AbiPayload for GuestHookV1 {
427 const ABI_VERSION: u32 = version::V1;
428 const OP: u32 = op::GUEST_HOOK;
429}
430
431pub fn encode_message<M: ProstMessage>(message: &M) -> Result<Vec<u8>, i32> {
433 let mut out = Vec::new();
434 message.encode(&mut out).map_err(|_| code::PROTOCOL_ERROR)?;
435 Ok(out)
436}
437
438pub fn decode_message<M: ProstMessage + Default>(bytes: &[u8]) -> Result<M, i32> {
440 M::decode(bytes).map_err(|_| code::PROTOCOL_ERROR)
441}
442
443pub fn success_reply(payload: Vec<u8>) -> Result<Vec<u8>, i32> {
445 encode_message(&AbiReply {
446 abi_version: version::V1,
447 status: code::SUCCESS,
448 payload,
449 })
450}
451
452pub fn error_reply(status: i32, message: impl Into<Vec<u8>>) -> Result<Vec<u8>, i32> {
454 encode_message(&AbiReply {
455 abi_version: version::V1,
456 status,
457 payload: message.into(),
458 })
459}
460
461pub fn dest_to_v1(dest: &crate::Dest) -> DestV1 {
467 match dest {
468 crate::Dest::Host => DestV1::host(),
469 crate::Dest::Workload => DestV1::workload(),
470 crate::Dest::Peer(id) => DestV1::peer(id.clone()),
471 }
472}
473
474pub fn dest_v1_to_dest(v1: &DestV1) -> Option<crate::Dest> {
478 v1.clone().try_into_dest().ok()
479}
480
481pub fn abi_error_to_actr(code_val: i32) -> actr_protocol::ActrError {
483 use actr_protocol::ActrError;
484 match code_val {
485 code::GENERIC_ERROR => ActrError::Internal("host returned generic ABI error".into()),
486 code::INIT_FAILED => ActrError::Internal("host initialization failed".into()),
487 code::HANDLE_FAILED => ActrError::Internal("guest handle failed".into()),
488 code::ALLOC_FAILED => ActrError::Internal("memory allocation failed".into()),
489 code::PROTOCOL_ERROR => ActrError::DecodeFailure("ABI payload decode failed".into()),
490 code::BUFFER_TOO_SMALL => {
491 ActrError::Internal("reply buffer too small for host invoke".into())
492 }
493 code::UNSUPPORTED_OP => ActrError::NotImplemented("unsupported ABI operation".into()),
494 other => ActrError::Internal(format!("unexpected ABI status code {other}")),
495 }
496}
497
498pub fn reply_to_actr_error(reply: AbiReply) -> actr_protocol::ActrError {
500 use actr_protocol::ActrError;
501 if reply.payload.is_empty() {
502 return abi_error_to_actr(reply.status);
503 }
504
505 let message = String::from_utf8(reply.payload)
506 .unwrap_or_else(|_| format!("guest returned status {}", reply.status));
507
508 match reply.status {
509 code::PROTOCOL_ERROR => ActrError::DecodeFailure(message),
510 code::UNSUPPORTED_OP => ActrError::NotImplemented(message),
511 _ => ActrError::Internal(message),
512 }
513}