1#![allow(non_snake_case)]
6
7#[cfg(all(target_arch = "wasm32", feature = "v2x", feature = "json"))]
8use wasm_bindgen::prelude::*;
9
10#[cfg(all(target_arch = "wasm32", feature = "v2x", feature = "json"))]
11use crate::transport::decode::Decode as _;
12#[cfg(all(target_arch = "wasm32", feature = "v2x", feature = "json"))]
13macro_rules! btp {
14 ($btp_ty:ty, $input:ident) => {
15 <$btp_ty>::decode($input)
16 .map_err(crate::map_err_to_string)
17 .and_then(|(rem, tp)| {
18 tp.encode_to_json()
19 .map_err(crate::map_err_to_string)
20 .map(|json| (rem, json))
21 })
22 };
23}
24
25#[cfg(feature = "_etsi")]
26#[allow(clippy::too_many_lines)]
27pub fn decode(
41 input: &'_ [u8],
42 headers: crate::Headers,
43) -> Result<crate::ItsMessage<'_>, alloc::string::String> {
44 use alloc::borrow::ToOwned as _;
45 use alloc::string::ToString as _;
46
47 use crate::{ItsMessage, standards};
48
49 let (input, transport, geonetworking) = match headers {
50 crate::Headers::None => Ok((input, None, None)),
51 crate::Headers::GnBtp => {
52 decode_gn_btp_headers(input).map(|(rem, tp, gn)| (rem, Some(tp), Some(gn)))
53 }
54 crate::Headers::IEEE802LlcGnBtp => crate::pcap::remove_wlan_headers(input)
55 .and_then(decode_gn_btp_headers)
56 .map(|(rem, tp, gn)| (rem, Some(tp), Some(gn))),
57 crate::Headers::RadioTap802LlcGnBtp => crate::pcap::remove_pcap_headers(input)
58 .and_then(decode_gn_btp_headers)
59 .map(|(rem, tp, gn)| (rem, Some(tp), Some(gn))),
60 }?;
61 let (encoding_rules, mut protocol_version, msg_type) = message_type(input)?;
62
63 let input = match msg_type {
64 1 | 6 => {
66 if let Ok(data) = core::primitive::str::from_utf8(input)
67 && (data.trim_start().starts_with('<') || data.trim_start().starts_with('{'))
68 && data.contains("messageID")
69 {
70 protocol_version = 1;
76 }
77 input.to_owned()
78 }
79 4 | 5 | 9 | 10 => {
81 if let Ok(data) = core::primitive::str::from_utf8(input) {
82 if data.trim_start().starts_with('<') && data.contains("messageID") {
85 let patched_msg = match data.find("</header") {
87 Some(header_end_pos) => {
88 let (header, remains) = data.split_at(header_end_pos);
89
90 let patched_msg = header.replace("messageID", "messageId");
91 let patched_msg = patched_msg.replace("stationID", "stationId");
92
93 patched_msg + remains
94 }
95 None => data.to_string(),
96 };
97
98 patched_msg.as_bytes().to_owned()
99 } else if data.trim_start().starts_with('{') && data.contains("messageID") {
100 let patched_msg = data.replace("messageID", "messageId");
101
102 let patched_msg = patched_msg.replacen("stationID", "stationId", 1);
104
105 patched_msg.as_bytes().to_owned()
106 } else {
107 input.to_owned()
108 }
109 } else {
110 input.to_owned()
111 }
112 }
113 _ => input.to_owned(),
114 };
115
116 match (msg_type, protocol_version) {
117 #[cfg(feature = "denm_2_2_1")]
118 (1, 2) => encoding_rules
119 .codec()
120 .decode_from_binary::<standards::denm_2_2_1::denm_pdu_description::DENM>(&input)
121 .map(|etsi| ItsMessage::DenmV2 {
122 geonetworking,
123 transport,
124 etsi: alloc::boxed::Box::new(etsi)
125 }),
126 #[cfg(feature = "denm_1_3_1")]
127 (1, _) => encoding_rules
128 .codec()
129 .decode_from_binary::<standards::denm_1_3_1::denm_pdu_descriptions::DENM>(&input)
130 .map(|etsi| ItsMessage::DenmV1 {
131 geonetworking,
132 transport,
133 etsi: alloc::boxed::Box::new(etsi)
134 }),
135 #[cfg(feature = "cam_1_4_1")]
136 (2, _) => encoding_rules
137 .codec()
138 .decode_from_binary::<standards::cam_1_4_1::cam_pdu_descriptions::CAM>(&input)
139 .map(|etsi| ItsMessage::Cam {
140 geonetworking,
141 transport,
142 etsi: alloc::boxed::Box::new(etsi)
143 }),
144 #[cfg(feature = "spatem_2_2_1")]
145 (4, _) => encoding_rules
146 .codec()
147 .decode_from_binary::<standards::spatem_2_2_1::spatem_pdu_descriptions::SPATEM>(&input)
148 .map(|etsi| ItsMessage::Spatem {
149 geonetworking,
150 transport,
151 etsi: alloc::boxed::Box::new(etsi)
152 }),
153 #[cfg(feature = "mapem_2_2_1")]
154 (5, _) => encoding_rules
155 .codec()
156 .decode_from_binary::<standards::mapem_2_2_1::mapem_pdu_descriptions::MAPEM>(&input)
157 .map(|etsi| ItsMessage::Mapem {
158 geonetworking,
159 transport,
160 etsi: alloc::boxed::Box::new(etsi)
161 }),
162 #[cfg(feature = "ivim_2_2_1")]
163 (6, 2) => encoding_rules
164 .codec()
165 .decode_from_binary::<standards::ivim_2_2_1::ivim_pdu_descriptions::IVIM>(&input)
166 .map(|etsi| ItsMessage::IvimV2 {
167 geonetworking,
168 transport,
169 etsi: alloc::boxed::Box::new(etsi)
170 }),
171 #[cfg(feature = "ivim_2_1_1")]
172 (6, _) => encoding_rules
173 .codec()
174 .decode_from_binary::<standards::ivim_2_1_1::ivim_pdu_descriptions::IVIM>(&input)
175 .map(|etsi| ItsMessage::IvimV1 {
176 geonetworking,
177 transport,
178 etsi: alloc::boxed::Box::new(etsi)
179 }),
180 #[cfg(feature = "srem_2_2_1")]
181 (9, _) => encoding_rules
182 .codec()
183 .decode_from_binary::<standards::srem_2_2_1::srem_pdu_descriptions::SREM>(&input)
184 .map(|etsi| ItsMessage::Srem {
185 geonetworking,
186 transport,
187 etsi: alloc::boxed::Box::new(etsi)
188 }),
189 #[cfg(feature = "ssem_2_2_1")]
190 (10, _) => encoding_rules
191 .codec()
192 .decode_from_binary::<standards::ssem_2_2_1::ssem_pdu_descriptions::SSEM>(&input)
193 .map(|etsi| ItsMessage::Ssem {
194 geonetworking,
195 transport,
196 etsi: alloc::boxed::Box::new(etsi)
197 }),
198 #[cfg(feature = "cpm_2_1_1")]
199 (14, 2) => encoding_rules
200 .codec()
201 .decode_from_binary::<standards::cpm_2_1_1::cpm_pdu_descriptions::CollectivePerceptionMessage>(&input)
202 .map(|etsi| ItsMessage::CpmV2 {
203 geonetworking,
204 transport,
205 etsi: alloc::boxed::Box::new(etsi)
206 }),
207 #[cfg(feature = "cpm_1")]
208 (14, _) => encoding_rules
209 .codec()
210 .decode_from_binary::<standards::cpm_1::cpm_pdu_descriptions::CPM>(&input)
211 .map(|etsi| ItsMessage::CpmV1 {
212 geonetworking,
213 transport,
214 etsi: alloc::boxed::Box::new(etsi)
215 }),
216 (message_i_d, _) => {
217 return Err(alloc::format!(
218 "Unsupported ITS message type: Found message id {message_i_d}."
219 ))
220 }
221 }.map_err(crate::map_err_to_string)
222}
223
224#[cfg(feature = "transport")]
225pub fn decode_gn_btp_headers(
230 input: &'_ [u8],
231) -> Result<
232 (
233 &'_ [u8],
234 alloc::boxed::Box<crate::transport::TransportHeader>,
235 geonetworking::Packet<'_>,
236 ),
237 alloc::string::String,
238> {
239 use alloc::string::ToString as _;
240
241 use geonetworking::Decode as _;
242
243 use crate::transport::decode::Decode as _;
244
245 let result = geonetworking::Packet::decode(input).map_err(crate::map_err_to_string)?;
246 let Some(payload) = result.decoded.payload() else {
247 return Err("No payload in secured geonetworking header!".to_string());
248 };
249 let (remaining, tp) = match result.decoded.common().next_header {
250 geonetworking::en302636_4_1::NextAfterCommon::Any => {
251 Err("Currently, only BTP and IPv6 Headers can be decoded!".to_string())
252 }
253 geonetworking::en302636_4_1::NextAfterCommon::BTPA => {
254 crate::transport::BasicTransportAHeader::decode(payload)
255 .map(|(rem, btpa)| (rem, crate::transport::TransportHeader::BtpA(btpa)))
256 .map_err(crate::map_err_to_string)
257 }
258 geonetworking::en302636_4_1::NextAfterCommon::BTPB => {
259 crate::transport::BasicTransportBHeader::decode(payload)
260 .map(|(rem, btpb)| (rem, crate::transport::TransportHeader::BtpB(btpb)))
261 .map_err(crate::map_err_to_string)
262 }
263 geonetworking::en302636_4_1::NextAfterCommon::IPv6 => {
264 crate::transport::IPv6Header::decode(payload)
265 .map(|(rem, ipv6)| {
266 (
267 rem,
268 crate::transport::TransportHeader::IPv6(alloc::boxed::Box::new(ipv6)),
269 )
270 })
271 .map_err(crate::map_err_to_string)
272 }
273 }?;
274 Ok((remaining, alloc::boxed::Box::new(tp), result.decoded))
275}
276
277#[cfg(all(not(target_arch = "wasm32"), feature = "_etsi"))]
278pub fn decode_from_uper<T: rasn::Decode>(input: &[u8]) -> Result<T, alloc::string::String> {
283 rasn::Codec::Uper
284 .decode_from_binary(input)
285 .map_err(crate::map_err_to_string)
286}
287
288#[cfg(all(not(target_arch = "wasm32"), feature = "_etsi"))]
289pub fn decode_from_xer_buf<T: rasn::Decode>(input: &[u8]) -> Result<T, alloc::string::String> {
294 rasn::Codec::Xer
295 .decode_from_binary(input)
296 .map_err(crate::map_err_to_string)
297}
298
299#[cfg(all(not(target_arch = "wasm32"), feature = "_etsi"))]
300pub fn decode_from_xer_str<T: rasn::Decode>(input: &str) -> Result<T, alloc::string::String> {
305 rasn::Codec::Xer
306 .decode_from_str(input)
307 .map_err(crate::map_err_to_string)
308}
309
310#[cfg(all(not(target_arch = "wasm32"), feature = "_etsi"))]
311pub fn decode_from_jer_buf<T: rasn::Decode>(input: &[u8]) -> Result<T, alloc::string::String> {
316 rasn::Codec::Jer
317 .decode_from_binary(input)
318 .map_err(crate::map_err_to_string)
319}
320
321#[cfg(all(not(target_arch = "wasm32"), feature = "_etsi"))]
322pub fn decode_from_jer_str<T: rasn::Decode>(input: &str) -> Result<T, alloc::string::String> {
327 rasn::Codec::Jer
328 .decode_from_str(input)
329 .map_err(crate::map_err_to_string)
330}
331
332#[cfg(all(not(target_arch = "wasm32"), feature = "_etsi"))]
333macro_rules! decode_some_type {
337 ($t:ty) => {
338 impl $t {
339 pub fn decode_from_uper(input: &[u8]) -> Result<Self, alloc::string::String> {
344 crate::de::decode_from_uper::<Self>(input)
345 }
346
347 pub fn decode_from_xer_buf(input: &[u8]) -> Result<Self, alloc::string::String> {
352 crate::de::decode_from_xer_buf::<Self>(input)
353 }
354
355 pub fn decode_from_xer_str(input: &str) -> Result<Self, alloc::string::String> {
360 crate::de::decode_from_xer_str::<Self>(input)
361 }
362
363 pub fn decode_from_jer_buf(input: &[u8]) -> Result<Self, alloc::string::String> {
368 crate::de::decode_from_jer_buf::<Self>(input)
369 }
370
371 pub fn decode_from_jer_str(input: &str) -> Result<Self, alloc::string::String> {
376 crate::de::decode_from_jer_str::<Self>(input)
377 }
378 }
379 };
380}
381
382#[cfg(all(not(target_arch = "wasm32"), feature = "_cdd_1_3_1_1"))]
383decode_some_type!(crate::standards::cdd_1_3_1_1::its_container::ItsPduHeader);
384#[cfg(all(not(target_arch = "wasm32"), feature = "_cdd_2_2_1"))]
385decode_some_type!(crate::standards::cdd_2_2_1::etsi_its_cdd::ItsPduHeader);
386
387#[cfg(all(not(target_arch = "wasm32"), feature = "denm_2_2_1"))]
388decode_some_type!(crate::standards::denm_2_2_1::denm_pdu_description::DENM);
389#[cfg(all(not(target_arch = "wasm32"), feature = "denm_2_2_1"))]
390decode_some_type!(crate::standards::denm_2_2_1::denm_pdu_description::DenmPayload);
391
392#[cfg(all(not(target_arch = "wasm32"), feature = "denm_1_3_1"))]
393decode_some_type!(crate::standards::denm_1_3_1::denm_pdu_descriptions::DENM);
394#[cfg(all(not(target_arch = "wasm32"), feature = "denm_1_3_1"))]
395decode_some_type!(
396 crate::standards::denm_1_3_1::denm_pdu_descriptions::DecentralizedEnvironmentalNotificationMessage
397);
398
399#[cfg(all(not(target_arch = "wasm32"), feature = "cam_1_4_1"))]
400decode_some_type!(crate::standards::cam_1_4_1::cam_pdu_descriptions::CAM);
401#[cfg(all(not(target_arch = "wasm32"), feature = "cam_1_4_1"))]
402decode_some_type!(crate::standards::cam_1_4_1::cam_pdu_descriptions::CoopAwareness);
403
404#[cfg(all(not(target_arch = "wasm32"), feature = "spatem_2_2_1"))]
405decode_some_type!(crate::standards::spatem_2_2_1::spatem_pdu_descriptions::SPATEM);
406#[cfg(all(not(target_arch = "wasm32"), feature = "_dsrc_2_2_1"))]
407decode_some_type!(crate::standards::dsrc_2_2_1::etsi_its_dsrc::SPAT);
408
409#[cfg(all(not(target_arch = "wasm32"), feature = "mapem_2_2_1"))]
410decode_some_type!(crate::standards::mapem_2_2_1::mapem_pdu_descriptions::MAPEM);
411#[cfg(all(not(target_arch = "wasm32"), feature = "_dsrc_2_2_1"))]
412decode_some_type!(crate::standards::dsrc_2_2_1::etsi_its_dsrc::MapData);
413
414#[cfg(all(not(target_arch = "wasm32"), feature = "ivim_2_2_1"))]
415decode_some_type!(crate::standards::ivim_2_2_1::ivim_pdu_descriptions::IVIM);
416#[cfg(all(not(target_arch = "wasm32"), feature = "ivim_2_2_1"))]
417decode_some_type!(crate::standards::ivim_2_2_1::ivi::IviStructure);
418
419#[cfg(all(not(target_arch = "wasm32"), feature = "ivim_2_1_1"))]
420decode_some_type!(crate::standards::ivim_2_1_1::ivim_pdu_descriptions::IVIM);
421#[cfg(all(not(target_arch = "wasm32"), feature = "ivim_2_1_1"))]
422decode_some_type!(crate::standards::ivim_2_1_1::ivi::IviStructure);
423
424#[cfg(all(not(target_arch = "wasm32"), feature = "srem_2_2_1"))]
425decode_some_type!(crate::standards::srem_2_2_1::srem_pdu_descriptions::SREM);
426#[cfg(all(not(target_arch = "wasm32"), feature = "_dsrc_2_2_1"))]
427decode_some_type!(crate::standards::dsrc_2_2_1::etsi_its_dsrc::SignalRequestMessage);
428
429#[cfg(all(not(target_arch = "wasm32"), feature = "ssem_2_2_1"))]
430decode_some_type!(crate::standards::ssem_2_2_1::ssem_pdu_descriptions::SSEM);
431#[cfg(all(not(target_arch = "wasm32"), feature = "_dsrc_2_2_1"))]
432decode_some_type!(crate::standards::dsrc_2_2_1::etsi_its_dsrc::SignalStatusMessage);
433
434#[cfg(all(not(target_arch = "wasm32"), feature = "cpm_2_1_1"))]
435decode_some_type!(crate::standards::cpm_2_1_1::cpm_pdu_descriptions::CollectivePerceptionMessage);
436#[cfg(all(not(target_arch = "wasm32"), feature = "cpm_2_1_1"))]
437decode_some_type!(crate::standards::cpm_2_1_1::cpm_pdu_descriptions::CpmPayload);
438
439#[cfg(all(not(target_arch = "wasm32"), feature = "cpm_1"))]
440decode_some_type!(crate::standards::cpm_1::cpm_pdu_descriptions::CPM);
441#[cfg(all(not(target_arch = "wasm32"), feature = "cpm_1"))]
442decode_some_type!(crate::standards::cpm_1::cpm_pdu_descriptions::CollectivePerceptionMessage);
443
444#[cfg(all(target_arch = "wasm32", feature = "v2x", feature = "json"))]
445#[wasm_bindgen(js_name = decode)]
446pub fn decode_to(
454 message: &[u8],
455 headersPresent: crate::Headers,
456 outputEncodingRules: crate::EncodingRules,
457) -> Result<crate::JsonItsMessage, String> {
458 let (input, mut etsi_json) = optionally_decode_headers(message, headersPresent)?;
459 let (input_encoding_rules, protocol_version, message_type) = message_type(input)?;
460 let (msg_ty, decoded) = match (message_type, protocol_version) {
461 (1, 2) => (
462 1,
463 decode_denm(
464 input,
465 Some(211),
466 crate::Headers::None,
467 input_encoding_rules,
468 outputEncodingRules,
469 )?
470 .its,
471 ),
472 (1, _) => (
473 1,
474 decode_denm(
475 input,
476 Some(131),
477 crate::Headers::None,
478 input_encoding_rules,
479 outputEncodingRules,
480 )?
481 .its,
482 ),
483 (2, _) => (
484 2,
485 decode_cam(
486 input,
487 None,
488 crate::Headers::None,
489 input_encoding_rules,
490 outputEncodingRules,
491 )?
492 .its,
493 ),
494 (4, _) => (
495 4,
496 decode_spatem(
497 input,
498 None,
499 crate::Headers::None,
500 input_encoding_rules,
501 outputEncodingRules,
502 )?
503 .its,
504 ),
505 (5, _) => (
506 5,
507 decode_mapem(
508 input,
509 None,
510 crate::Headers::None,
511 input_encoding_rules,
512 outputEncodingRules,
513 )?
514 .its,
515 ),
516 (6, 2) => (
517 6,
518 decode_ivim(
519 input,
520 Some(221),
521 crate::Headers::None,
522 input_encoding_rules,
523 outputEncodingRules,
524 )?
525 .its,
526 ),
527 (6, _) => (
528 6,
529 decode_ivim(
530 input,
531 Some(131),
532 crate::Headers::None,
533 input_encoding_rules,
534 outputEncodingRules,
535 )?
536 .its,
537 ),
538 (9, _) => (
539 9,
540 decode_srem(
541 input,
542 None,
543 crate::Headers::None,
544 input_encoding_rules,
545 outputEncodingRules,
546 )?
547 .its,
548 ),
549 (10, _) => (
550 10,
551 decode_ssem(
552 input,
553 None,
554 crate::Headers::None,
555 input_encoding_rules,
556 outputEncodingRules,
557 )?
558 .its,
559 ),
560 (14, 2) => (
561 14,
562 decode_cpm(
563 input,
564 Some(211),
565 crate::Headers::None,
566 input_encoding_rules,
567 outputEncodingRules,
568 )?
569 .its,
570 ),
571 (14, _) => (
572 14,
573 decode_cpm(
574 input,
575 Some(131),
576 crate::Headers::None,
577 input_encoding_rules,
578 outputEncodingRules,
579 )?
580 .its,
581 ),
582 (message_i_d, _) => {
583 return Err(format!(
584 "Unsupported ITS message type: Found message id {message_i_d}."
585 ));
586 }
587 };
588 etsi_json.its = decoded;
589 etsi_json.message_type = msg_ty;
590 Ok(etsi_json)
591}
592
593#[cfg(any(
594 feature = "_etsi",
595 all(target_arch = "wasm32", feature = "v2x", feature = "json"),
596 all(test, feature = "_etsi")
597))]
598fn message_type(input: &[u8]) -> Result<(crate::EncodingRules, u8, u8), alloc::string::String> {
599 use nom::FindSubstring as _;
600
601 let encoding_rules = match core::primitive::str::from_utf8(input) {
602 Ok(s) if s.trim_start().starts_with('<') => crate::EncodingRules::XER,
603 Ok(s) if s.trim_start().starts_with('{') => crate::EncodingRules::JER,
604 _ => crate::EncodingRules::UPER,
605 };
606 match encoding_rules {
607 crate::EncodingRules::XER => {
608 let message_id_start = input
609 .find_substring("messageID>")
610 .or(input.find_substring("messageId>"))
611 .ok_or("Failed to determine message ID.")?
612 + 10;
613 let message_id_end = (&input[message_id_start..])
614 .find_substring("</")
615 .ok_or("Failed to determine message ID.")?
616 + message_id_start;
617 let message_id =
618 core::primitive::str::from_utf8(&input[message_id_start..message_id_end])
619 .map_err(crate::map_err_to_string)?
620 .trim()
621 .parse()
622 .map_err(crate::map_err_to_string)?;
623 let protocol_version_start = input
624 .find_substring("protocolVersion>")
625 .ok_or("Failed to determine protocol version.")?
626 + 16;
627 let protocol_version_end = (&input[protocol_version_start..])
628 .find_substring("</")
629 .ok_or("Failed to determine protocol version.")?
630 + protocol_version_start;
631 let protocol_version = core::primitive::str::from_utf8(
632 &input[protocol_version_start..protocol_version_end],
633 )
634 .map_err(crate::map_err_to_string)?
635 .trim()
636 .parse()
637 .map_err(crate::map_err_to_string)?;
638 Ok((encoding_rules, protocol_version, message_id))
639 }
640 crate::EncodingRules::JER => {
641 let message_id = input
642 .find_substring("messageID\":")
643 .or(input.find_substring("messageId\":"))
644 .ok_or(alloc::string::String::from(
645 "Failed to determine message ID.",
646 ))
647 .and_then(|start| {
648 let mut end = start + 11;
649 let mut value = input[end] as char;
650 while end < input.len() - 1 && (value.is_whitespace() || value.is_numeric()) {
651 end += 1;
652 value = input[end] as char;
653 }
654 core::primitive::str::from_utf8(&input[(start + 11)..end])
655 .map_err(crate::map_err_to_string)
656 .and_then(|s| s.trim().parse::<u8>().map_err(crate::map_err_to_string))
657 })?;
658 let protocol_version = input
659 .find_substring("protocolVersion\":")
660 .ok_or(alloc::string::String::from(
661 "Failed to determine message ID.",
662 ))
663 .and_then(|start| {
664 let mut end = start + 17;
665 let mut value = input[end] as char;
666 while end < input.len() - 1 && (value.is_whitespace() || value.is_numeric()) {
667 end += 1;
668 value = input[end] as char;
669 }
670 core::primitive::str::from_utf8(&input[(start + 17)..end])
671 .map_err(crate::map_err_to_string)
672 .and_then(|s| s.trim().parse::<u8>().map_err(crate::map_err_to_string))
673 })?;
674 Ok((encoding_rules, protocol_version, message_id))
675 }
676 crate::EncodingRules::UPER => crate::EncodingRules::UPER
677 .codec()
678 .decode_from_binary::<crate::standards::cdd_2_2_1::etsi_its_cdd::ItsPduHeader>(input)
679 .map(|header| {
680 (
681 encoding_rules,
682 header.protocol_version.0,
683 header.message_id.0,
684 )
685 })
686 .map_err(crate::map_err_to_string),
687 }
688}
689
690#[cfg(all(target_arch = "wasm32", feature = "v2x", feature = "json"))]
691fn decode_denm(
692 denm: &[u8],
693 mut version: Option<u32>,
694 headers_present: crate::Headers,
695 input_encoding_rules: crate::EncodingRules,
696 output_encoding_rules: crate::EncodingRules,
697) -> Result<crate::JsonItsMessage, String> {
698 let (input, mut etsi_json) = optionally_decode_headers(denm, headers_present)?;
699 if version.is_none() {
700 version = match input.first() {
701 Some(1) => Some(131),
702 Some(2) => Some(211),
703 _ => None,
704 };
705 }
706 etsi_json.its = match version {
707 Some(131) => Some(transcode::<
708 crate::standards::denm_1_3_1::denm_pdu_descriptions::DENM,
709 >(input, input_encoding_rules, output_encoding_rules))
710 .transpose(),
711 None | Some(221) => Some(transcode::<
712 crate::standards::denm_2_2_1::denm_pdu_description::DENM,
713 >(input, input_encoding_rules, output_encoding_rules))
714 .transpose(),
715 _ => {
716 return Err(
717 "Unsupported DENM version: Supported DENM versions are 131 and 221.".to_string(),
718 );
719 }
720 }?;
721 Ok(etsi_json)
722}
723
724#[cfg(all(target_arch = "wasm32", feature = "v2x", feature = "json"))]
725fn decode_cam(
726 cam: &[u8],
727 version: Option<u32>,
728 headers_present: crate::Headers,
729 input_encoding_rules: crate::EncodingRules,
730 output_encoding_rules: crate::EncodingRules,
731) -> Result<crate::JsonItsMessage, String> {
732 let (input, mut etsi_json) = optionally_decode_headers(cam, headers_present)?;
733 etsi_json.its = match version {
734 None | Some(141) => Some(transcode::<
735 crate::standards::cam_1_4_1::cam_pdu_descriptions::CAM,
736 >(input, input_encoding_rules, output_encoding_rules))
737 .transpose(),
738 _ => return Err("Unsupported DENM version: Supported CAM version is 141.".to_string()),
739 }?;
740 Ok(etsi_json)
741}
742
743#[cfg(all(target_arch = "wasm32", feature = "v2x", feature = "json"))]
744fn decode_mapem(
745 mapem: &[u8],
746 version: Option<u32>,
747 headers_present: crate::Headers,
748 input_encoding_rules: crate::EncodingRules,
749 output_encoding_rules: crate::EncodingRules,
750) -> Result<crate::JsonItsMessage, String> {
751 let (input, mut etsi_json) = optionally_decode_headers(mapem, headers_present)?;
752 etsi_json.its = match version {
753 None | Some(221) => Some(transcode::<
754 crate::standards::mapem_2_2_1::mapem_pdu_descriptions::MAPEM,
755 >(input, input_encoding_rules, output_encoding_rules))
756 .transpose(),
757 _ => return Err("Unsupported MAPEM version: Supported MAPEM version is 221.".to_string()),
758 }?;
759 Ok(etsi_json)
760}
761
762#[cfg(all(target_arch = "wasm32", feature = "v2x", feature = "json"))]
763fn decode_spatem(
764 spatem: &[u8],
765 version: Option<u32>,
766 headers_present: crate::Headers,
767 input_encoding_rules: crate::EncodingRules,
768 output_encoding_rules: crate::EncodingRules,
769) -> Result<crate::JsonItsMessage, String> {
770 let (input, mut etsi_json) = optionally_decode_headers(spatem, headers_present)?;
771 etsi_json.its = match version {
772 None | Some(131) => Some(transcode::<
773 crate::standards::spatem_2_2_1::spatem_pdu_descriptions::SPATEM,
774 >(input, input_encoding_rules, output_encoding_rules))
775 .transpose(),
776 _ => {
777 return Err("Unsupported SPATEM version: Supported SPATEM version is 131.".to_string());
778 }
779 }?;
780 Ok(etsi_json)
781}
782
783#[cfg(all(target_arch = "wasm32", feature = "v2x", feature = "json"))]
784fn decode_ivim(
785 ivim: &[u8],
786 mut version: Option<u32>,
787 headers_present: crate::Headers,
788 input_encoding_rules: crate::EncodingRules,
789 output_encoding_rules: crate::EncodingRules,
790) -> Result<crate::JsonItsMessage, String> {
791 let (input, mut etsi_json) = optionally_decode_headers(ivim, headers_present)?;
792 if version.is_none() {
793 version = match input.first() {
794 Some(1) => Some(211),
795 Some(2) => Some(221),
796 _ => None,
797 };
798 }
799 etsi_json.its = match version {
800 Some(131) | Some(211) => Some(transcode::<
801 crate::standards::ivim_2_1_1::ivim_pdu_descriptions::IVIM,
802 >(input, input_encoding_rules, output_encoding_rules))
803 .transpose(),
804 None | Some(221) => Some(transcode::<
805 crate::standards::ivim_2_2_1::ivim_pdu_descriptions::IVIM,
806 >(input, input_encoding_rules, output_encoding_rules))
807 .transpose(),
808 _ => {
809 return Err(
810 "Unsupported IVIM version: Supported IVIM versions are 131, 211 and 221."
811 .to_string(),
812 );
813 }
814 }?;
815 Ok(etsi_json)
816}
817
818#[cfg(all(target_arch = "wasm32", feature = "v2x", feature = "json"))]
819fn decode_srem(
820 srem: &[u8],
821 version: Option<u32>,
822 headers_present: crate::Headers,
823 input_encoding_rules: crate::EncodingRules,
824 output_encoding_rules: crate::EncodingRules,
825) -> Result<crate::JsonItsMessage, String> {
826 let (input, mut etsi_json) = optionally_decode_headers(srem, headers_present)?;
827 etsi_json.its = match version {
828 None | Some(221) => Some(transcode::<
829 crate::standards::srem_2_2_1::srem_pdu_descriptions::SREM,
830 >(input, input_encoding_rules, output_encoding_rules))
831 .transpose(),
832 _ => return Err("Unsupported SREM version: Supported SREM version is 221.".to_string()),
833 }?;
834 Ok(etsi_json)
835}
836
837#[cfg(all(target_arch = "wasm32", feature = "v2x", feature = "json"))]
838fn decode_cpm(
839 cpm: &[u8],
840 mut version: Option<u32>,
841 headers_present: crate::Headers,
842 input_encoding_rules: crate::EncodingRules,
843 output_encoding_rules: crate::EncodingRules,
844) -> Result<crate::JsonItsMessage, String> {
845 let (input, mut etsi_json) = optionally_decode_headers(cpm, headers_present)?;
846 if version.is_none() {
847 version = match input.first() {
848 Some(1) => Some(131),
849 Some(2) => Some(211),
850 _ => None,
851 };
852 }
853 etsi_json.its = match version {
854 None | Some(211) => Some(transcode::<
855 crate::standards::cpm_2_1_1::cpm_pdu_descriptions::CollectivePerceptionMessage,
856 >(input, input_encoding_rules, output_encoding_rules))
857 .transpose(),
858 Some(131) => Some(transcode::<
859 crate::standards::cpm_1::cpm_pdu_descriptions::CPM,
860 >(input, input_encoding_rules, output_encoding_rules))
861 .transpose(),
862 _ => {
863 return Err(
864 "Unsupported CPM version: Supported CPM versions are 131 and 211.".to_string(),
865 );
866 }
867 }?;
868 Ok(etsi_json)
869}
870
871#[cfg(all(target_arch = "wasm32", feature = "v2x", feature = "json"))]
872fn decode_ssem(
873 ssem: &[u8],
874 version: Option<u32>,
875 headers_present: crate::Headers,
876 input_encoding_rules: crate::EncodingRules,
877 output_encoding_rules: crate::EncodingRules,
878) -> Result<crate::JsonItsMessage, String> {
879 let (input, mut etsi_json) = optionally_decode_headers(ssem, headers_present)?;
880 etsi_json.its = match version {
881 None | Some(221) => Some(transcode::<
882 crate::standards::ssem_2_2_1::ssem_pdu_descriptions::SSEM,
883 >(input, input_encoding_rules, output_encoding_rules))
884 .transpose(),
885 _ => return Err("Unsupported SSEM version: Supported SSEM version is 221.".to_string()),
886 }?;
887 Ok(etsi_json)
888}
889
890#[cfg(all(target_arch = "wasm32", feature = "v2x", feature = "json"))]
891pub fn optionally_decode_headers(
892 input: &[u8],
893 headers: crate::Headers,
894) -> Result<(&[u8], crate::JsonItsMessage), String> {
895 match headers {
896 crate::Headers::None => Ok((input, crate::JsonItsMessage::default())),
897 crate::Headers::GnBtp => transcode_gn_tp_to_json(input),
898 crate::Headers::IEEE802LlcGnBtp => {
899 crate::pcap::remove_wlan_headers(input).and_then(transcode_gn_tp_to_json)
900 }
901 crate::Headers::RadioTap802LlcGnBtp => {
902 crate::pcap::remove_pcap_headers(input).and_then(transcode_gn_tp_to_json)
903 }
904 }
905}
906
907#[cfg(all(target_arch = "wasm32", feature = "v2x", feature = "json"))]
908fn transcode_gn_tp_to_json(input: &[u8]) -> Result<(&[u8], crate::JsonItsMessage), String> {
909 decode_geonetworking_header(input).and_then(|(remaining, gn_json, next_header)| {
910 decode_transport_header(remaining, next_header).map(|(rem, tp)| {
911 (
912 rem,
913 crate::JsonItsMessage {
914 geonetworking: Some(gn_json),
915 transport: Some(tp),
916 ..Default::default()
917 },
918 )
919 })
920 })
921}
922
923#[cfg(all(target_arch = "wasm32", feature = "v2x", feature = "json"))]
924fn decode_geonetworking_header(
925 input: &[u8],
926) -> Result<(&[u8], String, geonetworking::en302636_4_1::NextAfterCommon), String> {
927 use geonetworking::{Decode as _, Encode as _};
928
929 let result = geonetworking::Packet::decode(input).map_err(crate::map_err_to_string)?;
930 let gn_json = result
931 .decoded
932 .encode_to_json()
933 .map_err(crate::map_err_to_string)?;
934 match result.decoded {
935 geonetworking::Packet::Unsecured {
936 common, payload, ..
937 } => Ok((payload, gn_json, common.next_header)),
938 p => p
939 .secured_payload_after_gn()
940 .ok_or("Secured GeoNetworking Packet carries no data!".into())
941 .map(|payload| (payload, gn_json, p.common().next_header)),
942 }
943}
944
945#[cfg(all(target_arch = "wasm32", feature = "v2x", feature = "json"))]
946fn decode_transport_header(
947 input: &[u8],
948 header_type: geonetworking::en302636_4_1::NextAfterCommon,
949) -> Result<(&[u8], String), String> {
950 match header_type {
951 geonetworking::en302636_4_1::NextAfterCommon::Any => {
952 Err("Currently, only BTP and IPv6 Headers can be decoded!".to_string())
953 }
954 geonetworking::en302636_4_1::NextAfterCommon::BTPA => {
955 btp![crate::transport::BasicTransportAHeader, input]
956 }
957 geonetworking::en302636_4_1::NextAfterCommon::BTPB => {
958 btp![crate::transport::BasicTransportBHeader, input]
959 }
960 geonetworking::en302636_4_1::NextAfterCommon::IPv6 => {
961 let (remaining, ipv6) =
962 crate::transport::IPv6Header::decode(input).map_err(crate::map_err_to_string)?;
963 Ok((remaining, to_ipv6_debug(ipv6)))
964 }
965 }
966}
967
968#[cfg(all(target_arch = "wasm32", feature = "v2x", feature = "json"))]
969fn transcode<T: rasn::Decode + rasn::Encode>(
970 input: &[u8],
971 input_encoding_rules: crate::EncodingRules,
972 output_encoding_rules: crate::EncodingRules,
973) -> Result<String, String> {
974 use core::fmt::Write as _;
975
976 if let (crate::EncodingRules::UPER, crate::EncodingRules::UPER) =
977 (input_encoding_rules, output_encoding_rules)
978 {
979 return input.iter().try_fold(String::new(), |mut acc, byte| {
980 write!(&mut acc, "{byte:02X?}")
981 .map_err(crate::map_err_to_string)
982 .map(|_| acc)
983 });
984 }
985 let decoded: T = input_encoding_rules
986 .codec()
987 .decode_from_binary(input)
988 .map_err(crate::map_err_to_string)?;
989 match output_encoding_rules {
990 crate::EncodingRules::UPER => rasn::uper::encode(&decoded)
991 .map(hex::encode)
992 .map_err(crate::map_err_to_string),
993 o => o
994 .codec()
995 .encode_to_string(&decoded)
996 .map_err(crate::map_err_to_string),
997 }
998}
999
1000#[cfg(all(target_arch = "wasm32", feature = "v2x", feature = "json"))]
1001fn to_ipv6_debug(ipv6: crate::transport::IPv6Header) -> String {
1002 alloc::format!(r#"{{"ipv6Debug":"{ipv6:?}"}}"#)
1003}
1004
1005#[cfg(all(test, feature = "_etsi"))]
1006mod tests {
1007 use crate::de::message_type;
1008
1009 #[test]
1010 fn recognizes_message_type_and_version() {
1011 assert_eq!((crate::EncodingRules::XER, 2,14), message_type("<CPM><header><protocolVersion>2</protocolVersion><messageID>14</messageID><stationID>".as_bytes()).unwrap());
1012 assert_eq!(
1013 (crate::EncodingRules::XER, 1, 5),
1014 message_type(
1015 r#"<?xml version="1.0"?><MAPEM><header><protocolVersion> 1 </protocolVersion><messageID>
1016 5
1017 </messageID><stationID>"#
1018 .as_bytes()
1019 )
1020 .unwrap()
1021 );
1022 assert_eq!(
1023 (crate::EncodingRules::JER, 2, 2),
1024 message_type(
1025 r#"{"header":{"protocolVersion":2,"messageID":2,"stationID":2624309139}"#
1026 .as_bytes()
1027 )
1028 .unwrap()
1029 );
1030 assert_eq!(
1031 (crate::EncodingRules::JER, 1, 9),
1032 message_type(
1033 r#"{
1034 "header": {
1035 "protocolVersion": 1,
1036 "messageID": 9,
1037 "stationID": 2624309139
1038 }"#
1039 .as_bytes()
1040 )
1041 .unwrap()
1042 );
1043 }
1044}