Skip to main content

sails_idl_meta/
lib.rs

1#![no_std]
2
3extern crate alloc;
4
5pub use sails_idl_ast::InterfaceId;
6use sails_type_registry::{MetaType, TypeInfo};
7
8mod header;
9pub use header::*;
10
11/// A trait for types that have a static Interface ID.
12pub trait Identifiable {
13    const INTERFACE_ID: InterfaceId;
14}
15
16/// A trait for types that represent a service method, providing its Entry ID.
17pub trait MethodMeta: Identifiable {
18    const ENTRY_ID: u16;
19}
20
21#[derive(Debug, Clone)]
22pub struct BaseServiceMeta {
23    pub name: &'static str,
24    pub interface_id: InterfaceId,
25    pub meta: AnyServiceMeta,
26    pub base: &'static [BaseServiceMeta],
27}
28
29impl BaseServiceMeta {
30    pub const fn new<S: ServiceMeta + ?Sized>(name: &'static str) -> Self {
31        Self {
32            name,
33            interface_id: S::INTERFACE_ID,
34            meta: S::META,
35            base: S::BASE_SERVICES,
36        }
37    }
38}
39
40/// Metadata for a service method.
41#[derive(Debug)]
42pub struct MethodMetadata {
43    pub name: &'static str,
44    pub entry_id: u16,
45    pub hash: [u8; 32],
46    pub is_async: bool,
47}
48
49pub trait ServiceMeta: Identifiable {
50    type CommandsMeta: TypeInfo;
51    type QueriesMeta: TypeInfo;
52    type EventsMeta: TypeInfo;
53    /// The order of base services here is lexicographical by their names
54    const BASE_SERVICES: &'static [BaseServiceMeta];
55    /// The order of base services here is lexicographical by their names
56    // const BASE_SERVICES_IDS: &'static [AnyServiceIds];
57    const METHODS: &'static [MethodMetadata];
58    const ASYNC: bool;
59    const META: AnyServiceMeta = AnyServiceMeta::new::<Self>();
60}
61
62#[derive(Debug, Clone)]
63pub struct AnyServiceMeta {
64    commands: MetaType,
65    queries: MetaType,
66    events: MetaType,
67    base_services: &'static [BaseServiceMeta],
68    interface_id: InterfaceId,
69}
70
71impl AnyServiceMeta {
72    pub const fn new<S: ServiceMeta + ?Sized>() -> Self {
73        Self {
74            commands: S::CommandsMeta::META,
75            queries: S::QueriesMeta::META,
76            events: S::EventsMeta::META,
77            base_services: S::BASE_SERVICES,
78            interface_id: S::INTERFACE_ID,
79        }
80    }
81
82    pub fn commands(&self) -> &MetaType {
83        &self.commands
84    }
85
86    pub fn queries(&self) -> &MetaType {
87        &self.queries
88    }
89
90    pub fn events(&self) -> &MetaType {
91        &self.events
92    }
93
94    pub fn base_services(&self) -> impl Iterator<Item = (&'static str, AnyServiceMeta)> {
95        self.base_services
96            .iter()
97            .map(|base| (base.name, base.meta.clone()))
98    }
99
100    pub fn interface_id(&self) -> InterfaceId {
101        self.interface_id
102    }
103}
104
105pub trait ProgramMeta {
106    type ConstructorsMeta: TypeInfo;
107    const SERVICES: &'static [(&'static str, AnyServiceMeta)];
108    const ASYNC: bool;
109}
110
111pub const fn count_base_services<S: ServiceMeta>() -> usize {
112    let mut counter = 0;
113
114    let direct_base_services = S::BASE_SERVICES;
115    let mut idx = 0;
116    while idx != direct_base_services.len() {
117        let base = &direct_base_services[idx];
118        count_base_services_recursive(&mut counter, base);
119        idx += 1;
120    }
121
122    counter
123}
124
125const fn count_base_services_recursive(counter: &mut usize, base: &BaseServiceMeta) {
126    *counter += 1;
127
128    let base_services = base.base;
129    let mut idx = 0;
130    while idx != base_services.len() {
131        count_base_services_recursive(counter, &base_services[idx]);
132        idx += 1;
133    }
134}
135
136/// Generate interface IDs array from exposed services
137pub const fn interface_ids<const N: usize>(
138    exposed_services: &'static [BaseServiceMeta],
139) -> [(InterfaceId, u8); N] {
140    let mut output = [(InterfaceId([0u8; 8]), 0u8); N];
141
142    let mut exposed_svc_idx = 0;
143    let mut output_offset = 0;
144    let mut route_id = 1;
145    while exposed_svc_idx != exposed_services.len() {
146        let service = &exposed_services[exposed_svc_idx];
147        fill_interface_ids_recursive(&mut output, &mut output_offset, service, route_id);
148        exposed_svc_idx += 1;
149        route_id += 1;
150    }
151
152    assert!(output_offset == N, "Mismatched interface IDs count");
153
154    output
155}
156
157const fn fill_interface_ids_recursive(
158    arr: &mut [(InterfaceId, u8)],
159    offset: &mut usize,
160    service: &BaseServiceMeta,
161    route_id: u8,
162) {
163    arr[*offset] = (service.interface_id, route_id);
164    *offset += 1;
165    let base_services = service.base;
166    let mut idx = 0;
167    while idx != base_services.len() {
168        fill_interface_ids_recursive(arr, offset, &base_services[idx], route_id);
169        idx += 1;
170    }
171}
172
173pub const fn service_has_interface_id(
174    service: &BaseServiceMeta,
175    interface_id: InterfaceId,
176) -> bool {
177    if service.interface_id.as_u64() == interface_id.as_u64() {
178        true
179    } else {
180        let mut idx = 0;
181        while idx != service.base.len() {
182            if service_has_interface_id(&service.base[idx], interface_id) {
183                return true;
184            }
185            idx += 1;
186        }
187        false
188    }
189}
190
191pub const fn str_eq(a: &str, b: &str) -> bool {
192    let a_bytes = a.as_bytes();
193    let b_bytes = b.as_bytes();
194    if a_bytes.len() != b_bytes.len() {
195        return false;
196    }
197    let mut i = 0;
198    while i < a_bytes.len() {
199        if a_bytes[i] != b_bytes[i] {
200            return false;
201        }
202        i += 1;
203    }
204    true
205}
206
207pub const fn bytes32_eq(a: &[u8; 32], b: &[u8; 32]) -> bool {
208    let mut i = 0;
209    while i < 32 {
210        if a[i] != b[i] {
211            return false;
212        }
213        i += 1;
214    }
215    true
216}
217
218pub const fn find_method_data(
219    methods: &'static [MethodMetadata],
220    name: &str,
221    entry_id: Option<u16>,
222) -> Option<&'static MethodMetadata> {
223    if let Some(id) = entry_id {
224        let i = id as usize;
225        if i < methods.len() {
226            return Some(&methods[i]);
227        }
228        return None;
229    }
230    let mut i = 0;
231    while i < methods.len() {
232        let m = &methods[i];
233        if str_eq(m.name, name) {
234            return Some(m);
235        }
236        i += 1;
237    }
238    None
239}
240
241pub const fn find_id(methods: &'static [MethodMetadata], name: &str) -> u16 {
242    let mut i = 0;
243    while i < methods.len() {
244        if str_eq(methods[i].name, name) {
245            return methods[i].entry_id;
246        }
247        i += 1;
248    }
249    0
250}