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