nomad_protocol/extensions/
priority.rs1use super::negotiation::{ext_type, Extension};
21
22#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
24#[repr(u8)]
25pub enum Priority {
26 Critical = 0,
29
30 High = 1,
33
34 #[default]
37 Normal = 2,
38
39 Low = 3,
42
43 Background = 4,
46}
47
48impl Priority {
49 pub fn from_byte(b: u8) -> Option<Self> {
51 match b {
52 0 => Some(Priority::Critical),
53 1 => Some(Priority::High),
54 2 => Some(Priority::Normal),
55 3 => Some(Priority::Low),
56 4 => Some(Priority::Background),
57 _ => None,
58 }
59 }
60
61 pub fn to_byte(self) -> u8 {
63 self as u8
64 }
65
66 pub fn to_bitmask(self) -> u8 {
68 1 << (self as u8)
69 }
70}
71
72#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
74pub struct PrioritySupportBitmap(pub u8);
75
76impl PrioritySupportBitmap {
77 pub const ALL: Self = Self(0b00011111);
79
80 pub const NORMAL_ONLY: Self = Self(0b00000100);
82
83 pub fn supports(&self, priority: Priority) -> bool {
85 (self.0 & priority.to_bitmask()) != 0
86 }
87
88 pub fn add(&mut self, priority: Priority) {
90 self.0 |= priority.to_bitmask();
91 }
92
93 pub fn remove(&mut self, priority: Priority) {
95 self.0 &= !priority.to_bitmask();
96 }
97
98 pub fn iter_supported(&self) -> impl Iterator<Item = Priority> + '_ {
100 [
101 Priority::Critical,
102 Priority::High,
103 Priority::Normal,
104 Priority::Low,
105 Priority::Background,
106 ]
107 .into_iter()
108 .filter(|p| self.supports(*p))
109 }
110}
111
112#[derive(Debug, Clone, PartialEq, Eq)]
114pub struct PriorityConfig {
115 pub supported: PrioritySupportBitmap,
117 pub default: Priority,
119}
120
121impl Default for PriorityConfig {
122 fn default() -> Self {
123 Self {
124 supported: PrioritySupportBitmap::ALL,
125 default: Priority::Normal,
126 }
127 }
128}
129
130impl PriorityConfig {
131 pub fn all() -> Self {
133 Self::default()
134 }
135
136 pub fn minimal() -> Self {
138 Self {
139 supported: PrioritySupportBitmap::NORMAL_ONLY,
140 default: Priority::Normal,
141 }
142 }
143
144 pub const fn wire_size() -> usize {
146 2 }
148
149 pub fn to_extension(&self) -> Extension {
151 Extension::new(ext_type::PRIORITY, vec![self.supported.0, self.default.to_byte()])
152 }
153
154 pub fn from_extension(ext: &Extension) -> Option<Self> {
156 if ext.ext_type != ext_type::PRIORITY || ext.data.len() < 2 {
157 return None;
158 }
159 let default = Priority::from_byte(ext.data[1])?;
160 Some(Self {
161 supported: PrioritySupportBitmap(ext.data[0]),
162 default,
163 })
164 }
165
166 pub fn negotiate(client: &Self, server: &Self) -> Self {
170 let supported = PrioritySupportBitmap(client.supported.0 & server.supported.0);
171 let default = client.default.max(server.default); Self { supported, default }
173 }
174}
175
176#[cfg(test)]
177mod tests {
178 use super::*;
179
180 #[test]
181 fn test_priority_ordering() {
182 assert!(Priority::Critical < Priority::High);
183 assert!(Priority::High < Priority::Normal);
184 assert!(Priority::Normal < Priority::Low);
185 assert!(Priority::Low < Priority::Background);
186 }
187
188 #[test]
189 fn test_priority_byte_roundtrip() {
190 for p in [
191 Priority::Critical,
192 Priority::High,
193 Priority::Normal,
194 Priority::Low,
195 Priority::Background,
196 ] {
197 assert_eq!(Priority::from_byte(p.to_byte()), Some(p));
198 }
199 assert_eq!(Priority::from_byte(5), None);
200 assert_eq!(Priority::from_byte(255), None);
201 }
202
203 #[test]
204 fn test_bitmap_operations() {
205 let mut bitmap = PrioritySupportBitmap(0);
206 assert!(!bitmap.supports(Priority::Normal));
207
208 bitmap.add(Priority::Normal);
209 bitmap.add(Priority::High);
210 assert!(bitmap.supports(Priority::Normal));
211 assert!(bitmap.supports(Priority::High));
212 assert!(!bitmap.supports(Priority::Critical));
213
214 bitmap.remove(Priority::Normal);
215 assert!(!bitmap.supports(Priority::Normal));
216 assert!(bitmap.supports(Priority::High));
217 }
218
219 #[test]
220 fn test_bitmap_all() {
221 let bitmap = PrioritySupportBitmap::ALL;
222 assert!(bitmap.supports(Priority::Critical));
223 assert!(bitmap.supports(Priority::High));
224 assert!(bitmap.supports(Priority::Normal));
225 assert!(bitmap.supports(Priority::Low));
226 assert!(bitmap.supports(Priority::Background));
227 }
228
229 #[test]
230 fn test_config_extension_roundtrip() {
231 let config = PriorityConfig {
232 supported: PrioritySupportBitmap::ALL,
233 default: Priority::Low,
234 };
235
236 let ext = config.to_extension();
237 assert_eq!(ext.ext_type, ext_type::PRIORITY);
238
239 let decoded = PriorityConfig::from_extension(&ext).unwrap();
240 assert_eq!(decoded, config);
241 }
242
243 #[test]
244 fn test_negotiate() {
245 let client = PriorityConfig {
246 supported: PrioritySupportBitmap(0b00011111), default: Priority::Normal,
248 };
249 let server = PriorityConfig {
250 supported: PrioritySupportBitmap(0b00000111), default: Priority::High,
252 };
253
254 let result = PriorityConfig::negotiate(&client, &server);
255
256 assert!(result.supported.supports(Priority::Critical));
258 assert!(result.supported.supports(Priority::High));
259 assert!(result.supported.supports(Priority::Normal));
260 assert!(!result.supported.supports(Priority::Low));
261 assert!(!result.supported.supports(Priority::Background));
262
263 assert_eq!(result.default, Priority::Normal);
265 }
266}