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