use byteorder::{ReadBytesExt, WriteBytesExt};
use crate::{
DTCRecord, DTCStatusMask, Error, IterableWireFormat, SingleValueWireFormat, WireFormat,
};
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum DTCExtDataRecordNumber {
ISOSAEReserved(u8),
VehicleManufacturer(u8),
RegulatedEmissionsOBDDTCExtDataRecords(u8),
RegulatedDTCExtDataRecords(u8),
AllRegulatedEmissionsOBDDTCExtDataRecords,
AllDTCExtDataRecords,
}
impl DTCExtDataRecordNumber {
#[must_use]
pub fn new(value: u8) -> Self {
match value {
0x00 | 0xF0..=0xFD => Self::ISOSAEReserved(value),
0x01..=0x8F => Self::VehicleManufacturer(value),
0x90..=0x9F => Self::RegulatedEmissionsOBDDTCExtDataRecords(value),
0xA0..=0xEF => Self::RegulatedDTCExtDataRecords(value),
0xFE => Self::AllRegulatedEmissionsOBDDTCExtDataRecords,
0xFF => Self::AllDTCExtDataRecords,
}
}
#[must_use]
#[allow(clippy::match_same_arms)]
pub fn value(&self) -> u8 {
match self {
Self::ISOSAEReserved(value) => *value,
Self::VehicleManufacturer(value) => *value,
Self::RegulatedEmissionsOBDDTCExtDataRecords(value) => *value,
Self::RegulatedDTCExtDataRecords(value) => *value,
Self::AllRegulatedEmissionsOBDDTCExtDataRecords => 0xFE,
Self::AllDTCExtDataRecords => 0xFF,
}
}
}
impl PartialEq<u8> for DTCExtDataRecordNumber {
fn eq(&self, other: &u8) -> bool {
self.value() == *other
}
}
impl WireFormat for DTCExtDataRecordNumber {
fn decode<T: std::io::Read>(reader: &mut T) -> Result<Option<Self>, Error> {
match reader.read_u8() {
Ok(ext_data_record_number) => Ok(Some(Self::new(ext_data_record_number))),
Err(_) => Ok(None),
}
}
fn required_size(&self) -> usize {
1
}
fn encode<T: std::io::Write>(&self, writer: &mut T) -> Result<usize, Error> {
writer.write_u8(self.value())?;
Ok(self.required_size())
}
}
impl SingleValueWireFormat for DTCExtDataRecordNumber {}
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[derive(Clone, Debug, PartialEq)]
pub struct DTCExtDataRecord<UserPayload> {
pub data: Vec<UserPayload>,
}
impl<UserPayload: IterableWireFormat> WireFormat for DTCExtDataRecord<UserPayload> {
fn decode<T: std::io::Read>(reader: &mut T) -> Result<Option<Self>, Error> {
let mut data = Vec::new();
for payload in UserPayload::decode_iterable(reader) {
match payload {
Err(_) => return Ok(None),
Ok(payload) => {
data.push(payload);
}
}
}
Ok(Some(Self { data }))
}
fn required_size(&self) -> usize {
self.data
.iter()
.map(WireFormat::required_size)
.sum::<usize>()
}
fn encode<T: std::io::Write>(&self, writer: &mut T) -> Result<usize, Error> {
for d in &self.data {
d.encode(writer)?;
}
Ok(self.required_size())
}
}
impl<UserPayload: IterableWireFormat> SingleValueWireFormat for DTCExtDataRecord<UserPayload> {}
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[derive(Clone, Debug, PartialEq)]
pub struct DTCExtDataRecordList<UserPayload> {
pub mask_record: DTCRecord,
pub status_mask: DTCStatusMask,
pub record_data: Vec<DTCExtDataRecord<UserPayload>>,
}
impl<UserPayload: IterableWireFormat> WireFormat for DTCExtDataRecordList<UserPayload> {
fn decode<T: std::io::Read>(reader: &mut T) -> Result<Option<Self>, Error> {
let mask_record = DTCRecord::decode_single_value(reader)?;
let status_mask = DTCStatusMask::decode_single_value(reader)?;
let mut record_data = Vec::new();
if let Some(record) = DTCExtDataRecord::decode(reader)? {
record_data.push(record);
}
Ok(Some(Self {
mask_record,
status_mask,
record_data,
}))
}
fn required_size(&self) -> usize {
self.mask_record.required_size()
+ self.status_mask.required_size()
+ self
.record_data
.iter()
.map(WireFormat::required_size)
.sum::<usize>()
}
fn encode<T: std::io::Write>(&self, writer: &mut T) -> Result<usize, Error> {
self.mask_record.encode(writer)?;
self.status_mask.encode(writer)?;
for record in &self.record_data {
record.encode(writer)?;
}
Ok(self.required_size())
}
}
impl<UserPayload: IterableWireFormat> SingleValueWireFormat for DTCExtDataRecordList<UserPayload> {}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn record_number() {
let record_number = DTCExtDataRecordNumber::new(0x00);
assert_eq!(record_number, DTCExtDataRecordNumber::ISOSAEReserved(0x00));
assert_eq!(record_number.value(), 0x00);
}
}