Skip to content

Commit

Permalink
Replace quick_error with failure
Browse files Browse the repository at this point in the history
  • Loading branch information
gadomski committed Sep 11, 2018
1 parent 6a2cda9 commit e822953
Show file tree
Hide file tree
Showing 8 changed files with 147 additions and 176 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Expand Up @@ -14,6 +14,7 @@ categories = ["science"]
byteorder = "1.1"
chrono = "0.4"
docopt = "0.8"
failure = "*"
log = "0.3"
quick-error = "1.2"
serde = "1.0"
Expand Down
88 changes: 2 additions & 86 deletions src/lib.rs
Expand Up @@ -50,97 +50,13 @@
extern crate byteorder;
extern crate chrono;
#[macro_use]
extern crate log;
extern crate failure;
#[macro_use]
extern crate quick_error;
extern crate log;
#[macro_use]
extern crate serde_derive;
extern crate walkdir;

pub mod directip;
pub mod mo;
pub mod storage;

/// Create-specific `Result`.
pub type Result<T> = std::result::Result<T, Error>;

quick_error! {
/// Crate-specific errors
#[derive(Debug)]
pub enum Error {
/// A wrapper around a `std::io::Error`.
Io(err: std::io::Error) {
from()
cause(err)
description(err.description())
display("io error: {}", err)
}
/// Invalid protocol revision number.
InvalidProtocolRevisionNumber(n: u8) {
description("invalid protocol revision number")
display("invalid protocol revision number: {}", n)
}
/// Invalid information element identifier.
InvalidInformationElementIdentifier(n: u8) {
description("invalid information element identifier")
display("invalid information element identifier: {}", n)
}
/// The timestamp is negative, but only positive ones are supported.
NegativeTimestamp(timestamp: i64) {
description("only positive timestamps are allowed in mo messages")
display("negative timestamp: {}", timestamp)
}
/// No header on a MO message.
NoHeader {
description("no header on a mo message")
}
/// No payload on a MO message.
NoPayload {
description("no payload on a mo message")
}
/// We expected a directory, but this isn't one.
///
/// TODO can this be a PathBuf?
NotADirectory(s: std::ffi::OsString) {
description("the os string is not a directory")
display("this os string is not a directory: {}", s.to_string_lossy())
}
/// The overall message length is too long.
OverallMessageLength(len: usize) {
description("the overall message length is too long")
display("the overall message length is too long: {}", len)
}
/// The payload is too long.
PayloadTooLong(len: usize) {
description("the mo payload is too long")
display("the payload is too long: {}", len)
}
/// Two headers in an MO message.
TwoHeaders {
description("two headers in a MO message")
}
/// Two payloads in an MO message.
TwoPayloads {
description("two payloads in a MO message")
}
/// Wrapper around `std::str::Utf8Error`.
Utf8(err: std::str::Utf8Error) {
from()
cause(err)
description(err.description())
display("utf8 error: {}", err)
}
/// The session status is unknown.
UnknownSessionStatus(n: u8) {
description("unknown session status")
display("uknown session status code: {}", n)
}
/// Wrapper around `walkdir::Error`.
WalkDir(err: walkdir::Error) {
from()
cause(err)
description(err.description())
display("walkdir error: {}", err)
}
}
}
36 changes: 25 additions & 11 deletions src/mo/information_element.rs
Expand Up @@ -3,7 +3,6 @@
//! Information elements come after the SBD header. They come in many types,
//! including more header-type information and the actual data payload.

use Result;
use chrono::Utc;
use mo::{Header, SessionStatus};
use std::io::{Read, Write};
Expand All @@ -26,10 +25,25 @@ pub enum InformationElement {
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> {
use Error;
pub fn read_from<R: Read>(mut read: R) -> Result<InformationElement, ::failure::Error> {
use byteorder::{BigEndian, ReadBytesExt};
use chrono::TimeZone;

Expand All @@ -43,9 +57,10 @@ impl InformationElement {
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(Error::from).map(|n| {
Utc.timestamp(i64::from(n), 0)
})?;
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,
Expand All @@ -66,7 +81,7 @@ impl InformationElement {
Ok(InformationElement::LocationInformation(bytes))
}
5 => unimplemented!(),
_ => Err(Error::InvalidInformationElementIdentifier(iei)),
_ => Err(Error::InvalidInformationElementIdentifier(iei).into()),
}
}

Expand All @@ -90,9 +105,8 @@ impl InformationElement {
}

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

match *self {
Expand All @@ -106,7 +120,7 @@ impl InformationElement {
write.write_u16::<BigEndian>(header.mtmsn)?;
let timestamp = header.time_of_session.timestamp();
if timestamp < 0 {
return Err(Error::NegativeTimestamp(timestamp));
return Err(Error::NegativeTimestamp(timestamp).into());
} else {
write.write_u32::<BigEndian>(timestamp as u32)?;
};
Expand All @@ -115,7 +129,7 @@ impl InformationElement {
write.write_u8(2)?;
let len = payload.len();
if len > u16::MAX as usize {
return Err(Error::PayloadTooLong(len));
return Err(Error::PayloadTooLong(len).into());
} else {
write.write_u16::<BigEndian>(len as u16)?;
}
Expand Down
93 changes: 60 additions & 33 deletions src/mo/message.rs
@@ -1,11 +1,41 @@
use Result;
use chrono::{DateTime, Utc};
use mo::{Header, InformationElement, SessionStatus};
use std::cmp::Ordering;
use std::io::{Read, Write};
use std::path::Path;

const PROTOCOL_REVISION_NUMBER: u8 = 1;
/// The only valid protocol revision number.
pub const PROTOCOL_REVISION_NUMBER: u8 = 1;

/// Errors returned when creating a message.
#[derive(Debug, Fail)]
pub enum Error {
/// The message has an invalid protocol revision number.
#[fail(display = "invalid protocol revision number: {}", _0)]
InvalidProtocolRevisionNumber(u8),

/// There are two headers in the message.
#[fail(display = "two headers")]
TwoHeaders(Header, Header),

/// There are two payloads in the message.
#[fail(display = "two payloads")]
TwoPayloads(Vec<u8>, Vec<u8>),

/// There is no header in the message.
#[fail(display = "no header")]
NoHeader,

/// There is no payload in the message.
#[fail(display = "no payload")]
NoPayload,

/// The overall message length is too big.
#[fail(display = "the overall message length is too big: {}", _0)]
OverallMessageLength(usize),
}

/// Error returned when there is no

/// A mobile-origined Iridium SBD message.
///
Expand All @@ -26,7 +56,7 @@ impl Message {
/// use sbd::mo::Message;
/// let message = Message::from_path("data/0-mo.sbd").unwrap();
/// ```
pub fn from_path<P: AsRef<Path>>(path: P) -> Result<Message> {
pub fn from_path<P: AsRef<Path>>(path: P) -> Result<Message, ::failure::Error> {
use std::fs::File;
let file = File::open(path)?;
Message::read_from(file)
Expand All @@ -44,16 +74,13 @@ impl Message {
/// let mut file = File::open("data/0-mo.sbd").unwrap();
/// let message = Message::read_from(file).unwrap();
/// ```
pub fn read_from<R: Read>(mut read: R) -> Result<Message> {
use Error;
pub fn read_from<R: Read>(mut read: R) -> Result<Message, ::failure::Error> {
use byteorder::{BigEndian, ReadBytesExt};
use std::io::Cursor;

let protocol_revision_number = read.read_u8()?;
if protocol_revision_number != PROTOCOL_REVISION_NUMBER {
return Err(Error::InvalidProtocolRevisionNumber(
protocol_revision_number,
));
return Err(Error::InvalidProtocolRevisionNumber(protocol_revision_number).into());
}
let overall_message_length = read.read_u16::<BigEndian>()?;
let mut message = vec![0; overall_message_length as usize];
Expand All @@ -65,7 +92,7 @@ impl Message {
information_elements.push(InformationElement::read_from(&mut cursor)?);
}

Message::new(information_elements)
Message::new(information_elements).map_err(::failure::Error::from)
}

/// Creates a new message from information elements.
Expand All @@ -90,25 +117,23 @@ impl Message {
/// let message = Message::new(vec![header, payload]);
/// # }
/// ```
pub fn new<I: IntoIterator<Item = InformationElement>>(iter: I) -> Result<Message> {
use Error;

pub fn new<I: IntoIterator<Item = InformationElement>>(iter: I) -> Result<Message, Error> {
let mut header = None;
let mut payload = None;
let mut information_elements = Vec::new();
for information_element in iter {
match information_element {
InformationElement::Header(h) => if header.is_some() {
return Err(Error::TwoHeaders);
InformationElement::Header(h) => if let Some(header) = header {
return Err(Error::TwoHeaders(h, header));
} else {
header = Some(h);
}
InformationElement::Payload(p) => if payload.is_some() {
return Err(Error::TwoPayloads);
},
InformationElement::Payload(p) => if let Some(payload) = payload {
return Err(Error::TwoPayloads(p, payload));
} else {
payload = Some(p);
}
ie => information_elements.push(ie)
},
ie => information_elements.push(ie),
}
}
Ok(Message {
Expand Down Expand Up @@ -226,16 +251,20 @@ impl Message {
/// let mut cursor = Cursor::new(Vec::new());
/// message.write_to(&mut cursor);
/// ```
pub fn write_to<W: Write>(&self, mut write: W) -> Result<()> {
use byteorder::{WriteBytesExt, BigEndian};
pub fn write_to<W: Write>(&self, mut write: W) -> Result<(), ::failure::Error> {
use byteorder::{BigEndian, WriteBytesExt};
use std::u16;
use Error;

let header = InformationElement::from(self.header);
let payload = InformationElement::from(self.payload.clone());
let overall_message_length = header.len() + payload.len() + self.information_elements.iter().map(|ie| ie.len()).sum::<usize>();
let overall_message_length = header.len() + payload.len()
+ self
.information_elements
.iter()
.map(|ie| ie.len())
.sum::<usize>();
if overall_message_length > u16::MAX as usize {
return Err(Error::OverallMessageLength(overall_message_length));
return Err(Error::OverallMessageLength(overall_message_length).into());
}

write.write_u8(PROTOCOL_REVISION_NUMBER)?;
Expand Down Expand Up @@ -353,7 +382,11 @@ mod tests {

#[test]
fn write() {
let message = Message::new(vec![header().into(), vec![1].into(), InformationElement::LocationInformation([0; 7])]).unwrap();
let message = Message::new(vec![
header().into(),
vec![1].into(),
InformationElement::LocationInformation([0; 7]),
]).unwrap();
let mut cursor = Cursor::new(Vec::new());
message.write_to(&mut cursor).unwrap();
cursor.set_position(0);
Expand All @@ -366,14 +399,8 @@ mod tests {
let header1 = header();
let mut header2 = header();
header2.time_of_session = Utc.ymd(2010, 6, 11).and_hms(0, 0, 0);
let message1 = Message::new(vec![
header1.into(),
Vec::new().into(),
]).unwrap();
let message2 = Message::new(vec![
header2.into(),
Vec::new().into(),
]).unwrap();
let message1 = Message::new(vec![header1.into(), Vec::new().into()]).unwrap();
let message2 = Message::new(vec![header2.into(), Vec::new().into()]).unwrap();
assert!(message2 < message1);
}
}

0 comments on commit e822953

Please sign in to comment.