use crate::usp::Msg;
use crate::usp_record::mod_MQTTConnectRecord::MQTTVersion;
use crate::usp_record::mod_Record::{OneOfrecord_type, PayloadSecurity};
use crate::usp_record::mod_SessionContextRecord::PayloadSARState;
use crate::usp_record::NoSessionContextRecord;
use crate::usp_record::Record;
use crate::usp_record::SessionContextRecord;
use crate::usp_record::{
DisconnectRecord, MQTTConnectRecord, STOMPConnectRecord, UDSConnectRecord,
WebSocketConnectRecord,
};
use anyhow::anyhow;
use anyhow::{Context, Result};
#[derive(Clone)]
enum RecordType {
None,
NoSessionContext,
SessionContext {
session_context: SessionContextBuilder,
},
WebSocketConnect,
MQTTConnect {
version: MQTTVersion,
subscribed_topic: String,
},
STOMPConnect {
version: crate::usp_record::mod_STOMPConnectRecord::STOMPVersion,
subscribed_destination: String,
},
Disconnect {
reason: String,
reason_code: u32,
},
UDSConnect,
}
#[derive(Clone)]
pub struct SessionContextBuilder {
session_id: Option<u64>,
sequence_id: Option<u64>,
expected_id: Option<u64>,
retransmit_id: u64,
payload: Option<Vec<u8>>,
}
impl SessionContextBuilder {
#[must_use]
pub const fn new() -> Self {
Self {
session_id: None,
sequence_id: None,
expected_id: None,
retransmit_id: 0,
payload: None,
}
}
#[must_use]
pub const fn with_session_id(mut self, session_id: u64) -> Self {
self.session_id = Some(session_id);
self
}
#[must_use]
pub const fn with_sequence_id(mut self, sequence_id: u64) -> Self {
self.sequence_id = Some(sequence_id);
self
}
#[must_use]
pub const fn with_expected_id(mut self, expected_id: u64) -> Self {
self.expected_id = Some(expected_id);
self
}
#[must_use]
pub const fn with_retransmit_id(mut self, retransmit_id: u64) -> Self {
self.retransmit_id = retransmit_id;
self
}
#[must_use]
pub fn with_payload(mut self, payload: Vec<u8>) -> Self {
self.payload = Some(payload);
self
}
pub fn build(self) -> Result<SessionContextRecord> {
let scr = SessionContextRecord {
session_id: self
.session_id
.ok_or_else(|| anyhow!("Need to supply a session ID for a session context"))?,
sequence_id: self
.sequence_id
.ok_or_else(|| anyhow!("Need to supply a sequence ID for a session context"))?,
expected_id: self
.expected_id
.ok_or_else(|| anyhow!("Need to supply a expected ID for a session context"))?,
retransmit_id: self.retransmit_id,
payload_sar_state: PayloadSARState::NONE,
payloadrec_sar_state: PayloadSARState::NONE,
payload: self.payload.map_or_else(Vec::new, |payload| vec![payload]),
};
Ok(scr)
}
}
#[derive(Clone)]
pub struct RecordBuilder {
version: String,
to_id: Option<String>,
from_id: Option<String>,
sender_cert: Vec<u8>,
mac_signature: Vec<u8>,
payload_security: PayloadSecurity,
payload: Option<Vec<u8>>,
typ: RecordType,
}
impl RecordBuilder {
#[must_use]
pub const fn new() -> Self {
Self {
version: String::new(),
to_id: None,
from_id: None,
sender_cert: vec![],
mac_signature: vec![],
payload_security: PayloadSecurity::PLAINTEXT,
payload: None,
typ: RecordType::None,
}
}
#[must_use]
pub fn with_version(mut self, version: String) -> Self {
self.version = version;
self
}
#[must_use]
pub fn with_to_id(mut self, id: String) -> Self {
self.to_id = Some(id);
self
}
#[must_use]
pub fn with_from_id(mut self, id: String) -> Self {
self.from_id = Some(id);
self
}
#[must_use]
pub fn with_sender_cert(mut self, sender_cert: Vec<u8>) -> Self {
self.sender_cert = sender_cert;
self
}
#[must_use]
pub fn with_mac_signature(mut self, mac_signature: Vec<u8>) -> Self {
self.mac_signature = mac_signature;
self
}
#[must_use]
pub const fn with_payload_security_tls12(mut self) -> Self {
self.payload_security = PayloadSecurity::TLS12;
self
}
#[must_use]
pub fn with_session_context_builder(mut self, session_context: SessionContextBuilder) -> Self {
self.typ = RecordType::SessionContext { session_context };
self
}
#[must_use]
pub fn with_no_session_context_payload(self, msg: &Msg) -> Self {
let mut buf = Vec::new();
let mut writer = quick_protobuf::Writer::new(&mut buf);
let _ = quick_protobuf::MessageWrite::write_message(msg, &mut writer);
self.with_no_session_context_payload_bytes(buf)
}
#[must_use]
pub fn with_no_session_context_payload_bytes(mut self, buf: Vec<u8>) -> Self {
self.payload = Some(buf);
self.typ = RecordType::NoSessionContext;
self
}
#[must_use]
pub fn as_websocket_connect_record(mut self) -> Self {
self.typ = RecordType::WebSocketConnect;
self
}
#[must_use]
pub fn as_mqtt_connect_record(
mut self,
version: MQTTVersion,
subscribed_topic: String,
) -> Self {
self.typ = RecordType::MQTTConnect {
version,
subscribed_topic,
};
self
}
#[must_use]
pub fn as_stomp_connect_record(
mut self,
version: crate::usp_record::mod_STOMPConnectRecord::STOMPVersion,
subscribed_destination: String,
) -> Self {
self.typ = RecordType::STOMPConnect {
version,
subscribed_destination,
};
self
}
#[must_use]
pub fn as_disconnect_record(mut self, reason: String, reason_code: u32) -> Self {
self.typ = RecordType::Disconnect {
reason,
reason_code,
};
self
}
#[must_use]
pub fn as_uds_connect_record(mut self) -> Self {
self.typ = RecordType::UDSConnect;
self
}
pub fn build(self) -> Result<Record> {
let to_id = self
.to_id
.with_context(|| "Cannot produce USP Record without to_id")?;
let from_id = self
.from_id
.with_context(|| "Cannot produce USP Record without from_id")?;
let mut record = Record {
version: if self.version.is_empty() {
"1.4".into()
} else {
self.version
},
to_id,
from_id,
sender_cert: self.sender_cert,
mac_signature: self.mac_signature,
payload_security: self.payload_security,
record_type: OneOfrecord_type::None,
};
match self.typ {
RecordType::None => Err(anyhow!("Cannot produce a USP Record without type"))?,
RecordType::NoSessionContext => {
let payload = self
.payload
.with_context(|| "Cannot produce USP Record without payload")?;
record.record_type =
OneOfrecord_type::no_session_context(NoSessionContextRecord { payload });
}
RecordType::SessionContext { session_context } => {
record.record_type = OneOfrecord_type::session_context(session_context.build()?);
}
RecordType::WebSocketConnect => {
record.record_type = OneOfrecord_type::websocket_connect(WebSocketConnectRecord {});
}
RecordType::MQTTConnect {
version,
subscribed_topic,
} => {
record.record_type = OneOfrecord_type::mqtt_connect(MQTTConnectRecord {
version,
subscribed_topic,
});
}
RecordType::STOMPConnect {
version,
subscribed_destination,
} => {
record.record_type = OneOfrecord_type::stomp_connect(STOMPConnectRecord {
version,
subscribed_destination,
});
}
RecordType::Disconnect {
reason,
reason_code,
} => {
record.record_type = OneOfrecord_type::disconnect(DisconnectRecord {
reason,
reason_code,
});
}
RecordType::UDSConnect => {
record.record_type = OneOfrecord_type::uds_connect(UDSConnectRecord {});
}
}
Ok(record)
}
}