1#[cfg(not(target_arch = "wasm32"))]
7use alloc::string::ToString;
8
9#[cfg(any(
10 all(target_arch = "wasm32", feature = "json"),
11 not(target_arch = "wasm32")
12))]
13use geonetworking::{Encode, ExtendedHeader, HeaderType, UnsecuredHeader};
14#[cfg(all(target_arch = "wasm32", feature = "json"))]
15use wasm_bindgen::prelude::*;
16
17#[cfg(not(target_arch = "wasm32"))]
18use crate::ItsMessage;
19#[cfg(all(target_arch = "wasm32", feature = "json"))]
20use crate::JsonItsMessage;
21#[cfg(any(
22 all(target_arch = "wasm32", feature = "json"),
23 not(target_arch = "wasm32")
24))]
25use crate::map_err_to_string;
26#[cfg(all(target_arch = "wasm32", feature = "json"))]
27use crate::transport::encode::Encode as TpEncode;
28#[cfg(not(target_arch = "wasm32"))]
29use crate::{EncodingRules, Packet};
30
31#[cfg(all(target_arch = "wasm32", feature = "json"))]
32pub type Encoded = js_sys::Uint8Array;
34#[cfg(not(target_arch = "wasm32"))]
35pub type Encoded = alloc::vec::Vec<u8>;
37
38#[cfg(not(target_arch = "wasm32"))]
39impl ItsMessage<'_> {
40 #[allow(clippy::too_many_lines)]
41 pub fn encode(self, encoding_rules: EncodingRules) -> Result<Encoded, alloc::string::String> {
55 let (geo, tp, mut etsi_uper) = match self {
56 #[cfg(feature = "denm_1_3_1")]
57 ItsMessage::DenmV1 {
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 = "denm_2_2_1")]
66 ItsMessage::DenmV2 {
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 = "cam_1_4_1")]
75 ItsMessage::Cam {
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 = "spatem_2_2_1")]
84 ItsMessage::Spatem {
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 = "mapem_2_2_1")]
93 ItsMessage::Mapem {
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_1_1")]
102 ItsMessage::IvimV1 {
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 = "ivim_2_2_1")]
111 ItsMessage::IvimV2 {
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 = "srem_2_2_1")]
120 ItsMessage::Srem {
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 = "ssem_2_2_1")]
129 ItsMessage::Ssem {
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_1")]
138 ItsMessage::CpmV1 {
139 geonetworking,
140 transport,
141 etsi,
142 } => encoding_rules
143 .codec()
144 .encode_to_binary(&etsi)
145 .map(|enc| (geonetworking, transport, enc)),
146 #[cfg(feature = "cpm_2_1_1")]
147 ItsMessage::CpmV2 {
148 geonetworking,
149 transport,
150 etsi,
151 } => encoding_rules
152 .codec()
153 .encode_to_binary(&etsi)
154 .map(|enc| (geonetworking, transport, enc)),
155 }
156 .map_err(map_err_to_string)?;
157
158 if encoding_rules == EncodingRules::UPER {
159 match (tp, geo) {
160 (None, None) => Ok(etsi_uper),
161 (
162 Some(tp),
163 Some(Packet::Unsecured {
164 basic,
165 common,
166 extended,
167 ..
168 } | Packet::Secured {
169 basic,
170 common,
171 extended,
172 ..
173 }),
174 ) => {
175 let mut encoded = tp.encode()?;
176 encoded.append(&mut etsi_uper);
177 fill_gn_and_encode(UnsecuredHeader { basic, common, extended }, &encoded)
178 }
179 _ => Err(
180 "Expecting either both or neither GeoNetworking and Transport headers to be present!"
181 .to_string(),
182 ),
183 }
184 } else {
185 Ok(etsi_uper)
186 }
187 }
188}
189
190#[cfg(all(target_arch = "wasm32", feature = "json"))]
191#[wasm_bindgen(js_name = encodeDenm)]
192pub fn encode_denm(denm: &JsonItsMessage, version: u32) -> Result<Encoded, String> {
197 let mut payload = vec![];
198 match (&denm.its, version) {
199 (None, 131) | (None, 221) => return Err("No DENM JSON provided.".to_string()),
200 (Some(denm_json), 131) => {
201 payload.append(&mut transcode_jer_to_uper::<
202 crate::standards::denm_1_3_1::denm_pdu_descriptions::DENM,
203 >(denm_json)?);
204 }
205 (Some(denm_json), 221) => {
206 payload.append(&mut transcode_jer_to_uper::<
207 crate::standards::denm_2_2_1::denm_pdu_description::DENM,
208 >(denm_json)?);
209 }
210 _ => {
211 return Err(
212 "Unsupported DENM version: Supported DENM versions are 131 and 221.".to_string(),
213 );
214 }
215 };
216 let encoded = optionally_encode_headers(&denm.geonetworking, &denm.transport, payload)?;
217 Ok(Encoded::from(encoded.as_slice()))
218}
219
220#[cfg(all(target_arch = "wasm32", feature = "json"))]
221#[wasm_bindgen(js_name = encodeCam)]
222pub fn encode_cam(cam: &JsonItsMessage, version: u32) -> Result<Encoded, String> {
227 let mut payload = vec![];
228 match (&cam.its, version) {
229 (None, 141) => return Err("No CAM JSON provided.".to_string()),
230 (Some(json), 141) => {
231 payload.append(&mut transcode_jer_to_uper::<
232 crate::standards::cam_1_4_1::cam_pdu_descriptions::CAM,
233 >(json)?);
234 }
235 _ => return Err("Unsupported CAM version: Supported CAM version is 141.".to_string()),
236 };
237 let encoded = optionally_encode_headers(&cam.geonetworking, &cam.transport, payload)?;
238 Ok(Encoded::from(encoded.as_slice()))
239}
240
241#[cfg(all(target_arch = "wasm32", feature = "json"))]
242#[wasm_bindgen(js_name = encodeMapem)]
243pub fn encode_mapem(mapem: &JsonItsMessage, version: u32) -> Result<Encoded, String> {
248 let mut payload = vec![];
249 match (&mapem.its, version) {
250 (None, 221) => return Err("No MAPEM JSON provided.".to_string()),
251 (Some(json), 221) => {
252 payload.append(&mut transcode_jer_to_uper::<
253 crate::standards::mapem_2_2_1::mapem_pdu_descriptions::MAPEM,
254 >(json)?);
255 }
256 _ => return Err("Unsupported MAPEM version: Supported MAPEM version is 221.".to_string()),
257 };
258 let encoded = optionally_encode_headers(&mapem.geonetworking, &mapem.transport, payload)?;
259 Ok(Encoded::from(encoded.as_slice()))
260}
261
262#[cfg(all(target_arch = "wasm32", feature = "json"))]
263#[wasm_bindgen(js_name = encodeSpatem)]
264pub fn encode_spatem(spatem: &JsonItsMessage, version: u32) -> Result<Encoded, String> {
269 let mut payload = vec![];
270 match (&spatem.its, version) {
271 (None, 221) => return Err("No SPATEM JSON provided.".to_string()),
272 (Some(json), 221) => {
273 payload.append(&mut transcode_jer_to_uper::<
274 crate::standards::spatem_2_2_1::spatem_pdu_descriptions::SPATEM,
275 >(json)?);
276 }
277 _ => {
278 return Err("Unsupported SPATEM version: Supported SPATEM version is 221.".to_string());
279 }
280 };
281 let encoded = optionally_encode_headers(&spatem.geonetworking, &spatem.transport, payload)?;
282 Ok(Encoded::from(encoded.as_slice()))
283}
284
285#[cfg(all(target_arch = "wasm32", feature = "json"))]
286#[wasm_bindgen(js_name = encodeIvim)]
287pub fn encode_ivim(ivim: &JsonItsMessage, version: u32) -> Result<Encoded, String> {
292 let mut payload = vec![];
293 match (&ivim.its, version) {
294 (None, 131) | (None, 211) | (None, 221) => return Err("No IVIM JSON provided.".to_string()),
295 (Some(json), 211) | (Some(json), 131) => {
296 payload.append(&mut transcode_jer_to_uper::<
297 crate::standards::ivim_2_1_1::ivim_pdu_descriptions::IVIM,
298 >(json)?);
299 }
300 (Some(json), 221) => {
301 payload.append(&mut transcode_jer_to_uper::<
302 crate::standards::ivim_2_2_1::ivim_pdu_descriptions::IVIM,
303 >(json)?);
304 }
305 _ => {
306 return Err(
307 "Unsupported IVIM version: Supported IVIM versions are 131, 211 and 221."
308 .to_string(),
309 );
310 }
311 };
312 let encoded = optionally_encode_headers(&ivim.geonetworking, &ivim.transport, payload)?;
313 Ok(Encoded::from(encoded.as_slice()))
314}
315
316#[cfg(all(target_arch = "wasm32", feature = "json"))]
317#[wasm_bindgen(js_name = encodeSrem)]
318pub fn encode_srem(srem: &JsonItsMessage, version: u32) -> Result<Encoded, String> {
323 let mut payload = vec![];
324 match (&srem.its, version) {
325 (None, 221) => return Err("No SREM JSON provided.".to_string()),
326 (Some(json), 221) => {
327 payload.append(&mut transcode_jer_to_uper::<
328 crate::standards::srem_2_2_1::srem_pdu_descriptions::SREM,
329 >(json)?);
330 }
331 _ => return Err("Unsupported SREM version: Supported SREM version is 221.".to_string()),
332 };
333 let encoded = optionally_encode_headers(&srem.geonetworking, &srem.transport, payload)?;
334 Ok(Encoded::from(encoded.as_slice()))
335}
336
337#[cfg(all(target_arch = "wasm32", feature = "json"))]
338#[wasm_bindgen(js_name = encodeCpm)]
339pub fn encode_cpm(cpm: &JsonItsMessage, version: u32) -> Result<Encoded, String> {
344 let mut payload = vec![];
345 match (&cpm.its, version) {
346 (None, 131) | (None, 211) => return Err("No CPM JSON provided.".to_string()),
347 (Some(json), 131) => {
348 payload.append(&mut transcode_jer_to_uper::<
349 crate::standards::cpm_1::cpm_pdu_descriptions::CPM,
350 >(json)?);
351 }
352 (Some(json), 211) => {
353 payload.append(&mut transcode_jer_to_uper::<
354 crate::standards::cpm_2_1_1::cpm_pdu_descriptions::CollectivePerceptionMessage,
355 >(json)?);
356 }
357 _ => {
358 return Err(
359 "Unsupported CPM version: Supported CPM versions are 131 and 211.".to_string(),
360 );
361 }
362 };
363 let encoded = optionally_encode_headers(&cpm.geonetworking, &cpm.transport, payload)?;
364 Ok(Encoded::from(encoded.as_slice()))
365}
366
367#[cfg(all(target_arch = "wasm32", feature = "json"))]
368#[wasm_bindgen(js_name = encodeSsem)]
369pub fn encode_ssem(ssem: &JsonItsMessage, version: u32) -> Result<Encoded, String> {
374 let mut payload = vec![];
375 match (&ssem.its, version) {
376 (None, 221) => return Err("No SSEM JSON provided.".to_string()),
377 (Some(json), 221) => {
378 payload.append(&mut transcode_jer_to_uper::<
379 crate::standards::ssem_2_2_1::ssem_pdu_descriptions::SSEM,
380 >(json)?);
381 }
382 _ => return Err("Unsupported SSEM version: Supported SSEM version is 221.".to_string()),
383 };
384 let encoded = optionally_encode_headers(&ssem.geonetworking, &ssem.transport, payload)?;
385 Ok(Encoded::from(encoded.as_slice()))
386}
387
388#[cfg(all(target_arch = "wasm32", feature = "json"))]
389fn optionally_encode_headers(
390 gn_json: &Option<String>,
391 tp_json: &Option<String>,
392 mut its: alloc::vec::Vec<u8>,
393) -> Result<alloc::vec::Vec<u8>, String> {
394 match (gn_json, tp_json) {
395 (Some(_), None) | (None, Some(_)) => Err(
396 "Expecting either both or neither GeoNetworking and Transport headers to be present!"
397 .to_string(),
398 ),
399 (Some(gn), Some(tp)) => {
400 let geonetworking = UnsecuredHeader::from_json(gn).map_err(map_err_to_string)?;
401 let mut transport = match geonetworking.common.next_header {
402 geonetworking::NextAfterCommon::BTPA => {
403 crate::transport::BasicTransportAHeader::decode_from_json(tp)
404 .map_err(map_err_to_string)?
405 .encode()
406 .map_err(map_err_to_string)?
407 }
408 geonetworking::NextAfterCommon::BTPB => {
409 crate::transport::BasicTransportBHeader::decode_from_json(tp)
410 .map_err(map_err_to_string)?
411 .encode()
412 .map_err(map_err_to_string)?
413 }
414 h => {
415 return Err(alloc::format!(
416 "Currently only BTP-A and BTP-B headers can be encoded: Encountered {h:?}"
417 ));
418 }
419 };
420 transport.append(&mut its);
421 fill_gn_and_encode(geonetworking, &transport)
422 }
423 _ => Ok(its),
424 }
425}
426
427#[cfg(any(
428 all(target_arch = "wasm32", feature = "json"),
429 not(target_arch = "wasm32")
430))]
431fn fill_gn_and_encode(
432 mut geonetworking: UnsecuredHeader,
433 payload: &[u8],
434) -> Result<alloc::vec::Vec<u8>, alloc::string::String> {
435 #[allow(clippy::cast_possible_truncation)]
436 let gn_payload_length = payload.len() as u16;
437
438 geonetworking.common.payload_length = gn_payload_length;
439 geonetworking.common.header_type_and_subtype = match geonetworking.extended {
440 Some(ExtendedHeader::Beacon(_)) => HeaderType::Beacon,
441 Some(ExtendedHeader::GAC(_)) => HeaderType::GeoAnycast(geonetworking::AreaType::Circular),
442 Some(ExtendedHeader::GBC(_)) => HeaderType::GeoBroadcast(geonetworking::AreaType::Circular),
443 Some(ExtendedHeader::GUC(_)) => HeaderType::GeoUnicast,
444 Some(ExtendedHeader::TSB(_)) => {
445 HeaderType::TopologicallyScopedBroadcast(geonetworking::BroadcastType::MultiHop)
446 }
447 Some(ExtendedHeader::SHB(_)) => {
448 HeaderType::TopologicallyScopedBroadcast(geonetworking::BroadcastType::SingleHop)
449 }
450 Some(ExtendedHeader::LSRequest(_)) => {
451 HeaderType::LocationService(geonetworking::LocationServiceType::Request)
452 }
453 Some(ExtendedHeader::LSReply(_)) => {
454 HeaderType::LocationService(geonetworking::LocationServiceType::Reply)
455 }
456 None => HeaderType::Any,
457 };
458 geonetworking
459 .with_payload(payload)
460 .map_err(map_err_to_string)?
461 .encode_to_vec()
462 .map_err(map_err_to_string)
463}
464
465#[cfg(all(target_arch = "wasm32", feature = "json"))]
466fn transcode_jer_to_uper<T: rasn::Decode + rasn::Encode>(
467 input: &String,
468) -> Result<alloc::vec::Vec<u8>, String> {
469 rasn::uper::encode(&rasn::jer::decode::<T>(input).map_err(map_err_to_string)?)
470 .map_err(map_err_to_string)
471}