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 runtime_hook {
65 pub const ON_SIGNALING_CONNECTING: u32 = 1;
66 pub const ON_SIGNALING_CONNECTED: u32 = 2;
67 pub const ON_SIGNALING_DISCONNECTED: u32 = 3;
68 pub const ON_WEBSOCKET_CONNECTING: u32 = 4;
69 pub const ON_WEBSOCKET_CONNECTED: u32 = 5;
70 pub const ON_WEBSOCKET_DISCONNECTED: u32 = 6;
71 pub const ON_WEBRTC_CONNECTING: u32 = 7;
72 pub const ON_WEBRTC_CONNECTED: u32 = 8;
73 pub const ON_WEBRTC_DISCONNECTED: u32 = 9;
74 pub const ON_CREDENTIAL_RENEWED: u32 = 10;
75 pub const ON_CREDENTIAL_EXPIRING: u32 = 11;
76 pub const ON_MAILBOX_BACKPRESSURE: u32 = 12;
77}
78
79#[derive(Clone, PartialEq, prost::Message)]
81pub struct InitPayloadV1 {
82 #[prost(uint32, tag = "1")]
83 pub version: u32,
84 #[prost(string, tag = "2")]
85 pub actr_type: String,
86 #[prost(bytes = "vec", tag = "3")]
87 pub credential: Vec<u8>,
88 #[prost(bytes = "vec", tag = "4")]
89 pub actor_id: Vec<u8>,
90 #[prost(uint32, tag = "5")]
91 pub realm_id: u32,
92}
93
94#[derive(Clone, PartialEq, prost::Message)]
102pub struct AbiFrame {
103 #[prost(uint32, tag = "1")]
104 pub abi_version: u32,
105 #[prost(uint32, tag = "2")]
106 pub op: u32,
107 #[prost(bytes = "vec", tag = "3")]
108 pub payload: Vec<u8>,
109}
110
111#[derive(Clone, PartialEq, prost::Message)]
117pub struct AbiReply {
118 #[prost(uint32, tag = "1")]
119 pub abi_version: u32,
120 #[prost(int32, tag = "2")]
121 pub status: i32,
122 #[prost(bytes = "vec", tag = "3")]
123 pub payload: Vec<u8>,
124}
125
126#[derive(Clone, PartialEq, prost::Message)]
128pub struct InvocationContextV1 {
129 #[prost(message, required, tag = "1")]
130 pub self_id: ActrId,
131 #[prost(message, optional, tag = "2")]
132 pub caller_id: Option<ActrId>,
133 #[prost(string, tag = "3")]
134 pub request_id: String,
135}
136
137#[derive(Clone, PartialEq, prost::Message)]
139pub struct GuestHandleV1 {
140 #[prost(message, required, tag = "1")]
141 pub ctx: InvocationContextV1,
142 #[prost(bytes = "vec", tag = "2")]
143 pub rpc_envelope: Vec<u8>,
144}
145
146#[derive(Clone, PartialEq, prost::Message)]
148pub struct GuestDataStreamV1 {
149 #[prost(message, required, tag = "1")]
150 pub chunk: DataStream,
151 #[prost(message, required, tag = "2")]
152 pub sender: ActrId,
153}
154
155#[derive(Clone, PartialEq, prost::Message)]
157pub struct GuestLifecycleV1 {
158 #[prost(message, required, tag = "1")]
159 pub ctx: InvocationContextV1,
160 #[prost(uint32, tag = "2")]
161 pub hook: u32,
162}
163
164#[derive(Clone, PartialEq, prost::Message)]
166pub struct TimestampV1 {
167 #[prost(uint64, tag = "1")]
168 pub seconds: u64,
169 #[prost(uint32, tag = "2")]
170 pub nanoseconds: u32,
171}
172
173#[derive(Clone, PartialEq, prost::Message)]
175pub struct PeerEventV1 {
176 #[prost(message, required, tag = "1")]
177 pub peer: ActrId,
178 #[prost(bool, optional, tag = "2")]
179 pub relayed: Option<bool>,
180}
181
182#[derive(Clone, PartialEq, prost::Message)]
184pub struct CredentialEventV1 {
185 #[prost(message, required, tag = "1")]
186 pub new_expiry: TimestampV1,
187}
188
189#[derive(Clone, PartialEq, prost::Message)]
191pub struct BackpressureEventV1 {
192 #[prost(uint64, tag = "1")]
193 pub queue_len: u64,
194 #[prost(uint64, tag = "2")]
195 pub threshold: u64,
196}
197
198#[derive(Clone, PartialEq, prost::Message)]
200pub struct GuestHookV1 {
201 #[prost(message, required, tag = "1")]
202 pub ctx: InvocationContextV1,
203 #[prost(uint32, tag = "2")]
204 pub hook: u32,
205 #[prost(message, optional, tag = "3")]
206 pub peer: Option<PeerEventV1>,
207 #[prost(message, optional, tag = "4")]
208 pub credential: Option<CredentialEventV1>,
209 #[prost(message, optional, tag = "5")]
210 pub backpressure: Option<BackpressureEventV1>,
211}
212
213#[derive(Clone, PartialEq, prost::Message)]
215pub struct DestV1 {
216 #[prost(oneof = "DestKind", tags = "1, 2, 3")]
217 pub kind: Option<DestKind>,
218}
219
220#[derive(Clone, PartialEq, prost::Oneof)]
222pub enum DestKind {
223 #[prost(bool, tag = "1")]
224 Shell(bool),
225 #[prost(bool, tag = "2")]
226 Local(bool),
227 #[prost(message, tag = "3")]
228 Actor(ActrId),
229}
230
231impl DestV1 {
232 pub fn shell() -> Self {
234 Self {
235 kind: Some(DestKind::Shell(true)),
236 }
237 }
238
239 pub fn local() -> Self {
241 Self {
242 kind: Some(DestKind::Local(true)),
243 }
244 }
245
246 pub fn actor(id: ActrId) -> Self {
248 Self {
249 kind: Some(DestKind::Actor(id)),
250 }
251 }
252
253 pub fn try_into_dest(self) -> Result<Dest, ActrError> {
255 match self.kind {
256 Some(DestKind::Shell(_)) => Ok(Dest::Shell),
257 Some(DestKind::Local(_)) => Ok(Dest::Local),
258 Some(DestKind::Actor(id)) => Ok(Dest::Actor(id)),
259 None => Err(ActrError::DecodeFailure(
260 "destination kind is missing".into(),
261 )),
262 }
263 }
264}
265
266#[derive(Clone, PartialEq, prost::Message)]
268pub struct HostCallV1 {
269 #[prost(string, tag = "1")]
270 pub route_key: String,
271 #[prost(message, required, tag = "2")]
272 pub dest: DestV1,
273 #[prost(bytes = "vec", tag = "3")]
274 pub payload: Vec<u8>,
275}
276
277#[derive(Clone, PartialEq, prost::Message)]
279pub struct HostTellV1 {
280 #[prost(string, tag = "1")]
281 pub route_key: String,
282 #[prost(message, required, tag = "2")]
283 pub dest: DestV1,
284 #[prost(bytes = "vec", tag = "3")]
285 pub payload: Vec<u8>,
286}
287
288#[derive(Clone, PartialEq, prost::Message)]
290pub struct HostCallRawV1 {
291 #[prost(string, tag = "1")]
292 pub route_key: String,
293 #[prost(message, required, tag = "2")]
294 pub target: ActrId,
295 #[prost(bytes = "vec", tag = "3")]
296 pub payload: Vec<u8>,
297}
298
299#[derive(Clone, PartialEq, prost::Message)]
301pub struct HostDiscoverV1 {
302 #[prost(message, required, tag = "1")]
303 pub target_type: ActrType,
304}
305
306#[derive(Clone, PartialEq, prost::Message)]
308pub struct HostRegisterStreamV1 {
309 #[prost(string, tag = "1")]
310 pub stream_id: String,
311}
312
313#[derive(Clone, PartialEq, prost::Message)]
315pub struct HostUnregisterStreamV1 {
316 #[prost(string, tag = "1")]
317 pub stream_id: String,
318}
319
320#[derive(Clone, PartialEq, prost::Message)]
322pub struct HostSendDataStreamV1 {
323 #[prost(message, required, tag = "1")]
324 pub dest: DestV1,
325 #[prost(message, required, tag = "2")]
326 pub chunk: DataStream,
327 #[prost(enumeration = "PayloadType", tag = "3")]
328 pub payload_type: i32,
329}
330
331pub trait AbiPayload: ProstMessage + Default + Sized {
333 const ABI_VERSION: u32;
334 const OP: u32;
335
336 fn to_frame(&self) -> Result<AbiFrame, i32> {
337 let mut payload = Vec::new();
338 self.encode(&mut payload)
339 .map_err(|_| code::PROTOCOL_ERROR)?;
340
341 Ok(AbiFrame {
342 abi_version: Self::ABI_VERSION,
343 op: Self::OP,
344 payload,
345 })
346 }
347
348 fn decode_payload(bytes: &[u8]) -> Result<Self, i32> {
349 Self::decode(bytes).map_err(|_| code::PROTOCOL_ERROR)
350 }
351}
352
353impl AbiPayload for HostCallV1 {
354 const ABI_VERSION: u32 = version::V1;
355 const OP: u32 = op::HOST_CALL;
356}
357
358impl AbiPayload for HostTellV1 {
359 const ABI_VERSION: u32 = version::V1;
360 const OP: u32 = op::HOST_TELL;
361}
362
363impl AbiPayload for HostCallRawV1 {
364 const ABI_VERSION: u32 = version::V1;
365 const OP: u32 = op::HOST_CALL_RAW;
366}
367
368impl AbiPayload for HostDiscoverV1 {
369 const ABI_VERSION: u32 = version::V1;
370 const OP: u32 = op::HOST_DISCOVER;
371}
372
373impl AbiPayload for HostRegisterStreamV1 {
374 const ABI_VERSION: u32 = version::V1;
375 const OP: u32 = op::HOST_REGISTER_STREAM;
376}
377
378impl AbiPayload for HostUnregisterStreamV1 {
379 const ABI_VERSION: u32 = version::V1;
380 const OP: u32 = op::HOST_UNREGISTER_STREAM;
381}
382
383impl AbiPayload for HostSendDataStreamV1 {
384 const ABI_VERSION: u32 = version::V1;
385 const OP: u32 = op::HOST_SEND_DATA_STREAM;
386}
387
388impl AbiPayload for GuestHandleV1 {
389 const ABI_VERSION: u32 = version::V1;
390 const OP: u32 = op::GUEST_HANDLE;
391}
392
393impl AbiPayload for GuestDataStreamV1 {
394 const ABI_VERSION: u32 = version::V1;
395 const OP: u32 = op::GUEST_DATA_STREAM;
396}
397
398impl AbiPayload for GuestLifecycleV1 {
399 const ABI_VERSION: u32 = version::V1;
400 const OP: u32 = op::GUEST_LIFECYCLE;
401}
402
403impl AbiPayload for GuestHookV1 {
404 const ABI_VERSION: u32 = version::V1;
405 const OP: u32 = op::GUEST_HOOK;
406}
407
408pub fn encode_message<M: ProstMessage>(message: &M) -> Result<Vec<u8>, i32> {
410 let mut out = Vec::new();
411 message.encode(&mut out).map_err(|_| code::PROTOCOL_ERROR)?;
412 Ok(out)
413}
414
415pub fn decode_message<M: ProstMessage + Default>(bytes: &[u8]) -> Result<M, i32> {
417 M::decode(bytes).map_err(|_| code::PROTOCOL_ERROR)
418}
419
420pub fn success_reply(payload: Vec<u8>) -> Result<Vec<u8>, i32> {
422 encode_message(&AbiReply {
423 abi_version: version::V1,
424 status: code::SUCCESS,
425 payload,
426 })
427}
428
429pub fn error_reply(status: i32, message: impl Into<Vec<u8>>) -> Result<Vec<u8>, i32> {
431 encode_message(&AbiReply {
432 abi_version: version::V1,
433 status,
434 payload: message.into(),
435 })
436}
437
438pub fn dest_to_v1(dest: &crate::Dest) -> DestV1 {
444 match dest {
445 crate::Dest::Shell => DestV1::shell(),
446 crate::Dest::Local => DestV1::local(),
447 crate::Dest::Actor(id) => DestV1::actor(id.clone()),
448 }
449}
450
451pub fn dest_v1_to_dest(v1: &DestV1) -> Option<crate::Dest> {
455 v1.clone().try_into_dest().ok()
456}
457
458pub fn abi_error_to_actr(code_val: i32) -> actr_protocol::ActrError {
460 use actr_protocol::ActrError;
461 match code_val {
462 code::GENERIC_ERROR => ActrError::Internal("host returned generic ABI error".into()),
463 code::INIT_FAILED => ActrError::Internal("host initialization failed".into()),
464 code::HANDLE_FAILED => ActrError::Internal("guest handle failed".into()),
465 code::ALLOC_FAILED => ActrError::Internal("memory allocation failed".into()),
466 code::PROTOCOL_ERROR => ActrError::DecodeFailure("ABI payload decode failed".into()),
467 code::BUFFER_TOO_SMALL => {
468 ActrError::Internal("reply buffer too small for host invoke".into())
469 }
470 code::UNSUPPORTED_OP => ActrError::NotImplemented("unsupported ABI operation".into()),
471 other => ActrError::Internal(format!("unexpected ABI status code {other}")),
472 }
473}
474
475pub fn reply_to_actr_error(reply: AbiReply) -> actr_protocol::ActrError {
477 use actr_protocol::ActrError;
478 if reply.payload.is_empty() {
479 return abi_error_to_actr(reply.status);
480 }
481
482 let message = String::from_utf8(reply.payload)
483 .unwrap_or_else(|_| format!("guest returned status {}", reply.status));
484
485 match reply.status {
486 code::PROTOCOL_ERROR => ActrError::DecodeFailure(message),
487 code::UNSUPPORTED_OP => ActrError::NotImplemented(message),
488 _ => ActrError::Internal(message),
489 }
490}