peat_lite/protocol/
capabilities.rs1#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
8pub struct NodeCapabilities(u16);
9
10impl NodeCapabilities {
11 pub const PERSISTENT_STORAGE: u16 = 0x0001;
13 pub const RELAY_CAPABLE: u16 = 0x0002;
15 pub const DOCUMENT_CRDT: u16 = 0x0004;
17 pub const PRIMITIVE_CRDT: u16 = 0x0008;
19 pub const BLOB_STORAGE: u16 = 0x0010;
21 pub const HISTORY_QUERY: u16 = 0x0020;
23 pub const AGGREGATION: u16 = 0x0040;
25 pub const SENSOR_INPUT: u16 = 0x0080;
27 pub const DISPLAY_OUTPUT: u16 = 0x0100;
29 pub const ACTUATION: u16 = 0x0200;
31
32 pub const fn empty() -> Self {
34 Self(0)
35 }
36
37 pub const fn all() -> Self {
39 Self(0xFFFF)
40 }
41
42 pub const fn lite() -> Self {
44 Self(Self::PRIMITIVE_CRDT | Self::SENSOR_INPUT)
45 }
46
47 pub const fn full() -> Self {
49 Self(
50 Self::PERSISTENT_STORAGE
51 | Self::RELAY_CAPABLE
52 | Self::DOCUMENT_CRDT
53 | Self::PRIMITIVE_CRDT
54 | Self::BLOB_STORAGE
55 | Self::HISTORY_QUERY
56 | Self::AGGREGATION,
57 )
58 }
59
60 pub const fn from_bits(bits: u16) -> Self {
62 Self(bits)
63 }
64
65 pub const fn bits(&self) -> u16 {
67 self.0
68 }
69
70 pub const fn has(&self, cap: u16) -> bool {
72 (self.0 & cap) != 0
73 }
74
75 pub fn set(&mut self, cap: u16) {
77 self.0 |= cap;
78 }
79
80 pub fn clear(&mut self, cap: u16) {
82 self.0 &= !cap;
83 }
84
85 pub const fn intersection(&self, other: &Self) -> Self {
87 Self(self.0 & other.0)
88 }
89
90 pub const fn can_sync_with(&self, other: &Self) -> bool {
92 self.has(Self::PRIMITIVE_CRDT) && other.has(Self::PRIMITIVE_CRDT)
93 }
94
95 pub fn encode(&self) -> [u8; 2] {
97 self.0.to_le_bytes()
98 }
99
100 pub fn decode(bytes: [u8; 2]) -> Self {
102 Self(u16::from_le_bytes(bytes))
103 }
104}
105
106impl core::fmt::Display for NodeCapabilities {
107 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
108 write!(f, "[")?;
109 let mut first = true;
110
111 macro_rules! flag {
112 ($cap:expr, $name:expr) => {
113 if self.has($cap) {
114 if !first {
115 write!(f, ", ")?;
116 }
117 write!(f, $name)?;
118 #[allow(unused_assignments)]
119 {
120 first = false;
121 }
122 }
123 };
124 }
125
126 flag!(Self::PERSISTENT_STORAGE, "storage");
127 flag!(Self::RELAY_CAPABLE, "relay");
128 flag!(Self::DOCUMENT_CRDT, "doc-crdt");
129 flag!(Self::PRIMITIVE_CRDT, "prim-crdt");
130 flag!(Self::BLOB_STORAGE, "blob");
131 flag!(Self::HISTORY_QUERY, "history");
132 flag!(Self::AGGREGATION, "agg");
133 flag!(Self::SENSOR_INPUT, "sensor");
134 flag!(Self::DISPLAY_OUTPUT, "display");
135 flag!(Self::ACTUATION, "actuate");
136
137 write!(f, "]")
138 }
139}
140
141#[cfg(test)]
142mod tests {
143 extern crate alloc;
144 use super::*;
145 use alloc::format;
146
147 #[test]
148 fn test_lite_capabilities() {
149 let caps = NodeCapabilities::lite();
150 assert!(caps.has(NodeCapabilities::PRIMITIVE_CRDT));
151 assert!(caps.has(NodeCapabilities::SENSOR_INPUT));
152 assert!(!caps.has(NodeCapabilities::PERSISTENT_STORAGE));
153 assert!(!caps.has(NodeCapabilities::DOCUMENT_CRDT));
154 }
155
156 #[test]
157 fn test_full_capabilities() {
158 let caps = NodeCapabilities::full();
159 assert!(caps.has(NodeCapabilities::PERSISTENT_STORAGE));
160 assert!(caps.has(NodeCapabilities::DOCUMENT_CRDT));
161 assert!(caps.has(NodeCapabilities::PRIMITIVE_CRDT));
162 }
163
164 #[test]
165 fn test_can_sync() {
166 let lite = NodeCapabilities::lite();
167 let full = NodeCapabilities::full();
168 assert!(lite.can_sync_with(&full));
169 assert!(full.can_sync_with(&lite));
170 }
171
172 #[test]
173 fn test_encode_decode() {
174 let caps = NodeCapabilities::lite();
175 let encoded = caps.encode();
176 let decoded = NodeCapabilities::decode(encoded);
177 assert_eq!(caps, decoded);
178 }
179
180 #[test]
181 fn test_display_lite() {
182 let caps = NodeCapabilities::lite();
183 let s = format!("{}", caps);
184 assert_eq!(s, "[prim-crdt, sensor]");
185 }
186
187 #[test]
188 fn test_display_empty() {
189 let caps = NodeCapabilities::empty();
190 let s = format!("{}", caps);
191 assert_eq!(s, "[]");
192 }
193
194 #[test]
195 fn test_bit_values_match_spec() {
196 assert_eq!(NodeCapabilities::PERSISTENT_STORAGE, 0x0001);
197 assert_eq!(NodeCapabilities::RELAY_CAPABLE, 0x0002);
198 assert_eq!(NodeCapabilities::DOCUMENT_CRDT, 0x0004);
199 assert_eq!(NodeCapabilities::PRIMITIVE_CRDT, 0x0008);
200 assert_eq!(NodeCapabilities::BLOB_STORAGE, 0x0010);
201 assert_eq!(NodeCapabilities::HISTORY_QUERY, 0x0020);
202 assert_eq!(NodeCapabilities::AGGREGATION, 0x0040);
203 assert_eq!(NodeCapabilities::SENSOR_INPUT, 0x0080);
204 assert_eq!(NodeCapabilities::DISPLAY_OUTPUT, 0x0100);
205 assert_eq!(NodeCapabilities::ACTUATION, 0x0200);
206 }
207}