1use crate::Dest;
12use actr_protocol::prost::Message as ProstMessage;
13use actr_protocol::{ActrError, ActrId, ActrType, DataStream, 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_STREAM: u32 = 7;
50 pub const GUEST_HANDLE: u32 = 101;
51 pub const GUEST_DATA_STREAM: 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}
156
157#[derive(Clone, PartialEq, prost::Message)]
159pub struct GuestDataStreamV1 {
160 #[prost(message, required, tag = "1")]
161 pub chunk: DataStream,
162 #[prost(message, required, tag = "2")]
163 pub sender: ActrId,
164}
165
166#[derive(Clone, PartialEq, prost::Message)]
168pub struct GuestLifecycleV1 {
169 #[prost(message, required, tag = "1")]
170 pub ctx: InvocationContextV1,
171 #[prost(uint32, tag = "2")]
172 pub hook: u32,
173}
174
175#[derive(Clone, PartialEq, prost::Message)]
177pub struct TimestampV1 {
178 #[prost(uint64, tag = "1")]
179 pub seconds: u64,
180 #[prost(uint32, tag = "2")]
181 pub nanoseconds: u32,
182}
183
184#[derive(Clone, PartialEq, prost::Message)]
186pub struct PeerEventV1 {
187 #[prost(message, required, tag = "1")]
188 pub peer: ActrId,
189 #[prost(bool, optional, tag = "2")]
190 pub relayed: Option<bool>,
191 #[prost(uint32, optional, tag = "3")]
194 pub status: Option<u32>,
195}
196
197#[derive(Clone, PartialEq, prost::Message)]
199pub struct CredentialEventV1 {
200 #[prost(message, required, tag = "1")]
201 pub new_expiry: TimestampV1,
202}
203
204#[derive(Clone, PartialEq, prost::Message)]
206pub struct BackpressureEventV1 {
207 #[prost(uint64, tag = "1")]
208 pub queue_len: u64,
209 #[prost(uint64, tag = "2")]
210 pub threshold: u64,
211}
212
213#[derive(Clone, PartialEq, prost::Message)]
215pub struct GuestHookV1 {
216 #[prost(message, required, tag = "1")]
217 pub ctx: InvocationContextV1,
218 #[prost(uint32, tag = "2")]
219 pub hook: u32,
220 #[prost(message, optional, tag = "3")]
221 pub peer: Option<PeerEventV1>,
222 #[prost(message, optional, tag = "4")]
223 pub credential: Option<CredentialEventV1>,
224 #[prost(message, optional, tag = "5")]
225 pub backpressure: Option<BackpressureEventV1>,
226}
227
228#[derive(Clone, PartialEq, prost::Message)]
230pub struct DestV1 {
231 #[prost(oneof = "DestKind", tags = "1, 2, 3")]
232 pub kind: Option<DestKind>,
233}
234
235#[derive(Clone, PartialEq, prost::Oneof)]
237pub enum DestKind {
238 #[prost(bool, tag = "1")]
239 Shell(bool),
240 #[prost(bool, tag = "2")]
241 Local(bool),
242 #[prost(message, tag = "3")]
243 Actor(ActrId),
244}
245
246impl DestV1 {
247 pub fn shell() -> Self {
249 Self {
250 kind: Some(DestKind::Shell(true)),
251 }
252 }
253
254 pub fn local() -> Self {
256 Self {
257 kind: Some(DestKind::Local(true)),
258 }
259 }
260
261 pub fn actor(id: ActrId) -> Self {
263 Self {
264 kind: Some(DestKind::Actor(id)),
265 }
266 }
267
268 pub fn try_into_dest(self) -> Result<Dest, ActrError> {
270 match self.kind {
271 Some(DestKind::Shell(_)) => Ok(Dest::Shell),
272 Some(DestKind::Local(_)) => Ok(Dest::Local),
273 Some(DestKind::Actor(id)) => Ok(Dest::Actor(id)),
274 None => Err(ActrError::DecodeFailure(
275 "destination kind is missing".into(),
276 )),
277 }
278 }
279}
280
281#[derive(Clone, PartialEq, prost::Message)]
283pub struct HostCallV1 {
284 #[prost(string, tag = "1")]
285 pub route_key: String,
286 #[prost(message, required, tag = "2")]
287 pub dest: DestV1,
288 #[prost(bytes = "vec", tag = "3")]
289 pub payload: Vec<u8>,
290}
291
292#[derive(Clone, PartialEq, prost::Message)]
294pub struct HostTellV1 {
295 #[prost(string, tag = "1")]
296 pub route_key: String,
297 #[prost(message, required, tag = "2")]
298 pub dest: DestV1,
299 #[prost(bytes = "vec", tag = "3")]
300 pub payload: Vec<u8>,
301}
302
303#[derive(Clone, PartialEq, prost::Message)]
305pub struct HostCallRawV1 {
306 #[prost(string, tag = "1")]
307 pub route_key: String,
308 #[prost(message, required, tag = "2")]
309 pub target: ActrId,
310 #[prost(bytes = "vec", tag = "3")]
311 pub payload: Vec<u8>,
312}
313
314#[derive(Clone, PartialEq, prost::Message)]
316pub struct HostDiscoverV1 {
317 #[prost(message, required, tag = "1")]
318 pub target_type: ActrType,
319}
320
321#[derive(Clone, PartialEq, prost::Message)]
323pub struct HostRegisterStreamV1 {
324 #[prost(string, tag = "1")]
325 pub stream_id: String,
326}
327
328#[derive(Clone, PartialEq, prost::Message)]
330pub struct HostUnregisterStreamV1 {
331 #[prost(string, tag = "1")]
332 pub stream_id: String,
333}
334
335#[derive(Clone, PartialEq, prost::Message)]
337pub struct HostSendDataStreamV1 {
338 #[prost(message, required, tag = "1")]
339 pub dest: DestV1,
340 #[prost(message, required, tag = "2")]
341 pub chunk: DataStream,
342 #[prost(enumeration = "PayloadType", tag = "3")]
343 pub payload_type: i32,
344}
345
346pub trait AbiPayload: ProstMessage + Default + Sized {
348 const ABI_VERSION: u32;
349 const OP: u32;
350
351 fn to_frame(&self) -> Result<AbiFrame, i32> {
352 let mut payload = Vec::new();
353 self.encode(&mut payload)
354 .map_err(|_| code::PROTOCOL_ERROR)?;
355
356 Ok(AbiFrame {
357 abi_version: Self::ABI_VERSION,
358 op: Self::OP,
359 payload,
360 })
361 }
362
363 fn decode_payload(bytes: &[u8]) -> Result<Self, i32> {
364 Self::decode(bytes).map_err(|_| code::PROTOCOL_ERROR)
365 }
366}
367
368impl AbiPayload for HostCallV1 {
369 const ABI_VERSION: u32 = version::V1;
370 const OP: u32 = op::HOST_CALL;
371}
372
373impl AbiPayload for HostTellV1 {
374 const ABI_VERSION: u32 = version::V1;
375 const OP: u32 = op::HOST_TELL;
376}
377
378impl AbiPayload for HostCallRawV1 {
379 const ABI_VERSION: u32 = version::V1;
380 const OP: u32 = op::HOST_CALL_RAW;
381}
382
383impl AbiPayload for HostDiscoverV1 {
384 const ABI_VERSION: u32 = version::V1;
385 const OP: u32 = op::HOST_DISCOVER;
386}
387
388impl AbiPayload for HostRegisterStreamV1 {
389 const ABI_VERSION: u32 = version::V1;
390 const OP: u32 = op::HOST_REGISTER_STREAM;
391}
392
393impl AbiPayload for HostUnregisterStreamV1 {
394 const ABI_VERSION: u32 = version::V1;
395 const OP: u32 = op::HOST_UNREGISTER_STREAM;
396}
397
398impl AbiPayload for HostSendDataStreamV1 {
399 const ABI_VERSION: u32 = version::V1;
400 const OP: u32 = op::HOST_SEND_DATA_STREAM;
401}
402
403impl AbiPayload for GuestHandleV1 {
404 const ABI_VERSION: u32 = version::V1;
405 const OP: u32 = op::GUEST_HANDLE;
406}
407
408impl AbiPayload for GuestDataStreamV1 {
409 const ABI_VERSION: u32 = version::V1;
410 const OP: u32 = op::GUEST_DATA_STREAM;
411}
412
413impl AbiPayload for GuestLifecycleV1 {
414 const ABI_VERSION: u32 = version::V1;
415 const OP: u32 = op::GUEST_LIFECYCLE;
416}
417
418impl AbiPayload for GuestHookV1 {
419 const ABI_VERSION: u32 = version::V1;
420 const OP: u32 = op::GUEST_HOOK;
421}
422
423pub fn encode_message<M: ProstMessage>(message: &M) -> Result<Vec<u8>, i32> {
425 let mut out = Vec::new();
426 message.encode(&mut out).map_err(|_| code::PROTOCOL_ERROR)?;
427 Ok(out)
428}
429
430pub fn decode_message<M: ProstMessage + Default>(bytes: &[u8]) -> Result<M, i32> {
432 M::decode(bytes).map_err(|_| code::PROTOCOL_ERROR)
433}
434
435pub fn success_reply(payload: Vec<u8>) -> Result<Vec<u8>, i32> {
437 encode_message(&AbiReply {
438 abi_version: version::V1,
439 status: code::SUCCESS,
440 payload,
441 })
442}
443
444pub fn error_reply(status: i32, message: impl Into<Vec<u8>>) -> Result<Vec<u8>, i32> {
446 encode_message(&AbiReply {
447 abi_version: version::V1,
448 status,
449 payload: message.into(),
450 })
451}
452
453pub fn dest_to_v1(dest: &crate::Dest) -> DestV1 {
459 match dest {
460 crate::Dest::Shell => DestV1::shell(),
461 crate::Dest::Local => DestV1::local(),
462 crate::Dest::Actor(id) => DestV1::actor(id.clone()),
463 }
464}
465
466pub fn dest_v1_to_dest(v1: &DestV1) -> Option<crate::Dest> {
470 v1.clone().try_into_dest().ok()
471}
472
473pub fn abi_error_to_actr(code_val: i32) -> actr_protocol::ActrError {
475 use actr_protocol::ActrError;
476 match code_val {
477 code::GENERIC_ERROR => ActrError::Internal("host returned generic ABI error".into()),
478 code::INIT_FAILED => ActrError::Internal("host initialization failed".into()),
479 code::HANDLE_FAILED => ActrError::Internal("guest handle failed".into()),
480 code::ALLOC_FAILED => ActrError::Internal("memory allocation failed".into()),
481 code::PROTOCOL_ERROR => ActrError::DecodeFailure("ABI payload decode failed".into()),
482 code::BUFFER_TOO_SMALL => {
483 ActrError::Internal("reply buffer too small for host invoke".into())
484 }
485 code::UNSUPPORTED_OP => ActrError::NotImplemented("unsupported ABI operation".into()),
486 other => ActrError::Internal(format!("unexpected ABI status code {other}")),
487 }
488}
489
490pub fn reply_to_actr_error(reply: AbiReply) -> actr_protocol::ActrError {
492 use actr_protocol::ActrError;
493 if reply.payload.is_empty() {
494 return abi_error_to_actr(reply.status);
495 }
496
497 let message = String::from_utf8(reply.payload)
498 .unwrap_or_else(|_| format!("guest returned status {}", reply.status));
499
500 match reply.status {
501 code::PROTOCOL_ERROR => ActrError::DecodeFailure(message),
502 code::UNSUPPORTED_OP => ActrError::NotImplemented(message),
503 _ => ActrError::Internal(message),
504 }
505}