1pub mod dynclib_abi;
36pub mod vtable;
37
38pub fn peer_status_from_v1(status: u32) -> Option<crate::WebRtcPeerStatus> {
42 use crate::guest::dynclib_abi::webrtc_peer_status as st;
43 match status {
44 st::IDLE => Some(crate::WebRtcPeerStatus::Idle),
45 st::CONNECTING => Some(crate::WebRtcPeerStatus::Connecting),
46 st::CONNECTED => Some(crate::WebRtcPeerStatus::Connected),
47 st::RECOVERING => Some(crate::WebRtcPeerStatus::Recovering),
48 other => {
49 tracing::warn!(
50 discriminant = other,
51 "unknown dynclib WebRtcPeerStatus discriminant; dropping peer status"
52 );
53 None
54 }
55 }
56}
57
58#[cfg(all(target_arch = "wasm32", not(feature = "web")))]
63pub mod wasm;
64
65#[cfg(feature = "cdylib")]
66pub mod dynclib;
67
68#[cfg(all(target_arch = "wasm32", not(feature = "web")))]
72#[doc(hidden)]
73pub mod __wasm_macro_support {
74 pub use super::wasm::adapter::{
80 WorkloadCell, run_dispatch, run_on_credential_expiring, run_on_credential_renewed,
81 run_on_data_chunk, run_on_error, run_on_mailbox_backpressure, run_on_ready,
82 run_on_signaling_connected, run_on_signaling_connecting, run_on_signaling_disconnected,
83 run_on_start, run_on_stop, run_on_webrtc_connected, run_on_webrtc_connecting,
84 run_on_webrtc_disconnected, run_on_websocket_connected, run_on_websocket_connecting,
85 run_on_websocket_disconnected,
86 };
87 pub use super::wasm::generated::actr::workload::types::{
88 ActrError as WitActrError, ActrId as WitActrId, BackpressureEvent as WitBackpressureEvent,
89 CredentialEvent as WitCredentialEvent, DataChunk as WitDataChunk,
90 ErrorEvent as WitErrorEvent, InvocationCtx as WitInvocationCtx, PeerEvent as WitPeerEvent,
91 RpcEnvelope as WitRpcEnvelope,
92 };
93 pub use super::wasm::generated::exports::actr::workload::workload::Guest;
94}
95
96#[macro_export]
137macro_rules! entry {
138 ($workload_type:ty) => {
140 $crate::entry!($workload_type, <$workload_type as ::core::default::Default>::default());
141 };
142
143 ($workload_type:ty, $init_expr:expr) => {
145 #[cfg(all(target_arch = "wasm32", not(feature = "web")))]
156 const _: () = {
157 static __ACTR_WORKLOAD: $crate::guest::__wasm_macro_support::WorkloadCell<$workload_type> =
160 $crate::guest::__wasm_macro_support::WorkloadCell::new();
161
162 fn __actr_workload() -> &'static $workload_type {
163 __ACTR_WORKLOAD.get_or_init(|| -> $workload_type { $init_expr })
164 }
165
166 struct __ActrEntryAdapter;
167
168 impl $crate::guest::__wasm_macro_support::Guest for __ActrEntryAdapter {
169 async fn dispatch(
170 envelope: $crate::guest::__wasm_macro_support::WitRpcEnvelope,
171 ctx: $crate::guest::__wasm_macro_support::WitInvocationCtx,
172 ) -> ::core::result::Result<
173 ::std::vec::Vec<u8>,
174 $crate::guest::__wasm_macro_support::WitActrError,
175 > {
176 $crate::guest::__wasm_macro_support::run_dispatch(
177 __actr_workload(),
178 envelope,
179 ctx,
180 ).await
181 }
182
183 async fn on_start(
184 ctx: $crate::guest::__wasm_macro_support::WitInvocationCtx,
185 ) -> ::core::result::Result<
186 (),
187 $crate::guest::__wasm_macro_support::WitActrError,
188 > {
189 $crate::guest::__wasm_macro_support::run_on_start(__actr_workload(), ctx).await
190 }
191
192 async fn on_ready(
193 ctx: $crate::guest::__wasm_macro_support::WitInvocationCtx,
194 ) -> ::core::result::Result<
195 (),
196 $crate::guest::__wasm_macro_support::WitActrError,
197 > {
198 $crate::guest::__wasm_macro_support::run_on_ready(__actr_workload(), ctx).await
199 }
200
201 async fn on_stop(
202 ctx: $crate::guest::__wasm_macro_support::WitInvocationCtx,
203 ) -> ::core::result::Result<
204 (),
205 $crate::guest::__wasm_macro_support::WitActrError,
206 > {
207 $crate::guest::__wasm_macro_support::run_on_stop(__actr_workload(), ctx).await
208 }
209
210 async fn on_error(
211 event: $crate::guest::__wasm_macro_support::WitErrorEvent,
212 ctx: $crate::guest::__wasm_macro_support::WitInvocationCtx,
213 ) -> ::core::result::Result<
214 (),
215 $crate::guest::__wasm_macro_support::WitActrError,
216 > {
217 $crate::guest::__wasm_macro_support::run_on_error(__actr_workload(), event, ctx).await
218 }
219
220 async fn on_signaling_connecting(
221 ctx: $crate::guest::__wasm_macro_support::WitInvocationCtx,
222 ) {
223 $crate::guest::__wasm_macro_support::run_on_signaling_connecting(
224 __actr_workload(),
225 ctx,
226 ).await
227 }
228
229 async fn on_signaling_connected(
230 ctx: $crate::guest::__wasm_macro_support::WitInvocationCtx,
231 ) {
232 $crate::guest::__wasm_macro_support::run_on_signaling_connected(
233 __actr_workload(),
234 ctx,
235 ).await
236 }
237
238 async fn on_signaling_disconnected(
239 ctx: $crate::guest::__wasm_macro_support::WitInvocationCtx,
240 ) {
241 $crate::guest::__wasm_macro_support::run_on_signaling_disconnected(
242 __actr_workload(),
243 ctx,
244 ).await
245 }
246
247 async fn on_websocket_connecting(
248 event: $crate::guest::__wasm_macro_support::WitPeerEvent,
249 ctx: $crate::guest::__wasm_macro_support::WitInvocationCtx,
250 ) {
251 $crate::guest::__wasm_macro_support::run_on_websocket_connecting(
252 __actr_workload(),
253 event,
254 ctx,
255 ).await
256 }
257
258 async fn on_websocket_connected(
259 event: $crate::guest::__wasm_macro_support::WitPeerEvent,
260 ctx: $crate::guest::__wasm_macro_support::WitInvocationCtx,
261 ) {
262 $crate::guest::__wasm_macro_support::run_on_websocket_connected(
263 __actr_workload(),
264 event,
265 ctx,
266 ).await
267 }
268
269 async fn on_websocket_disconnected(
270 event: $crate::guest::__wasm_macro_support::WitPeerEvent,
271 ctx: $crate::guest::__wasm_macro_support::WitInvocationCtx,
272 ) {
273 $crate::guest::__wasm_macro_support::run_on_websocket_disconnected(
274 __actr_workload(),
275 event,
276 ctx,
277 ).await
278 }
279
280 async fn on_webrtc_connecting(
281 event: $crate::guest::__wasm_macro_support::WitPeerEvent,
282 ctx: $crate::guest::__wasm_macro_support::WitInvocationCtx,
283 ) {
284 $crate::guest::__wasm_macro_support::run_on_webrtc_connecting(
285 __actr_workload(),
286 event,
287 ctx,
288 ).await
289 }
290
291 async fn on_webrtc_connected(
292 event: $crate::guest::__wasm_macro_support::WitPeerEvent,
293 ctx: $crate::guest::__wasm_macro_support::WitInvocationCtx,
294 ) {
295 $crate::guest::__wasm_macro_support::run_on_webrtc_connected(
296 __actr_workload(),
297 event,
298 ctx,
299 ).await
300 }
301
302 async fn on_webrtc_disconnected(
303 event: $crate::guest::__wasm_macro_support::WitPeerEvent,
304 ctx: $crate::guest::__wasm_macro_support::WitInvocationCtx,
305 ) {
306 $crate::guest::__wasm_macro_support::run_on_webrtc_disconnected(
307 __actr_workload(),
308 event,
309 ctx,
310 ).await
311 }
312
313 async fn on_credential_renewed(
314 event: $crate::guest::__wasm_macro_support::WitCredentialEvent,
315 ctx: $crate::guest::__wasm_macro_support::WitInvocationCtx,
316 ) {
317 $crate::guest::__wasm_macro_support::run_on_credential_renewed(
318 __actr_workload(),
319 event,
320 ctx,
321 ).await
322 }
323
324 async fn on_credential_expiring(
325 event: $crate::guest::__wasm_macro_support::WitCredentialEvent,
326 ctx: $crate::guest::__wasm_macro_support::WitInvocationCtx,
327 ) {
328 $crate::guest::__wasm_macro_support::run_on_credential_expiring(
329 __actr_workload(),
330 event,
331 ctx,
332 ).await
333 }
334
335 async fn on_mailbox_backpressure(
336 event: $crate::guest::__wasm_macro_support::WitBackpressureEvent,
337 ctx: $crate::guest::__wasm_macro_support::WitInvocationCtx,
338 ) {
339 $crate::guest::__wasm_macro_support::run_on_mailbox_backpressure(
340 __actr_workload(),
341 event,
342 ctx,
343 ).await
344 }
345
346 async fn on_data_chunk(
347 chunk: $crate::guest::__wasm_macro_support::WitDataChunk,
348 sender: $crate::guest::__wasm_macro_support::WitActrId,
349 ctx: $crate::guest::__wasm_macro_support::WitInvocationCtx,
350 ) -> ::core::result::Result<
351 (),
352 $crate::guest::__wasm_macro_support::WitActrError,
353 > {
354 $crate::guest::__wasm_macro_support::run_on_data_chunk(chunk, sender, ctx).await
355 }
356 }
357
358 $crate::guest::wasm::generated::export!(__ActrEntryAdapter with_types_in $crate::guest::wasm::generated);
359 };
360
361 #[cfg(all(target_arch = "wasm32", feature = "web"))]
371 const _: () = {
372 #[$crate::web::__web_macro_support::wasm_bindgen(start)]
382 fn __actr_web_bootstrap() {
383 let workload: $workload_type = $init_expr;
384 let adapter =
385 $crate::web::__web_macro_support::WebWorkloadAdapter::new(workload);
386 $crate::web::__web_macro_support::register_workload(adapter);
387 }
388 };
389
390 #[cfg(feature = "cdylib")]
395 const _: () = {
396 static mut __ACTR_WORKLOAD: Option<$workload_type> = None;
397 static mut __ACTR_VTABLE: Option<*const $crate::guest::vtable::HostVTable> = None;
398
399 #[unsafe(no_mangle)]
405 pub unsafe extern "C" fn actr_init(
406 vtable: *const $crate::guest::vtable::HostVTable,
407 init_ptr: *const u8,
408 init_len: usize,
409 ) -> i32 {
410 if vtable.is_null() {
411 return $crate::guest::dynclib_abi::code::INIT_FAILED;
412 }
413
414 let init_bytes = if init_ptr.is_null() || init_len == 0 {
415 &[][..]
416 } else {
417 unsafe { std::slice::from_raw_parts(init_ptr, init_len) }
418 };
419
420 if $crate::guest::dynclib_abi::decode_message::<$crate::guest::dynclib_abi::InitPayloadV1>(
425 init_bytes,
426 )
427 .is_err()
428 {
429 return $crate::guest::dynclib_abi::code::PROTOCOL_ERROR;
430 }
431
432 let workload: $workload_type = $init_expr;
433 unsafe {
434 if __ACTR_WORKLOAD.is_some() {
435 return $crate::guest::dynclib_abi::code::INIT_FAILED;
436 }
437 __ACTR_VTABLE = Some(vtable);
438 __ACTR_WORKLOAD = Some(workload);
439 }
440 $crate::guest::dynclib_abi::code::SUCCESS
441 }
442
443 #[unsafe(no_mangle)]
445 pub unsafe extern "C" fn actr_handle(
446 req_ptr: *const u8,
447 req_len: usize,
448 resp_out: *mut *mut u8,
449 resp_len_out: *mut usize,
450 ) -> i32 {
451 use actr_protocol::prost::Message as ProstMessage;
452 use $crate::{MessageDispatcher, Workload};
453
454 let vtable = match unsafe { __ACTR_VTABLE } {
456 Some(vt) => vt,
457 None => return $crate::guest::dynclib_abi::code::INIT_FAILED,
458 };
459
460 if req_ptr.is_null() {
462 return $crate::guest::dynclib_abi::code::PROTOCOL_ERROR;
463 }
464 let req_bytes = unsafe { std::slice::from_raw_parts(req_ptr, req_len) };
465
466 let frame = match $crate::guest::dynclib_abi::decode_message::<
467 $crate::guest::dynclib_abi::AbiFrame,
468 >(req_bytes) {
469 Ok(f) => f,
470 Err(_) => return $crate::guest::dynclib_abi::code::PROTOCOL_ERROR,
471 };
472
473 if frame.op == $crate::guest::dynclib_abi::op::GUEST_LIFECYCLE {
474 let payload = match <$crate::guest::dynclib_abi::GuestLifecycleV1 as $crate::guest::dynclib_abi::AbiPayload>::decode_payload(&frame.payload) {
475 Ok(payload) => payload,
476 Err(_) => return $crate::guest::dynclib_abi::code::PROTOCOL_ERROR,
477 };
478
479 let ctx = match unsafe {
480 $crate::guest::dynclib::context::DynclibContext::from_invocation(vtable, payload.ctx)
481 } {
482 Ok(c) => c,
483 Err(_) => return $crate::guest::dynclib_abi::code::HANDLE_FAILED,
484 };
485
486 let workload = unsafe {
487 match __ACTR_WORKLOAD.as_ref() {
488 Some(w) => w,
489 None => return $crate::guest::dynclib_abi::code::INIT_FAILED,
490 }
491 };
492
493 let lifecycle_result = match payload.hook {
494 $crate::guest::dynclib_abi::lifecycle_hook::ON_START => {
495 let fut = workload.on_start(&ctx);
496 let waker = std::task::Waker::noop();
497 let mut cx = std::task::Context::from_waker(waker);
498 let mut pinned = std::pin::pin!(fut);
499 match pinned.as_mut().poll(&mut cx) {
500 std::task::Poll::Ready(v) => v,
501 std::task::Poll::Pending => {
502 return $crate::guest::dynclib_abi::code::HANDLE_FAILED;
503 }
504 }
505 }
506 $crate::guest::dynclib_abi::lifecycle_hook::ON_READY => {
507 let fut = workload.on_ready(&ctx);
508 let waker = std::task::Waker::noop();
509 let mut cx = std::task::Context::from_waker(waker);
510 let mut pinned = std::pin::pin!(fut);
511 match pinned.as_mut().poll(&mut cx) {
512 std::task::Poll::Ready(v) => v,
513 std::task::Poll::Pending => {
514 return $crate::guest::dynclib_abi::code::HANDLE_FAILED;
515 }
516 }
517 }
518 $crate::guest::dynclib_abi::lifecycle_hook::ON_STOP => {
519 let fut = workload.on_stop(&ctx);
520 let waker = std::task::Waker::noop();
521 let mut cx = std::task::Context::from_waker(waker);
522 let mut pinned = std::pin::pin!(fut);
523 match pinned.as_mut().poll(&mut cx) {
524 std::task::Poll::Ready(v) => v,
525 std::task::Poll::Pending => {
526 return $crate::guest::dynclib_abi::code::HANDLE_FAILED;
527 }
528 }
529 }
530 _ => return $crate::guest::dynclib_abi::code::UNSUPPORTED_OP,
531 };
532
533 let resp_bytes = match lifecycle_result {
534 Ok(()) => match $crate::guest::dynclib_abi::success_reply(::std::vec::Vec::new()) {
535 Ok(bytes) => bytes,
536 Err(code) => return code,
537 },
538 Err(err) => match $crate::guest::dynclib_abi::error_reply(
539 $crate::guest::dynclib_abi::code::HANDLE_FAILED,
540 err.to_string().into_bytes(),
541 ) {
542 Ok(bytes) => bytes,
543 Err(code) => return code,
544 },
545 };
546
547 let resp_len = resp_bytes.len();
548 let layout = match std::alloc::Layout::from_size_align(resp_len.max(1), 1) {
549 Ok(l) => l,
550 Err(_) => return $crate::guest::dynclib_abi::code::GENERIC_ERROR,
551 };
552 let ptr = unsafe { std::alloc::alloc(layout) };
553 if ptr.is_null() {
554 return $crate::guest::dynclib_abi::code::GENERIC_ERROR;
555 }
556
557 unsafe {
558 std::ptr::copy_nonoverlapping(resp_bytes.as_ptr(), ptr, resp_len);
559 *resp_out = ptr;
560 *resp_len_out = resp_len;
561 }
562
563 return $crate::guest::dynclib_abi::code::SUCCESS;
564 }
565
566 if frame.op == $crate::guest::dynclib_abi::op::GUEST_HOOK {
567 let payload = match <$crate::guest::dynclib_abi::GuestHookV1 as $crate::guest::dynclib_abi::AbiPayload>::decode_payload(&frame.payload) {
568 Ok(payload) => payload,
569 Err(_) => return $crate::guest::dynclib_abi::code::PROTOCOL_ERROR,
570 };
571
572 let ctx = match unsafe {
573 $crate::guest::dynclib::context::DynclibContext::from_invocation(vtable, payload.ctx)
574 } {
575 Ok(c) => c,
576 Err(_) => return $crate::guest::dynclib_abi::code::HANDLE_FAILED,
577 };
578
579 let workload = unsafe {
580 match __ACTR_WORKLOAD.as_ref() {
581 Some(w) => w,
582 None => return $crate::guest::dynclib_abi::code::INIT_FAILED,
583 }
584 };
585
586 macro_rules! __actr_poll_unit {
587 ($future:expr) => {{
588 let fut = $future;
589 let waker = std::task::Waker::noop();
590 let mut cx = std::task::Context::from_waker(waker);
591 let mut pinned = std::pin::pin!(fut);
592 match pinned.as_mut().poll(&mut cx) {
593 std::task::Poll::Ready(()) => {}
594 std::task::Poll::Pending => {
595 return $crate::guest::dynclib_abi::code::HANDLE_FAILED;
596 }
597 }
598 }};
599 }
600
601 let peer_event = |peer: $crate::guest::dynclib_abi::PeerEventV1| {
602 $crate::PeerEvent {
603 peer: peer.peer,
604 relayed: peer.relayed,
605 status: peer
606 .status
607 .and_then($crate::guest::peer_status_from_v1),
608 }
609 };
610
611 let timestamp = |ts: $crate::guest::dynclib_abi::TimestampV1| {
612 std::time::UNIX_EPOCH
613 + std::time::Duration::new(ts.seconds, ts.nanoseconds)
614 };
615
616 match payload.hook {
617 $crate::guest::dynclib_abi::runtime_hook::ON_SIGNALING_CONNECTING => {
618 __actr_poll_unit!(workload.on_signaling_connecting(Some(&ctx)));
619 }
620 $crate::guest::dynclib_abi::runtime_hook::ON_SIGNALING_CONNECTED => {
621 __actr_poll_unit!(workload.on_signaling_connected(Some(&ctx)));
622 }
623 $crate::guest::dynclib_abi::runtime_hook::ON_SIGNALING_DISCONNECTED => {
624 __actr_poll_unit!(workload.on_signaling_disconnected(&ctx));
625 }
626 $crate::guest::dynclib_abi::runtime_hook::ON_WEBSOCKET_CONNECTING => {
627 let event = match payload.peer {
628 Some(peer) => peer_event(peer),
629 None => return $crate::guest::dynclib_abi::code::PROTOCOL_ERROR,
630 };
631 __actr_poll_unit!(workload.on_websocket_connecting(&ctx, &event));
632 }
633 $crate::guest::dynclib_abi::runtime_hook::ON_WEBSOCKET_CONNECTED => {
634 let event = match payload.peer {
635 Some(peer) => peer_event(peer),
636 None => return $crate::guest::dynclib_abi::code::PROTOCOL_ERROR,
637 };
638 __actr_poll_unit!(workload.on_websocket_connected(&ctx, &event));
639 }
640 $crate::guest::dynclib_abi::runtime_hook::ON_WEBSOCKET_DISCONNECTED => {
641 let event = match payload.peer {
642 Some(peer) => peer_event(peer),
643 None => return $crate::guest::dynclib_abi::code::PROTOCOL_ERROR,
644 };
645 __actr_poll_unit!(workload.on_websocket_disconnected(&ctx, &event));
646 }
647 $crate::guest::dynclib_abi::runtime_hook::ON_WEBRTC_CONNECTING => {
648 let event = match payload.peer {
649 Some(peer) => peer_event(peer),
650 None => return $crate::guest::dynclib_abi::code::PROTOCOL_ERROR,
651 };
652 __actr_poll_unit!(workload.on_webrtc_connecting(&ctx, &event));
653 }
654 $crate::guest::dynclib_abi::runtime_hook::ON_WEBRTC_CONNECTED => {
655 let event = match payload.peer {
656 Some(peer) => peer_event(peer),
657 None => return $crate::guest::dynclib_abi::code::PROTOCOL_ERROR,
658 };
659 __actr_poll_unit!(workload.on_webrtc_connected(&ctx, &event));
660 }
661 $crate::guest::dynclib_abi::runtime_hook::ON_WEBRTC_DISCONNECTED => {
662 let event = match payload.peer {
663 Some(peer) => peer_event(peer),
664 None => return $crate::guest::dynclib_abi::code::PROTOCOL_ERROR,
665 };
666 __actr_poll_unit!(workload.on_webrtc_disconnected(&ctx, &event));
667 }
668 $crate::guest::dynclib_abi::runtime_hook::ON_CREDENTIAL_RENEWED => {
669 let event = match payload.credential {
670 Some(credential) => $crate::CredentialEvent {
671 new_expiry: timestamp(credential.new_expiry),
672 },
673 None => return $crate::guest::dynclib_abi::code::PROTOCOL_ERROR,
674 };
675 __actr_poll_unit!(workload.on_credential_renewed(&ctx, &event));
676 }
677 $crate::guest::dynclib_abi::runtime_hook::ON_CREDENTIAL_EXPIRING => {
678 let event = match payload.credential {
679 Some(credential) => $crate::CredentialEvent {
680 new_expiry: timestamp(credential.new_expiry),
681 },
682 None => return $crate::guest::dynclib_abi::code::PROTOCOL_ERROR,
683 };
684 __actr_poll_unit!(workload.on_credential_expiring(&ctx, &event));
685 }
686 $crate::guest::dynclib_abi::runtime_hook::ON_MAILBOX_BACKPRESSURE => {
687 let event = match payload.backpressure {
688 Some(backpressure) => $crate::BackpressureEvent {
689 queue_len: backpressure.queue_len as usize,
690 threshold: backpressure.threshold as usize,
691 },
692 None => return $crate::guest::dynclib_abi::code::PROTOCOL_ERROR,
693 };
694 __actr_poll_unit!(workload.on_mailbox_backpressure(&ctx, &event));
695 }
696 _ => return $crate::guest::dynclib_abi::code::UNSUPPORTED_OP,
697 }
698
699 let resp_bytes = match $crate::guest::dynclib_abi::success_reply(::std::vec::Vec::new()) {
700 Ok(bytes) => bytes,
701 Err(code) => return code,
702 };
703
704 let resp_len = resp_bytes.len();
705 let layout = match std::alloc::Layout::from_size_align(resp_len.max(1), 1) {
706 Ok(l) => l,
707 Err(_) => return $crate::guest::dynclib_abi::code::GENERIC_ERROR,
708 };
709 let ptr = unsafe { std::alloc::alloc(layout) };
710 if ptr.is_null() {
711 return $crate::guest::dynclib_abi::code::GENERIC_ERROR;
712 }
713
714 unsafe {
715 std::ptr::copy_nonoverlapping(resp_bytes.as_ptr(), ptr, resp_len);
716 *resp_out = ptr;
717 *resp_len_out = resp_len;
718 }
719
720 return $crate::guest::dynclib_abi::code::SUCCESS;
721 }
722
723 if frame.op == $crate::guest::dynclib_abi::op::GUEST_DATA_CHUNK {
724 let payload = match <$crate::guest::dynclib_abi::GuestDataChunkV1 as $crate::guest::dynclib_abi::AbiPayload>::decode_payload(&frame.payload) {
725 Ok(payload) => payload,
726 Err(_) => return $crate::guest::dynclib_abi::code::PROTOCOL_ERROR,
727 };
728
729 let resp_bytes = match $crate::guest::dynclib::context::dispatch_registered_stream(payload) {
730 Ok(()) => match $crate::guest::dynclib_abi::success_reply(::std::vec::Vec::new()) {
731 Ok(bytes) => bytes,
732 Err(code) => return code,
733 },
734 Err(err) => match $crate::guest::dynclib_abi::error_reply(
735 $crate::guest::dynclib_abi::code::HANDLE_FAILED,
736 err.to_string().into_bytes(),
737 ) {
738 Ok(bytes) => bytes,
739 Err(code) => return code,
740 },
741 };
742
743 let resp_len = resp_bytes.len();
744 let layout = match std::alloc::Layout::from_size_align(resp_len.max(1), 1) {
745 Ok(l) => l,
746 Err(_) => return $crate::guest::dynclib_abi::code::GENERIC_ERROR,
747 };
748 let ptr = unsafe { std::alloc::alloc(layout) };
749 if ptr.is_null() {
750 return $crate::guest::dynclib_abi::code::GENERIC_ERROR;
751 }
752
753 unsafe {
754 std::ptr::copy_nonoverlapping(resp_bytes.as_ptr(), ptr, resp_len);
755 *resp_out = ptr;
756 *resp_len_out = resp_len;
757 }
758
759 return $crate::guest::dynclib_abi::code::SUCCESS;
760 }
761
762 if frame.op != $crate::guest::dynclib_abi::op::GUEST_HANDLE {
763 return $crate::guest::dynclib_abi::code::UNSUPPORTED_OP;
764 }
765
766 let handle = match <$crate::guest::dynclib_abi::GuestHandleV1 as $crate::guest::dynclib_abi::AbiPayload>::decode_payload(&frame.payload) {
767 Ok(handle) => handle,
768 Err(_) => return $crate::guest::dynclib_abi::code::PROTOCOL_ERROR,
769 };
770
771 let envelope = match actr_protocol::RpcEnvelope::decode(handle.rpc_envelope.as_slice()) {
772 Ok(e) => e,
773 Err(_) => return $crate::guest::dynclib_abi::code::PROTOCOL_ERROR,
774 };
775
776 let ctx = match unsafe {
777 $crate::guest::dynclib::context::DynclibContext::from_invocation(vtable, handle.ctx)
778 } {
779 Ok(c) => c,
780 Err(_) => return $crate::guest::dynclib_abi::code::HANDLE_FAILED,
781 };
782
783 let workload = unsafe {
785 match __ACTR_WORKLOAD.as_ref() {
786 Some(w) => w,
787 None => return $crate::guest::dynclib_abi::code::INIT_FAILED,
788 }
789 };
790
791 type Dispatcher = <$workload_type as Workload>::Dispatcher;
793
794 let resp_result = {
799 let fut = Dispatcher::dispatch(workload, envelope, &ctx);
800 let waker = std::task::Waker::noop();
801 let mut cx = std::task::Context::from_waker(waker);
802 let mut pinned = std::pin::pin!(fut);
803 match pinned.as_mut().poll(&mut cx) {
804 std::task::Poll::Ready(v) => v,
805 std::task::Poll::Pending => {
806 return $crate::guest::dynclib_abi::code::HANDLE_FAILED;
807 }
808 }
809 };
810
811 let resp_bytes = match resp_result {
812 Ok(b) => match $crate::guest::dynclib_abi::success_reply(b.to_vec()) {
813 Ok(bytes) => bytes,
814 Err(code) => return code,
815 },
816 Err(err) => match $crate::guest::dynclib_abi::error_reply(
817 $crate::guest::dynclib_abi::code::HANDLE_FAILED,
818 err.to_string().into_bytes(),
819 ) {
820 Ok(bytes) => bytes,
821 Err(code) => return code,
822 },
823 };
824
825 let resp_len = resp_bytes.len();
827 let layout = match std::alloc::Layout::from_size_align(resp_len.max(1), 1) {
828 Ok(l) => l,
829 Err(_) => return $crate::guest::dynclib_abi::code::GENERIC_ERROR,
830 };
831 let ptr = unsafe { std::alloc::alloc(layout) };
832 if ptr.is_null() {
833 return $crate::guest::dynclib_abi::code::GENERIC_ERROR;
834 }
835
836 unsafe {
837 std::ptr::copy_nonoverlapping(resp_bytes.as_ptr(), ptr, resp_len);
838 *resp_out = ptr;
839 *resp_len_out = resp_len;
840 }
841
842 $crate::guest::dynclib_abi::code::SUCCESS
843 }
844
845 #[unsafe(no_mangle)]
849 pub unsafe extern "C" fn actr_free_response(ptr: *mut u8, len: usize) {
850 if ptr.is_null() || len == 0 {
851 return;
852 }
853 let layout = match std::alloc::Layout::from_size_align(len, 1) {
854 Ok(l) => l,
855 Err(_) => return,
856 };
857 unsafe { std::alloc::dealloc(ptr, layout) };
858 }
859 };
860 };
861}
862
863#[cfg(test)]
864mod tests {
865 use super::*;
866
867 #[test]
868 fn peer_status_from_v1_drops_unknown_discriminants() {
869 use crate::guest::dynclib_abi::webrtc_peer_status as st;
870
871 assert_eq!(
872 peer_status_from_v1(st::IDLE),
873 Some(crate::WebRtcPeerStatus::Idle)
874 );
875 assert_eq!(
876 peer_status_from_v1(st::CONNECTING),
877 Some(crate::WebRtcPeerStatus::Connecting)
878 );
879 assert_eq!(
880 peer_status_from_v1(st::CONNECTED),
881 Some(crate::WebRtcPeerStatus::Connected)
882 );
883 assert_eq!(
884 peer_status_from_v1(st::RECOVERING),
885 Some(crate::WebRtcPeerStatus::Recovering)
886 );
887 assert_eq!(peer_status_from_v1(u32::MAX), None);
888 }
889}