doip_codec/
encoder.rs

1use doip_definitions::{header::PayloadType, payload::DoipPayload, DoipMessage};
2use heapless::Vec;
3
4use crate::{
5    doip_message::{header::HeaderCodec, payload::PayloadCodec},
6    error::EncodeError,
7    DoipCodec, Encoder,
8};
9
10impl<const N: usize> Encoder<DoipMessage<N>, N> for DoipCodec<N> {
11    type Error = EncodeError;
12
13    fn to_bytes(&mut self, item: DoipMessage<N>, dst: &mut Vec<u8, N>) -> Result<(), Self::Error> {
14        validate_payload_match(&item)?;
15
16        let header_len = item.header.payload_length as usize;
17        let () = HeaderCodec {}.to_bytes(item.header, dst)?;
18
19        let before_len = dst.len();
20        let () = PayloadCodec {}.to_bytes(item.payload, dst)?;
21        let after_len = dst.len();
22
23        validate_payload_length(header_len, after_len - before_len)?;
24
25        Ok(())
26    }
27}
28
29fn validate_payload_match<const N: usize>(item: &DoipMessage<N>) -> Result<(), EncodeError> {
30    let valid = match item.payload {
31        DoipPayload::GenericNack(_) => item.header.payload_type == PayloadType::GenericNack,
32        DoipPayload::VehicleIdentificationRequest(_) => {
33            item.header.payload_type == PayloadType::VehicleIdentificationRequest
34        }
35        DoipPayload::VehicleIdentificationRequestEid(_) => {
36            item.header.payload_type == PayloadType::VehicleIdentificationRequestEid
37        }
38        DoipPayload::VehicleIdentificationRequestVin(_) => {
39            item.header.payload_type == PayloadType::VehicleIdentificationRequestVin
40        }
41        DoipPayload::VehicleAnnouncementMessage(_) => {
42            item.header.payload_type == PayloadType::VehicleAnnouncementMessage
43        }
44        DoipPayload::RoutingActivationRequest(_) => {
45            item.header.payload_type == PayloadType::RoutingActivationRequest
46        }
47        DoipPayload::RoutingActivationResponse(_) => {
48            item.header.payload_type == PayloadType::RoutingActivationResponse
49        }
50        DoipPayload::AliveCheckRequest(_) => {
51            item.header.payload_type == PayloadType::AliveCheckRequest
52        }
53        DoipPayload::AliveCheckResponse(_) => {
54            item.header.payload_type == PayloadType::AliveCheckResponse
55        }
56        DoipPayload::EntityStatusRequest(_) => {
57            item.header.payload_type == PayloadType::EntityStatusRequest
58        }
59        DoipPayload::EntityStatusResponse(_) => {
60            item.header.payload_type == PayloadType::EntityStatusResponse
61        }
62        DoipPayload::PowerInformationRequest(_) => {
63            item.header.payload_type == PayloadType::PowerInformationRequest
64        }
65        DoipPayload::PowerInformationResponse(_) => {
66            item.header.payload_type == PayloadType::PowerInformationResponse
67        }
68        DoipPayload::DiagnosticMessage(_) => {
69            item.header.payload_type == PayloadType::DiagnosticMessage
70        }
71        DoipPayload::DiagnosticMessageAck(_) => {
72            item.header.payload_type == PayloadType::DiagnosticMessageAck
73        }
74        DoipPayload::DiagnosticMessageNack(_) => {
75            item.header.payload_type == PayloadType::DiagnosticMessageNack
76        }
77    };
78
79    if valid {
80        Ok(())
81    } else {
82        Err(EncodeError::PayloadTypeValidation)
83    }
84}
85
86fn validate_payload_length(header_len: usize, length: usize) -> Result<(), EncodeError> {
87    if header_len != length {
88        return Err(EncodeError::PayloadLengthValidation);
89    }
90    Ok(())
91}
92
93#[cfg(feature = "std")]
94impl<const N: usize> tokio_util::codec::Encoder<DoipMessage<N>> for DoipCodec<N> {
95    type Error = EncodeError;
96
97    fn encode(
98        &mut self,
99        item: DoipMessage<N>,
100        dst: &mut tokio_util::bytes::BytesMut,
101    ) -> Result<(), Self::Error> {
102        println!("{:?}", item);
103        let mut heapless_dst = heapless::Vec::<u8, N>::new();
104
105        DoipCodec {}.to_bytes(item, &mut heapless_dst)?;
106        dst.extend_from_slice(&heapless_dst);
107
108        Ok(())
109    }
110}
111
112#[cfg(test)]
113mod tests {
114    use doip_definitions::{
115        header::{DoipHeader, PayloadType, ProtocolVersion},
116        payload::{AliveCheckRequest, DoipPayload, GenericNack, NackCode},
117        DoipMessage,
118    };
119
120    use crate::encoder::validate_payload_length;
121
122    use super::validate_payload_match;
123
124    #[test]
125    fn test_validate_payload_match() {
126        let item_valid = DoipMessage {
127            header: DoipHeader {
128                protocol_version: ProtocolVersion::Iso13400_2012,
129                inverse_protocol_version: 0xfd,
130                payload_type: PayloadType::GenericNack,
131                payload_length: 1u32,
132            },
133            payload: DoipPayload::<1>::GenericNack(GenericNack {
134                nack_code: NackCode::OutOfMemory,
135            }),
136        };
137        let valid = validate_payload_match(&item_valid);
138        assert!(valid.is_ok());
139
140        let item_invalid = DoipMessage {
141            header: DoipHeader {
142                protocol_version: ProtocolVersion::Iso13400_2012,
143                inverse_protocol_version: 0xfd,
144                payload_type: PayloadType::GenericNack,
145                payload_length: 1u32,
146            },
147            payload: DoipPayload::<1>::AliveCheckRequest(AliveCheckRequest {}),
148        };
149
150        let invalid = validate_payload_match(&item_invalid);
151        assert!(invalid.is_err());
152    }
153
154    #[test]
155    fn test_validate_payload_length() {
156        let valid = validate_payload_length(1, 1);
157        assert!(valid.is_ok());
158
159        let invalid = validate_payload_length(1, 2);
160        assert!(invalid.is_err());
161    }
162}