1#[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"))]
13pub type Encoded = js_sys::Uint8Array;
15#[cfg(not(target_arch = "wasm32"))]
16pub type Encoded = alloc::vec::Vec<u8>;
18
19#[cfg(not(target_arch = "wasm32"))]
20impl crate::ItsMessage<'_> {
21 #[allow(clippy::too_many_lines)]
22 pub fn encode(
36 self,
37 encoding_rules: crate::EncodingRules,
38 ) -> Result<Encoded, alloc::string::String> {
39 use crate::ItsMessage;
40
41 let (geo, tp, mut etsi_uper) = match self {
42 #[cfg(feature = "denm_1_3_1")]
43 ItsMessage::DenmV1 {
44 geonetworking,
45 transport,
46 etsi,
47 } => encoding_rules
48 .codec()
49 .encode_to_binary(&etsi)
50 .map(|enc| (geonetworking, transport, enc)),
51 #[cfg(feature = "denm_2_2_1")]
52 ItsMessage::DenmV2 {
53 geonetworking,
54 transport,
55 etsi,
56 } => encoding_rules
57 .codec()
58 .encode_to_binary(&etsi)
59 .map(|enc| (geonetworking, transport, enc)),
60 #[cfg(feature = "cam_1_4_1")]
61 ItsMessage::Cam {
62 geonetworking,
63 transport,
64 etsi,
65 } => encoding_rules
66 .codec()
67 .encode_to_binary(&etsi)
68 .map(|enc| (geonetworking, transport, enc)),
69 #[cfg(feature = "spatem_2_2_1")]
70 ItsMessage::Spatem {
71 geonetworking,
72 transport,
73 etsi,
74 } => encoding_rules
75 .codec()
76 .encode_to_binary(&etsi)
77 .map(|enc| (geonetworking, transport, enc)),
78 #[cfg(feature = "mapem_2_2_1")]
79 ItsMessage::Mapem {
80 geonetworking,
81 transport,
82 etsi,
83 } => encoding_rules
84 .codec()
85 .encode_to_binary(&etsi)
86 .map(|enc| (geonetworking, transport, enc)),
87 #[cfg(feature = "ivim_2_1_1")]
88 ItsMessage::IvimV1 {
89 geonetworking,
90 transport,
91 etsi,
92 } => encoding_rules
93 .codec()
94 .encode_to_binary(&etsi)
95 .map(|enc| (geonetworking, transport, enc)),
96 #[cfg(feature = "ivim_2_2_1")]
97 ItsMessage::IvimV2 {
98 geonetworking,
99 transport,
100 etsi,
101 } => encoding_rules
102 .codec()
103 .encode_to_binary(&etsi)
104 .map(|enc| (geonetworking, transport, enc)),
105 #[cfg(feature = "srem_2_2_1")]
106 ItsMessage::Srem {
107 geonetworking,
108 transport,
109 etsi,
110 } => encoding_rules
111 .codec()
112 .encode_to_binary(&etsi)
113 .map(|enc| (geonetworking, transport, enc)),
114 #[cfg(feature = "ssem_2_2_1")]
115 ItsMessage::Ssem {
116 geonetworking,
117 transport,
118 etsi,
119 } => encoding_rules
120 .codec()
121 .encode_to_binary(&etsi)
122 .map(|enc| (geonetworking, transport, enc)),
123 #[cfg(feature = "cpm_1")]
124 ItsMessage::CpmV1 {
125 geonetworking,
126 transport,
127 etsi,
128 } => encoding_rules
129 .codec()
130 .encode_to_binary(&etsi)
131 .map(|enc| (geonetworking, transport, enc)),
132 #[cfg(feature = "cpm_2_1_1")]
133 ItsMessage::CpmV2 {
134 geonetworking,
135 transport,
136 etsi,
137 } => encoding_rules
138 .codec()
139 .encode_to_binary(&etsi)
140 .map(|enc| (geonetworking, transport, enc)),
141 }
142 .map_err(crate::map_err_to_string)?;
143
144 if encoding_rules == crate::EncodingRules::UPER {
145 match (tp, geo) {
146 (None, None) => Ok(etsi_uper),
147 (
148 Some(tp),
149 Some(geonetworking::Packet::Unsecured {
150 basic,
151 common,
152 extended,
153 ..
154 } | geonetworking::Packet::Secured {
155 basic,
156 common,
157 extended,
158 ..
159 }),
160 ) => {
161 let mut encoded = tp.encode()?;
162 encoded.append(&mut etsi_uper);
163 fill_gn_and_encode(geonetworking::UnsecuredHeader { basic, common, extended }, &encoded)
164 }
165 _ => Err(
166 "Expecting either both or neither GeoNetworking and Transport headers to be present!"
167 .to_string(),
168 ),
169 }
170 } else {
171 Ok(etsi_uper)
172 }
173 }
174}
175
176#[cfg(all(target_arch = "wasm32", feature = "json"))]
177#[wasm_bindgen(js_name = encodeDenm)]
178pub fn encode_denm(denm: &crate::JsonItsMessage, version: u32) -> Result<Encoded, String> {
183 let mut payload = vec![];
184 match (&denm.its, version) {
185 (None, 131) | (None, 221) => return Err("No DENM JSON provided.".to_string()),
186 (Some(denm_json), 131) => {
187 payload.append(&mut transcode_jer_to_uper::<
188 crate::standards::denm_1_3_1::denm_pdu_descriptions::DENM,
189 >(denm_json)?);
190 }
191 (Some(denm_json), 221) => {
192 payload.append(&mut transcode_jer_to_uper::<
193 crate::standards::denm_2_2_1::denm_pdu_description::DENM,
194 >(denm_json)?);
195 }
196 _ => {
197 return Err(
198 "Unsupported DENM version: Supported DENM versions are 131 and 221.".to_string(),
199 );
200 }
201 };
202 let encoded = optionally_encode_headers(&denm.geonetworking, &denm.transport, payload)?;
203 Ok(Encoded::from(encoded.as_slice()))
204}
205
206#[cfg(all(target_arch = "wasm32", feature = "json"))]
207#[wasm_bindgen(js_name = encodeCam)]
208pub fn encode_cam(cam: &crate::JsonItsMessage, version: u32) -> Result<Encoded, String> {
213 let mut payload = vec![];
214 match (&cam.its, version) {
215 (None, 141) => return Err("No CAM JSON provided.".to_string()),
216 (Some(json), 141) => {
217 payload.append(&mut transcode_jer_to_uper::<
218 crate::standards::cam_1_4_1::cam_pdu_descriptions::CAM,
219 >(json)?);
220 }
221 _ => return Err("Unsupported CAM version: Supported CAM version is 141.".to_string()),
222 };
223 let encoded = optionally_encode_headers(&cam.geonetworking, &cam.transport, payload)?;
224 Ok(Encoded::from(encoded.as_slice()))
225}
226
227#[cfg(all(target_arch = "wasm32", feature = "json"))]
228#[wasm_bindgen(js_name = encodeMapem)]
229pub fn encode_mapem(mapem: &crate::JsonItsMessage, version: u32) -> Result<Encoded, String> {
234 let mut payload = vec![];
235 match (&mapem.its, version) {
236 (None, 221) => return Err("No MAPEM JSON provided.".to_string()),
237 (Some(json), 221) => {
238 payload.append(&mut transcode_jer_to_uper::<
239 crate::standards::mapem_2_2_1::mapem_pdu_descriptions::MAPEM,
240 >(json)?);
241 }
242 _ => return Err("Unsupported MAPEM version: Supported MAPEM version is 221.".to_string()),
243 };
244 let encoded = optionally_encode_headers(&mapem.geonetworking, &mapem.transport, payload)?;
245 Ok(Encoded::from(encoded.as_slice()))
246}
247
248#[cfg(all(target_arch = "wasm32", feature = "json"))]
249#[wasm_bindgen(js_name = encodeSpatem)]
250pub fn encode_spatem(spatem: &crate::JsonItsMessage, version: u32) -> Result<Encoded, String> {
255 let mut payload = vec![];
256 match (&spatem.its, version) {
257 (None, 221) => return Err("No SPATEM JSON provided.".to_string()),
258 (Some(json), 221) => {
259 payload.append(&mut transcode_jer_to_uper::<
260 crate::standards::spatem_2_2_1::spatem_pdu_descriptions::SPATEM,
261 >(json)?);
262 }
263 _ => {
264 return Err("Unsupported SPATEM version: Supported SPATEM version is 221.".to_string());
265 }
266 };
267 let encoded = optionally_encode_headers(&spatem.geonetworking, &spatem.transport, payload)?;
268 Ok(Encoded::from(encoded.as_slice()))
269}
270
271#[cfg(all(target_arch = "wasm32", feature = "json"))]
272#[wasm_bindgen(js_name = encodeIvim)]
273pub fn encode_ivim(ivim: &crate::JsonItsMessage, version: u32) -> Result<Encoded, String> {
278 let mut payload = vec![];
279 match (&ivim.its, version) {
280 (None, 131) | (None, 211) | (None, 221) => return Err("No IVIM JSON provided.".to_string()),
281 (Some(json), 211) | (Some(json), 131) => {
282 payload.append(&mut transcode_jer_to_uper::<
283 crate::standards::ivim_2_1_1::ivim_pdu_descriptions::IVIM,
284 >(json)?);
285 }
286 (Some(json), 221) => {
287 payload.append(&mut transcode_jer_to_uper::<
288 crate::standards::ivim_2_2_1::ivim_pdu_descriptions::IVIM,
289 >(json)?);
290 }
291 _ => {
292 return Err(
293 "Unsupported IVIM version: Supported IVIM versions are 131, 211 and 221."
294 .to_string(),
295 );
296 }
297 };
298 let encoded = optionally_encode_headers(&ivim.geonetworking, &ivim.transport, payload)?;
299 Ok(Encoded::from(encoded.as_slice()))
300}
301
302#[cfg(all(target_arch = "wasm32", feature = "json"))]
303#[wasm_bindgen(js_name = encodeSrem)]
304pub fn encode_srem(srem: &crate::JsonItsMessage, version: u32) -> Result<Encoded, String> {
309 let mut payload = vec![];
310 match (&srem.its, version) {
311 (None, 221) => return Err("No SREM JSON provided.".to_string()),
312 (Some(json), 221) => {
313 payload.append(&mut transcode_jer_to_uper::<
314 crate::standards::srem_2_2_1::srem_pdu_descriptions::SREM,
315 >(json)?);
316 }
317 _ => return Err("Unsupported SREM version: Supported SREM version is 221.".to_string()),
318 };
319 let encoded = optionally_encode_headers(&srem.geonetworking, &srem.transport, payload)?;
320 Ok(Encoded::from(encoded.as_slice()))
321}
322
323#[cfg(all(target_arch = "wasm32", feature = "json"))]
324#[wasm_bindgen(js_name = encodeCpm)]
325pub fn encode_cpm(cpm: &crate::JsonItsMessage, version: u32) -> Result<Encoded, String> {
330 let mut payload = vec![];
331 match (&cpm.its, version) {
332 (None, 131) | (None, 211) => return Err("No CPM JSON provided.".to_string()),
333 (Some(json), 131) => {
334 payload.append(&mut transcode_jer_to_uper::<
335 crate::standards::cpm_1::cpm_pdu_descriptions::CPM,
336 >(json)?);
337 }
338 (Some(json), 211) => {
339 payload.append(&mut transcode_jer_to_uper::<
340 crate::standards::cpm_2_1_1::cpm_pdu_descriptions::CollectivePerceptionMessage,
341 >(json)?);
342 }
343 _ => {
344 return Err(
345 "Unsupported CPM version: Supported CPM versions are 131 and 211.".to_string(),
346 );
347 }
348 };
349 let encoded = optionally_encode_headers(&cpm.geonetworking, &cpm.transport, payload)?;
350 Ok(Encoded::from(encoded.as_slice()))
351}
352
353#[cfg(all(target_arch = "wasm32", feature = "json"))]
354#[wasm_bindgen(js_name = encodeSsem)]
355pub fn encode_ssem(ssem: &crate::JsonItsMessage, version: u32) -> Result<Encoded, String> {
360 let mut payload = vec![];
361 match (&ssem.its, version) {
362 (None, 221) => return Err("No SSEM JSON provided.".to_string()),
363 (Some(json), 221) => {
364 payload.append(&mut transcode_jer_to_uper::<
365 crate::standards::ssem_2_2_1::ssem_pdu_descriptions::SSEM,
366 >(json)?);
367 }
368 _ => return Err("Unsupported SSEM version: Supported SSEM version is 221.".to_string()),
369 };
370 let encoded = optionally_encode_headers(&ssem.geonetworking, &ssem.transport, payload)?;
371 Ok(Encoded::from(encoded.as_slice()))
372}
373
374#[cfg(all(target_arch = "wasm32", feature = "json"))]
375fn optionally_encode_headers(
376 gn_json: &Option<String>,
377 tp_json: &Option<String>,
378 mut its: alloc::vec::Vec<u8>,
379) -> Result<alloc::vec::Vec<u8>, String> {
380 use crate::transport::encode::Encode as _;
381
382 match (gn_json, tp_json) {
383 (Some(_), None) | (None, Some(_)) => Err(
384 "Expecting either both or neither GeoNetworking and Transport headers to be present!"
385 .to_string(),
386 ),
387 (Some(gn), Some(tp)) => {
388 let geonetworking =
389 geonetworking::UnsecuredHeader::from_json(gn).map_err(crate::map_err_to_string)?;
390 let mut transport = match geonetworking.common.next_header {
391 geonetworking::NextAfterCommon::BTPA => {
392 crate::transport::BasicTransportAHeader::decode_from_json(tp)
393 .map_err(crate::map_err_to_string)?
394 .encode()
395 .map_err(crate::map_err_to_string)?
396 }
397 geonetworking::NextAfterCommon::BTPB => {
398 crate::transport::BasicTransportBHeader::decode_from_json(tp)
399 .map_err(crate::map_err_to_string)?
400 .encode()
401 .map_err(crate::map_err_to_string)?
402 }
403 h => {
404 return Err(alloc::format!(
405 "Currently only BTP-A and BTP-B headers can be encoded: Encountered {h:?}"
406 ));
407 }
408 };
409 transport.append(&mut its);
410 fill_gn_and_encode(geonetworking, &transport)
411 }
412 _ => Ok(its),
413 }
414}
415
416#[cfg(any(
417 all(target_arch = "wasm32", feature = "json"),
418 not(target_arch = "wasm32")
419))]
420fn fill_gn_and_encode(
421 mut geonetworking: geonetworking::UnsecuredHeader,
422 payload: &[u8],
423) -> Result<alloc::vec::Vec<u8>, alloc::string::String> {
424 use geonetworking::{
425 AreaType,
426 BroadcastType,
427 Encode as _,
428 ExtendedHeader,
429 HeaderType,
430 LocationServiceType,
431 };
432
433 #[allow(clippy::cast_possible_truncation)]
434 let gn_payload_length = payload.len() as u16;
435
436 geonetworking.common.payload_length = gn_payload_length;
437 geonetworking.common.header_type_and_subtype = match geonetworking.extended {
438 Some(ExtendedHeader::Beacon(_)) => HeaderType::Beacon,
439 Some(ExtendedHeader::GAC(_)) => HeaderType::GeoAnycast(AreaType::Circular),
440 Some(ExtendedHeader::GBC(_)) => HeaderType::GeoBroadcast(AreaType::Circular),
441 Some(ExtendedHeader::GUC(_)) => HeaderType::GeoUnicast,
442 Some(ExtendedHeader::TSB(_)) => {
443 HeaderType::TopologicallyScopedBroadcast(BroadcastType::MultiHop)
444 }
445 Some(ExtendedHeader::SHB(_)) => {
446 HeaderType::TopologicallyScopedBroadcast(BroadcastType::SingleHop)
447 }
448 Some(ExtendedHeader::LSRequest(_)) => {
449 HeaderType::LocationService(LocationServiceType::Request)
450 }
451 Some(ExtendedHeader::LSReply(_)) => HeaderType::LocationService(LocationServiceType::Reply),
452 None => HeaderType::Any,
453 };
454 geonetworking
455 .with_payload(payload)
456 .map_err(crate::map_err_to_string)?
457 .encode_to_vec()
458 .map_err(crate::map_err_to_string)
459}
460
461#[cfg(all(target_arch = "wasm32", feature = "json"))]
462fn transcode_jer_to_uper<T: rasn::Decode + rasn::Encode>(
463 input: &String,
464) -> Result<alloc::vec::Vec<u8>, String> {
465 rasn::uper::encode(&rasn::jer::decode::<T>(input).map_err(crate::map_err_to_string)?)
466 .map_err(crate::map_err_to_string)
467}