1use super::*;
17
18#[derive(Copy, Eq, PartialEq, Hash, Clone, Ord, PartialOrd)]
20pub struct OptionNumber(pub u16);
21
22impl OptionNumber {
23 pub const IF_MATCH: OptionNumber = OptionNumber(1);
25
26 pub const URI_HOST: OptionNumber = OptionNumber(3);
28
29 pub const ETAG: OptionNumber = OptionNumber(4);
31
32 pub const IF_NONE_MATCH: OptionNumber = OptionNumber(5);
34
35 pub const OBSERVE: OptionNumber = OptionNumber(6);
37
38 pub const URI_PORT: OptionNumber = OptionNumber(7);
40
41 pub const LOCATION_PATH: OptionNumber = OptionNumber(8);
43
44 pub const OSCORE: OptionNumber = OptionNumber(9);
46
47 pub const URI_PATH: OptionNumber = OptionNumber(11);
49
50 pub const CONTENT_FORMAT: OptionNumber = OptionNumber(12);
52
53 pub const MAX_AGE: OptionNumber = OptionNumber(14);
55
56 pub const URI_QUERY: OptionNumber = OptionNumber(15);
58
59 pub const ACCEPT: OptionNumber = OptionNumber(17);
61
62 pub const LOCATION_QUERY: OptionNumber = OptionNumber(20);
64
65 pub const BLOCK2: OptionNumber = OptionNumber(23);
67
68 pub const BLOCK1: OptionNumber = OptionNumber(27);
70
71 pub const SIZE2: OptionNumber = OptionNumber(28);
73
74 pub const PROXY_URI: OptionNumber = OptionNumber(35);
76
77 pub const PROXY_SCHEME: OptionNumber = OptionNumber(39);
79
80 pub const SIZE1: OptionNumber = OptionNumber(60);
82
83 pub const NO_RESPONSE: OptionNumber = OptionNumber(258);
85
86 pub fn is_critical(self) -> bool {
88 const FLAG_CRITICAL: u16 = 1;
89 self.0 & FLAG_CRITICAL == FLAG_CRITICAL
90 }
91
92 pub fn is_un_safe(self) -> bool {
94 const FLAG_UN_SAFE: u16 = 2;
95 self.0 & FLAG_UN_SAFE == FLAG_UN_SAFE
96 }
97
98 pub fn is_no_cache_key(self) -> bool {
100 const FLAG_NO_CACHE_KEY_MASK: u16 = 0x1e;
101 const FLAG_NO_CACHE_KEY_MAGIC: u16 = 0x1c;
102 self.0 & FLAG_NO_CACHE_KEY_MASK == FLAG_NO_CACHE_KEY_MAGIC
103 }
104
105 pub fn option_value_type(self) -> OptionValueType {
107 match self {
108 OptionNumber::IF_MATCH => OptionValueType::Opaque,
109 OptionNumber::URI_HOST => OptionValueType::String,
110 OptionNumber::ETAG => OptionValueType::Opaque,
111 OptionNumber::IF_NONE_MATCH => OptionValueType::Flag,
112 OptionNumber::OBSERVE => OptionValueType::Integer,
113 OptionNumber::URI_PORT => OptionValueType::Integer,
114 OptionNumber::LOCATION_PATH => OptionValueType::String,
115 OptionNumber::OSCORE => OptionValueType::Opaque,
116 OptionNumber::URI_PATH => OptionValueType::String,
117 OptionNumber::CONTENT_FORMAT => OptionValueType::ContentFormat,
118 OptionNumber::MAX_AGE => OptionValueType::Integer,
119 OptionNumber::URI_QUERY => OptionValueType::String,
120 OptionNumber::ACCEPT => OptionValueType::ContentFormat,
121 OptionNumber::LOCATION_QUERY => OptionValueType::String,
122 OptionNumber::BLOCK2 => OptionValueType::Block,
123 OptionNumber::BLOCK1 => OptionValueType::Block,
124 OptionNumber::SIZE2 => OptionValueType::Integer,
125 OptionNumber::PROXY_URI => OptionValueType::String,
126 OptionNumber::PROXY_SCHEME => OptionValueType::String,
127 OptionNumber::SIZE1 => OptionValueType::Integer,
128 OptionNumber::NO_RESPONSE => OptionValueType::Integer,
129 OptionNumber(_) => OptionValueType::Opaque,
130 }
131 }
132
133 pub fn is_ok_in_request(self) -> bool {
135 match self {
136 OptionNumber::IF_MATCH => true,
137 OptionNumber::URI_HOST => true,
138 OptionNumber::ETAG => true,
139 OptionNumber::IF_NONE_MATCH => true,
140 OptionNumber::OBSERVE => true,
141 OptionNumber::URI_PORT => true,
142 OptionNumber::LOCATION_PATH => false,
143 OptionNumber::URI_PATH => true,
144 OptionNumber::CONTENT_FORMAT => true,
145 OptionNumber::MAX_AGE => false,
146 OptionNumber::URI_QUERY => true,
147 OptionNumber::ACCEPT => true,
148 OptionNumber::LOCATION_QUERY => false,
149 OptionNumber::BLOCK2 => true,
150 OptionNumber::BLOCK1 => true,
151 OptionNumber::SIZE2 => false,
152 OptionNumber::PROXY_URI => true,
153 OptionNumber::PROXY_SCHEME => true,
154 OptionNumber::SIZE1 => true,
155 OptionNumber::NO_RESPONSE => true,
156
157 OptionNumber(_) => true,
159 }
160 }
161
162 pub fn is_ok_in_response(self) -> bool {
164 match self {
165 OptionNumber::IF_MATCH => false,
166 OptionNumber::URI_HOST => false,
167 OptionNumber::ETAG => true,
168 OptionNumber::IF_NONE_MATCH => false,
169 OptionNumber::OBSERVE => true,
170 OptionNumber::URI_PORT => false,
171 OptionNumber::LOCATION_PATH => true,
172 OptionNumber::URI_PATH => false,
173 OptionNumber::CONTENT_FORMAT => true,
174 OptionNumber::MAX_AGE => true,
175 OptionNumber::URI_QUERY => false,
176 OptionNumber::ACCEPT => false,
177 OptionNumber::LOCATION_QUERY => true,
178 OptionNumber::BLOCK2 => true,
179 OptionNumber::BLOCK1 => true,
180 OptionNumber::SIZE2 => true,
181 OptionNumber::PROXY_URI => false,
182 OptionNumber::PROXY_SCHEME => false,
183 OptionNumber::SIZE1 => false,
184 OptionNumber::NO_RESPONSE => false,
185
186 OptionNumber(_) => true,
188 }
189 }
190
191 pub fn is_repeatable(self) -> bool {
194 match self {
195 OptionNumber::IF_MATCH => true,
196 OptionNumber::URI_HOST => false,
197 OptionNumber::ETAG => true,
198 OptionNumber::IF_NONE_MATCH => false,
199 OptionNumber::OBSERVE => false,
200 OptionNumber::URI_PORT => false,
201 OptionNumber::LOCATION_PATH => true,
202 OptionNumber::URI_PATH => true,
203 OptionNumber::CONTENT_FORMAT => false,
204 OptionNumber::MAX_AGE => false,
205 OptionNumber::URI_QUERY => true,
206 OptionNumber::ACCEPT => false,
207 OptionNumber::LOCATION_QUERY => true,
208 OptionNumber::BLOCK2 => false,
209 OptionNumber::BLOCK1 => false,
210 OptionNumber::SIZE2 => false,
211 OptionNumber::PROXY_URI => false,
212 OptionNumber::PROXY_SCHEME => false,
213 OptionNumber::SIZE1 => false,
214 OptionNumber::NO_RESPONSE => false,
215
216 OptionNumber(_) => true,
218 }
219 }
220
221 pub fn static_name(self) -> Option<&'static str> {
225 match self {
226 OptionNumber::IF_MATCH => Some("If-Match"),
227 OptionNumber::URI_HOST => Some("Uri-Host"),
228 OptionNumber::ETAG => Some("ETag"),
229 OptionNumber::IF_NONE_MATCH => Some("If-None-Match"),
230 OptionNumber::OBSERVE => Some("Observe"),
231 OptionNumber::URI_PORT => Some("Uri-Port"),
232 OptionNumber::LOCATION_PATH => Some("Location-Path"),
233 OptionNumber::OSCORE => Some("OSCORE"),
234 OptionNumber::URI_PATH => Some("Uri-Path"),
235 OptionNumber::CONTENT_FORMAT => Some("Content-Format"),
236 OptionNumber::MAX_AGE => Some("Max-Age"),
237 OptionNumber::URI_QUERY => Some("Uri-Query"),
238 OptionNumber::ACCEPT => Some("Accept"),
239 OptionNumber::LOCATION_QUERY => Some("Location-Query"),
240 OptionNumber::BLOCK2 => Some("Block2"),
241 OptionNumber::BLOCK1 => Some("Block1"),
242 OptionNumber::SIZE2 => Some("Size2"),
243 OptionNumber::PROXY_URI => Some("Proxy-Uri"),
244 OptionNumber::PROXY_SCHEME => Some("Proxy-Scheme"),
245 OptionNumber::SIZE1 => Some("Size1"),
246 OptionNumber::NO_RESPONSE => Some("No-Response"),
247 _ => None,
248 }
249 }
250
251 pub fn fmt_with_value(self, f: &mut std::fmt::Formatter<'_>, value: &[u8]) -> std::fmt::Result {
254 write!(f, "{}", self)?;
255 match self.option_value_type() {
256 OptionValueType::Opaque | OptionValueType::Flag => {
257 if !value.is_empty() {
258 f.write_str(":")?;
259 for b in value {
260 write!(f, "{:02X}", b)?;
261 }
262 }
263 }
264 OptionValueType::Integer => {
265 if let Some(i) = try_decode_u32(value) {
266 write!(f, ":{}", i)?;
267 } else {
268 f.write_str("ERR")?;
269 }
270 }
271 OptionValueType::Block => {
272 if let Some(i) = try_decode_u32(value) {
273 write!(f, ":{}", BlockInfo(i))?;
274 } else {
275 f.write_str("ERR")?;
276 }
277 }
278 OptionValueType::ContentFormat => {
279 if let Some(i) = try_decode_u16(value) {
280 write!(f, ":{}", ContentFormat(i))?;
281 } else {
282 f.write_str("ERR")?;
283 }
284 }
285 OptionValueType::String => {
286 if let Ok(s) = std::str::from_utf8(value) {
287 write!(f, ":{:?}", s)?;
288 } else {
289 f.write_str("ERR")?;
290 }
291 }
292 }
293
294 Ok(())
295 }
296}
297
298impl core::fmt::Display for OptionNumber {
299 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
300 if let Some(name) = self.static_name() {
301 f.write_str(name)
302 } else {
303 if self.is_critical() {
305 f.write_str("Opt-")?;
306 } else {
307 f.write_str("Crit-")?;
308 }
309
310 if self.is_un_safe() {
311 f.write_str("UnSafe-")?;
312 }
313
314 if self.is_no_cache_key() {
315 f.write_str("NoCacheKey-")?;
316 }
317
318 write!(f, "{}", self.0)
319 }
320 }
321}
322
323impl core::fmt::Debug for OptionNumber {
324 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
325 write!(f, "{}({})", self.0, self)
326 }
327}
328
329impl core::ops::Add<u16> for OptionNumber {
330 type Output = Self;
331 fn add(self, other: u16) -> Self {
332 OptionNumber(self.0 + other)
333 }
334}
335
336impl core::ops::Sub<OptionNumber> for OptionNumber {
337 type Output = u16;
338 fn sub(self, other: OptionNumber) -> u16 {
339 assert!(self.0 >= other.0);
340 self.0 - other.0
341 }
342}
343
344impl core::cmp::PartialOrd<u16> for OptionNumber {
345 fn partial_cmp(&self, other: &u16) -> Option<core::cmp::Ordering> {
346 Some(self.0.cmp(other))
347 }
348}
349
350impl core::cmp::PartialEq<u16> for OptionNumber {
351 fn eq(&self, other: &u16) -> bool {
352 self.0.eq(other)
353 }
354}
355
356impl Default for OptionNumber {
357 fn default() -> Self {
358 OptionNumber(0)
359 }
360}