Skip to main content

v8/
crdtp.rs

1// Copyright 2024 the Deno authors. All rights reserved. MIT license.
2
3use crate::support::CxxVTable;
4use crate::support::Opaque;
5use std::cell::UnsafeCell;
6use std::ffi::CString;
7use std::mem::MaybeUninit;
8use std::pin::Pin;
9
10unsafe extern "C" {
11  fn crdtp__FrontendChannel__BASE__CONSTRUCT(
12    buf: *mut MaybeUninit<RawFrontendChannel>,
13  );
14
15  fn crdtp__Serializable__DELETE(this: *mut RawSerializable);
16  fn crdtp__Serializable__AppendSerialized(
17    this: *const RawSerializable,
18    out: *mut CppVecU8,
19  );
20
21  fn crdtp__Dispatchable__new(data: *const u8, len: usize)
22  -> *mut Dispatchable;
23  fn crdtp__Dispatchable__DELETE(this: *mut Dispatchable);
24  fn crdtp__Dispatchable__ok(this: *const Dispatchable) -> bool;
25  fn crdtp__Dispatchable__callId(this: *const Dispatchable) -> i32;
26  fn crdtp__Dispatchable__hasCallId(this: *const Dispatchable) -> bool;
27  fn crdtp__Dispatchable__methodLen(this: *const Dispatchable) -> usize;
28  fn crdtp__Dispatchable__methodCopy(this: *const Dispatchable, out: *mut u8);
29  fn crdtp__Dispatchable__sessionIdLen(this: *const Dispatchable) -> usize;
30  fn crdtp__Dispatchable__sessionIdCopy(
31    this: *const Dispatchable,
32    out: *mut u8,
33  );
34  fn crdtp__Dispatchable__paramsLen(this: *const Dispatchable) -> usize;
35  fn crdtp__Dispatchable__paramsCopy(this: *const Dispatchable, out: *mut u8);
36
37  fn crdtp__DispatchResponse__Success() -> *mut DispatchResponseWrapper;
38  fn crdtp__DispatchResponse__FallThrough() -> *mut DispatchResponseWrapper;
39  fn crdtp__DispatchResponse__ParseError(
40    msg: *const u8,
41    len: usize,
42  ) -> *mut DispatchResponseWrapper;
43  fn crdtp__DispatchResponse__InvalidRequest(
44    msg: *const u8,
45    len: usize,
46  ) -> *mut DispatchResponseWrapper;
47  fn crdtp__DispatchResponse__MethodNotFound(
48    msg: *const u8,
49    len: usize,
50  ) -> *mut DispatchResponseWrapper;
51  fn crdtp__DispatchResponse__InvalidParams(
52    msg: *const u8,
53    len: usize,
54  ) -> *mut DispatchResponseWrapper;
55  fn crdtp__DispatchResponse__ServerError(
56    msg: *const u8,
57    len: usize,
58  ) -> *mut DispatchResponseWrapper;
59  fn crdtp__DispatchResponse__DELETE(this: *mut DispatchResponseWrapper);
60  fn crdtp__DispatchResponse__isSuccess(
61    this: *const DispatchResponseWrapper,
62  ) -> bool;
63  fn crdtp__DispatchResponse__isError(
64    this: *const DispatchResponseWrapper,
65  ) -> bool;
66  fn crdtp__DispatchResponse__isFallThrough(
67    this: *const DispatchResponseWrapper,
68  ) -> bool;
69  fn crdtp__DispatchResponse__code(this: *const DispatchResponseWrapper)
70  -> i32;
71  fn crdtp__DispatchResponse__messageLen(
72    this: *const DispatchResponseWrapper,
73  ) -> usize;
74  fn crdtp__DispatchResponse__messageCopy(
75    this: *const DispatchResponseWrapper,
76    out: *mut u8,
77  );
78
79  fn crdtp__UberDispatcher__new(
80    channel: *mut RawFrontendChannel,
81  ) -> *mut UberDispatcher;
82  fn crdtp__UberDispatcher__DELETE(this: *mut UberDispatcher);
83  fn crdtp__UberDispatcher__channel(
84    this: *mut UberDispatcher,
85  ) -> *mut RawFrontendChannel;
86  fn crdtp__UberDispatcher__Dispatch(
87    this: *mut UberDispatcher,
88    dispatchable: *const Dispatchable,
89  ) -> *mut DispatchResultWrapper;
90
91  fn crdtp__DispatchResult__DELETE(this: *mut DispatchResultWrapper);
92  fn crdtp__DispatchResult__MethodFound(
93    this: *const DispatchResultWrapper,
94  ) -> bool;
95  fn crdtp__DispatchResult__Run(this: *mut DispatchResultWrapper);
96
97  fn crdtp__vec_u8__new() -> *mut CppVecU8;
98  fn crdtp__vec_u8__DELETE(this: *mut CppVecU8);
99  fn crdtp__vec_u8__size(this: *const CppVecU8) -> usize;
100  fn crdtp__vec_u8__copy(this: *const CppVecU8, out: *mut u8);
101
102  fn crdtp__json__ConvertJSONToCBOR(
103    json_data: *const u8,
104    json_len: usize,
105    cbor_out: *mut CppVecU8,
106  ) -> bool;
107  fn crdtp__json__ConvertCBORToJSON(
108    cbor_data: *const u8,
109    cbor_len: usize,
110    json_out: *mut CppVecU8,
111  ) -> bool;
112
113  fn crdtp__CreateErrorResponse(
114    call_id: i32,
115    response: *mut DispatchResponseWrapper,
116  ) -> *mut RawSerializable;
117  fn crdtp__CreateResponse(
118    call_id: i32,
119    params: *mut RawSerializable,
120  ) -> *mut RawSerializable;
121  fn crdtp__CreateNotification(
122    method: *const std::ffi::c_char,
123    params: *mut RawSerializable,
124  ) -> *mut RawSerializable;
125  fn crdtp__CreateErrorNotification(
126    response: *mut DispatchResponseWrapper,
127  ) -> *mut RawSerializable;
128
129  fn crdtp__DomainDispatcher__new(
130    channel: *mut RawFrontendChannel,
131    rust_dispatcher: *mut std::ffi::c_void,
132  ) -> *mut RawDomainDispatcher;
133  fn crdtp__DomainDispatcher__sendResponse(
134    this: *mut RawDomainDispatcher,
135    call_id: i32,
136    response: *mut DispatchResponseWrapper,
137    result: *mut RawSerializable,
138  );
139  fn crdtp__UberDispatcher__WireBackend(
140    uber: *mut UberDispatcher,
141    domain_data: *const u8,
142    domain_len: usize,
143    dispatcher: *mut RawDomainDispatcher,
144  );
145}
146
147#[repr(C)]
148pub struct Dispatchable(Opaque);
149
150#[repr(C)]
151struct DispatchResponseWrapper(Opaque);
152
153#[repr(C)]
154pub struct UberDispatcher(Opaque);
155
156#[repr(C)]
157struct DispatchResultWrapper(Opaque);
158
159#[repr(C)]
160struct CppVecU8(Opaque);
161
162#[repr(C)]
163struct RawSerializable(Opaque);
164
165#[repr(C)]
166struct RawDomainDispatcher(Opaque);
167
168pub struct Serializable {
169  ptr: *mut RawSerializable,
170}
171
172impl Serializable {
173  pub fn to_bytes(&self) -> Vec<u8> {
174    unsafe {
175      let vec = crdtp__vec_u8__new();
176      crdtp__Serializable__AppendSerialized(self.ptr, vec);
177      let len = crdtp__vec_u8__size(vec);
178      let mut result = vec![0u8; len];
179      crdtp__vec_u8__copy(vec, result.as_mut_ptr());
180      crdtp__vec_u8__DELETE(vec);
181      result
182    }
183  }
184
185  fn into_raw(self) -> *mut RawSerializable {
186    let ptr = self.ptr;
187    std::mem::forget(self);
188    ptr
189  }
190}
191
192impl Drop for Serializable {
193  fn drop(&mut self) {
194    unsafe {
195      crdtp__Serializable__DELETE(self.ptr);
196    }
197  }
198}
199
200impl Dispatchable {
201  pub fn new(cbor_data: &[u8]) -> Box<Self> {
202    unsafe {
203      let ptr = crdtp__Dispatchable__new(cbor_data.as_ptr(), cbor_data.len());
204      Box::from_raw(ptr)
205    }
206  }
207
208  pub fn ok(&self) -> bool {
209    unsafe { crdtp__Dispatchable__ok(self) }
210  }
211
212  pub fn call_id(&self) -> i32 {
213    unsafe { crdtp__Dispatchable__callId(self) }
214  }
215
216  pub fn has_call_id(&self) -> bool {
217    unsafe { crdtp__Dispatchable__hasCallId(self) }
218  }
219
220  pub fn method(&self) -> Vec<u8> {
221    unsafe {
222      let len = crdtp__Dispatchable__methodLen(self);
223      let mut buf = vec![0u8; len];
224      crdtp__Dispatchable__methodCopy(self, buf.as_mut_ptr());
225      buf
226    }
227  }
228
229  pub fn method_str(&self) -> String {
230    String::from_utf8_lossy(&self.method()).into_owned()
231  }
232
233  pub fn session_id(&self) -> Vec<u8> {
234    unsafe {
235      let len = crdtp__Dispatchable__sessionIdLen(self);
236      let mut buf = vec![0u8; len];
237      crdtp__Dispatchable__sessionIdCopy(self, buf.as_mut_ptr());
238      buf
239    }
240  }
241
242  pub fn params(&self) -> Vec<u8> {
243    unsafe {
244      let len = crdtp__Dispatchable__paramsLen(self);
245      let mut buf = vec![0u8; len];
246      crdtp__Dispatchable__paramsCopy(self, buf.as_mut_ptr());
247      buf
248    }
249  }
250}
251
252impl Drop for Dispatchable {
253  fn drop(&mut self) {
254    unsafe {
255      crdtp__Dispatchable__DELETE(self);
256    }
257  }
258}
259
260pub struct DispatchResponse {
261  ptr: *mut DispatchResponseWrapper,
262}
263
264impl DispatchResponse {
265  pub fn success() -> Self {
266    unsafe {
267      Self {
268        ptr: crdtp__DispatchResponse__Success(),
269      }
270    }
271  }
272
273  pub fn fall_through() -> Self {
274    unsafe {
275      Self {
276        ptr: crdtp__DispatchResponse__FallThrough(),
277      }
278    }
279  }
280
281  pub fn parse_error(message: &str) -> Self {
282    unsafe {
283      Self {
284        ptr: crdtp__DispatchResponse__ParseError(
285          message.as_ptr(),
286          message.len(),
287        ),
288      }
289    }
290  }
291
292  pub fn invalid_request(message: &str) -> Self {
293    unsafe {
294      Self {
295        ptr: crdtp__DispatchResponse__InvalidRequest(
296          message.as_ptr(),
297          message.len(),
298        ),
299      }
300    }
301  }
302
303  pub fn method_not_found(message: &str) -> Self {
304    unsafe {
305      Self {
306        ptr: crdtp__DispatchResponse__MethodNotFound(
307          message.as_ptr(),
308          message.len(),
309        ),
310      }
311    }
312  }
313
314  pub fn invalid_params(message: &str) -> Self {
315    unsafe {
316      Self {
317        ptr: crdtp__DispatchResponse__InvalidParams(
318          message.as_ptr(),
319          message.len(),
320        ),
321      }
322    }
323  }
324
325  pub fn server_error(message: &str) -> Self {
326    unsafe {
327      Self {
328        ptr: crdtp__DispatchResponse__ServerError(
329          message.as_ptr(),
330          message.len(),
331        ),
332      }
333    }
334  }
335
336  pub fn is_success(&self) -> bool {
337    unsafe { crdtp__DispatchResponse__isSuccess(self.ptr) }
338  }
339
340  pub fn is_error(&self) -> bool {
341    unsafe { crdtp__DispatchResponse__isError(self.ptr) }
342  }
343
344  /// Returns true if this is a fall-through response.
345  pub fn is_fall_through(&self) -> bool {
346    unsafe { crdtp__DispatchResponse__isFallThrough(self.ptr) }
347  }
348
349  /// Get the error code.
350  pub fn code(&self) -> i32 {
351    unsafe { crdtp__DispatchResponse__code(self.ptr) }
352  }
353
354  /// Get the error message.
355  pub fn message(&self) -> String {
356    unsafe {
357      let len = crdtp__DispatchResponse__messageLen(self.ptr);
358      let mut buf = vec![0u8; len];
359      crdtp__DispatchResponse__messageCopy(self.ptr, buf.as_mut_ptr());
360      String::from_utf8_lossy(&buf).into_owned()
361    }
362  }
363
364  fn into_raw(self) -> *mut DispatchResponseWrapper {
365    let ptr = self.ptr;
366    std::mem::forget(self);
367    ptr
368  }
369}
370
371impl Drop for DispatchResponse {
372  fn drop(&mut self) {
373    unsafe {
374      crdtp__DispatchResponse__DELETE(self.ptr);
375    }
376  }
377}
378
379/// Trait for sending protocol responses and notifications to clients.
380pub trait FrontendChannelImpl {
381  /// Send a response to a protocol request.
382  fn send_protocol_response(&mut self, call_id: i32, message: Serializable);
383  /// Send a notification (no call_id).
384  fn send_protocol_notification(&mut self, message: Serializable);
385  /// Indicate that the message should be handled by another layer.
386  fn fall_through(&mut self, call_id: i32, method: &[u8], message: &[u8]);
387  /// Flush any queued notifications.
388  fn flush_protocol_notifications(&mut self);
389}
390
391#[repr(C)]
392struct RawFrontendChannel {
393  _cxx_vtable: CxxVTable,
394}
395
396/// Wraps a Rust `FrontendChannelImpl` for use with the C++ dispatcher.
397pub struct FrontendChannel {
398  raw: UnsafeCell<RawFrontendChannel>,
399  imp: Box<dyn FrontendChannelImpl>,
400}
401
402impl FrontendChannel {
403  /// Create a new FrontendChannel wrapping the given implementation.
404  pub fn new(imp: Box<dyn FrontendChannelImpl>) -> Pin<Box<Self>> {
405    let channel = Box::new(Self {
406      raw: UnsafeCell::new(unsafe { MaybeUninit::zeroed().assume_init() }),
407      imp,
408    });
409    unsafe {
410      crdtp__FrontendChannel__BASE__CONSTRUCT(
411        channel.raw.get() as *mut _ as *mut MaybeUninit<RawFrontendChannel>
412      );
413    }
414    Box::into_pin(channel)
415  }
416
417  fn raw_ptr(&self) -> *mut RawFrontendChannel {
418    self.raw.get()
419  }
420
421  unsafe fn from_raw<'a>(ptr: *mut RawFrontendChannel) -> &'a mut Self {
422    unsafe {
423      let channel_ptr = ptr as *mut u8;
424      let offset = std::mem::offset_of!(FrontendChannel, raw);
425      let self_ptr = channel_ptr.sub(offset) as *mut Self;
426      &mut *self_ptr
427    }
428  }
429}
430
431#[unsafe(no_mangle)]
432unsafe extern "C" fn crdtp__FrontendChannel__BASE__sendProtocolResponse(
433  this: *mut RawFrontendChannel,
434  call_id: i32,
435  message: *mut RawSerializable,
436) {
437  unsafe {
438    let channel = FrontendChannel::from_raw(this);
439    let msg = Serializable { ptr: message };
440    channel.imp.send_protocol_response(call_id, msg);
441  }
442}
443
444#[unsafe(no_mangle)]
445unsafe extern "C" fn crdtp__FrontendChannel__BASE__sendProtocolNotification(
446  this: *mut RawFrontendChannel,
447  message: *mut RawSerializable,
448) {
449  unsafe {
450    let channel = FrontendChannel::from_raw(this);
451    let msg = Serializable { ptr: message };
452    channel.imp.send_protocol_notification(msg);
453  }
454}
455
456#[unsafe(no_mangle)]
457unsafe extern "C" fn crdtp__FrontendChannel__BASE__fallThrough(
458  this: *mut RawFrontendChannel,
459  call_id: i32,
460  method_data: *const u8,
461  method_len: usize,
462  message_data: *const u8,
463  message_len: usize,
464) {
465  unsafe {
466    let channel = FrontendChannel::from_raw(this);
467    let method = std::slice::from_raw_parts(method_data, method_len);
468    let message = std::slice::from_raw_parts(message_data, message_len);
469    channel.imp.fall_through(call_id, method, message);
470  }
471}
472
473#[unsafe(no_mangle)]
474unsafe extern "C" fn crdtp__FrontendChannel__BASE__flushProtocolNotifications(
475  this: *mut RawFrontendChannel,
476) {
477  unsafe {
478    let channel = FrontendChannel::from_raw(this);
479    channel.imp.flush_protocol_notifications();
480  }
481}
482
483/// Result of dispatching a protocol message through UberDispatcher.
484pub struct DispatchResult {
485  ptr: *mut DispatchResultWrapper,
486}
487
488impl DispatchResult {
489  /// Returns true if a handler was found for the method.
490  pub fn method_found(&self) -> bool {
491    unsafe { crdtp__DispatchResult__MethodFound(self.ptr) }
492  }
493
494  /// Run the dispatched handler.
495  pub fn run(self) {
496    unsafe {
497      crdtp__DispatchResult__Run(self.ptr);
498    }
499    // Drop will call crdtp__DispatchResult__DELETE to free the wrapper.
500  }
501}
502
503impl Drop for DispatchResult {
504  fn drop(&mut self) {
505    unsafe {
506      crdtp__DispatchResult__DELETE(self.ptr);
507    }
508  }
509}
510
511impl UberDispatcher {
512  /// Create a new UberDispatcher with the given frontend channel.
513  pub fn new(channel: &Pin<Box<FrontendChannel>>) -> Box<Self> {
514    unsafe {
515      let ptr = crdtp__UberDispatcher__new(channel.raw_ptr());
516      Box::from_raw(ptr)
517    }
518  }
519
520  /// Dispatch a protocol message.
521  pub fn dispatch(&mut self, dispatchable: &Dispatchable) -> DispatchResult {
522    unsafe {
523      let ptr = crdtp__UberDispatcher__Dispatch(self, dispatchable);
524      DispatchResult { ptr }
525    }
526  }
527}
528
529impl Drop for UberDispatcher {
530  fn drop(&mut self) {
531    unsafe {
532      crdtp__UberDispatcher__DELETE(self);
533    }
534  }
535}
536
537/// Convert JSON bytes to CBOR bytes.
538pub fn json_to_cbor(json: &[u8]) -> Option<Vec<u8>> {
539  unsafe {
540    let vec = crdtp__vec_u8__new();
541    let ok = crdtp__json__ConvertJSONToCBOR(json.as_ptr(), json.len(), vec);
542    if ok {
543      let len = crdtp__vec_u8__size(vec);
544      let mut result = vec![0u8; len];
545      crdtp__vec_u8__copy(vec, result.as_mut_ptr());
546      crdtp__vec_u8__DELETE(vec);
547      Some(result)
548    } else {
549      crdtp__vec_u8__DELETE(vec);
550      None
551    }
552  }
553}
554
555/// Convert CBOR bytes to JSON bytes.
556pub fn cbor_to_json(cbor: &[u8]) -> Option<Vec<u8>> {
557  unsafe {
558    let vec = crdtp__vec_u8__new();
559    let ok = crdtp__json__ConvertCBORToJSON(cbor.as_ptr(), cbor.len(), vec);
560    if ok {
561      let len = crdtp__vec_u8__size(vec);
562      let mut result = vec![0u8; len];
563      crdtp__vec_u8__copy(vec, result.as_mut_ptr());
564      crdtp__vec_u8__DELETE(vec);
565      Some(result)
566    } else {
567      crdtp__vec_u8__DELETE(vec);
568      None
569    }
570  }
571}
572
573/// Create an error response message.
574pub fn create_error_response(
575  call_id: i32,
576  response: DispatchResponse,
577) -> Serializable {
578  unsafe {
579    let ptr = crdtp__CreateErrorResponse(call_id, response.into_raw());
580    Serializable { ptr }
581  }
582}
583
584/// Create an error notification message.
585pub fn create_error_notification(response: DispatchResponse) -> Serializable {
586  unsafe {
587    let ptr = crdtp__CreateErrorNotification(response.into_raw());
588    Serializable { ptr }
589  }
590}
591
592/// Create a success response message with optional result params.
593pub fn create_response(
594  call_id: i32,
595  params: Option<Serializable>,
596) -> Serializable {
597  unsafe {
598    let params_ptr = match params {
599      Some(p) => p.into_raw(),
600      None => std::ptr::null_mut(),
601    };
602    let ptr = crdtp__CreateResponse(call_id, params_ptr);
603    Serializable { ptr }
604  }
605}
606
607/// Create a notification message with a method name and optional params.
608///
609/// # Panics
610/// Panics if `method` contains interior null bytes.
611pub fn create_notification(
612  method: &str,
613  params: Option<Serializable>,
614) -> Serializable {
615  unsafe {
616    let method_cstr =
617      CString::new(method).expect("method name must not contain null bytes");
618    let params_ptr = match params {
619      Some(p) => p.into_raw(),
620      None => std::ptr::null_mut(),
621    };
622    let ptr = crdtp__CreateNotification(method_cstr.as_ptr(), params_ptr);
623    Serializable { ptr }
624  }
625}
626
627/// Trait for implementing a domain-specific protocol dispatcher.
628///
629/// The `dispatch` method is called in two phases:
630/// 1. **Probe phase** (`dispatchable` is `None`): Return `true` if this
631///    domain handles the given command name.
632/// 2. **Execute phase** (`dispatchable` is `Some`): Handle the command
633///    and send a response via the `DomainDispatcherHandle`.
634pub trait DomainDispatcherImpl {
635  fn dispatch(
636    &mut self,
637    command: &[u8],
638    dispatchable: Option<&Dispatchable>,
639    handle: &DomainDispatcherHandle,
640  ) -> bool;
641}
642
643/// Handle to the C++ DomainDispatcher, used to send responses.
644pub struct DomainDispatcherHandle {
645  ptr: *mut RawDomainDispatcher,
646}
647
648impl DomainDispatcherHandle {
649  /// Send a response for a dispatched command.
650  pub fn send_response(
651    &self,
652    call_id: i32,
653    response: DispatchResponse,
654    result: Option<Serializable>,
655  ) {
656    unsafe {
657      let result_ptr = match result {
658        Some(r) => r.into_raw(),
659        None => std::ptr::null_mut(),
660      };
661      crdtp__DomainDispatcher__sendResponse(
662        self.ptr,
663        call_id,
664        response.into_raw(),
665        result_ptr,
666      );
667    }
668  }
669}
670
671/// A domain dispatcher that delegates to a Rust `DomainDispatcherImpl`.
672///
673/// Ownership model: the Rust `DomainDispatcher` is heap-allocated and its
674/// pointer is stored in the C++ `crdtp__DomainDispatcher__BASE`. When the
675/// C++ side is destroyed (by `UberDispatcher`'s destructor), it calls back
676/// into Rust via `crdtp__DomainDispatcher__BASE__Drop` to free the Rust
677/// allocation.
678struct DomainDispatcherData {
679  ptr: *mut RawDomainDispatcher,
680  imp: Box<dyn DomainDispatcherImpl>,
681  domain_bytes: Vec<u8>,
682}
683
684pub struct DomainDispatcher;
685
686impl DomainDispatcher {
687  /// Wire a Rust domain dispatcher implementation to an `UberDispatcher`.
688  ///
689  /// The implementation will handle commands for the given `domain` name.
690  /// Ownership of `imp` is transferred to the C++ `UberDispatcher`; it
691  /// will be dropped when the `UberDispatcher` is destroyed.
692  pub fn wire(
693    uber: &mut UberDispatcher,
694    domain: &str,
695    imp: Box<dyn DomainDispatcherImpl>,
696  ) {
697    // Keep domain bytes alive as long as the DomainDispatcherData, since
698    // UberDispatcher stores domain as a span (pointer + length).
699    let domain_bytes = domain.as_bytes().to_vec();
700
701    let mut dd = Box::new(DomainDispatcherData {
702      ptr: std::ptr::null_mut(),
703      imp,
704      domain_bytes,
705    });
706
707    unsafe {
708      let rust_ptr =
709        &mut *dd as *mut DomainDispatcherData as *mut std::ffi::c_void;
710      let channel = crdtp__UberDispatcher__channel(uber);
711      let raw = crdtp__DomainDispatcher__new(channel, rust_ptr);
712      dd.ptr = raw;
713
714      crdtp__UberDispatcher__WireBackend(
715        uber,
716        dd.domain_bytes.as_ptr(),
717        dd.domain_bytes.len(),
718        raw,
719      );
720    }
721
722    // Transfer ownership to the C++ side. The C++ destructor will call
723    // crdtp__DomainDispatcher__BASE__Drop to reclaim this allocation.
724    let _ = Box::into_raw(dd);
725  }
726}
727
728#[unsafe(no_mangle)]
729unsafe extern "C" fn crdtp__DomainDispatcher__BASE__Dispatch(
730  rust_dispatcher: *mut std::ffi::c_void,
731  command_data: *const u8,
732  command_len: usize,
733  dispatchable: *const Dispatchable,
734) -> bool {
735  unsafe {
736    let dd = &mut *(rust_dispatcher as *mut DomainDispatcherData);
737    let command = std::slice::from_raw_parts(command_data, command_len);
738    let handle = DomainDispatcherHandle { ptr: dd.ptr };
739    let dispatchable_ref = if dispatchable.is_null() {
740      None
741    } else {
742      Some(&*dispatchable)
743    };
744    dd.imp.dispatch(command, dispatchable_ref, &handle)
745  }
746}
747
748#[unsafe(no_mangle)]
749unsafe extern "C" fn crdtp__DomainDispatcher__BASE__Drop(
750  rust_dispatcher: *mut std::ffi::c_void,
751) {
752  unsafe {
753    drop(Box::from_raw(rust_dispatcher as *mut DomainDispatcherData));
754  }
755}