alsa_ctl_tlv_codec/
lib.rs1#![doc = include_str!("../README.md")]
5
6#[allow(dead_code)]
7mod uapi;
8
9mod containers;
10mod items;
11mod range_utils;
12
13pub use {containers::*, items::*, range_utils::*};
14
15use uapi::*;
16
17#[derive(Debug, Clone, PartialEq, Eq)]
19pub enum TlvDecodeErrorCtx {
20 Length(
22 usize,
24 usize,
26 ),
27 ValueType(
29 u32,
31 &'static [u32],
33 ),
34 ValueLength(
36 usize,
38 usize,
40 ),
41 ValueContent(Box<TlvDecodeError>),
43}
44
45#[derive(Debug, Clone, PartialEq, Eq)]
47pub struct TlvDecodeError {
48 pub ctx: TlvDecodeErrorCtx,
50 pub offset: usize,
54}
55
56impl TlvDecodeError {
57 pub fn new(ctx: TlvDecodeErrorCtx, offset: usize) -> Self {
58 Self { ctx, offset }
59 }
60
61 pub fn delve_into_lowest_error(&self, mut offset: usize) -> (&TlvDecodeError, usize) {
62 offset += self.offset;
63 match &self.ctx {
64 TlvDecodeErrorCtx::ValueContent(e) => e.delve_into_lowest_error(offset),
65 _ => (self, offset),
66 }
67 }
68}
69
70impl std::fmt::Display for TlvDecodeError {
71 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
72 let (err, offset) = self.delve_into_lowest_error(0);
73 match err.ctx {
74 TlvDecodeErrorCtx::Length(len, at_least) => {
75 write!(
76 f,
77 "Insufficient length of array {}, should be greater than {}, at {}",
78 len, at_least, offset
79 )
80 }
81 TlvDecodeErrorCtx::ValueType(val_type, allowed_types) => {
82 let mut allowed = format!("{}", allowed_types[0]);
83 allowed_types[1..]
84 .iter()
85 .for_each(|allowed_type| allowed = format!("{}, {}", allowed, allowed_type));
86 write!(
87 f,
88 "Invalid value {} in type field, expected {}, at {}",
89 val_type, allowed, offset
90 )
91 }
92 TlvDecodeErrorCtx::ValueLength(val_len, array_len) => {
93 write!(
94 f,
95 "Invalid value {} in length field, actual {}, at {}",
96 val_len, array_len, offset
97 )
98 }
99 _ => unreachable!(),
100 }
101 }
102}
103
104impl std::error::Error for TlvDecodeError {}
105
106pub trait TlvData<'a>: std::convert::TryFrom<&'a [u32]>
111where
112 for<'b> Vec<u32>: From<&'b Self>,
113{
114 fn value_type(&self) -> u32;
116
117 fn value_length(&self) -> usize;
119
120 fn value(&self) -> Vec<u32>;
122}
123
124#[derive(Debug, Clone, PartialEq, Eq)]
130pub enum TlvItem {
131 Container(Container),
132 DbRange(DbRange),
133 DbScale(DbScale),
134 DbInterval(DbInterval),
135 Chmap(Chmap),
136 Unknown(Vec<u32>),
137}
138
139impl<'a> std::convert::TryFrom<&'a [u32]> for TlvItem {
140 type Error = TlvDecodeError;
141
142 fn try_from(raw: &[u32]) -> Result<Self, Self::Error> {
143 match raw[0] {
144 SNDRV_CTL_TLVT_CONTAINER => {
145 Container::try_from(raw).map(|data| TlvItem::Container(data))
146 }
147 SNDRV_CTL_TLVT_DB_RANGE => DbRange::try_from(raw).map(|data| TlvItem::DbRange(data)),
148 SNDRV_CTL_TLVT_DB_SCALE => DbScale::try_from(raw).map(|data| TlvItem::DbScale(data)),
149 SNDRV_CTL_TLVT_DB_LINEAR | SNDRV_CTL_TLVT_DB_MINMAX | SNDRV_CTL_TLVT_DB_MINMAX_MUTE => {
150 DbInterval::try_from(raw).map(|data| TlvItem::DbInterval(data))
151 }
152 SNDRV_CTL_TLVT_CHMAP_FIXED | SNDRV_CTL_TLVT_CHMAP_VAR | SNDRV_CTL_TLVT_CHMAP_PAIRED => {
153 Chmap::try_from(raw).map(|data| TlvItem::Chmap(data))
154 }
155 _ => Ok(TlvItem::Unknown(raw.to_owned())),
156 }
157 }
158}
159
160impl From<&TlvItem> for Vec<u32> {
161 fn from(item: &TlvItem) -> Self {
162 match item {
163 TlvItem::Container(d) => d.into(),
164 TlvItem::DbRange(d) => d.into(),
165 TlvItem::DbScale(d) => d.into(),
166 TlvItem::DbInterval(d) => d.into(),
167 TlvItem::Chmap(d) => d.into(),
168 TlvItem::Unknown(d) => d.to_owned(),
169 }
170 }
171}
172
173impl From<TlvItem> for Vec<u32> {
174 fn from(item: TlvItem) -> Self {
175 (&item).into()
176 }
177}