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)]
381 pub unsafe extern "C" fn actr_init(
382 vtable: *const $crate::guest::vtable::HostVTable,
383 init_ptr: *const u8,
384 init_len: usize,
385 ) -> i32 {
386 if vtable.is_null() {
387 return $crate::guest::dynclib_abi::code::INIT_FAILED;
388 }
389
390 let init_bytes = if init_ptr.is_null() || init_len == 0 {
391 &[][..]
392 } else {
393 unsafe { std::slice::from_raw_parts(init_ptr, init_len) }
394 };
395
396 if $crate::guest::dynclib_abi::decode_message::<$crate::guest::dynclib_abi::InitPayloadV1>(
401 init_bytes,
402 )
403 .is_err()
404 {
405 return $crate::guest::dynclib_abi::code::PROTOCOL_ERROR;
406 }
407
408 let workload: $workload_type = $init_expr;
409 unsafe {
410 if __ACTR_WORKLOAD.is_some() {
411 return $crate::guest::dynclib_abi::code::INIT_FAILED;
412 }
413 __ACTR_VTABLE = Some(vtable);
414 __ACTR_WORKLOAD = Some(workload);
415 }
416 $crate::guest::dynclib_abi::code::SUCCESS
417 }
418
419 #[unsafe(no_mangle)]
421 pub unsafe extern "C" fn actr_handle(
422 req_ptr: *const u8,
423 req_len: usize,
424 resp_out: *mut *mut u8,
425 resp_len_out: *mut usize,
426 ) -> i32 {
427 use actr_protocol::prost::Message as ProstMessage;
428 use $crate::{MessageDispatcher, Workload};
429
430 let vtable = match unsafe { __ACTR_VTABLE } {
432 Some(vt) => vt,
433 None => return $crate::guest::dynclib_abi::code::INIT_FAILED,
434 };
435
436 if req_ptr.is_null() {
438 return $crate::guest::dynclib_abi::code::PROTOCOL_ERROR;
439 }
440 let req_bytes = unsafe { std::slice::from_raw_parts(req_ptr, req_len) };
441
442 let frame = match $crate::guest::dynclib_abi::decode_message::<
443 $crate::guest::dynclib_abi::AbiFrame,
444 >(req_bytes) {
445 Ok(f) => f,
446 Err(_) => return $crate::guest::dynclib_abi::code::PROTOCOL_ERROR,
447 };
448
449 if frame.op == $crate::guest::dynclib_abi::op::GUEST_LIFECYCLE {
450 let payload = match <$crate::guest::dynclib_abi::GuestLifecycleV1 as $crate::guest::dynclib_abi::AbiPayload>::decode_payload(&frame.payload) {
451 Ok(payload) => payload,
452 Err(_) => return $crate::guest::dynclib_abi::code::PROTOCOL_ERROR,
453 };
454
455 let ctx = match unsafe {
456 $crate::guest::dynclib::context::DynclibContext::from_invocation(vtable, payload.ctx)
457 } {
458 Ok(c) => c,
459 Err(_) => return $crate::guest::dynclib_abi::code::HANDLE_FAILED,
460 };
461
462 let workload = unsafe {
463 match __ACTR_WORKLOAD.as_ref() {
464 Some(w) => w,
465 None => return $crate::guest::dynclib_abi::code::INIT_FAILED,
466 }
467 };
468
469 let lifecycle_result = match payload.hook {
470 $crate::guest::dynclib_abi::lifecycle_hook::ON_START => {
471 let fut = workload.on_start(&ctx);
472 let waker = std::task::Waker::noop();
473 let mut cx = std::task::Context::from_waker(waker);
474 let mut pinned = std::pin::pin!(fut);
475 match pinned.as_mut().poll(&mut cx) {
476 std::task::Poll::Ready(v) => v,
477 std::task::Poll::Pending => {
478 return $crate::guest::dynclib_abi::code::HANDLE_FAILED;
479 }
480 }
481 }
482 $crate::guest::dynclib_abi::lifecycle_hook::ON_READY => {
483 let fut = workload.on_ready(&ctx);
484 let waker = std::task::Waker::noop();
485 let mut cx = std::task::Context::from_waker(waker);
486 let mut pinned = std::pin::pin!(fut);
487 match pinned.as_mut().poll(&mut cx) {
488 std::task::Poll::Ready(v) => v,
489 std::task::Poll::Pending => {
490 return $crate::guest::dynclib_abi::code::HANDLE_FAILED;
491 }
492 }
493 }
494 $crate::guest::dynclib_abi::lifecycle_hook::ON_STOP => {
495 let fut = workload.on_stop(&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 _ => return $crate::guest::dynclib_abi::code::UNSUPPORTED_OP,
507 };
508
509 let resp_bytes = match lifecycle_result {
510 Ok(()) => match $crate::guest::dynclib_abi::success_reply(::std::vec::Vec::new()) {
511 Ok(bytes) => bytes,
512 Err(code) => return code,
513 },
514 Err(err) => match $crate::guest::dynclib_abi::error_reply(
515 $crate::guest::dynclib_abi::code::HANDLE_FAILED,
516 err.to_string().into_bytes(),
517 ) {
518 Ok(bytes) => bytes,
519 Err(code) => return code,
520 },
521 };
522
523 let resp_len = resp_bytes.len();
524 let layout = match std::alloc::Layout::from_size_align(resp_len.max(1), 1) {
525 Ok(l) => l,
526 Err(_) => return $crate::guest::dynclib_abi::code::GENERIC_ERROR,
527 };
528 let ptr = unsafe { std::alloc::alloc(layout) };
529 if ptr.is_null() {
530 return $crate::guest::dynclib_abi::code::GENERIC_ERROR;
531 }
532
533 unsafe {
534 std::ptr::copy_nonoverlapping(resp_bytes.as_ptr(), ptr, resp_len);
535 *resp_out = ptr;
536 *resp_len_out = resp_len;
537 }
538
539 return $crate::guest::dynclib_abi::code::SUCCESS;
540 }
541
542 if frame.op == $crate::guest::dynclib_abi::op::GUEST_HOOK {
543 let payload = match <$crate::guest::dynclib_abi::GuestHookV1 as $crate::guest::dynclib_abi::AbiPayload>::decode_payload(&frame.payload) {
544 Ok(payload) => payload,
545 Err(_) => return $crate::guest::dynclib_abi::code::PROTOCOL_ERROR,
546 };
547
548 let ctx = match unsafe {
549 $crate::guest::dynclib::context::DynclibContext::from_invocation(vtable, payload.ctx)
550 } {
551 Ok(c) => c,
552 Err(_) => return $crate::guest::dynclib_abi::code::HANDLE_FAILED,
553 };
554
555 let workload = unsafe {
556 match __ACTR_WORKLOAD.as_ref() {
557 Some(w) => w,
558 None => return $crate::guest::dynclib_abi::code::INIT_FAILED,
559 }
560 };
561
562 macro_rules! __actr_poll_unit {
563 ($future:expr) => {{
564 let fut = $future;
565 let waker = std::task::Waker::noop();
566 let mut cx = std::task::Context::from_waker(waker);
567 let mut pinned = std::pin::pin!(fut);
568 match pinned.as_mut().poll(&mut cx) {
569 std::task::Poll::Ready(()) => {}
570 std::task::Poll::Pending => {
571 return $crate::guest::dynclib_abi::code::HANDLE_FAILED;
572 }
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 ctx = match unsafe {
753 $crate::guest::dynclib::context::DynclibContext::from_invocation(vtable, handle.ctx)
754 } {
755 Ok(c) => c,
756 Err(_) => return $crate::guest::dynclib_abi::code::HANDLE_FAILED,
757 };
758
759 let workload = unsafe {
761 match __ACTR_WORKLOAD.as_ref() {
762 Some(w) => w,
763 None => return $crate::guest::dynclib_abi::code::INIT_FAILED,
764 }
765 };
766
767 type Dispatcher = <$workload_type as Workload>::Dispatcher;
769
770 let resp_result = {
775 let fut = Dispatcher::dispatch(workload, envelope, &ctx);
776 let waker = std::task::Waker::noop();
777 let mut cx = std::task::Context::from_waker(waker);
778 let mut pinned = std::pin::pin!(fut);
779 match pinned.as_mut().poll(&mut cx) {
780 std::task::Poll::Ready(v) => v,
781 std::task::Poll::Pending => {
782 return $crate::guest::dynclib_abi::code::HANDLE_FAILED;
783 }
784 }
785 };
786
787 let resp_bytes = match resp_result {
788 Ok(b) => match $crate::guest::dynclib_abi::success_reply(b.to_vec()) {
789 Ok(bytes) => bytes,
790 Err(code) => return code,
791 },
792 Err(err) => match $crate::guest::dynclib_abi::error_reply(
793 $crate::guest::dynclib_abi::code::HANDLE_FAILED,
794 err.to_string().into_bytes(),
795 ) {
796 Ok(bytes) => bytes,
797 Err(code) => return code,
798 },
799 };
800
801 let resp_len = resp_bytes.len();
803 let layout = match std::alloc::Layout::from_size_align(resp_len.max(1), 1) {
804 Ok(l) => l,
805 Err(_) => return $crate::guest::dynclib_abi::code::GENERIC_ERROR,
806 };
807 let ptr = unsafe { std::alloc::alloc(layout) };
808 if ptr.is_null() {
809 return $crate::guest::dynclib_abi::code::GENERIC_ERROR;
810 }
811
812 unsafe {
813 std::ptr::copy_nonoverlapping(resp_bytes.as_ptr(), ptr, resp_len);
814 *resp_out = ptr;
815 *resp_len_out = resp_len;
816 }
817
818 $crate::guest::dynclib_abi::code::SUCCESS
819 }
820
821 #[unsafe(no_mangle)]
825 pub unsafe extern "C" fn actr_free_response(ptr: *mut u8, len: usize) {
826 if ptr.is_null() || len == 0 {
827 return;
828 }
829 let layout = match std::alloc::Layout::from_size_align(len, 1) {
830 Ok(l) => l,
831 Err(_) => return,
832 };
833 unsafe { std::alloc::dealloc(ptr, layout) };
834 }
835 };
836 };
837}
838
839#[cfg(test)]
840mod tests {
841 use super::*;
842
843 #[test]
844 fn peer_status_from_v1_drops_unknown_discriminants() {
845 use crate::guest::dynclib_abi::webrtc_peer_status as st;
846
847 assert_eq!(
848 peer_status_from_v1(st::IDLE),
849 Some(crate::WebRtcPeerStatus::Idle)
850 );
851 assert_eq!(
852 peer_status_from_v1(st::CONNECTING),
853 Some(crate::WebRtcPeerStatus::Connecting)
854 );
855 assert_eq!(
856 peer_status_from_v1(st::CONNECTED),
857 Some(crate::WebRtcPeerStatus::Connected)
858 );
859 assert_eq!(
860 peer_status_from_v1(st::RECOVERING),
861 Some(crate::WebRtcPeerStatus::Recovering)
862 );
863 assert_eq!(peer_status_from_v1(u32::MAX), None);
864 }
865}