rust/bt-common: Type for Ltv decode Errors
Add core::ltv::Error which is used for decoding types that
implement Ltv.
This new error type produces better error text as well as exposing the
type of the items that had problems decoding, which is needed for some
upcoming ASCS responses.
Test: updated unit tests; cargo test
Change-Id: Ibe4efae2ae0d1ecffea5a5e6a5fec1b364fd1f8a
Reviewed-on: https://bluetooth-review.git.corp.google.com/c/bluetooth/+/1720
Reviewed-by: Ani Ramakrishnan <aniramakri@google.com>
diff --git a/rust/bt-common/src/core/ltv.rs b/rust/bt-common/src/core/ltv.rs
index 7c71085..d8923e2 100644
--- a/rust/bt-common/src/core/ltv.rs
+++ b/rust/bt-common/src/core/ltv.rs
@@ -2,13 +2,15 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+use thiserror::Error;
+
use crate::packet_encoding::{Decodable, Encodable};
/// Implement Ltv when a collection of types is represented in the Bluetooth
/// specifications as a length-type-value structure. They should have an
/// associated type which can be retrieved from a type byte.
pub trait LtValue: Sized {
- type Type: Into<u8> + Copy;
+ type Type: Into<u8> + Copy + std::fmt::Debug;
const NAME: &'static str;
@@ -44,7 +46,7 @@
/// so and includes the error. If an unrecoverable error occurs, does
/// not consume the final item and the last element in the result is the
/// error.
- fn decode_all(buf: &[u8]) -> (Vec<Result<Self, crate::packet_encoding::Error>>, usize) {
+ fn decode_all(buf: &[u8]) -> (Vec<Result<Self, Error<Self::Type>>>, usize) {
let mut results = Vec::new();
let mut total_consumed = 0;
loop {
@@ -57,7 +59,9 @@
results.push(Ok(item));
total_consumed += consumed;
}
- Err(e @ crate::packet_encoding::Error::UnexpectedDataLength) => {
+ // If we are missing the type / length completely, or missing some of the data, we
+ // can't continue
+ Err(e @ Error::MissingType) | Err(e @ Error::MissingData(_)) => {
results.push(Err(e));
return (results, total_consumed);
}
@@ -71,6 +75,32 @@
}
}
+#[derive(Error, Debug, PartialEq)]
+pub enum Error<Type: std::fmt::Debug + Into<u8> + Copy> {
+ #[error("Buffer too short for next type")]
+ MissingType,
+ #[error("Buffer missing data indicated by length (type {0:?})")]
+ MissingData(Type),
+ #[error("Unrecognized type value for {0}: {1}")]
+ UnrecognizedType(String, u8),
+ #[error("Length of item ({0}) is outside allowed range for {1:?}: {2:?}")]
+ LengthOutOfRange(u8, Type, std::ops::RangeInclusive<u8>),
+ #[error("Error decoding type {0:?}: {1}")]
+ TypeFailedToDecode(Type, crate::packet_encoding::Error),
+}
+
+impl<Type: std::fmt::Debug + Into<u8> + Copy> Error<Type> {
+ pub fn type_value(&self) -> Option<u8> {
+ match self {
+ Self::MissingType => None,
+ Self::MissingData(t)
+ | Self::LengthOutOfRange(_, t, _)
+ | Self::TypeFailedToDecode(t, _) => Some((*t).into()),
+ Self::UnrecognizedType(_, value) => Some(*value),
+ }
+ }
+}
+
impl<T: LtValue> Encodable for T {
type Error = crate::packet_encoding::Error;
@@ -90,29 +120,28 @@
}
impl<T: LtValue> Decodable for T {
- type Error = crate::packet_encoding::Error;
+ type Error = Error<T::Type>;
fn decode(buf: &[u8]) -> core::result::Result<(Self, usize), Self::Error> {
if buf.len() < 2 {
- return Err(crate::packet_encoding::Error::UnexpectedDataLength);
+ return Err(Error::MissingType);
}
let indicated_len = buf[0] as usize;
- if buf.len() < indicated_len + 1 {
- return Err(crate::packet_encoding::Error::UnexpectedDataLength);
- }
-
let Some(ty) = Self::type_from_octet(buf[1]) else {
- return Err(crate::packet_encoding::Error::UnrecognizedType(
- Self::NAME.to_owned(),
- buf[1],
- ));
+ return Err(Error::UnrecognizedType(Self::NAME.to_owned(), buf[1]));
};
-
- if !Self::length_range_from_type(ty).contains(&((buf.len() - 1) as u8)) {
- return Err(crate::packet_encoding::Error::UnexpectedDataLength);
+ if buf.len() < indicated_len + 1 {
+ return Err(Error::MissingData(ty));
}
-
- Ok((Self::decode_value(&ty, &buf[2..=indicated_len])?, indicated_len + 1))
+ let size_range = Self::length_range_from_type(ty);
+ let remaining_len = (buf.len() - 1) as u8;
+ if !size_range.contains(&remaining_len) {
+ return Err(Error::LengthOutOfRange(remaining_len, ty, size_range));
+ }
+ match Self::decode_value(&ty, &buf[2..=indicated_len]) {
+ Err(e) => Err(Error::TypeFailedToDecode(ty, e)),
+ Ok(s) => Ok((s, indicated_len + 1)),
+ }
}
}
@@ -251,10 +280,7 @@
let (decoded, consumed) = TestValues::decode_all(&encoded);
assert_eq!(consumed, encoded.len());
assert_eq!(decoded[0], Ok(TestValues::TwoBytes(4097)));
- assert_eq!(
- decoded[1],
- Err(crate::packet_encoding::Error::UnrecognizedType("TestValues".to_owned(), 6))
- );
+ assert_eq!(decoded[1], Err(Error::UnrecognizedType("TestValues".to_owned(), 6)));
}
#[track_caller]
@@ -297,7 +323,13 @@
let (decoded, consumed) = TestValues::decode_all(&encoded);
assert_eq!(consumed, encoded.len());
assert_eq!(decoded[0], Ok(TestValues::TwoBytes(4097)));
- assert_eq!(decoded[1], Err(crate::packet_encoding::Error::OutOfRange),);
+ assert_eq!(
+ decoded[1],
+ Err(Error::TypeFailedToDecode(
+ TestType::AlwaysError,
+ crate::packet_encoding::Error::OutOfRange
+ ))
+ );
assert_eq!(decoded[2], Ok(TestValues::OneByte(3)));
}