coap_lite/
option_value.rs1use alloc::{
4 string::{String, ToString},
5 vec::Vec,
6};
7use core::convert::TryFrom;
8
9use crate::error::IncompatibleOptionValueFormat;
10
11pub trait OptionValueType:
13 Into<Vec<u8>> + TryFrom<Vec<u8>, Error = IncompatibleOptionValueFormat>
14{
15}
16
17macro_rules! option_value_uint_impl {
18 ($struct_name:ident, $type:ty, $bytes:expr) => {
19 #[derive(Debug, Clone, PartialEq)]
20 pub struct $struct_name(pub $type);
21
22 impl From<$struct_name> for Vec<u8> {
23 fn from(value: $struct_name) -> Self {
24 option_from_uint(value.0.into(), $bytes)
25 }
26 }
27
28 impl TryFrom<Vec<u8>> for $struct_name {
29 type Error = IncompatibleOptionValueFormat;
30
31 fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
32 option_to_uint(&value, $bytes)
33 .map(|value_as_u64| $struct_name(value_as_u64 as $type))
36 }
37 }
38
39 impl OptionValueType for $struct_name {}
40 };
41}
42
43fn option_from_uint(value_as_u64: u64, value_size: usize) -> Vec<u8> {
44 if value_as_u64 == 0 {
46 vec![]
47 } else if value_as_u64 < 256 {
48 vec![value_as_u64 as u8]
49 } else {
50 let mut draining_value = value_as_u64;
51
52 let mut output = Vec::with_capacity(value_size);
53 while draining_value > 0 {
54 let next_lowest_byte = (draining_value & 0xff) as u8;
55 assert!(output.len() < value_size);
56 output.push(next_lowest_byte);
57 draining_value >>= 8;
58 }
59
60 output.reverse();
63
64 output
65 }
66}
67
68fn option_to_uint(
69 encoded: &[u8],
70 value_size: usize,
71) -> Result<u64, IncompatibleOptionValueFormat> {
72 if encoded.len() > value_size {
73 Err(IncompatibleOptionValueFormat {
74 message: format!(
75 "overflow: got {} bytes, expected {}",
76 encoded.len(),
77 value_size
78 ),
79 })
80 } else {
81 Ok(encoded.iter().fold(0, |acc, &b| (acc << 8) + b as u64))
82 }
83}
84
85option_value_uint_impl!(OptionValueU8, u8, 1);
86option_value_uint_impl!(OptionValueU16, u16, 2);
87option_value_uint_impl!(OptionValueU32, u32, 4);
88option_value_uint_impl!(OptionValueU64, u64, 8);
89
90#[derive(Debug, Clone, PartialEq)]
91pub struct OptionValueString(pub String);
92
93impl From<OptionValueString> for Vec<u8> {
94 fn from(option_value: OptionValueString) -> Self {
95 option_value.0.into_bytes()
96 }
97}
98
99impl TryFrom<Vec<u8>> for OptionValueString {
100 type Error = IncompatibleOptionValueFormat;
101
102 fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
103 String::from_utf8(value)
104 .map(OptionValueString)
105 .map_err(|e| IncompatibleOptionValueFormat {
106 message: e.to_string(),
107 })
108 }
109}
110
111impl OptionValueType for OptionValueString {}