use crate::information_element::SbdHeader;
use crate::mo::session_status::SessionStatus;
use crate::Result;
use chrono::{DateTime, Utc};
#[cfg(feature = "serde-derive")]
use serde::{Deserialize, Serialize};
use std::io::{Read, Write};
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde-derive", derive(Serialize, Deserialize))]
pub struct Header {
pub auto_id: u32,
#[cfg_attr(feature = "serde-derive",
serde(serialize_with = "as_str", deserialize_with = "str_to_imei"))]
pub imei: [u8; 15],
pub session_status: SessionStatus,
pub momsn: u16,
pub mtmsn: u16,
pub time_of_session: DateTime<Utc>,
}
impl SbdHeader for Header {
fn write_to(&self, write: &mut dyn Write) -> Result<()> {
use crate::Error;
use byteorder::{BigEndian, WriteBytesExt};
write.write_u8(1)?;
write.write_u16::<BigEndian>(28)?;
write.write_u32::<BigEndian>(self.auto_id)?;
write.write_all(&self.imei)?;
write.write_u8(self.session_status as u8)?;
write.write_u16::<BigEndian>(self.momsn)?;
write.write_u16::<BigEndian>(self.mtmsn)?;
let timestamp = self.time_of_session.timestamp();
if timestamp < 0 {
return Err(Error::NegativeTimestamp(timestamp));
} else {
write.write_u32::<BigEndian>(timestamp as u32)?;
};
Ok(())
}
fn imei(&self) -> &str {
use std::str;
str::from_utf8(&self.imei).expect("IMEI numbers are specified to be ascii number")
}
fn len(&self) -> usize {
31
}
fn as_mo(&self) -> Option<&Header> {
Some(&self)
}
}
impl Header {
pub fn read_from(read: &mut dyn Read) -> Result<Self> {
use crate::Error;
use byteorder::{BigEndian, ReadBytesExt};
use chrono::TimeZone;
let auto_id = read.read_u32::<BigEndian>().map_err(Error::Io)?;
let mut imei = [0; 15];
read.read_exact(&mut imei).map_err(Error::Io)?;
let session_status = SessionStatus::new(read.read_u8().map_err(Error::Io)?)?;
let momsn = read.read_u16::<BigEndian>().map_err(Error::Io)?;
let mtmsn = read.read_u16::<BigEndian>().map_err(Error::Io)?;
let time_of_session = read
.read_u32::<BigEndian>()
.map_err(Error::from)
.map(|n| Utc.timestamp(i64::from(n), 0))?;
Ok(Header {
auto_id,
imei,
session_status,
momsn,
mtmsn,
time_of_session,
})
}
pub fn session_status(&self) -> SessionStatus {
self.session_status
}
pub fn momsn(&self) -> u16 {
self.momsn
}
pub fn mtmsn(&self) -> u16 {
self.mtmsn
}
pub fn time_of_session(&self) -> DateTime<Utc> {
self.time_of_session
}
}
#[cfg(feature = "serde-derive")]
fn as_str<S>(imei: &[u8], serializer: S) -> std::result::Result<S::Ok, S::Error>
where S: serde::Serializer
{
use std::str;
use serde::ser;
match str::from_utf8(imei) {
Ok(s) => serializer.serialize_str(s),
_=> Err(ser::Error::custom("imei contains invalid UTF-8 characters")),
}
}
#[cfg(feature = "serde-derive")]
pub fn str_to_imei<'de, D>(deserializer: D) -> std::result::Result<[u8; 15], D::Error>
where D: serde::Deserializer<'de>
{
use serde::de;
let mut value = [0; 15];
let data = <&'de [u8]>::deserialize(deserializer)?;
if data.len() == value.len() {
value.copy_from_slice(data);
Ok(value)
} else {
Err(de::Error::custom("imei wrong len"))
}
}