mod header;
mod payload;
use std::io::Read;
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
use crate::error::{Error, Result};
use crate::InformationElement;
use header::Header;
use payload::Payload;
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[allow(dead_code)]
#[derive(Debug, PartialEq)]
enum InformationElementType {
H(Header),
P(Payload),
}
impl InformationElement for InformationElementType {
fn identifier(&self) -> u8 {
match self {
InformationElementType::H(element) => element.identifier(),
InformationElementType::P(element) => element.identifier(),
}
}
fn len(&self) -> u16 {
match self {
InformationElementType::H(element) => element.len(),
InformationElementType::P(element) => element.len(),
}
}
fn write<W: std::io::Write>(&self, wtr: &mut W) -> Result<usize> {
match self {
InformationElementType::H(element) => element.write(wtr),
InformationElementType::P(element) => element.write(wtr),
}
}
}
impl InformationElementType {
pub(super) fn from_reader<R: std::io::Read>(mut rdr: R) -> Result<Self> {
let iei = rdr.read_u8()?;
let buffer = [iei; 1];
let buffer = buffer.chain(rdr);
let element = match iei {
0x01 => {
let header = Header::from_reader(buffer).unwrap();
InformationElementType::H(header)
}
0x02 => {
let payload = Payload::from_reader(buffer).unwrap();
InformationElementType::P(payload)
}
_ => return Err(Error::Undefined),
};
Ok(element)
}
}
impl From<Header> for InformationElementType {
fn from(header: Header) -> Self {
InformationElementType::H(header)
}
}
impl From<Payload> for InformationElementType {
fn from(payload: Payload) -> Self {
InformationElementType::P(payload)
}
}
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug)]
pub struct MOMessage {
elements: Vec<InformationElementType>,
}
impl MOMessage {
#[allow(dead_code)]
fn new() -> MOMessage {
MOMessage {
elements: Vec::new(),
}
}
fn len(&self) -> u16 {
self.elements.iter().map(|e| 3 + e.len()).sum::<u16>()
}
#[allow(dead_code)]
fn total_size(&self) -> usize {
3 + self.len() as usize
}
fn write<W: std::io::Write>(&self, wtr: &mut W) -> Result<usize> {
wtr.write_u8(1)?;
wtr.write_u16::<BigEndian>(self.len())?;
let mut n = 3;
for e in &self.elements {
n += e.write(wtr)?;
}
Ok(n)
}
pub fn to_vec(&self) -> Vec<u8> {
let mut buffer: Vec<u8> = Vec::new();
self.write(&mut buffer)
.expect("Failed to write Information Element to a vec.");
buffer
}
pub fn from_reader<R: std::io::Read>(mut rdr: R) -> Result<Self> {
let version = rdr.read_u8()?;
assert_eq!(version, 1);
let length = rdr.read_u16::<BigEndian>().expect("Failed to read length") as usize;
let mut msg = MOMessage { elements: vec![] };
let mut n = 0;
while n < length {
let element = InformationElementType::from_reader(&mut rdr)?;
n += element.total_size();
msg.push(element);
}
Ok(msg)
}
fn push(&mut self, element: InformationElementType) {
self.elements.push(element);
}
fn header(&self) -> Option<&Header> {
self.elements
.iter()
.find(|elem| matches!(elem, InformationElementType::H(_)))
.map(|e| {
if let InformationElementType::H(h) = e {
h
} else {
unreachable!()
}
})
}
pub fn imei(&self) -> Option<[u8; 15]> {
self.header().map(|h| h.imei())
}
}
#[cfg(all(test, feature = "serde"))]
mod test_mo_information_element_serde {
use super::{header::SessionStatus, Header, InformationElementType, Payload};
use chrono::{DateTime, Utc};
#[test]
fn payload_roundtrip() {
let mut payload = vec![0u8; 10];
payload[0] = 0x42;
let p = Payload::builder().payload(payload).build().unwrap();
let ie: InformationElementType = p.into();
let json = serde_json::to_string(&ie).unwrap();
let roundtrip: InformationElementType = serde_json::from_str(&json).unwrap();
assert_eq!(ie, roundtrip);
}
#[test]
fn header_roundtrip() {
let header = Header::builder()
.cdr_uid(9999)
.session_status(SessionStatus::Success)
.momsn(16)
.mtmsn(18)
.imei([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4])
.time_of_session("2000-03-14T12:12:12Z".parse::<DateTime<Utc>>().unwrap())
.build()
.unwrap();
let ie: InformationElementType = header.into();
let json = serde_json::to_string(&ie).unwrap();
let roundtrip: InformationElementType = serde_json::from_str(&json).unwrap();
assert_eq!(ie, roundtrip);
}
}