Skip to main content

c_its_parser/
en.rs

1//! C-ITS Message Encoding
2//!
3//! Provides Rust and wasm functions to encode C-ITS messages.
4//! The Rust API just needs one method ([`ItsMessage::encode`]) for the [`ItsMessage`] struct while wasm has one function per message type.
5
6#[cfg(not(target_arch = "wasm32"))]
7use alloc::string::ToString;
8
9#[cfg(all(target_arch = "wasm32", feature = "json"))]
10use wasm_bindgen::prelude::*;
11
12#[cfg(all(target_arch = "wasm32", feature = "json"))]
13/// Wasm output is a Javascript uint8 array
14pub type Encoded = js_sys::Uint8Array;
15#[cfg(not(target_arch = "wasm32"))]
16/// Rust output is a `Vec<u8>`
17pub type Encoded = alloc::vec::Vec<u8>;
18
19#[cfg(not(target_arch = "wasm32"))]
20impl crate::ItsMessage<'_> {
21    #[allow(clippy::too_many_lines)]
22    /// Encodes an ITS message with optional headers.
23    ///
24    /// Supports XER, JER, and UPER encoding rules.
25    /// XER and JER values are returned as UTF-8 buffers.
26    ///
27    /// Note: When given a secured packet in the `geonetworking` option, this will always generate an unsecured packet.
28    /// (Since the geonetworking crate doesn't support creating messages signatures.)
29    ///
30    /// Note: When selecting JER or XER rules, geonetworking and transport headers will be omitted.
31    /// If a JSON representation is required call `encode_to_json` on the geonetworking packet and transport headers.
32    ///
33    /// Note: The following fields in the `geonetworking::Packet` will be filled automatically:
34    ///
35    /// - `common.payload_length`: Set to binary size of ITS message and transport header
36    /// - `basic.remaining_hop_limit`: Set to `maximum_hop_limit` from common header
37    ///
38    /// # Errors
39    /// Gives a human-readable error description when ASN.1 parsing failed or an unexpected set of headers was found.
40    pub fn encode(
41        self,
42        encoding_rules: crate::EncodingRules,
43    ) -> Result<Encoded, alloc::string::String> {
44        use crate::ItsMessage;
45
46        let (geo, tp, mut etsi_uper) = match self {
47            #[cfg(feature = "denm_1_3_1")]
48            ItsMessage::DenmV1 {
49                geonetworking,
50                transport,
51                etsi,
52            } => encoding_rules
53                .codec()
54                .encode_to_binary(&etsi)
55                .map(|enc| (geonetworking, transport, enc)),
56            #[cfg(feature = "denm_2_2_1")]
57            ItsMessage::DenmV2 {
58                geonetworking,
59                transport,
60                etsi,
61            } => encoding_rules
62                .codec()
63                .encode_to_binary(&etsi)
64                .map(|enc| (geonetworking, transport, enc)),
65            #[cfg(feature = "cam_1_4_1")]
66            ItsMessage::Cam {
67                geonetworking,
68                transport,
69                etsi,
70            } => encoding_rules
71                .codec()
72                .encode_to_binary(&etsi)
73                .map(|enc| (geonetworking, transport, enc)),
74            #[cfg(feature = "spatem_2_2_1")]
75            ItsMessage::Spatem {
76                geonetworking,
77                transport,
78                etsi,
79            } => encoding_rules
80                .codec()
81                .encode_to_binary(&etsi)
82                .map(|enc| (geonetworking, transport, enc)),
83            #[cfg(feature = "mapem_2_2_1")]
84            ItsMessage::Mapem {
85                geonetworking,
86                transport,
87                etsi,
88            } => encoding_rules
89                .codec()
90                .encode_to_binary(&etsi)
91                .map(|enc| (geonetworking, transport, enc)),
92            #[cfg(feature = "ivim_2_1_1")]
93            ItsMessage::IvimV1 {
94                geonetworking,
95                transport,
96                etsi,
97            } => encoding_rules
98                .codec()
99                .encode_to_binary(&etsi)
100                .map(|enc| (geonetworking, transport, enc)),
101            #[cfg(feature = "ivim_2_2_1")]
102            ItsMessage::IvimV2 {
103                geonetworking,
104                transport,
105                etsi,
106            } => encoding_rules
107                .codec()
108                .encode_to_binary(&etsi)
109                .map(|enc| (geonetworking, transport, enc)),
110            #[cfg(feature = "srem_2_2_1")]
111            ItsMessage::Srem {
112                geonetworking,
113                transport,
114                etsi,
115            } => encoding_rules
116                .codec()
117                .encode_to_binary(&etsi)
118                .map(|enc| (geonetworking, transport, enc)),
119            #[cfg(feature = "ssem_2_2_1")]
120            ItsMessage::Ssem {
121                geonetworking,
122                transport,
123                etsi,
124            } => encoding_rules
125                .codec()
126                .encode_to_binary(&etsi)
127                .map(|enc| (geonetworking, transport, enc)),
128            #[cfg(feature = "cpm_1")]
129            ItsMessage::CpmV1 {
130                geonetworking,
131                transport,
132                etsi,
133            } => encoding_rules
134                .codec()
135                .encode_to_binary(&etsi)
136                .map(|enc| (geonetworking, transport, enc)),
137            #[cfg(feature = "cpm_2_1_1")]
138            ItsMessage::CpmV2 {
139                geonetworking,
140                transport,
141                etsi,
142            } => encoding_rules
143                .codec()
144                .encode_to_binary(&etsi)
145                .map(|enc| (geonetworking, transport, enc)),
146        }
147        .map_err(crate::map_err_to_string)?;
148
149        if encoding_rules == crate::EncodingRules::UPER {
150            match (tp, geo) {
151            (None, None) => Ok(etsi_uper),
152            (
153                Some(tp),
154                Some(geonetworking::Packet::Unsecured {
155                    basic,
156                    common,
157                    extended,
158                    ..
159                } | geonetworking::Packet::Secured {
160                    basic,
161                    common,
162                    extended,
163                    ..
164                }),
165            ) => {
166                let mut encoded = tp.encode()?;
167                encoded.append(&mut etsi_uper);
168                fill_gn_and_encode(geonetworking::UnsecuredHeader { basic, common, extended }, &encoded)
169            }
170            _ => Err(
171                "Expecting either both or neither GeoNetworking and Transport headers to be present!"
172                    .to_string(),
173            ),
174        }
175        } else {
176            Ok(etsi_uper)
177        }
178    }
179}
180
181#[cfg(all(not(target_arch = "wasm32"), feature = "_etsi"))]
182/// Encodes some ASN.1 data type to UPER buffer
183///
184/// # Errors
185/// Returns a human-readable string when encoding failed
186pub fn encode_to_uper<T: rasn::Encode>(input: &T) -> Result<Encoded, alloc::string::String> {
187    rasn::Codec::Uper
188        .encode_to_binary(input)
189        .map_err(crate::map_err_to_string)
190}
191
192#[cfg(all(not(target_arch = "wasm32"), feature = "_etsi"))]
193/// Encodes some ASN.1 data type to XER buffer
194///
195/// # Errors
196/// Returns a human-readable string when encoding failed
197pub fn encode_to_xer_buf<T: rasn::Encode>(input: &T) -> Result<Encoded, alloc::string::String> {
198    rasn::Codec::Xer
199        .encode_to_binary(input)
200        .map_err(crate::map_err_to_string)
201}
202
203#[cfg(all(not(target_arch = "wasm32"), feature = "_etsi"))]
204/// Encodes some ASN.1 data type to XER string
205///
206/// # Errors
207/// Returns a human-readable string when encoding failed
208pub fn encode_to_xer_str<T: rasn::Encode>(
209    input: &T,
210) -> Result<alloc::string::String, alloc::string::String> {
211    rasn::Codec::Xer
212        .encode_to_string(input)
213        .map_err(crate::map_err_to_string)
214}
215
216#[cfg(all(not(target_arch = "wasm32"), feature = "_etsi"))]
217/// Encodes some ASN.1 data type to JER buffer
218///
219/// # Errors
220/// Returns a human-readable string when encoding failed
221pub fn encode_to_jer_buf<T: rasn::Encode>(input: &T) -> Result<Encoded, alloc::string::String> {
222    rasn::Codec::Jer
223        .encode_to_binary(input)
224        .map_err(crate::map_err_to_string)
225}
226
227#[cfg(all(not(target_arch = "wasm32"), feature = "_etsi"))]
228/// Encodes some ASN.1 data type to JER string
229///
230/// # Errors
231/// Returns a human-readable string when encoding failed
232pub fn encode_to_jer_str<T: rasn::Encode>(
233    input: &T,
234) -> Result<alloc::string::String, alloc::string::String> {
235    rasn::Codec::Jer
236        .encode_to_string(input)
237        .map_err(crate::map_err_to_string)
238}
239
240#[cfg(all(not(target_arch = "wasm32"), feature = "_etsi"))]
241/// Adds encoding methods to encodable types
242///
243/// This doesn't provide anything more than the encode functions, but adds them as methods for ease of use.
244macro_rules! encode_some_type {
245    ($t:ty) => {
246        impl $t {
247            /// Encodes [`Self`] to UPER buffer
248            ///
249            /// # Errors
250            /// Returns a human-readable string when decoding failed
251            pub fn encode_to_uper(&self) -> Result<Encoded, alloc::string::String> {
252                crate::en::encode_to_uper(&self)
253            }
254
255            /// Encodes [`Self`] to XER buffer
256            ///
257            /// # Errors
258            /// Returns a human-readable string when decoding failed
259            pub fn encode_to_xer_buf(&self) -> Result<Encoded, alloc::string::String> {
260                crate::en::encode_to_xer_buf(&self)
261            }
262
263            /// Encodes [`Self`] to XER buffer
264            ///
265            /// # Errors
266            /// Returns a human-readable string when decoding failed
267            pub fn encode_to_xer_str(
268                &self,
269            ) -> Result<alloc::string::String, alloc::string::String> {
270                crate::en::encode_to_xer_str(&self)
271            }
272
273            /// Encodes [`Self`] to JER buffer
274            ///
275            /// # Errors
276            /// Returns a human-readable string when decoding failed
277            pub fn encode_to_jer_buf(&self) -> Result<Encoded, alloc::string::String> {
278                crate::en::encode_to_jer_buf(&self)
279            }
280
281            /// Encodes [`Self`] to JER buffer
282            ///
283            /// # Errors
284            /// Returns a human-readable string when decoding failed
285            pub fn encode_to_jer_str(
286                &self,
287            ) -> Result<alloc::string::String, alloc::string::String> {
288                crate::en::encode_to_jer_str(&self)
289            }
290        }
291    };
292}
293
294#[cfg(all(not(target_arch = "wasm32"), feature = "_cdd_1_3_1_1"))]
295encode_some_type!(crate::standards::cdd_1_3_1_1::its_container::ItsPduHeader);
296#[cfg(all(not(target_arch = "wasm32"), feature = "_cdd_2_2_1"))]
297encode_some_type!(crate::standards::cdd_2_2_1::etsi_its_cdd::ItsPduHeader);
298
299#[cfg(all(not(target_arch = "wasm32"), feature = "denm_2_2_1"))]
300encode_some_type!(crate::standards::denm_2_2_1::denm_pdu_description::DENM);
301#[cfg(all(not(target_arch = "wasm32"), feature = "denm_2_2_1"))]
302encode_some_type!(crate::standards::denm_2_2_1::denm_pdu_description::DenmPayload);
303
304#[cfg(all(not(target_arch = "wasm32"), feature = "denm_1_3_1"))]
305encode_some_type!(crate::standards::denm_1_3_1::denm_pdu_descriptions::DENM);
306#[cfg(all(not(target_arch = "wasm32"), feature = "denm_1_3_1"))]
307encode_some_type!(
308    crate::standards::denm_1_3_1::denm_pdu_descriptions::DecentralizedEnvironmentalNotificationMessage
309);
310
311#[cfg(all(not(target_arch = "wasm32"), feature = "cam_1_4_1"))]
312encode_some_type!(crate::standards::cam_1_4_1::cam_pdu_descriptions::CAM);
313#[cfg(all(not(target_arch = "wasm32"), feature = "cam_1_4_1"))]
314encode_some_type!(crate::standards::cam_1_4_1::cam_pdu_descriptions::CoopAwareness);
315
316#[cfg(all(not(target_arch = "wasm32"), feature = "spatem_2_2_1"))]
317encode_some_type!(crate::standards::spatem_2_2_1::spatem_pdu_descriptions::SPATEM);
318#[cfg(all(not(target_arch = "wasm32"), feature = "_dsrc_2_2_1"))]
319encode_some_type!(crate::standards::dsrc_2_2_1::etsi_its_dsrc::SPAT);
320
321#[cfg(all(not(target_arch = "wasm32"), feature = "mapem_2_2_1"))]
322encode_some_type!(crate::standards::mapem_2_2_1::mapem_pdu_descriptions::MAPEM);
323#[cfg(all(not(target_arch = "wasm32"), feature = "_dsrc_2_2_1"))]
324encode_some_type!(crate::standards::dsrc_2_2_1::etsi_its_dsrc::MapData);
325
326#[cfg(all(not(target_arch = "wasm32"), feature = "ivim_2_2_1"))]
327encode_some_type!(crate::standards::ivim_2_2_1::ivim_pdu_descriptions::IVIM);
328#[cfg(all(not(target_arch = "wasm32"), feature = "ivim_2_2_1"))]
329encode_some_type!(crate::standards::ivim_2_2_1::ivi::IviStructure);
330
331#[cfg(all(not(target_arch = "wasm32"), feature = "ivim_2_1_1"))]
332encode_some_type!(crate::standards::ivim_2_1_1::ivim_pdu_descriptions::IVIM);
333#[cfg(all(not(target_arch = "wasm32"), feature = "ivim_2_1_1"))]
334encode_some_type!(crate::standards::ivim_2_1_1::ivi::IviStructure);
335
336#[cfg(all(not(target_arch = "wasm32"), feature = "srem_2_2_1"))]
337encode_some_type!(crate::standards::srem_2_2_1::srem_pdu_descriptions::SREM);
338#[cfg(all(not(target_arch = "wasm32"), feature = "_dsrc_2_2_1"))]
339encode_some_type!(crate::standards::dsrc_2_2_1::etsi_its_dsrc::SignalRequestMessage);
340
341#[cfg(all(not(target_arch = "wasm32"), feature = "ssem_2_2_1"))]
342encode_some_type!(crate::standards::ssem_2_2_1::ssem_pdu_descriptions::SSEM);
343#[cfg(all(not(target_arch = "wasm32"), feature = "_dsrc_2_2_1"))]
344encode_some_type!(crate::standards::dsrc_2_2_1::etsi_its_dsrc::SignalStatusMessage);
345
346#[cfg(all(not(target_arch = "wasm32"), feature = "cpm_2_1_1"))]
347encode_some_type!(crate::standards::cpm_2_1_1::cpm_pdu_descriptions::CollectivePerceptionMessage);
348#[cfg(all(not(target_arch = "wasm32"), feature = "cpm_2_1_1"))]
349encode_some_type!(crate::standards::cpm_2_1_1::cpm_pdu_descriptions::CpmPayload);
350
351#[cfg(all(not(target_arch = "wasm32"), feature = "cpm_1"))]
352encode_some_type!(crate::standards::cpm_1::cpm_pdu_descriptions::CPM);
353#[cfg(all(not(target_arch = "wasm32"), feature = "cpm_1"))]
354encode_some_type!(crate::standards::cpm_1::cpm_pdu_descriptions::CollectivePerceptionMessage);
355
356#[cfg(all(target_arch = "wasm32", feature = "json"))]
357#[wasm_bindgen(js_name = encodeDenm)]
358/// Encodes a DENM message into binary UPER with optional headers
359/// The encoder expects either both (GeoNetworking and Transport) headers or none
360/// Currently, denms of the following versions are supported: v2.2.1 (221) and v1.3.1 (131)
361/// Throws string error on encoding error
362pub fn encode_denm(denm: &crate::JsonItsMessage, version: u32) -> Result<Encoded, String> {
363    let mut payload = vec![];
364    match (&denm.its, version) {
365        (None, 131) | (None, 221) => return Err("No DENM JSON provided.".to_string()),
366        (Some(denm_json), 131) => {
367            payload.append(&mut transcode_jer_to_uper::<
368                crate::standards::denm_1_3_1::denm_pdu_descriptions::DENM,
369            >(denm_json)?);
370        }
371        (Some(denm_json), 221) => {
372            payload.append(&mut transcode_jer_to_uper::<
373                crate::standards::denm_2_2_1::denm_pdu_description::DENM,
374            >(denm_json)?);
375        }
376        _ => {
377            return Err(
378                "Unsupported DENM version: Supported DENM versions are 131 and 221.".to_string(),
379            );
380        }
381    };
382    let encoded = optionally_encode_headers(&denm.geonetworking, &denm.transport, payload)?;
383    Ok(Encoded::from(encoded.as_slice()))
384}
385
386#[cfg(all(target_arch = "wasm32", feature = "json"))]
387#[wasm_bindgen(js_name = encodeCam)]
388/// Encodes a CAM message into binary UPER with optional headers
389/// The encoder expects either both (GeoNetworking and Transport) headers or none
390/// Currently, cams of the following versions are supported: v1.4.1 (141)
391/// Throws string error on encoding error
392pub fn encode_cam(cam: &crate::JsonItsMessage, version: u32) -> Result<Encoded, String> {
393    let mut payload = vec![];
394    match (&cam.its, version) {
395        (None, 141) => return Err("No CAM JSON provided.".to_string()),
396        (Some(json), 141) => {
397            payload.append(&mut transcode_jer_to_uper::<
398                crate::standards::cam_1_4_1::cam_pdu_descriptions::CAM,
399            >(json)?);
400        }
401        _ => return Err("Unsupported CAM version: Supported CAM version is 141.".to_string()),
402    };
403    let encoded = optionally_encode_headers(&cam.geonetworking, &cam.transport, payload)?;
404    Ok(Encoded::from(encoded.as_slice()))
405}
406
407#[cfg(all(target_arch = "wasm32", feature = "json"))]
408#[wasm_bindgen(js_name = encodeMapem)]
409/// Encodes a MAPEM message into binary UPER with optional headers
410/// The encoder expects either both (GeoNetworking and Transport) headers or none
411/// Currently, mapems of the following versions are supported: v2.2.1 (221)
412/// Throws string error on encoding error
413pub fn encode_mapem(mapem: &crate::JsonItsMessage, version: u32) -> Result<Encoded, String> {
414    let mut payload = vec![];
415    match (&mapem.its, version) {
416        (None, 221) => return Err("No MAPEM JSON provided.".to_string()),
417        (Some(json), 221) => {
418            payload.append(&mut transcode_jer_to_uper::<
419                crate::standards::mapem_2_2_1::mapem_pdu_descriptions::MAPEM,
420            >(json)?);
421        }
422        _ => return Err("Unsupported MAPEM version: Supported MAPEM version is 221.".to_string()),
423    };
424    let encoded = optionally_encode_headers(&mapem.geonetworking, &mapem.transport, payload)?;
425    Ok(Encoded::from(encoded.as_slice()))
426}
427
428#[cfg(all(target_arch = "wasm32", feature = "json"))]
429#[wasm_bindgen(js_name = encodeSpatem)]
430/// Encodes a SPATEM message into binary UPER with optional headers
431/// The encoder expects either both (GeoNetworking and Transport) headers or none
432/// Currently, spatems of the following versions are supported: v2.2.1 (221)
433/// Throws string error on encoding error
434pub fn encode_spatem(spatem: &crate::JsonItsMessage, version: u32) -> Result<Encoded, String> {
435    let mut payload = vec![];
436    match (&spatem.its, version) {
437        (None, 221) => return Err("No SPATEM JSON provided.".to_string()),
438        (Some(json), 221) => {
439            payload.append(&mut transcode_jer_to_uper::<
440                crate::standards::spatem_2_2_1::spatem_pdu_descriptions::SPATEM,
441            >(json)?);
442        }
443        _ => {
444            return Err("Unsupported SPATEM version: Supported SPATEM version is 221.".to_string());
445        }
446    };
447    let encoded = optionally_encode_headers(&spatem.geonetworking, &spatem.transport, payload)?;
448    Ok(Encoded::from(encoded.as_slice()))
449}
450
451#[cfg(all(target_arch = "wasm32", feature = "json"))]
452#[wasm_bindgen(js_name = encodeIvim)]
453/// Encodes a IVIM message into binary UPER with optional headers
454/// The encoder expects either both (GeoNetworking and Transport) headers or none
455/// Currently, ivims of the following versions are supported: v1.3.1 (131)/ v2.1.1 (211), v2.2.1 (221)
456/// Throws string error on encoding error
457pub fn encode_ivim(ivim: &crate::JsonItsMessage, version: u32) -> Result<Encoded, String> {
458    let mut payload = vec![];
459    match (&ivim.its, version) {
460        (None, 131) | (None, 211) | (None, 221) => return Err("No IVIM JSON provided.".to_string()),
461        (Some(json), 211) | (Some(json), 131) => {
462            payload.append(&mut transcode_jer_to_uper::<
463                crate::standards::ivim_2_1_1::ivim_pdu_descriptions::IVIM,
464            >(json)?);
465        }
466        (Some(json), 221) => {
467            payload.append(&mut transcode_jer_to_uper::<
468                crate::standards::ivim_2_2_1::ivim_pdu_descriptions::IVIM,
469            >(json)?);
470        }
471        _ => {
472            return Err(
473                "Unsupported IVIM version: Supported IVIM versions are 131, 211 and 221."
474                    .to_string(),
475            );
476        }
477    };
478    let encoded = optionally_encode_headers(&ivim.geonetworking, &ivim.transport, payload)?;
479    Ok(Encoded::from(encoded.as_slice()))
480}
481
482#[cfg(all(target_arch = "wasm32", feature = "json"))]
483#[wasm_bindgen(js_name = encodeSrem)]
484/// Encodes a SREM message into binary UPER with optional headers
485/// The encoder expects either both (GeoNetworking and Transport) headers or none
486/// Currently, srems of the following versions are supported: v2.2.1 (221)
487/// Throws string error on encoding error
488pub fn encode_srem(srem: &crate::JsonItsMessage, version: u32) -> Result<Encoded, String> {
489    let mut payload = vec![];
490    match (&srem.its, version) {
491        (None, 221) => return Err("No SREM JSON provided.".to_string()),
492        (Some(json), 221) => {
493            payload.append(&mut transcode_jer_to_uper::<
494                crate::standards::srem_2_2_1::srem_pdu_descriptions::SREM,
495            >(json)?);
496        }
497        _ => return Err("Unsupported SREM version: Supported SREM version is 221.".to_string()),
498    };
499    let encoded = optionally_encode_headers(&srem.geonetworking, &srem.transport, payload)?;
500    Ok(Encoded::from(encoded.as_slice()))
501}
502
503#[cfg(all(target_arch = "wasm32", feature = "json"))]
504#[wasm_bindgen(js_name = encodeCpm)]
505/// Encodes a CPM message into binary UPER with optional headers
506/// The encoder expects either both (GeoNetworking and Transport) headers or none
507/// Currently, cpms of the following versions are supported: v1.3.1 (131), v2.1.1 (211)
508/// Throws string error on encoding error
509pub fn encode_cpm(cpm: &crate::JsonItsMessage, version: u32) -> Result<Encoded, String> {
510    let mut payload = vec![];
511    match (&cpm.its, version) {
512        (None, 131) | (None, 211) => return Err("No CPM JSON provided.".to_string()),
513        (Some(json), 131) => {
514            payload.append(&mut transcode_jer_to_uper::<
515                crate::standards::cpm_1::cpm_pdu_descriptions::CPM,
516            >(json)?);
517        }
518        (Some(json), 211) => {
519            payload.append(&mut transcode_jer_to_uper::<
520                crate::standards::cpm_2_1_1::cpm_pdu_descriptions::CollectivePerceptionMessage,
521            >(json)?);
522        }
523        _ => {
524            return Err(
525                "Unsupported CPM version: Supported CPM versions are 131 and 211.".to_string(),
526            );
527        }
528    };
529    let encoded = optionally_encode_headers(&cpm.geonetworking, &cpm.transport, payload)?;
530    Ok(Encoded::from(encoded.as_slice()))
531}
532
533#[cfg(all(target_arch = "wasm32", feature = "json"))]
534#[wasm_bindgen(js_name = encodeSsem)]
535/// Encodes a SSEM message into binary UPER with optional headers
536/// The encoder expects either both (GeoNetworking and Transport) headers or none
537/// Currently, ssems of the following versions are supported: v2.2.1 (221)
538/// Throws string error on encoding error
539pub fn encode_ssem(ssem: &crate::JsonItsMessage, version: u32) -> Result<Encoded, String> {
540    let mut payload = vec![];
541    match (&ssem.its, version) {
542        (None, 221) => return Err("No SSEM JSON provided.".to_string()),
543        (Some(json), 221) => {
544            payload.append(&mut transcode_jer_to_uper::<
545                crate::standards::ssem_2_2_1::ssem_pdu_descriptions::SSEM,
546            >(json)?);
547        }
548        _ => return Err("Unsupported SSEM version: Supported SSEM version is 221.".to_string()),
549    };
550    let encoded = optionally_encode_headers(&ssem.geonetworking, &ssem.transport, payload)?;
551    Ok(Encoded::from(encoded.as_slice()))
552}
553
554#[cfg(all(target_arch = "wasm32", feature = "json"))]
555fn optionally_encode_headers(
556    gn_json: &Option<String>,
557    tp_json: &Option<String>,
558    mut its: alloc::vec::Vec<u8>,
559) -> Result<alloc::vec::Vec<u8>, String> {
560    use crate::transport::encode::Encode as _;
561
562    match (gn_json, tp_json) {
563        (Some(_), None) | (None, Some(_)) => Err(
564            "Expecting either both or neither GeoNetworking and Transport headers to be present!"
565                .to_string(),
566        ),
567        (Some(gn), Some(tp)) => {
568            let geonetworking =
569                geonetworking::UnsecuredHeader::from_json(gn).map_err(crate::map_err_to_string)?;
570            let mut transport = match geonetworking.common.next_header {
571                geonetworking::en302636_4_1::NextAfterCommon::BTPA => {
572                    crate::transport::BasicTransportAHeader::decode_from_json(tp)
573                        .map_err(crate::map_err_to_string)?
574                        .encode()
575                        .map_err(crate::map_err_to_string)?
576                }
577                geonetworking::en302636_4_1::NextAfterCommon::BTPB => {
578                    crate::transport::BasicTransportBHeader::decode_from_json(tp)
579                        .map_err(crate::map_err_to_string)?
580                        .encode()
581                        .map_err(crate::map_err_to_string)?
582                }
583                h => {
584                    return Err(alloc::format!(
585                        "Currently only BTP-A and BTP-B headers can be encoded: Encountered {h:?}"
586                    ));
587                }
588            };
589            transport.append(&mut its);
590            fill_gn_and_encode(geonetworking, &transport)
591        }
592        _ => Ok(its),
593    }
594}
595
596#[cfg(any(
597    all(target_arch = "wasm32", feature = "json"),
598    not(target_arch = "wasm32")
599))]
600fn fill_gn_and_encode(
601    mut geonetworking: geonetworking::UnsecuredHeader,
602    payload: &[u8],
603) -> Result<alloc::vec::Vec<u8>, alloc::string::String> {
604    use geonetworking::Encode as _;
605
606    #[allow(clippy::cast_possible_truncation)]
607    let gn_payload_length = payload.len() as u16;
608
609    geonetworking.common.payload_length = gn_payload_length;
610    geonetworking.basic.remaining_hop_limit = geonetworking.common.maximum_hop_limit;
611    geonetworking
612        .with_payload(payload)
613        .map_err(crate::map_err_to_string)?
614        .encode_to_vec()
615        .map_err(crate::map_err_to_string)
616}
617
618#[cfg(all(target_arch = "wasm32", feature = "json"))]
619fn transcode_jer_to_uper<T: rasn::Decode + rasn::Encode>(
620    input: &String,
621) -> Result<alloc::vec::Vec<u8>, String> {
622    rasn::uper::encode(&rasn::jer::decode::<T>(input).map_err(crate::map_err_to_string)?)
623        .map_err(crate::map_err_to_string)
624}