<commit_before>//! An information element is a portion of a SBD message.
//!
//! Information elements come after the SBD header. They come in many types,
//! including more header-type information and the actual data payload.

use chrono::Utc;
use mo::{Header, SessionStatus};
use std::io::{Read, Write};

/// A mobile-originated information element, or IE.
///
/// These are the building blocks of a SBD message.
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum InformationElement {
    /// Information element holding the mobile-originated header.
    Header(Header),
    /// The mobile originated payload.
    Payload(Vec<u8>),
    /// The mobile originated location information.
    ///
    /// FIXME I don't have a test case for this, yet, so I haven't actually broken out the location
    /// information into fields.
    ///
    /// See #9.
    LocationInformation([u8; 7]),
}

/// Mobile-originated information element errors.
#[derive(Clone, Copy, Debug, Fail)]
pub enum Error {
    /// The identifier is invalid.
    #[fail(display = "invalid information element identifier: {}", _0)]
    InvalidInformationElementIdentifier(u8),

    /// The timestamp is negative.
    #[fail(display = "negative timestamp: {}", _0)]
    NegativeTimestamp(i64),

    /// The payload is too long.
    #[fail(display = "the payload is too long at {} bytes", _0)]
    PayloadTooLong(usize),
}

impl InformationElement {
    /// Reads this information element from a `Read`.
    pub fn read_from<R: Read>(mut read: R) -> Result<InformationElement, ::failure::Error> {
        use byteorder::{BigEndian, ReadBytesExt};
        use chrono::TimeZone;

        let iei = read.read_u8()?;
        let length = read.read_u16::<BigEndian>()?;
        match iei {
            1 => {
                let auto_id = read.read_u32::<BigEndian>()?;
                let mut imei = [0; 15];
                read.read_exact(&mut imei)?;
                let session_status = SessionStatus::new(read.read_u8()?)?;
                let momsn = read.read_u16::<BigEndian>()?;
                let mtmsn = read.read_u16::<BigEndian>()?;
                let time_of_session = read
                    .read_u32::<BigEndian>()
                    .map_err(::failure::Error::from)
                    .map(|n| Utc.timestamp(i64::from(n), 0))?;
                Ok(InformationElement::Header(Header {
                    auto_id: auto_id,
                    imei: imei,
                    session_status: session_status,
                    momsn: momsn,
                    mtmsn: mtmsn,
                    time_of_session: time_of_session,
                }))
            }
            2 => {
                let mut payload = vec![0; length as usize];
                read.read_exact(&mut payload)?;
                Ok(InformationElement::Payload(payload))
            }
            3 => {
                let mut bytes = [0; 7];
                read.read_exact(&mut bytes)?;
                Ok(InformationElement::LocationInformation(bytes))
            }
            5 => unimplemented!(),
            _ => Err(Error::InvalidInformationElementIdentifier(iei).into()),
        }
    }

    /// Returns the length of this information element, including the information element header.
    pub fn len(&self) -> usize {
        match *self {
            InformationElement::Header(_) => 31,
            InformationElement::Payload(ref payload) => 3 + payload.len(),
            InformationElement::LocationInformation(_) => 10,
        }
    }

    /// Returns true if this information element is empty.
    ///
    /// At this point, only can be true if the payload is empty.
    pub fn is_empty(&self) -> bool {
        match *self {
            InformationElement::Payload(ref payload) => payload.is_empty(),
            _ => false,
        }
    }

    /// Writes this information element to a `Write`.
    pub fn write_to<W: Write>(&self, mut write: W) -> Result<(), ::failure::Error> {
        use byteorder::{BigEndian, WriteBytesExt};
        use std::u16;

        match *self {
            InformationElement::Header(ref header) => {
                write.write_u8(1)?;
                write.write_u16::<BigEndian>(31)?;
                write.write_u32::<BigEndian>(header.auto_id)?;
                write.write_all(&header.imei)?;
                write.write_u8(header.session_status as u8)?;
                write.write_u16::<BigEndian>(header.momsn)?;
                write.write_u16::<BigEndian>(header.mtmsn)?;
                let timestamp = header.time_of_session.timestamp();
                if timestamp < 0 {
                    return Err(Error::NegativeTimestamp(timestamp).into());
                } else {
                    write.write_u32::<BigEndian>(timestamp as u32)?;
                };
            }
            InformationElement::Payload(ref payload) => {
                write.write_u8(2)?;
                let len = payload.len();
                if len > u16::MAX as usize {
                    return Err(Error::PayloadTooLong(len).into());
                } else {
                    write.write_u16::<BigEndian>(len as u16)?;
                }
                write.write_all(payload)?;
            }
            InformationElement::LocationInformation(ref bytes) => {
                write.write_u8(3)?;
                write.write_u16::<BigEndian>(7)?;
                write.write_all(bytes)?;
            }
        }
        Ok(())<commit_msg>Fix incorrect MO Location Info Length<commit_after>//! An information element is a portion of a SBD message.
//!
//! Information elements come after the SBD header. They come in many types,
//! including more header-type information and the actual data payload.

use chrono::Utc;
use mo::{Header, SessionStatus};
use std::io::{Read, Write};

/// A mobile-originated information element, or IE.
///
/// These are the building blocks of a SBD message.
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum InformationElement {
    /// Information element holding the mobile-originated header.
    Header(Header),
    /// The mobile originated payload.
    Payload(Vec<u8>),
    /// The mobile originated location information.
    ///
    /// FIXME I don't have a test case for this, yet, so I haven't actually broken out the location
    /// information into fields.
    ///
    /// See #9.
    LocationInformation([u8; 7]),
}

/// Mobile-originated information element errors.
#[derive(Clone, Copy, Debug, Fail)]
pub enum Error {
    /// The identifier is invalid.
    #[fail(display = "invalid information element identifier: {}", _0)]
    InvalidInformationElementIdentifier(u8),

    /// The timestamp is negative.
    #[fail(display = "negative timestamp: {}", _0)]
    NegativeTimestamp(i64),

    /// The payload is too long.
    #[fail(display = "the payload is too long at {} bytes", _0)]
    PayloadTooLong(usize),
}

impl InformationElement {
    /// Reads this information element from a `Read`.
    pub fn read_from<R: Read>(mut read: R) -> Result<InformationElement, ::failure::Error> {
        use byteorder::{BigEndian, ReadBytesExt};
        use chrono::TimeZone;

        let iei = read.read_u8()?;
        let length = read.read_u16::<BigEndian>()?;
        match iei {
            1 => {
                let auto_id = read.read_u32::<BigEndian>()?;
                let mut imei = [0; 15];
                read.read_exact(&mut imei)?;
                let session_status = SessionStatus::new(read.read_u8()?)?;
                let momsn = read.read_u16::<BigEndian>()?;
                let mtmsn = read.read_u16::<BigEndian>()?;
                let time_of_session = read
                    .read_u32::<BigEndian>()
                    .map_err(::failure::Error::from)
                    .map(|n| Utc.timestamp(i64::from(n), 0))?;
                Ok(InformationElement::Header(Header {
                    auto_id: auto_id,
                    imei: imei,
                    session_status: session_status,
                    momsn: momsn,
                    mtmsn: mtmsn,
                    time_of_session: time_of_session,
                }))
            }
            2 => {
                let mut payload = vec![0; length as usize];
                read.read_exact(&mut payload)?;
                Ok(InformationElement::Payload(payload))
            }
            3 => {
                let mut bytes = [0; 7];
                read.read_exact(&mut bytes)?;
                Ok(InformationElement::LocationInformation(bytes))
            }
            5 => unimplemented!(),
            _ => Err(Error::InvalidInformationElementIdentifier(iei).into()),
        }
    }

    /// Returns the length of this information element, including the information element header.
    pub fn len(&self) -> usize {
        match *self {
            InformationElement::Header(_) => 31,
            InformationElement::Payload(ref payload) => 3 + payload.len(),
            InformationElement::LocationInformation(_) => 10,
        }
    }

    /// Returns true if this information element is empty.
    ///
    /// At this point, only can be true if the payload is empty.
    pub fn is_empty(&self) -> bool {
        match *self {
            InformationElement::Payload(ref payload) => payload.is_empty(),
            _ => false,
        }
    }

    /// Writes this information element to a `Write`.
    pub fn write_to<W: Write>(&self, mut write: W) -> Result<(), ::failure::Error> {
        use byteorder::{BigEndian, WriteBytesExt};
        use std::u16;

        match *self {
            InformationElement::Header(ref header) => {
                write.write_u8(1)?;
                write.write_u16::<BigEndian>(31)?;
                write.write_u32::<BigEndian>(header.auto_id)?;
                write.write_all(&header.imei)?;
                write.write_u8(header.session_status as u8)?;
                write.write_u16::<BigEndian>(header.momsn)?;
                write.write_u16::<BigEndian>(header.mtmsn)?;
                let timestamp = header.time_of_session.timestamp();
                if timestamp < 0 {
                    return Err(Error::NegativeTimestamp(timestamp).into());
                } else {
                    write.write_u32::<BigEndian>(timestamp as u32)?;
                };
            }
            InformationElement::Payload(ref payload) => {
                write.write_u8(2)?;
                let len = payload.len();
                if len > u16::MAX as usize {
                    return Err(Error::PayloadTooLong(len).into());
                } else {
                    write.write_u16::<BigEndian>(len as u16)?;
                }
                write.write_all(payload)?;
            }
            InformationElement::LocationInformation(ref bytes) => {
                write.write_u8(3)?;
                write.write_u16::<BigEndian>(20)?;
                write.write_all(bytes)?;
            }
        }
        Ok(())
{'fraction_non_alphanumeric': 0.12608655869704455, 'fraction_numerical': 0.01363345228291701, 'mean_word_length': 1.8360145303580695, 'pattern_counts': {'":': 0, '<': 39, '<?xml version=': 0, '>': 73, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5260848', 'n_tokens_mistral': 3039, 'n_tokens_neox': 2943, 'n_words': 744}