#[cfg(not(target_arch = "wasm32"))]
use alloc::string::ToString;
#[cfg(any(
all(target_arch = "wasm32", feature = "json"),
not(target_arch = "wasm32")
))]
use geonetworking::{Encode, ExtendedHeader, HeaderType, UnsecuredHeader};
#[cfg(all(target_arch = "wasm32", feature = "json"))]
use wasm_bindgen::prelude::*;
#[cfg(not(target_arch = "wasm32"))]
use crate::ItsMessage;
#[cfg(all(target_arch = "wasm32", feature = "json"))]
use crate::JsonItsMessage;
#[cfg(any(
all(target_arch = "wasm32", feature = "json"),
not(target_arch = "wasm32")
))]
use crate::map_err_to_string;
#[cfg(all(target_arch = "wasm32", feature = "json"))]
use crate::transport::encode::Encode as TpEncode;
#[cfg(not(target_arch = "wasm32"))]
use crate::{EncodingRules, Packet};
#[cfg(all(target_arch = "wasm32", feature = "json"))]
pub type Encoded = js_sys::Uint8Array;
#[cfg(not(target_arch = "wasm32"))]
pub type Encoded = alloc::vec::Vec<u8>;
#[cfg(not(target_arch = "wasm32"))]
impl ItsMessage<'_> {
#[allow(clippy::too_many_lines)]
pub fn encode(self, encoding_rules: EncodingRules) -> Result<Encoded, alloc::string::String> {
let (geo, tp, mut etsi_uper) = match self {
#[cfg(feature = "denm_1_3_1")]
ItsMessage::DenmV1 {
geonetworking,
transport,
etsi,
} => encoding_rules
.codec()
.encode_to_binary(&etsi)
.map(|enc| (geonetworking, transport, enc)),
#[cfg(feature = "denm_2_2_1")]
ItsMessage::DenmV2 {
geonetworking,
transport,
etsi,
} => encoding_rules
.codec()
.encode_to_binary(&etsi)
.map(|enc| (geonetworking, transport, enc)),
#[cfg(feature = "cam_1_4_1")]
ItsMessage::Cam {
geonetworking,
transport,
etsi,
} => encoding_rules
.codec()
.encode_to_binary(&etsi)
.map(|enc| (geonetworking, transport, enc)),
#[cfg(feature = "spatem_2_2_1")]
ItsMessage::Spatem {
geonetworking,
transport,
etsi,
} => encoding_rules
.codec()
.encode_to_binary(&etsi)
.map(|enc| (geonetworking, transport, enc)),
#[cfg(feature = "mapem_2_2_1")]
ItsMessage::Mapem {
geonetworking,
transport,
etsi,
} => encoding_rules
.codec()
.encode_to_binary(&etsi)
.map(|enc| (geonetworking, transport, enc)),
#[cfg(feature = "ivim_2_1_1")]
ItsMessage::IvimV1 {
geonetworking,
transport,
etsi,
} => encoding_rules
.codec()
.encode_to_binary(&etsi)
.map(|enc| (geonetworking, transport, enc)),
#[cfg(feature = "ivim_2_2_1")]
ItsMessage::IvimV2 {
geonetworking,
transport,
etsi,
} => encoding_rules
.codec()
.encode_to_binary(&etsi)
.map(|enc| (geonetworking, transport, enc)),
#[cfg(feature = "srem_2_2_1")]
ItsMessage::Srem {
geonetworking,
transport,
etsi,
} => encoding_rules
.codec()
.encode_to_binary(&etsi)
.map(|enc| (geonetworking, transport, enc)),
#[cfg(feature = "ssem_2_2_1")]
ItsMessage::Ssem {
geonetworking,
transport,
etsi,
} => encoding_rules
.codec()
.encode_to_binary(&etsi)
.map(|enc| (geonetworking, transport, enc)),
#[cfg(feature = "cpm_1")]
ItsMessage::CpmV1 {
geonetworking,
transport,
etsi,
} => encoding_rules
.codec()
.encode_to_binary(&etsi)
.map(|enc| (geonetworking, transport, enc)),
#[cfg(feature = "cpm_2_1_1")]
ItsMessage::CpmV2 {
geonetworking,
transport,
etsi,
} => encoding_rules
.codec()
.encode_to_binary(&etsi)
.map(|enc| (geonetworking, transport, enc)),
}
.map_err(map_err_to_string)?;
match (tp, geo) {
(None, None) => Ok(etsi_uper),
(
Some(tp),
Some(Packet::Unsecured {
basic,
common,
extended,
..
}),
) => {
let mut encoded = tp.encode()?;
encoded.append(&mut etsi_uper);
fill_gn_and_encode(UnsecuredHeader { basic, common, extended }, &encoded)
}
_ => Err(
"Expecting either both or neither GeoNetworking and Transport headers to be present!"
.to_string(),
),
}
}
}
#[cfg(all(target_arch = "wasm32", feature = "json"))]
#[wasm_bindgen(js_name = encodeDenm)]
pub fn encode_denm(denm: &JsonItsMessage, version: u32) -> Result<Encoded, String> {
let mut payload = vec![];
match (&denm.its, version) {
(None, 131) | (None, 221) => return Err("No DENM JSON provided.".to_string()),
(Some(denm_json), 131) => {
payload.append(&mut transcode_jer_to_uper::<
crate::standards::denm_1_3_1::denm_pdu_descriptions::DENM,
>(denm_json)?);
}
(Some(denm_json), 221) => {
payload.append(&mut transcode_jer_to_uper::<
crate::standards::denm_2_2_1::denm_pdu_description::DENM,
>(denm_json)?);
}
_ => {
return Err(
"Unsupported DENM version: Supported DENM versions are 131 and 221.".to_string(),
);
}
};
let encoded = optionally_encode_headers(&denm.geonetworking, &denm.transport, payload)?;
Ok(Encoded::from(encoded.as_slice()))
}
#[cfg(all(target_arch = "wasm32", feature = "json"))]
#[wasm_bindgen(js_name = encodeCam)]
pub fn encode_cam(cam: &JsonItsMessage, version: u32) -> Result<Encoded, String> {
let mut payload = vec![];
match (&cam.its, version) {
(None, 141) => return Err("No CAM JSON provided.".to_string()),
(Some(json), 141) => {
payload.append(&mut transcode_jer_to_uper::<
crate::standards::cam_1_4_1::cam_pdu_descriptions::CAM,
>(json)?);
}
_ => return Err("Unsupported CAM version: Supported CAM version is 141.".to_string()),
};
let encoded = optionally_encode_headers(&cam.geonetworking, &cam.transport, payload)?;
Ok(Encoded::from(encoded.as_slice()))
}
#[cfg(all(target_arch = "wasm32", feature = "json"))]
#[wasm_bindgen(js_name = encodeMapem)]
pub fn encode_mapem(mapem: &JsonItsMessage, version: u32) -> Result<Encoded, String> {
let mut payload = vec![];
match (&mapem.its, version) {
(None, 221) => return Err("No MAPEM JSON provided.".to_string()),
(Some(json), 221) => {
payload.append(&mut transcode_jer_to_uper::<
crate::standards::mapem_2_2_1::mapem_pdu_descriptions::MAPEM,
>(json)?);
}
_ => return Err("Unsupported MAPEM version: Supported MAPEM version is 221.".to_string()),
};
let encoded = optionally_encode_headers(&mapem.geonetworking, &mapem.transport, payload)?;
Ok(Encoded::from(encoded.as_slice()))
}
#[cfg(all(target_arch = "wasm32", feature = "json"))]
#[wasm_bindgen(js_name = encodeSpatem)]
pub fn encode_spatem(spatem: &JsonItsMessage, version: u32) -> Result<Encoded, String> {
let mut payload = vec![];
match (&spatem.its, version) {
(None, 221) => return Err("No SPATEM JSON provided.".to_string()),
(Some(json), 221) => {
payload.append(&mut transcode_jer_to_uper::<
crate::standards::spatem_2_2_1::spatem_pdu_descriptions::SPATEM,
>(json)?);
}
_ => {
return Err("Unsupported SPATEM version: Supported SPATEM version is 221.".to_string());
}
};
let encoded = optionally_encode_headers(&spatem.geonetworking, &spatem.transport, payload)?;
Ok(Encoded::from(encoded.as_slice()))
}
#[cfg(all(target_arch = "wasm32", feature = "json"))]
#[wasm_bindgen(js_name = encodeIvim)]
pub fn encode_ivim(ivim: &JsonItsMessage, version: u32) -> Result<Encoded, String> {
let mut payload = vec![];
match (&ivim.its, version) {
(None, 131) | (None, 211) | (None, 221) => return Err("No IVIM JSON provided.".to_string()),
(Some(json), 211) | (Some(json), 131) => {
payload.append(&mut transcode_jer_to_uper::<
crate::standards::ivim_2_1_1::ivim_pdu_descriptions::IVIM,
>(json)?);
}
(Some(json), 221) => {
payload.append(&mut transcode_jer_to_uper::<
crate::standards::ivim_2_2_1::ivim_pdu_descriptions::IVIM,
>(json)?);
}
_ => {
return Err(
"Unsupported IVIM version: Supported IVIM versions are 131, 211 and 221."
.to_string(),
);
}
};
let encoded = optionally_encode_headers(&ivim.geonetworking, &ivim.transport, payload)?;
Ok(Encoded::from(encoded.as_slice()))
}
#[cfg(all(target_arch = "wasm32", feature = "json"))]
#[wasm_bindgen(js_name = encodeSrem)]
pub fn encode_srem(srem: &JsonItsMessage, version: u32) -> Result<Encoded, String> {
let mut payload = vec![];
match (&srem.its, version) {
(None, 221) => return Err("No SREM JSON provided.".to_string()),
(Some(json), 221) => {
payload.append(&mut transcode_jer_to_uper::<
crate::standards::srem_2_2_1::srem_pdu_descriptions::SREM,
>(json)?);
}
_ => return Err("Unsupported SREM version: Supported SREM version is 221.".to_string()),
};
let encoded = optionally_encode_headers(&srem.geonetworking, &srem.transport, payload)?;
Ok(Encoded::from(encoded.as_slice()))
}
#[cfg(all(target_arch = "wasm32", feature = "json"))]
#[wasm_bindgen(js_name = encodeCpm)]
pub fn encode_cpm(cpm: &JsonItsMessage, version: u32) -> Result<Encoded, String> {
let mut payload = vec![];
match (&cpm.its, version) {
(None, 131) | (None, 211) => return Err("No CPM JSON provided.".to_string()),
(Some(json), 131) => {
payload.append(&mut transcode_jer_to_uper::<
crate::standards::cpm_1::cpm_pdu_descriptions::CPM,
>(json)?);
}
(Some(json), 211) => {
payload.append(&mut transcode_jer_to_uper::<
crate::standards::cpm_2_1_1::cpm_pdu_descriptions::CollectivePerceptionMessage,
>(json)?);
}
_ => {
return Err(
"Unsupported CPM version: Supported CPM versions are 131 and 211.".to_string(),
);
}
};
let encoded = optionally_encode_headers(&cpm.geonetworking, &cpm.transport, payload)?;
Ok(Encoded::from(encoded.as_slice()))
}
#[cfg(all(target_arch = "wasm32", feature = "json"))]
#[wasm_bindgen(js_name = encodeSsem)]
pub fn encode_ssem(ssem: &JsonItsMessage, version: u32) -> Result<Encoded, String> {
let mut payload = vec![];
match (&ssem.its, version) {
(None, 221) => return Err("No SSEM JSON provided.".to_string()),
(Some(json), 221) => {
payload.append(&mut transcode_jer_to_uper::<
crate::standards::ssem_2_2_1::ssem_pdu_descriptions::SSEM,
>(json)?);
}
_ => return Err("Unsupported SSEM version: Supported SSEM version is 221.".to_string()),
};
let encoded = optionally_encode_headers(&ssem.geonetworking, &ssem.transport, payload)?;
Ok(Encoded::from(encoded.as_slice()))
}
#[cfg(all(target_arch = "wasm32", feature = "json"))]
fn optionally_encode_headers(
gn_json: &Option<String>,
tp_json: &Option<String>,
mut its: alloc::vec::Vec<u8>,
) -> Result<alloc::vec::Vec<u8>, String> {
match (gn_json, tp_json) {
(Some(_), None) | (None, Some(_)) => Err(
"Expecting either both or neither GeoNetworking and Transport headers to be present!"
.to_string(),
),
(Some(gn), Some(tp)) => {
let geonetworking = UnsecuredHeader::from_json(gn).map_err(map_err_to_string)?;
let mut transport = match geonetworking.common.next_header {
geonetworking::NextAfterCommon::BTPA => {
crate::transport::BasicTransportAHeader::decode_from_json(tp)
.map_err(map_err_to_string)?
.encode()
.map_err(map_err_to_string)?
}
geonetworking::NextAfterCommon::BTPB => {
crate::transport::BasicTransportBHeader::decode_from_json(tp)
.map_err(map_err_to_string)?
.encode()
.map_err(map_err_to_string)?
}
h => {
return Err(alloc::format!(
"Currently only BTP-A and BTP-B headers can be encoded: Encountered {h:?}"
));
}
};
transport.append(&mut its);
fill_gn_and_encode(geonetworking, &transport)
}
_ => Ok(its),
}
}
#[cfg(any(
all(target_arch = "wasm32", feature = "json"),
not(target_arch = "wasm32")
))]
fn fill_gn_and_encode(
mut geonetworking: UnsecuredHeader,
payload: &[u8],
) -> Result<alloc::vec::Vec<u8>, alloc::string::String> {
#[allow(clippy::cast_possible_truncation)]
let gn_payload_length = payload.len() as u16;
geonetworking.common.payload_length = gn_payload_length;
geonetworking.common.header_type_and_subtype = match geonetworking.extended {
Some(ExtendedHeader::Beacon(_)) => HeaderType::Beacon,
Some(ExtendedHeader::GAC(_)) => HeaderType::GeoAnycast(geonetworking::AreaType::Circular),
Some(ExtendedHeader::GBC(_)) => HeaderType::GeoBroadcast(geonetworking::AreaType::Circular),
Some(ExtendedHeader::GUC(_)) => HeaderType::GeoUnicast,
Some(ExtendedHeader::TSB(_)) => {
HeaderType::TopologicallyScopedBroadcast(geonetworking::BroadcastType::MultiHop)
}
Some(ExtendedHeader::SHB(_)) => {
HeaderType::TopologicallyScopedBroadcast(geonetworking::BroadcastType::SingleHop)
}
Some(ExtendedHeader::LSRequest(_)) => {
HeaderType::LocationService(geonetworking::LocationServiceType::Request)
}
Some(ExtendedHeader::LSReply(_)) => {
HeaderType::LocationService(geonetworking::LocationServiceType::Reply)
}
None => HeaderType::Any,
};
geonetworking
.with_payload(payload)
.map_err(map_err_to_string)?
.encode_to_vec()
.map_err(map_err_to_string)
}
#[cfg(all(target_arch = "wasm32", feature = "json"))]
fn transcode_jer_to_uper<T: rasn::Decode + rasn::Encode>(
input: &String,
) -> Result<alloc::vec::Vec<u8>, String> {
rasn::uper::encode(&rasn::jer::decode::<T>(input).map_err(map_err_to_string)?)
.map_err(map_err_to_string)
}