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