arkhe_kernel/runtime/
registry.rs1use std::collections::BTreeMap;
9
10use crate::abi::TypeCode;
11use crate::state::{Action, ActionContext, DeserializeError, Op};
12
13pub(crate) trait ActionDyn: 'static {
19 #[cfg_attr(not(test), allow(dead_code))]
20 fn canonical_bytes_dyn(&self) -> Vec<u8>;
21 fn compute_dyn(&self, ctx: &ActionContext) -> Vec<Op>;
22}
23
24impl<T: Action> ActionDyn for T {
25 fn canonical_bytes_dyn(&self) -> Vec<u8> {
26 self.canonical_bytes()
27 }
28 fn compute_dyn(&self, ctx: &ActionContext) -> Vec<Op> {
29 self.compute(ctx)
30 }
31}
32
33pub(crate) type ActionDeserializer = fn(u32, &[u8]) -> Result<Box<dyn ActionDyn>, DeserializeError>;
34
35#[derive(Clone)]
36pub(crate) struct ActionRegistration {
37 pub schema_version: u32,
38 pub deserializer: ActionDeserializer,
39}
40
41#[derive(Default)]
42pub(crate) struct ActionRegistry {
43 by_type_code: BTreeMap<TypeCode, ActionRegistration>,
44}
45
46impl ActionRegistry {
47 pub(crate) fn new() -> Self {
48 Self::default()
49 }
50
51 pub(crate) fn register<A: Action>(&mut self) {
52 let reg = ActionRegistration {
53 schema_version: A::SCHEMA_VERSION,
54 deserializer: |version, bytes| {
55 A::from_bytes(version, bytes).map(|boxed| boxed as Box<dyn ActionDyn>)
56 },
57 };
58 self.by_type_code.insert(A::TYPE_CODE, reg);
59 }
60
61 pub(crate) fn get(&self, tc: TypeCode) -> Option<&ActionRegistration> {
62 self.by_type_code.get(&tc)
63 }
64
65 #[cfg_attr(not(test), allow(dead_code))]
66 pub(crate) fn len(&self) -> usize {
67 self.by_type_code.len()
68 }
69}
70
71#[cfg(test)]
72mod tests {
73 use super::*;
74 use crate::abi::Principal;
75 use crate::state::traits::_sealed::Sealed;
76 use crate::state::{ActionCompute, ActionDeriv};
77 use serde::{Deserialize, Serialize};
78
79 #[derive(Serialize, Deserialize)]
80 struct ProbeAction;
81 impl Sealed for ProbeAction {}
82 impl ActionDeriv for ProbeAction {
83 const TYPE_CODE: TypeCode = TypeCode(0xAB_CD);
84 const SCHEMA_VERSION: u32 = 1;
85 }
86 impl ActionCompute for ProbeAction {
87 fn compute(&self, _ctx: &ActionContext) -> Vec<Op> {
88 Vec::new()
89 }
90 }
91
92 #[test]
93 fn registry_register_and_lookup() {
94 let mut r = ActionRegistry::new();
95 assert_eq!(r.len(), 0);
96 r.register::<ProbeAction>();
97 assert_eq!(r.len(), 1);
98 let reg = r.get(TypeCode(0xAB_CD)).expect("registered");
99 assert_eq!(reg.schema_version, 1);
100 }
101
102 #[test]
103 fn registry_deserializer_roundtrip() {
104 let mut r = ActionRegistry::new();
105 r.register::<ProbeAction>();
106 let reg = r.get(TypeCode(0xAB_CD)).unwrap();
107 let action = (reg.deserializer)(1, &[]).expect("deserialize");
108 let _bytes = action.canonical_bytes_dyn();
109 let inst = crate::state::Instance::new(
111 crate::abi::InstanceId::new(1).unwrap(),
112 crate::state::InstanceConfig::default(),
113 );
114 let ctx = ActionContext::new(None, crate::abi::Tick(0), inst.id(), &inst);
115 assert!(action.compute_dyn(&ctx).is_empty());
116 let _ = Principal::System;
117 }
118
119 #[test]
120 fn registry_unknown_type_code_returns_none() {
121 let r = ActionRegistry::new();
122 assert!(r.get(TypeCode(0xDEAD)).is_none());
123 }
124}