Skip to main content

nprpc/
interface.rs

1//! Interface and Interface Methods
2
3use postcard_schema_ng::{
4    Schema,
5    key::{
6        Key,
7        hash::{HashItem, HashOptions, fnv1a64::hasher},
8    },
9    schema::DataModelType,
10};
11use serde::{Deserialize, Serialize};
12
13const fn method_hasher<T: Schema, U: Schema>(path: &str) -> Key {
14    let mut options = HashOptions::DEFAULT;
15    options.hash_max_size = true;
16    let items = [
17        HashItem::String(path),
18        HashItem::Schema(T::SCHEMA),
19        HashItem::Schema(U::SCHEMA),
20    ];
21    Key::from_bytes(hasher(&options, &items))
22}
23
24/// A trait containing the metadata of a given method of an method
25pub trait Method {
26    type Request<'rqst>: Schema + Serialize + Deserialize<'rqst>;
27    type Response<'resp>: Schema + Serialize + Deserialize<'resp>;
28
29    const NAME: &'static str;
30
31    const KEY: Key = method_hasher::<Self::Request<'static>, Self::Response<'static>>(Self::NAME);
32
33    const INFO: MethodInfo = MethodInfo {
34        name: Self::NAME,
35        key: Self::KEY,
36        req_schema: <Self::Request<'static> as Schema>::SCHEMA,
37        resp_schema: <Self::Response<'static> as Schema>::SCHEMA,
38    };
39}
40
41/// A struct containing metadata for a given method of an interface
42#[derive(Debug, Clone, Copy)]
43pub struct MethodInfo {
44    pub name: &'static str,
45    pub key: Key,
46    pub req_schema: &'static DataModelType,
47    pub resp_schema: &'static DataModelType,
48}
49
50/// A struct containing metadata for a given interface
51#[derive(Debug, Clone, Copy)]
52pub struct InterfaceInfo {
53    pub max_request_size: Option<usize>,
54    pub max_response_size: Option<usize>,
55    pub methods: &'static [MethodInfo],
56}
57
58/// Calculated the largest request body payload in bytes.
59///
60/// Returns `None` if one or more request type is unbounded.
61pub const fn rqst_body_max_buf_required(infos: &[MethodInfo]) -> Option<usize> {
62    // Ensure that buffers have AT LEAST enough for the WireError type
63    let mut max = crate::wire::WireError::SCHEMA.max_size().unwrap();
64    let mut idx = 0;
65    while idx < infos.len() {
66        let Some(m) = infos[idx].req_schema.max_size() else {
67            return None;
68        };
69        if m > max {
70            max = m;
71        }
72        idx += 1;
73    }
74    Some(max)
75}
76
77/// Calculated the largest response body payload in bytes.
78///
79/// Returns `None` if one or more response type is unbounded.
80pub const fn resp_body_max_buf_required(infos: &[MethodInfo]) -> Option<usize> {
81    // Ensure that buffers have AT LEAST enough for the WireError type
82    let mut max = crate::wire::WireError::SCHEMA.max_size().unwrap();
83    let mut idx = 0;
84    while idx < infos.len() {
85        let Some(m) = infos[idx].resp_schema.max_size() else {
86            return None;
87        };
88        if m > max {
89            max = m;
90        }
91        idx += 1;
92    }
93    Some(max)
94}
95
96/// Calculates the total number of `MethodInfo`s in an array of array of
97/// `MethodInfo`s.
98pub const fn total_len(sets: &[&[MethodInfo]]) -> usize {
99    let mut i = 0;
100    let mut ct = 0;
101    while i < sets.len() {
102        ct += sets[i].len();
103        i += 1;
104    }
105    ct
106}
107
108/// Flattens an array of array of `MethodInfo`s into a flat array of `MethodInfo`s.
109///
110/// N should be the total length of `sets`, calculated by [`total_len()`].
111pub const fn flatten<const N: usize>(sets: &[&[MethodInfo]]) -> [MethodInfo; N] {
112    pub const ONE: MethodInfo = MethodInfo {
113        name: "",
114        key: Key::from_bytes([0; 8]),
115        req_schema: &DataModelType::Unit,
116        resp_schema: &DataModelType::Unit,
117    };
118
119    let mut out = [ONE; N];
120    let mut i = 0;
121    let mut n = 0;
122    while i < sets.len() {
123        let mut k = 0;
124        while k < sets[i].len() {
125            out[n] = sets[i][k];
126            k += 1;
127            n += 1;
128        }
129        i += 1;
130    }
131    assert!(n == N);
132    out
133}
134
135/// Create an array of [`Key`]s from an array of [`MethodInfo`]s.
136///
137/// N must equal `infos.len()`.
138pub const fn extract_keys<const N: usize>(infos: &[MethodInfo]) -> [Key; N] {
139    assert!(N == infos.len());
140    let mut buf = [Key::from_bytes([0u8; 8]); N];
141    let mut idx = 0;
142    while idx < N {
143        buf[idx] = infos[idx].key;
144        idx += 1;
145    }
146    buf
147}