1use std::ffi::c_void;
5
6use crate::{
7 BizInvariantReadContext, RecordKey, RecordKind, RuntimeCommandRef, RuntimeHostContext,
8 RuntimeHostError, RuntimePlugin, RuntimePluginFactory, SysId,
9};
10
11#[repr(C)]
13#[derive(Debug, Clone, Copy, PartialEq, Eq)]
14pub enum RuntimeCallStatus {
15 Success = 0,
17 Failure = 1,
19}
20
21#[repr(C)]
23#[derive(Debug, Clone, Copy, PartialEq, Eq)]
24pub enum RuntimeErrorPhase {
25 Load = 1,
27 Create = 2,
29 RunTx = 3,
31 ValidateBizInvariants = 4,
33 Unload = 5,
35}
36
37#[repr(transparent)]
39#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
40pub struct RuntimeErrorKind(pub u16);
41
42pub mod runtime_error_kind {
44 use super::RuntimeErrorKind;
45
46 pub const ABI_CONTRACT_VIOLATION: RuntimeErrorKind = RuntimeErrorKind(1);
48
49 pub const SCHEMA_MISMATCH: RuntimeErrorKind = RuntimeErrorKind(1001);
51 pub const CONFIG_ERROR: RuntimeErrorKind = RuntimeErrorKind(1002);
53
54 pub const PLUGIN_REJECTED: RuntimeErrorKind = RuntimeErrorKind(2001);
56 pub const INVALID_COMMAND: RuntimeErrorKind = RuntimeErrorKind(2002);
58
59 pub const HOST_REJECTED: RuntimeErrorKind = RuntimeErrorKind(4001);
61 pub const HOST_INTERNAL_ERROR: RuntimeErrorKind = RuntimeErrorKind(4002);
63
64 pub const PLUGIN_INTERNAL_ERROR: RuntimeErrorKind = RuntimeErrorKind(5001);
66}
67
68#[repr(C)]
73#[derive(Debug, Clone, Copy, PartialEq, Eq)]
74pub struct RuntimeBytesRef {
75 pub ptr: *const u8,
77 pub len: usize,
79}
80
81impl RuntimeBytesRef {
82 pub const fn empty() -> Self {
84 Self {
85 ptr: std::ptr::null(),
86 len: 0,
87 }
88 }
89
90 pub fn from_slice(bytes: &[u8]) -> Self {
92 Self {
93 ptr: bytes.as_ptr(),
94 len: bytes.len(),
95 }
96 }
97}
98
99#[repr(C)]
105#[derive(Debug, Clone, Copy, PartialEq, Eq)]
106pub struct RuntimeBytesMutRef {
107 pub ptr: *mut u8,
109 pub len: usize,
111}
112
113impl RuntimeBytesMutRef {
114 pub const fn empty() -> Self {
116 Self {
117 ptr: std::ptr::null_mut(),
118 len: 0,
119 }
120 }
121
122 pub fn from_slice(bytes: &mut [u8]) -> Self {
124 Self {
125 ptr: bytes.as_mut_ptr(),
126 len: bytes.len(),
127 }
128 }
129}
130
131#[repr(C)]
133#[derive(Debug, Clone, Copy, PartialEq, Eq)]
134pub struct RuntimeErrorBuf {
135 pub phase: RuntimeErrorPhase,
137 pub kind: RuntimeErrorKind,
139 pub message_len: usize,
141 pub message_buf: [u8; RUNTIME_ERROR_MESSAGE_CAPACITY],
143}
144
145impl RuntimeErrorBuf {
146 pub const fn new(
148 phase: RuntimeErrorPhase,
149 kind: RuntimeErrorKind,
150 _message: RuntimeBytesRef,
151 ) -> Self {
152 Self {
153 phase,
154 kind,
155 message_len: 0,
156 message_buf: [0; RUNTIME_ERROR_MESSAGE_CAPACITY],
157 }
158 }
159}
160
161pub const RUNTIME_ERROR_MESSAGE_CAPACITY: usize = 512;
163
164#[repr(C)]
166#[derive(Debug, Clone, Copy, PartialEq, Eq)]
167pub struct RuntimeCommandView {
168 pub command_kind: u8,
170 pub ext_seq: u64,
172 pub ref_ext_time_us: u64,
174 pub payload: RuntimeBytesRef,
176}
177
178#[repr(C)]
180#[derive(Debug, Clone, Copy, PartialEq, Eq)]
181pub struct RuntimeRecordKeyView {
182 pub kind: RecordKind,
184 pub sys_id: SysId,
186}
187
188impl From<RecordKey> for RuntimeRecordKeyView {
189 fn from(value: RecordKey) -> Self {
190 Self {
191 kind: value.kind,
192 sys_id: value.sys_id,
193 }
194 }
195}
196
197impl From<RuntimeRecordKeyView> for RecordKey {
198 fn from(value: RuntimeRecordKeyView) -> Self {
199 Self {
200 kind: value.kind,
201 sys_id: value.sys_id,
202 }
203 }
204}
205
206pub type RuntimeBytesVisitor =
208 unsafe extern "C" fn(visitor_ctx: *mut c_void, bytes: RuntimeBytesRef);
209pub type RuntimeBytesMutVisitor =
211 unsafe extern "C" fn(visitor_ctx: *mut c_void, bytes: RuntimeBytesMutRef);
212pub type RuntimeRecordKeyVisitor =
214 unsafe extern "C" fn(visitor_ctx: *mut c_void, key: RuntimeRecordKeyView);
215
216#[repr(C)]
218#[derive(Debug, Clone, Copy)]
219pub struct RuntimeHostContextV1 {
220 pub ctx_ptr: *mut c_void,
222 pub vtable: *const RuntimeHostVTableV1,
224}
225
226#[repr(C)]
228#[derive(Debug, Clone, Copy)]
229pub struct RuntimeReadContextV1 {
230 pub ctx_ptr: *mut c_void,
232 pub vtable: *const RuntimeReadVTableV1,
234}
235
236#[repr(C)]
238pub struct RuntimeHostVTableV1 {
239 pub with_read_typed_raw: unsafe extern "C" fn(
241 ctx_ptr: *const c_void,
242 record_kind: RecordKind,
243 sys_id: SysId,
244 visitor_ctx: *mut c_void,
245 visitor: RuntimeBytesVisitor,
246 out_found: *mut bool,
247 out_error: *mut RuntimeErrorBuf,
248 ) -> RuntimeCallStatus,
249 pub with_read_typed_by_pk_raw: unsafe extern "C" fn(
251 ctx_ptr: *const c_void,
252 record_kind: RecordKind,
253 pk: RuntimeBytesRef,
254 visitor_ctx: *mut c_void,
255 visitor: RuntimeBytesVisitor,
256 out_found: *mut bool,
257 out_error: *mut RuntimeErrorBuf,
258 ) -> RuntimeCallStatus,
259 pub create_typed_raw: unsafe extern "C" fn(
261 ctx_ptr: *mut c_void,
262 record_kind: RecordKind,
263 init_ctx: *mut c_void,
264 init: RuntimeBytesMutVisitor,
265 out_key: *mut RuntimeRecordKeyView,
266 out_error: *mut RuntimeErrorBuf,
267 ) -> RuntimeCallStatus,
268 pub update_typed_by_pk_raw: unsafe extern "C" fn(
270 ctx_ptr: *mut c_void,
271 record_kind: RecordKind,
272 pk: RuntimeBytesRef,
273 update_ctx: *mut c_void,
274 update: RuntimeBytesMutVisitor,
275 out_found: *mut bool,
276 out_error: *mut RuntimeErrorBuf,
277 ) -> RuntimeCallStatus,
278 pub delete_by_pk_raw: unsafe extern "C" fn(
280 ctx_ptr: *mut c_void,
281 record_kind: RecordKind,
282 pk: RuntimeBytesRef,
283 out_deleted: *mut bool,
284 out_error: *mut RuntimeErrorBuf,
285 ) -> RuntimeCallStatus,
286 pub emit_typed_event_raw: unsafe extern "C" fn(
288 ctx_ptr: *mut c_void,
289 event_kind: u8,
290 payload: RuntimeBytesRef,
291 out_error: *mut RuntimeErrorBuf,
292 ) -> RuntimeCallStatus,
293 pub for_each_record_key_raw: unsafe extern "C" fn(
295 ctx_ptr: *const c_void,
296 kind: RecordKind,
297 visitor_ctx: *mut c_void,
298 visitor: RuntimeRecordKeyVisitor,
299 out_error: *mut RuntimeErrorBuf,
300 ) -> RuntimeCallStatus,
301 pub debug_log: unsafe extern "C" fn(
303 ctx_ptr: *mut c_void,
304 message: RuntimeBytesRef,
305 out_error: *mut RuntimeErrorBuf,
306 ) -> RuntimeCallStatus,
307}
308
309#[repr(C)]
311pub struct RuntimeReadVTableV1 {
312 pub with_read_typed_raw: unsafe extern "C" fn(
314 ctx_ptr: *const c_void,
315 record_kind: RecordKind,
316 sys_id: SysId,
317 visitor_ctx: *mut c_void,
318 visitor: RuntimeBytesVisitor,
319 out_found: *mut bool,
320 out_error: *mut RuntimeErrorBuf,
321 ) -> RuntimeCallStatus,
322 pub with_read_typed_by_pk_raw: unsafe extern "C" fn(
324 ctx_ptr: *const c_void,
325 record_kind: RecordKind,
326 pk: RuntimeBytesRef,
327 visitor_ctx: *mut c_void,
328 visitor: RuntimeBytesVisitor,
329 out_found: *mut bool,
330 out_error: *mut RuntimeErrorBuf,
331 ) -> RuntimeCallStatus,
332 pub for_each_record_key_raw: unsafe extern "C" fn(
334 ctx_ptr: *const c_void,
335 kind: RecordKind,
336 visitor_ctx: *mut c_void,
337 visitor: RuntimeRecordKeyVisitor,
338 out_error: *mut RuntimeErrorBuf,
339 ) -> RuntimeCallStatus,
340}
341
342pub const RUNTIME_PLUGIN_ABI_VERSION_V1: u32 = 1;
344pub const RUNTIME_PLUGIN_ENTRY_V1_SYMBOL: &[u8] = b"statevec_runtime_plugin_entry_v1\0";
346
347#[repr(C)]
349pub struct RuntimePluginApiV1 {
350 pub abi_version: u32,
352 pub plugin_name: unsafe extern "C" fn() -> RuntimeBytesRef,
354 pub schema_bytes: unsafe extern "C" fn() -> RuntimeBytesRef,
356 pub create_runtime: unsafe extern "C" fn(
358 config: RuntimeBytesRef,
359 out_runtime: *mut *mut c_void,
360 out_error: *mut RuntimeErrorBuf,
361 ) -> RuntimeCallStatus,
362 pub destroy_runtime: unsafe extern "C" fn(runtime: *mut c_void),
364 pub run_tx: unsafe extern "C" fn(
366 runtime: *mut c_void,
367 host: RuntimeHostContextV1,
368 command: RuntimeCommandView,
369 out_error: *mut RuntimeErrorBuf,
370 ) -> RuntimeCallStatus,
371 pub validate_biz_invariants: unsafe extern "C" fn(
373 runtime: *mut c_void,
374 host: RuntimeReadContextV1,
375 out_error: *mut RuntimeErrorBuf,
376 ) -> RuntimeCallStatus,
377 pub on_unload: unsafe extern "C" fn(
379 runtime: *mut c_void,
380 out_error: *mut RuntimeErrorBuf,
381 ) -> RuntimeCallStatus,
382}
383
384pub type RuntimePluginEntryV1 = unsafe extern "C" fn() -> RuntimePluginApiV1;
386
387pub struct ExportedRuntimePluginV1Handle {
389 pub plugin: Box<dyn RuntimePlugin>,
391}
392
393pub unsafe fn runtime_bytes_slice<'a>(bytes: RuntimeBytesRef) -> Result<&'a [u8], &'static str> {
400 if bytes.ptr.is_null() {
401 if bytes.len == 0 {
402 return Ok(&[]);
403 }
404 return Err("bytes pointer is null but len is non-zero");
405 }
406 Ok(unsafe { std::slice::from_raw_parts(bytes.ptr, bytes.len) })
407}
408
409pub unsafe fn runtime_bytes_slice_mut<'a>(
416 bytes: RuntimeBytesMutRef,
417) -> Result<&'a mut [u8], &'static str> {
418 if bytes.ptr.is_null() {
419 if bytes.len == 0 {
420 return Ok(&mut []);
421 }
422 return Err("bytes pointer is null but len is non-zero");
423 }
424 Ok(unsafe { std::slice::from_raw_parts_mut(bytes.ptr, bytes.len) })
425}
426
427#[allow(clippy::not_unsafe_ptr_arg_deref)]
429pub fn clear_runtime_error(out_error: *mut RuntimeErrorBuf) {
430 if out_error.is_null() {
431 return;
432 }
433 unsafe {
434 *out_error = RuntimeErrorBuf::new(
435 RuntimeErrorPhase::Load,
436 runtime_error_kind::HOST_INTERNAL_ERROR,
437 RuntimeBytesRef::empty(),
438 );
439 }
440}
441
442#[allow(clippy::not_unsafe_ptr_arg_deref)]
444pub fn write_runtime_error(
445 out_error: *mut RuntimeErrorBuf,
446 phase: RuntimeErrorPhase,
447 kind: RuntimeErrorKind,
448 message: &str,
449) {
450 if out_error.is_null() {
451 return;
452 }
453 let mut error = RuntimeErrorBuf::new(phase, kind, RuntimeBytesRef::empty());
454 let mut message_len = message.len().min(RUNTIME_ERROR_MESSAGE_CAPACITY);
455 while !message.is_char_boundary(message_len) {
456 message_len -= 1;
457 }
458 error.message_len = message_len;
459 error.message_buf[..message_len].copy_from_slice(&message.as_bytes()[..message_len]);
460 unsafe {
461 *out_error = error;
462 }
463}
464
465pub fn runtime_error_text(error: &RuntimeErrorBuf) -> String {
467 let message_len = error.message_len.min(RUNTIME_ERROR_MESSAGE_CAPACITY);
468 String::from_utf8_lossy(&error.message_buf[..message_len]).into_owned()
469}
470
471pub fn runtime_error_message(error: &RuntimeErrorBuf) -> String {
473 format!(
474 "phase={:?} kind={} message={}",
475 error.phase,
476 error.kind.0,
477 runtime_error_text(error)
478 )
479}
480
481pub fn runtime_plugin_name_v1(factory: fn() -> Box<dyn RuntimePluginFactory>) -> RuntimeBytesRef {
483 let name = factory().plugin_name();
484 RuntimeBytesRef::from_slice(name.as_bytes())
485}
486
487pub fn runtime_plugin_schema_bytes_v1(
489 factory: fn() -> Box<dyn RuntimePluginFactory>,
490 cache: &'static std::sync::OnceLock<String>,
491) -> RuntimeBytesRef {
492 let bytes = cache.get_or_init(|| factory().schema_registry().to_idl_json());
493 RuntimeBytesRef::from_slice(bytes.as_bytes())
494}
495
496unsafe fn exported_runtime_plugin_handle_mut(
497 runtime: *mut c_void,
498) -> Result<&'static mut ExportedRuntimePluginV1Handle, &'static str> {
499 if runtime.is_null() {
500 Err("runtime handle is null")
501 } else {
502 Ok(unsafe { &mut *runtime.cast::<ExportedRuntimePluginV1Handle>() })
503 }
504}
505
506pub unsafe fn runtime_plugin_create_runtime_v1(
514 factory: fn() -> Box<dyn RuntimePluginFactory>,
515 config: RuntimeBytesRef,
516 out_runtime: *mut *mut c_void,
517 out_error: *mut RuntimeErrorBuf,
518) -> RuntimeCallStatus {
519 clear_runtime_error(out_error);
520
521 if out_runtime.is_null() {
522 write_runtime_error(
523 out_error,
524 RuntimeErrorPhase::Create,
525 runtime_error_kind::ABI_CONTRACT_VIOLATION,
526 "out_runtime is null",
527 );
528 return RuntimeCallStatus::Failure;
529 }
530
531 let config_bytes = match unsafe { runtime_bytes_slice(config) } {
532 Ok(bytes) => bytes,
533 Err(message) => {
534 write_runtime_error(
535 out_error,
536 RuntimeErrorPhase::Create,
537 runtime_error_kind::ABI_CONTRACT_VIOLATION,
538 message,
539 );
540 return RuntimeCallStatus::Failure;
541 }
542 };
543
544 let config_text = match std::str::from_utf8(config_bytes) {
545 Ok(value) => value,
546 Err(err) => {
547 write_runtime_error(
548 out_error,
549 RuntimeErrorPhase::Create,
550 runtime_error_kind::CONFIG_ERROR,
551 &format!("plugin config is not valid UTF-8: {err}"),
552 );
553 return RuntimeCallStatus::Failure;
554 }
555 };
556
557 let plugin = match factory().create(config_text) {
558 Ok(plugin) => plugin,
559 Err(err) => {
560 write_runtime_error(
561 out_error,
562 RuntimeErrorPhase::Create,
563 runtime_error_kind::CONFIG_ERROR,
564 &err.to_string(),
565 );
566 return RuntimeCallStatus::Failure;
567 }
568 };
569
570 let handle = Box::new(ExportedRuntimePluginV1Handle { plugin });
571 unsafe {
572 *out_runtime = Box::into_raw(handle).cast();
573 }
574 RuntimeCallStatus::Success
575}
576
577pub unsafe fn runtime_plugin_destroy_runtime_v1(runtime: *mut c_void) {
585 if runtime.is_null() {
586 return;
587 }
588 unsafe {
589 drop(Box::from_raw(
590 runtime.cast::<ExportedRuntimePluginV1Handle>(),
591 ));
592 }
593}
594
595pub unsafe fn runtime_plugin_run_tx_v1(
603 runtime: *mut c_void,
604 host: RuntimeHostContextV1,
605 command: RuntimeCommandView,
606 out_error: *mut RuntimeErrorBuf,
607) -> RuntimeCallStatus {
608 clear_runtime_error(out_error);
609
610 let payload = match unsafe { runtime_bytes_slice(command.payload) } {
611 Ok(bytes) => bytes,
612 Err(message) => {
613 write_runtime_error(
614 out_error,
615 RuntimeErrorPhase::RunTx,
616 runtime_error_kind::ABI_CONTRACT_VIOLATION,
617 message,
618 );
619 return RuntimeCallStatus::Failure;
620 }
621 };
622
623 let handle = match unsafe { exported_runtime_plugin_handle_mut(runtime) } {
624 Ok(handle) => handle,
625 Err(message) => {
626 write_runtime_error(
627 out_error,
628 RuntimeErrorPhase::RunTx,
629 runtime_error_kind::ABI_CONTRACT_VIOLATION,
630 message,
631 );
632 return RuntimeCallStatus::Failure;
633 }
634 };
635
636 let mut host_adapter = RuntimeHostContextV1Adapter::new(host);
637 let command = RuntimeCommandRef::new(
638 command.command_kind,
639 command.ext_seq,
640 command.ref_ext_time_us,
641 payload,
642 );
643 match handle.plugin.run_tx(&mut host_adapter, &command) {
644 Ok(()) => RuntimeCallStatus::Success,
645 Err(err) => {
646 write_runtime_error(
647 out_error,
648 RuntimeErrorPhase::RunTx,
649 runtime_error_kind::PLUGIN_REJECTED,
650 &err.to_string(),
651 );
652 RuntimeCallStatus::Failure
653 }
654 }
655}
656
657pub unsafe fn runtime_plugin_validate_biz_invariants_v1(
665 runtime: *mut c_void,
666 host: RuntimeReadContextV1,
667 out_error: *mut RuntimeErrorBuf,
668) -> RuntimeCallStatus {
669 clear_runtime_error(out_error);
670
671 let handle = match unsafe { exported_runtime_plugin_handle_mut(runtime) } {
672 Ok(handle) => handle,
673 Err(message) => {
674 write_runtime_error(
675 out_error,
676 RuntimeErrorPhase::ValidateBizInvariants,
677 runtime_error_kind::ABI_CONTRACT_VIOLATION,
678 message,
679 );
680 return RuntimeCallStatus::Failure;
681 }
682 };
683
684 let host_adapter = RuntimeReadContextV1Adapter::new(host);
685 match handle.plugin.validate_biz_invariants(&host_adapter) {
686 Ok(()) => RuntimeCallStatus::Success,
687 Err(err) => {
688 write_runtime_error(
689 out_error,
690 RuntimeErrorPhase::ValidateBizInvariants,
691 runtime_error_kind::PLUGIN_REJECTED,
692 &err,
693 );
694 RuntimeCallStatus::Failure
695 }
696 }
697}
698
699pub unsafe fn runtime_plugin_on_unload_v1(
706 runtime: *mut c_void,
707 out_error: *mut RuntimeErrorBuf,
708) -> RuntimeCallStatus {
709 clear_runtime_error(out_error);
710
711 let handle = match unsafe { exported_runtime_plugin_handle_mut(runtime) } {
712 Ok(handle) => handle,
713 Err(message) => {
714 write_runtime_error(
715 out_error,
716 RuntimeErrorPhase::Unload,
717 runtime_error_kind::ABI_CONTRACT_VIOLATION,
718 message,
719 );
720 return RuntimeCallStatus::Failure;
721 }
722 };
723
724 match handle.plugin.on_unload() {
725 Ok(()) => RuntimeCallStatus::Success,
726 Err(err) => {
727 write_runtime_error(
728 out_error,
729 RuntimeErrorPhase::Unload,
730 runtime_error_kind::PLUGIN_INTERNAL_ERROR,
731 &err.to_string(),
732 );
733 RuntimeCallStatus::Failure
734 }
735 }
736}
737
738struct BytesVisitorForward<'a> {
739 callback: &'a mut dyn FnMut(&[u8]),
740}
741
742struct BytesVisitorForwardMut<'a> {
743 callback: &'a mut dyn FnMut(&mut [u8]),
744}
745
746struct RecordKeyVisitorForward<'a> {
747 callback: &'a mut dyn FnMut(RecordKey),
748}
749
750unsafe extern "C" fn forward_bytes_visitor(visitor_ctx: *mut c_void, bytes: RuntimeBytesRef) {
751 let forward = unsafe { &mut *visitor_ctx.cast::<BytesVisitorForward<'_>>() };
752 let bytes = unsafe { runtime_bytes_slice(bytes) }.expect("host returned invalid bytes");
753 (forward.callback)(bytes);
754}
755
756unsafe extern "C" fn forward_bytes_visitor_mut(
757 visitor_ctx: *mut c_void,
758 bytes: RuntimeBytesMutRef,
759) {
760 let forward = unsafe { &mut *visitor_ctx.cast::<BytesVisitorForwardMut<'_>>() };
761 let bytes =
762 unsafe { runtime_bytes_slice_mut(bytes) }.expect("host returned invalid mutable bytes");
763 (forward.callback)(bytes);
764}
765
766unsafe extern "C" fn forward_record_key_visitor(
767 visitor_ctx: *mut c_void,
768 key: RuntimeRecordKeyView,
769) {
770 let forward = unsafe { &mut *visitor_ctx.cast::<RecordKeyVisitorForward<'_>>() };
771 (forward.callback)(key.into());
772}
773
774#[derive(Debug, Clone, Copy)]
775pub struct RuntimeHostContextV1Adapter {
776 raw: RuntimeHostContextV1,
777}
778
779impl RuntimeHostContextV1Adapter {
780 pub const fn new(raw: RuntimeHostContextV1) -> Self {
781 Self { raw }
782 }
783
784 fn vtable(&self) -> Result<&RuntimeHostVTableV1, RuntimeHostError> {
785 unsafe { self.raw.vtable.as_ref() }
786 .ok_or_else(|| RuntimeHostError::new("runtime host vtable is null"))
787 }
788}
789
790impl RuntimeHostContext for RuntimeHostContextV1Adapter {
791 fn with_read_typed_raw(
792 &self,
793 record_kind: RecordKind,
794 sys_id: SysId,
795 f: &mut dyn FnMut(&[u8]),
796 ) -> Result<bool, RuntimeHostError> {
797 let mut found = false;
798 let mut error = RuntimeErrorBuf::new(
799 RuntimeErrorPhase::RunTx,
800 runtime_error_kind::HOST_INTERNAL_ERROR,
801 RuntimeBytesRef::empty(),
802 );
803 let mut visitor = BytesVisitorForward { callback: f };
804 let status = unsafe {
805 (self.vtable()?.with_read_typed_raw)(
806 self.raw.ctx_ptr.cast_const(),
807 record_kind,
808 sys_id,
809 (&mut visitor as *mut BytesVisitorForward<'_>).cast(),
810 forward_bytes_visitor,
811 &mut found,
812 &mut error,
813 )
814 };
815 match status {
816 RuntimeCallStatus::Success => Ok(found),
817 RuntimeCallStatus::Failure => Err(RuntimeHostError::new(runtime_error_message(&error))),
818 }
819 }
820
821 fn with_read_typed_by_pk_raw(
822 &self,
823 record_kind: RecordKind,
824 pk: &[u8],
825 f: &mut dyn FnMut(&[u8]),
826 ) -> Result<bool, RuntimeHostError> {
827 let mut found = false;
828 let mut error = RuntimeErrorBuf::new(
829 RuntimeErrorPhase::RunTx,
830 runtime_error_kind::HOST_INTERNAL_ERROR,
831 RuntimeBytesRef::empty(),
832 );
833 let mut visitor = BytesVisitorForward { callback: f };
834 let status = unsafe {
835 (self.vtable()?.with_read_typed_by_pk_raw)(
836 self.raw.ctx_ptr.cast_const(),
837 record_kind,
838 RuntimeBytesRef::from_slice(pk),
839 (&mut visitor as *mut BytesVisitorForward<'_>).cast(),
840 forward_bytes_visitor,
841 &mut found,
842 &mut error,
843 )
844 };
845 match status {
846 RuntimeCallStatus::Success => Ok(found),
847 RuntimeCallStatus::Failure => Err(RuntimeHostError::new(runtime_error_message(&error))),
848 }
849 }
850
851 fn create_typed_raw(
852 &mut self,
853 record_kind: RecordKind,
854 init: &mut dyn FnMut(&mut [u8]),
855 ) -> Result<RecordKey, RuntimeHostError> {
856 let mut key = RuntimeRecordKeyView { kind: 0, sys_id: 0 };
857 let mut error = RuntimeErrorBuf::new(
858 RuntimeErrorPhase::RunTx,
859 runtime_error_kind::HOST_INTERNAL_ERROR,
860 RuntimeBytesRef::empty(),
861 );
862 let mut visitor = BytesVisitorForwardMut { callback: init };
863 let status = unsafe {
864 (self.vtable()?.create_typed_raw)(
865 self.raw.ctx_ptr,
866 record_kind,
867 (&mut visitor as *mut BytesVisitorForwardMut<'_>).cast(),
868 forward_bytes_visitor_mut,
869 &mut key,
870 &mut error,
871 )
872 };
873 match status {
874 RuntimeCallStatus::Success => Ok(key.into()),
875 RuntimeCallStatus::Failure => Err(RuntimeHostError::new(runtime_error_message(&error))),
876 }
877 }
878
879 fn update_typed_by_pk_raw(
880 &mut self,
881 record_kind: RecordKind,
882 pk: &[u8],
883 f: &mut dyn FnMut(&mut [u8]),
884 ) -> Result<bool, RuntimeHostError> {
885 let mut found = false;
886 let mut error = RuntimeErrorBuf::new(
887 RuntimeErrorPhase::RunTx,
888 runtime_error_kind::HOST_INTERNAL_ERROR,
889 RuntimeBytesRef::empty(),
890 );
891 let mut visitor = BytesVisitorForwardMut { callback: f };
892 let status = unsafe {
893 (self.vtable()?.update_typed_by_pk_raw)(
894 self.raw.ctx_ptr,
895 record_kind,
896 RuntimeBytesRef::from_slice(pk),
897 (&mut visitor as *mut BytesVisitorForwardMut<'_>).cast(),
898 forward_bytes_visitor_mut,
899 &mut found,
900 &mut error,
901 )
902 };
903 match status {
904 RuntimeCallStatus::Success => Ok(found),
905 RuntimeCallStatus::Failure => Err(RuntimeHostError::new(runtime_error_message(&error))),
906 }
907 }
908
909 fn delete_by_pk_raw(
910 &mut self,
911 record_kind: RecordKind,
912 pk: &[u8],
913 ) -> Result<bool, RuntimeHostError> {
914 let mut deleted = false;
915 let mut error = RuntimeErrorBuf::new(
916 RuntimeErrorPhase::RunTx,
917 runtime_error_kind::HOST_INTERNAL_ERROR,
918 RuntimeBytesRef::empty(),
919 );
920 let status = unsafe {
921 (self.vtable()?.delete_by_pk_raw)(
922 self.raw.ctx_ptr,
923 record_kind,
924 RuntimeBytesRef::from_slice(pk),
925 &mut deleted,
926 &mut error,
927 )
928 };
929 match status {
930 RuntimeCallStatus::Success => Ok(deleted),
931 RuntimeCallStatus::Failure => Err(RuntimeHostError::new(runtime_error_message(&error))),
932 }
933 }
934
935 fn emit_typed_event_raw(
936 &mut self,
937 event_kind: u8,
938 payload: &[u8],
939 ) -> Result<(), RuntimeHostError> {
940 let mut error = RuntimeErrorBuf::new(
941 RuntimeErrorPhase::RunTx,
942 runtime_error_kind::HOST_INTERNAL_ERROR,
943 RuntimeBytesRef::empty(),
944 );
945 let status = unsafe {
946 (self.vtable()?.emit_typed_event_raw)(
947 self.raw.ctx_ptr,
948 event_kind,
949 RuntimeBytesRef::from_slice(payload),
950 &mut error,
951 )
952 };
953 match status {
954 RuntimeCallStatus::Success => Ok(()),
955 RuntimeCallStatus::Failure => Err(RuntimeHostError::new(runtime_error_message(&error))),
956 }
957 }
958
959 fn for_each_record_key_raw(
960 &self,
961 kind: RecordKind,
962 f: &mut dyn FnMut(RecordKey),
963 ) -> Result<(), RuntimeHostError> {
964 let mut error = RuntimeErrorBuf::new(
965 RuntimeErrorPhase::RunTx,
966 runtime_error_kind::HOST_INTERNAL_ERROR,
967 RuntimeBytesRef::empty(),
968 );
969 let mut visitor = RecordKeyVisitorForward { callback: f };
970 let status = unsafe {
971 (self.vtable()?.for_each_record_key_raw)(
972 self.raw.ctx_ptr.cast_const(),
973 kind,
974 (&mut visitor as *mut RecordKeyVisitorForward<'_>).cast(),
975 forward_record_key_visitor,
976 &mut error,
977 )
978 };
979 match status {
980 RuntimeCallStatus::Success => Ok(()),
981 RuntimeCallStatus::Failure => Err(RuntimeHostError::new(runtime_error_message(&error))),
982 }
983 }
984
985 fn debug_log(&mut self, message: String) -> Result<(), RuntimeHostError> {
986 let mut error = RuntimeErrorBuf::new(
987 RuntimeErrorPhase::RunTx,
988 runtime_error_kind::HOST_INTERNAL_ERROR,
989 RuntimeBytesRef::empty(),
990 );
991 let status = unsafe {
992 (self.vtable()?.debug_log)(
993 self.raw.ctx_ptr,
994 RuntimeBytesRef::from_slice(message.as_bytes()),
995 &mut error,
996 )
997 };
998 match status {
999 RuntimeCallStatus::Success => Ok(()),
1000 RuntimeCallStatus::Failure => Err(RuntimeHostError::new(runtime_error_message(&error))),
1001 }
1002 }
1003}
1004
1005#[derive(Debug, Clone, Copy)]
1006pub struct RuntimeReadContextV1Adapter {
1007 raw: RuntimeReadContextV1,
1008}
1009
1010impl RuntimeReadContextV1Adapter {
1011 pub const fn new(raw: RuntimeReadContextV1) -> Self {
1012 Self { raw }
1013 }
1014
1015 fn vtable(&self) -> Result<&RuntimeReadVTableV1, RuntimeHostError> {
1016 unsafe { self.raw.vtable.as_ref() }
1017 .ok_or_else(|| RuntimeHostError::new("runtime read vtable is null"))
1018 }
1019}
1020
1021impl BizInvariantReadContext for RuntimeReadContextV1Adapter {
1022 fn with_read_typed_raw(
1023 &self,
1024 record_kind: RecordKind,
1025 sys_id: SysId,
1026 f: &mut dyn FnMut(&[u8]),
1027 ) -> Result<bool, RuntimeHostError> {
1028 let mut found = false;
1029 let mut error = RuntimeErrorBuf::new(
1030 RuntimeErrorPhase::ValidateBizInvariants,
1031 runtime_error_kind::HOST_INTERNAL_ERROR,
1032 RuntimeBytesRef::empty(),
1033 );
1034 let mut visitor = BytesVisitorForward { callback: f };
1035 let status = unsafe {
1036 (self.vtable()?.with_read_typed_raw)(
1037 self.raw.ctx_ptr.cast_const(),
1038 record_kind,
1039 sys_id,
1040 (&mut visitor as *mut BytesVisitorForward<'_>).cast(),
1041 forward_bytes_visitor,
1042 &mut found,
1043 &mut error,
1044 )
1045 };
1046 match status {
1047 RuntimeCallStatus::Success => Ok(found),
1048 RuntimeCallStatus::Failure => Err(RuntimeHostError::new(runtime_error_message(&error))),
1049 }
1050 }
1051
1052 fn with_read_typed_by_pk_raw(
1053 &self,
1054 record_kind: RecordKind,
1055 pk: &[u8],
1056 f: &mut dyn FnMut(&[u8]),
1057 ) -> Result<bool, RuntimeHostError> {
1058 let mut found = false;
1059 let mut error = RuntimeErrorBuf::new(
1060 RuntimeErrorPhase::ValidateBizInvariants,
1061 runtime_error_kind::HOST_INTERNAL_ERROR,
1062 RuntimeBytesRef::empty(),
1063 );
1064 let mut visitor = BytesVisitorForward { callback: f };
1065 let status = unsafe {
1066 (self.vtable()?.with_read_typed_by_pk_raw)(
1067 self.raw.ctx_ptr.cast_const(),
1068 record_kind,
1069 RuntimeBytesRef::from_slice(pk),
1070 (&mut visitor as *mut BytesVisitorForward<'_>).cast(),
1071 forward_bytes_visitor,
1072 &mut found,
1073 &mut error,
1074 )
1075 };
1076 match status {
1077 RuntimeCallStatus::Success => Ok(found),
1078 RuntimeCallStatus::Failure => Err(RuntimeHostError::new(runtime_error_message(&error))),
1079 }
1080 }
1081
1082 fn for_each_record_key_raw(
1083 &self,
1084 kind: RecordKind,
1085 f: &mut dyn FnMut(RecordKey),
1086 ) -> Result<(), RuntimeHostError> {
1087 let mut error = RuntimeErrorBuf::new(
1088 RuntimeErrorPhase::ValidateBizInvariants,
1089 runtime_error_kind::HOST_INTERNAL_ERROR,
1090 RuntimeBytesRef::empty(),
1091 );
1092 let mut visitor = RecordKeyVisitorForward { callback: f };
1093 let status = unsafe {
1094 (self.vtable()?.for_each_record_key_raw)(
1095 self.raw.ctx_ptr.cast_const(),
1096 kind,
1097 (&mut visitor as *mut RecordKeyVisitorForward<'_>).cast(),
1098 forward_record_key_visitor,
1099 &mut error,
1100 )
1101 };
1102 match status {
1103 RuntimeCallStatus::Success => Ok(()),
1104 RuntimeCallStatus::Failure => Err(RuntimeHostError::new(runtime_error_message(&error))),
1105 }
1106 }
1107}