1 // Copyright 2022 Google LLC 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 //! Data element for TX Power. 16 17 use crate::legacy::data_elements::de_type::{DeActualLength, DeEncodedLength, DeTypeCode}; 18 use crate::legacy::data_elements::{ 19 DataElementDeserializeError, DataElementSerializationBuffer, DataElementSerializeError, 20 DeserializeDataElement, DirectMapPredicate, DirectMapper, LengthMapper, SerializeDataElement, 21 }; 22 use crate::legacy::PacketFlavor; 23 use crate::private::Sealed; 24 use crate::shared_data::TxPower; 25 use sink::Sink; 26 27 /// Data element holding a [TxPower]. 28 #[derive(Debug, PartialEq, Eq, Clone)] 29 pub struct TxPowerDataElement { 30 /// The tx power value 31 pub tx_power: TxPower, 32 } 33 34 impl TxPowerDataElement { 35 /// Gets the underlying Tx Power value tx_power_value(&self) -> i836 pub fn tx_power_value(&self) -> i8 { 37 self.tx_power.as_i8() 38 } 39 } 40 41 impl From<TxPower> for TxPowerDataElement { from(tx_power: TxPower) -> Self42 fn from(tx_power: TxPower) -> Self { 43 Self { tx_power } 44 } 45 } 46 47 impl Sealed for TxPowerDataElement {} 48 49 impl<F: PacketFlavor> SerializeDataElement<F> for TxPowerDataElement { de_type_code(&self) -> DeTypeCode50 fn de_type_code(&self) -> DeTypeCode { 51 TxPowerDataElement::DE_TYPE_CODE 52 } 53 map_actual_len_to_encoded_len(&self, actual_len: DeActualLength) -> DeEncodedLength54 fn map_actual_len_to_encoded_len(&self, actual_len: DeActualLength) -> DeEncodedLength { 55 <Self as DeserializeDataElement>::LengthMapper::map_actual_len_to_encoded_len(actual_len) 56 } 57 serialize_contents( &self, sink: &mut DataElementSerializationBuffer, ) -> Result<(), DataElementSerializeError>58 fn serialize_contents( 59 &self, 60 sink: &mut DataElementSerializationBuffer, 61 ) -> Result<(), DataElementSerializeError> { 62 sink.try_extend_from_slice(self.tx_power.as_i8().to_be_bytes().as_slice()) 63 .ok_or(DataElementSerializeError::InsufficientSpace) 64 } 65 } 66 67 impl DeserializeDataElement for TxPowerDataElement { 68 const DE_TYPE_CODE: DeTypeCode = match DeTypeCode::try_from(0b0101) { 69 Ok(t) => t, 70 Err(_) => unreachable!(), 71 }; 72 73 type LengthMapper = DirectMapper<TxPowerLengthPredicate>; 74 deserialize<F: PacketFlavor>( de_contents: &[u8], ) -> Result<Self, DataElementDeserializeError>75 fn deserialize<F: PacketFlavor>( 76 de_contents: &[u8], 77 ) -> Result<Self, DataElementDeserializeError> { 78 de_contents 79 .try_into() 80 .ok() 81 .and_then(|arr: [u8; 1]| TxPower::try_from(i8::from_be_bytes(arr)).ok()) 82 .map(|tx_power| Self { tx_power }) 83 .ok_or(DataElementDeserializeError::DeserializeError { de_type: Self::DE_TYPE_CODE }) 84 } 85 } 86 87 pub(in crate::legacy) struct TxPowerLengthPredicate; 88 89 impl DirectMapPredicate for TxPowerLengthPredicate { is_valid(len: usize) -> bool90 fn is_valid(len: usize) -> bool { 91 len == 1 92 } 93 } 94 95 #[allow(clippy::unwrap_used)] 96 #[cfg(test)] 97 mod tests { 98 use crate::legacy::data_elements::de_type::{DeActualLength, DeEncodedLength}; 99 use crate::legacy::data_elements::tests::macros::de_roundtrip_test; 100 use crate::legacy::data_elements::tx_power::TxPowerDataElement; 101 use crate::legacy::data_elements::{DeserializeDataElement, LengthMapper}; 102 use crate::legacy::serialize::tests::serialize; 103 use crate::legacy::{Ciphertext, Plaintext}; 104 use crate::{shared_data, DeLengthOutOfRange}; 105 use std::panic; 106 107 extern crate std; 108 109 #[test] actual_length_must_be_1()110 fn actual_length_must_be_1() { 111 for l in [0, 2] { 112 let actual = DeActualLength::try_from(l).unwrap(); 113 let _ = panic::catch_unwind(|| { 114 <TxPowerDataElement as DeserializeDataElement>::LengthMapper::map_actual_len_to_encoded_len(actual) 115 }) 116 .unwrap_err(); 117 } 118 119 assert_eq!( 120 1, 121 <TxPowerDataElement as DeserializeDataElement>::LengthMapper::map_actual_len_to_encoded_len( 122 DeActualLength::try_from(1).unwrap(), 123 ) 124 .as_u8() 125 ) 126 } 127 128 #[test] encoded_length_must_be_1()129 fn encoded_length_must_be_1() { 130 for l in [0, 2] { 131 assert_eq!( 132 DeLengthOutOfRange, 133 <TxPowerDataElement as DeserializeDataElement>::LengthMapper::map_encoded_len_to_actual_len( 134 DeEncodedLength::try_from(l).unwrap() 135 ) 136 .unwrap_err() 137 ) 138 } 139 140 assert_eq!( 141 1, 142 <TxPowerDataElement as DeserializeDataElement>::LengthMapper::map_encoded_len_to_actual_len( 143 DeEncodedLength::from(1) 144 ) 145 .unwrap() 146 .as_u8() 147 ); 148 } 149 150 #[test] tx_power_de_contents_roundtrip_unencrypted()151 fn tx_power_de_contents_roundtrip_unencrypted() { 152 let tx = shared_data::TxPower::try_from(-10).unwrap(); 153 let _ = de_roundtrip_test!( 154 TxPowerDataElement, 155 TxPower, 156 TxPower, 157 Plaintext, 158 serialize::<Plaintext, _>(&TxPowerDataElement::from(tx)) 159 ); 160 } 161 162 #[test] tx_power_de_contents_roundtrip_ldt()163 fn tx_power_de_contents_roundtrip_ldt() { 164 let tx = shared_data::TxPower::try_from(-10).unwrap(); 165 166 let _ = de_roundtrip_test!( 167 TxPowerDataElement, 168 TxPower, 169 TxPower, 170 Ciphertext, 171 serialize::<Ciphertext, _>(&TxPowerDataElement::from(tx)) 172 ); 173 } 174 175 mod coverage_gaming { 176 use crate::legacy::data_elements::tx_power::TxPowerDataElement; 177 use crate::shared_data::TxPower; 178 use alloc::format; 179 180 #[test] tx_power_de()181 fn tx_power_de() { 182 let de = TxPowerDataElement::from(TxPower::try_from(3).unwrap()); 183 // debug 184 let _ = format!("{:?}", de); 185 // trivial accessor 186 assert_eq!(3, de.tx_power_value()); 187 } 188 } 189 } 190