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)]
407 pub unsafe extern "C" fn actr_init(
408 vtable: *const $crate::guest::vtable::HostVTable,
409 init_ptr: *const u8,
410 init_len: usize,
411 ) -> i32 {
412 if vtable.is_null() {
413 return $crate::guest::dynclib_abi::code::INIT_FAILED;
414 }
415
416 let init_bytes = if init_ptr.is_null() || init_len == 0 {
417 &[][..]
418 } else {
419 unsafe { std::slice::from_raw_parts(init_ptr, init_len) }
420 };
421
422 if $crate::guest::dynclib_abi::decode_message::<$crate::guest::dynclib_abi::InitPayloadV1>(
427 init_bytes,
428 )
429 .is_err()
430 {
431 return $crate::guest::dynclib_abi::code::PROTOCOL_ERROR;
432 }
433
434 if $crate::guest::dynclib::initialize().is_err() {
435 return $crate::guest::dynclib_abi::code::INIT_FAILED;
436 }
437
438 let workload: $workload_type = $init_expr;
439 unsafe {
440 if __ACTR_WORKLOAD.is_some() {
441 return $crate::guest::dynclib_abi::code::INIT_FAILED;
442 }
443 __ACTR_VTABLE = Some(vtable);
444 __ACTR_WORKLOAD = Some(workload);
445 }
446 $crate::guest::dynclib_abi::code::SUCCESS
447 }
448
449 #[unsafe(no_mangle)]
451 pub unsafe extern "C" fn actr_handle(
452 req_ptr: *const u8,
453 req_len: usize,
454 resp_out: *mut *mut u8,
455 resp_len_out: *mut usize,
456 ) -> i32 {
457 use actr_protocol::prost::Message as ProstMessage;
458 use $crate::{MessageDispatcher, Workload};
459
460 let vtable = match unsafe { __ACTR_VTABLE } {
462 Some(vt) => vt,
463 None => return $crate::guest::dynclib_abi::code::INIT_FAILED,
464 };
465
466 if req_ptr.is_null() {
468 return $crate::guest::dynclib_abi::code::PROTOCOL_ERROR;
469 }
470 let req_bytes = unsafe { std::slice::from_raw_parts(req_ptr, req_len) };
471
472 let frame = match $crate::guest::dynclib_abi::decode_message::<
473 $crate::guest::dynclib_abi::AbiFrame,
474 >(req_bytes) {
475 Ok(f) => f,
476 Err(_) => return $crate::guest::dynclib_abi::code::PROTOCOL_ERROR,
477 };
478
479 if frame.op == $crate::guest::dynclib_abi::op::GUEST_LIFECYCLE {
480 let payload = match <$crate::guest::dynclib_abi::GuestLifecycleV1 as $crate::guest::dynclib_abi::AbiPayload>::decode_payload(&frame.payload) {
481 Ok(payload) => payload,
482 Err(_) => return $crate::guest::dynclib_abi::code::PROTOCOL_ERROR,
483 };
484
485 let bridge_token = payload.bridge_token;
486 let ctx = match unsafe {
487 $crate::guest::dynclib::context::DynclibContext::from_invocation(
488 vtable,
489 payload.ctx,
490 bridge_token,
491 )
492 } {
493 Ok(c) => c,
494 Err(_) => return $crate::guest::dynclib_abi::code::HANDLE_FAILED,
495 };
496
497 let workload = unsafe {
498 match __ACTR_WORKLOAD.as_ref() {
499 Some(w) => w,
500 None => return $crate::guest::dynclib_abi::code::INIT_FAILED,
501 }
502 };
503
504 let lifecycle_result = match payload.hook {
505 $crate::guest::dynclib_abi::lifecycle_hook::ON_START => {
506 match $crate::guest::dynclib::block_on(
507 bridge_token,
508 workload.on_start(&ctx),
509 ) {
510 Ok(result) => result,
511 Err(_) => return $crate::guest::dynclib_abi::code::HANDLE_FAILED,
512 }
513 }
514 $crate::guest::dynclib_abi::lifecycle_hook::ON_READY => {
515 match $crate::guest::dynclib::block_on(
516 bridge_token,
517 workload.on_ready(&ctx),
518 ) {
519 Ok(result) => result,
520 Err(_) => return $crate::guest::dynclib_abi::code::HANDLE_FAILED,
521 }
522 }
523 $crate::guest::dynclib_abi::lifecycle_hook::ON_STOP => {
524 match $crate::guest::dynclib::block_on(
525 bridge_token,
526 workload.on_stop(&ctx),
527 ) {
528 Ok(result) => result,
529 Err(_) => return $crate::guest::dynclib_abi::code::HANDLE_FAILED,
530 }
531 }
532 _ => return $crate::guest::dynclib_abi::code::UNSUPPORTED_OP,
533 };
534
535 let resp_bytes = match lifecycle_result {
536 Ok(()) => match $crate::guest::dynclib_abi::success_reply(::std::vec::Vec::new()) {
537 Ok(bytes) => bytes,
538 Err(code) => return code,
539 },
540 Err(err) => match $crate::guest::dynclib_abi::error_reply(
541 $crate::guest::dynclib_abi::code::HANDLE_FAILED,
542 err.to_string().into_bytes(),
543 ) {
544 Ok(bytes) => bytes,
545 Err(code) => return code,
546 },
547 };
548
549 let resp_len = resp_bytes.len();
550 let layout = match std::alloc::Layout::from_size_align(resp_len.max(1), 1) {
551 Ok(l) => l,
552 Err(_) => return $crate::guest::dynclib_abi::code::GENERIC_ERROR,
553 };
554 let ptr = unsafe { std::alloc::alloc(layout) };
555 if ptr.is_null() {
556 return $crate::guest::dynclib_abi::code::GENERIC_ERROR;
557 }
558
559 unsafe {
560 std::ptr::copy_nonoverlapping(resp_bytes.as_ptr(), ptr, resp_len);
561 *resp_out = ptr;
562 *resp_len_out = resp_len;
563 }
564
565 return $crate::guest::dynclib_abi::code::SUCCESS;
566 }
567
568 if frame.op == $crate::guest::dynclib_abi::op::GUEST_HOOK {
569 let payload = match <$crate::guest::dynclib_abi::GuestHookV1 as $crate::guest::dynclib_abi::AbiPayload>::decode_payload(&frame.payload) {
570 Ok(payload) => payload,
571 Err(_) => return $crate::guest::dynclib_abi::code::PROTOCOL_ERROR,
572 };
573
574 let bridge_token = payload.bridge_token;
575 let ctx = match unsafe {
576 $crate::guest::dynclib::context::DynclibContext::from_invocation(
577 vtable,
578 payload.ctx,
579 bridge_token,
580 )
581 } {
582 Ok(c) => c,
583 Err(_) => return $crate::guest::dynclib_abi::code::HANDLE_FAILED,
584 };
585
586 let workload = unsafe {
587 match __ACTR_WORKLOAD.as_ref() {
588 Some(w) => w,
589 None => return $crate::guest::dynclib_abi::code::INIT_FAILED,
590 }
591 };
592
593 macro_rules! __actr_poll_unit {
594 ($future:expr) => {{
595 if $crate::guest::dynclib::block_on(bridge_token, $future).is_err() {
596 return $crate::guest::dynclib_abi::code::HANDLE_FAILED;
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 bridge_token = handle.bridge_token;
777 let ctx = match unsafe {
778 $crate::guest::dynclib::context::DynclibContext::from_invocation(
779 vtable,
780 handle.ctx,
781 bridge_token,
782 )
783 } {
784 Ok(c) => c,
785 Err(_) => return $crate::guest::dynclib_abi::code::HANDLE_FAILED,
786 };
787
788 let workload = unsafe {
790 match __ACTR_WORKLOAD.as_ref() {
791 Some(w) => w,
792 None => return $crate::guest::dynclib_abi::code::INIT_FAILED,
793 }
794 };
795
796 type Dispatcher = <$workload_type as Workload>::Dispatcher;
798
799 let resp_result = match $crate::guest::dynclib::block_on(
800 bridge_token,
801 Dispatcher::dispatch(workload, envelope, &ctx),
802 ) {
803 Ok(result) => result,
804 Err(_) => return $crate::guest::dynclib_abi::code::HANDLE_FAILED,
805 };
806
807 let resp_bytes = match resp_result {
808 Ok(b) => match $crate::guest::dynclib_abi::success_reply(b.to_vec()) {
809 Ok(bytes) => bytes,
810 Err(code) => return code,
811 },
812 Err(err) => match $crate::guest::dynclib_abi::error_reply(
813 $crate::guest::dynclib_abi::code::HANDLE_FAILED,
814 err.to_string().into_bytes(),
815 ) {
816 Ok(bytes) => bytes,
817 Err(code) => return code,
818 },
819 };
820
821 let resp_len = resp_bytes.len();
823 let layout = match std::alloc::Layout::from_size_align(resp_len.max(1), 1) {
824 Ok(l) => l,
825 Err(_) => return $crate::guest::dynclib_abi::code::GENERIC_ERROR,
826 };
827 let ptr = unsafe { std::alloc::alloc(layout) };
828 if ptr.is_null() {
829 return $crate::guest::dynclib_abi::code::GENERIC_ERROR;
830 }
831
832 unsafe {
833 std::ptr::copy_nonoverlapping(resp_bytes.as_ptr(), ptr, resp_len);
834 *resp_out = ptr;
835 *resp_len_out = resp_len;
836 }
837
838 $crate::guest::dynclib_abi::code::SUCCESS
839 }
840
841 #[unsafe(no_mangle)]
846 pub unsafe extern "C" fn actr_shutdown() -> i32 {
847 let shutdown_result = $crate::guest::dynclib::shutdown();
848 $crate::guest::dynclib::context::clear_stream_callbacks();
849 unsafe {
850 __ACTR_WORKLOAD = None;
851 __ACTR_VTABLE = None;
852 }
853
854 match shutdown_result {
855 Ok(()) => $crate::guest::dynclib_abi::code::SUCCESS,
856 Err(_) => $crate::guest::dynclib_abi::code::GENERIC_ERROR,
857 }
858 }
859
860 #[unsafe(no_mangle)]
864 pub unsafe extern "C" fn actr_free_response(ptr: *mut u8, len: usize) {
865 if ptr.is_null() || len == 0 {
866 return;
867 }
868 let layout = match std::alloc::Layout::from_size_align(len, 1) {
869 Ok(l) => l,
870 Err(_) => return,
871 };
872 unsafe { std::alloc::dealloc(ptr, layout) };
873 }
874 };
875 };
876}
877
878#[cfg(test)]
879mod tests {
880 use super::*;
881
882 #[test]
883 fn peer_status_from_v1_drops_unknown_discriminants() {
884 use crate::guest::dynclib_abi::webrtc_peer_status as st;
885
886 assert_eq!(
887 peer_status_from_v1(st::IDLE),
888 Some(crate::WebRtcPeerStatus::Idle)
889 );
890 assert_eq!(
891 peer_status_from_v1(st::CONNECTING),
892 Some(crate::WebRtcPeerStatus::Connecting)
893 );
894 assert_eq!(
895 peer_status_from_v1(st::CONNECTED),
896 Some(crate::WebRtcPeerStatus::Connected)
897 );
898 assert_eq!(
899 peer_status_from_v1(st::RECOVERING),
900 Some(crate::WebRtcPeerStatus::Recovering)
901 );
902 assert_eq!(peer_status_from_v1(u32::MAX), None);
903 }
904}