Repository: jmagnuson/canparse (language: rust)

Issue:
Handle multi-line entries
The current `PgnLibrary::from_dbc_file` parse logic assumes each line => entry which isn't correct, since multi-line entries such as the below can exist:
```
CM_ SG_ 2566838526 IndustryGroup "0 = Global; 
   1  = On-Highway; 
   2  = Agricultural and Forestry; 
   3  = Construction; 
   4  = Marine;
   5  = Industrial - Process
          Control - Stationary (Gen-Sets)
   6  = Reserve for SAE
   7  = Reserve for SAE";
```
Unfortunately, this means the underlying `Entry` parse logic will either need to have access to the entire reader, or will need to leak to the consumer so that knowledge of when to create the parseable `String` can be ascertained.

One solution is to do away with the `BufReader::lines` and modify `Entry::from_str` to take on the entire `String` which is already getting pre-buffered in full anyway. The relevant regex definitions already capture anything in between double-quotes, so they should be able to stay as-is. As mentioned, `impl FromStr for Entry` would need to get hacked in order to iterate through the consumed characters, which could get messy.

Another similar solution is to do away with regexes and just use [nom](https://crates.io/crates/nom) to provide more control over tokenization and error handling, and probably faster to boot. Disclaimer: I really like nom.


Discussion / hints:

Fix patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 38a02d8..0c2ee3e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](http://semver.org).
 
 ### Changed
 - Update and clean out old deps
+- Replace regex parsing with `nom` to handle strings with newlines
 
 ## [0.1.3] - 2018-02-09
 
diff --git a/Cargo.toml b/Cargo.toml
index 3389af5..913c0f4 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -25,6 +25,7 @@ encoding = "0.2"
 enum_primitive = "0.1"
 byteorder = "1.3"
 socketcan = { version = "1.7", optional = true }
+nom = "4.2"
 
 [dev-dependencies]
 rand = "0.6"
diff --git a/src/dbc.rs b/src/dbc.rs
index 592615c..5835c09 100644
--- a/src/dbc.rs
+++ b/src/dbc.rs
@@ -3,13 +3,13 @@
 #![allow(non_upper_case_globals)]
 
 use enum_primitive::FromPrimitive;
-use std::str::FromStr;
-use rustc_serialize::{Decodable, Decoder};
 use regex::{Regex, RegexSet};
-use std::fmt::{Display, Formatter};
-use std::fmt;
-use std::error::Error;
+use rustc_serialize::{Decodable, Decoder};
 use std;
+use std::error::Error;
+use std::fmt;
+use std::fmt::{Display, Formatter};
+use std::str::FromStr;
 
 #[derive(Debug, PartialEq, Clone)]
 pub struct Version(pub String);
@@ -22,14 +22,14 @@ pub struct MessageDefinition {
     pub id: String,
     pub name: String,
     pub message_len: u32,
-    pub sending_node: String
+    pub sending_node: String,
 }
 
 #[derive(Debug, PartialEq, Clone)]
 pub struct MessageDescription {
     pub id: String,
     pub signal_name: String,
-    pub description: String
+    pub description: String,
 }
 
 #[derive(Debug, PartialEq, Clone)]
@@ -37,7 +37,7 @@ pub struct MessageAttribute {
     pub name: String,
     pub id: String,
     pub signal_name: String,
-    pub value: String
+    pub value: String,
 }
 
 #[derive(Debug, PartialEq, Clone)]
@@ -52,14 +52,14 @@ pub struct SignalDefinition {
     pub min_value: f32,
     pub max_value: f32,
     pub units: String,
-    pub receiving_node: String
+    pub receiving_node: String,
 }
 
 #[derive(Debug, PartialEq, Clone)]
 pub struct SignalDescription {
     pub id: String,
     pub signal_name: String,
-    pub description: String
+    pub description: String,
 }
 
 #[derive(Debug, PartialEq, Clone)]
@@ -67,13 +67,12 @@ pub struct SignalAttribute {
     pub name: String,
     pub id: String,
     pub signal_name: String,
-    pub value: String
+    pub value: String,
 }
 
 /// Composed DBC entry.
 #[derive(Debug, PartialEq, Clone)]
 pub enum Entry {
-
     /// `VERSION`
     Version(Version),
 
@@ -85,7 +84,6 @@ pub enum Entry {
     // `CM_ BU_ [can id] [signal name] "[description]"`
     // CanNodesDescription,
     // CanNodesAttribute,
-
     /// `BO_ [can id] [message name]: [message length] [sending node]`
     MessageDefinition(MessageDefinition),
     /// `CM_ BO_ [can id] [signal name] "[description]"`
@@ -111,7 +109,7 @@ pub enum Entry {
 
     // `BA_ "[attribute name]" [BU_|BO_|SG_] [node|can id] [signal name] [attribute value];`
     // Attribute
-
+    Unknown(String),
 }
 
 impl Entry {
@@ -127,6 +125,7 @@ impl Entry {
             Entry::SignalDefinition(_) => EntryType::SignalDefinition,
             Entry::SignalDescription(_) => EntryType::SignalDescription,
             Entry::SignalAttribute(_) => EntryType::SignalAttribute,
+            Entry::Unknown(_) => EntryType::Unknown,
         }
     }
 }
@@ -160,6 +159,8 @@ pub enum EntryType {
     // AttributeDefinition,
     // AttributeDefault,
     // Attribute
+
+    Unknown,
 }
 }
 
@@ -174,65 +175,245 @@ impl Display for EntryType {
             EntryType::SignalDefinition => "SignalDefinition",
             EntryType::SignalDescription => "SignalDescription",
             EntryType::SignalAttribute => "SignalAttribute",
+
+            EntryType::Unknown => "Unknown",
         };
         write!(f, "{}", entry_str)
     }
 }
 
-lazy_static!{
-/// Regex patterns for matching DBC entry lines.
-///
-/// Allows for leading whitespace so that the pattern strings don't strictly
-/// need be formatted as they would be coming from a dbc file.
-static ref _patterns: [&'static str; 8] = [
-    // Version
-    r#"^\s*VERSION "(.*)""#,
+pub mod nom {
+    use super::*;
+    use nom::{alphanumeric, anychar, digit, float, line_ending, space, space0};
+    use std::str::FromStr;
 
-    // BusConfiguration
-    r#"^\s*BUS_: (.*)"#,
+    // TODO: convert `tag!(" ")` to `space`
+
+    named! {
+        quoted_str<&str, String>,
+        map!(
+            delimited!(
+                tag!("\""),
+                escaped_transform!(
+                    none_of!("\\\""),
+                    '\\',
+                    alt!(
+                        tag!("\\") => { |_| "\\" }
+                      | tag!("\"") => { |_| "\"" }
+                    )),
+                tag!("\"")),
+            |data| data)
+    }
 
-    // CanNodes
-    // CanNodesDescription
-    // CanNodesAttribute
+    named!(pub entry<&str, Entry>, alt!(
+        version                => { |r| Entry::Version(r) } |
+        bus_configuration      => { |r| Entry::BusConfiguration(r) } |
+        message_definition     => { |r| Entry::MessageDefinition(r) } |
+        message_description    => { |r| Entry::MessageDescription(r) } |
+        message_attribute      => { |r| Entry::MessageAttribute(r) } |
+        signal_definition      => { |r| Entry::SignalDefinition(r) } |
+        signal_description     => { |r| Entry::SignalDescription(r) } |
+        signal_attribute       => { |r| Entry::SignalAttribute(r) } |
+        unknown                => { |r| Entry::Unknown(r) }
+    ));
+
+    named!(pub unknown<&str, String>,
+        do_parse!(
+            // FIXME: many0!(quoted_str) >> line_ending
+            data: take_until_either!("\r\n") >>
+            line_ending >>
+            ( data.to_string() )
+        )
+    );
 
-    // MessageDefinition
-    r#"^\s*BO_ (\w+) (\w+) *: (\w+) (\w+)"#,
-    // MessageDescription
-    r#"^\s*CM_ BO_ (.*) "(.*?)";"#,
-    // MessageAttribute
-    r#"^\s*BA_ "(.*)" BO_ (.*) (.*);"#,
-
-    // SignalDefinition
-    r#"^\s*SG_ (.*) : (.*)\|(.*)@(.*)(\+|-) \((.*),(.*)\) \[(.*)\|(.*)\] "(.*)" (.*)"#,
-    // SignalDescription
-    r#"^\s*CM_ SG_ (.*) (.*) "(.*?)";"#,
-    // SignalAttribute
-    r#"^\s*BA_ "(.*)" SG_ (.*) (.*) (.*);"#
-
-];
-static ref patterns: [Regex; 8] = [
-    Regex::new(_patterns[0]).unwrap(),
-    Regex::new(_patterns[1]).unwrap(),
-    Regex::new(_patterns[2]).unwrap(),
-    Regex::new(_patterns[3]).unwrap(),
-    Regex::new(_patterns[4]).unwrap(),
-    Regex::new(_patterns[5]).unwrap(),
-    Regex::new(_patterns[6]).unwrap(),
-    Regex::new(_patterns[7]).unwrap(),
-];
-
-static ref Patterns: RegexSet = RegexSet::new(_patterns.into_iter()).unwrap();
-}
+    named!(pub version<&str, Version>,
+        do_parse!(
+            tag!("VERSION")   >>
+            tag!(" ")   >>
+            data: quoted_str >>
+            line_ending >>
+            ( Version(data) )
+        )
+    );
 
-impl Decodable for Entry {
-    fn decode<D: Decoder>(d: &mut D) -> Result<Entry, D::Error> {
-        let line = try!(d.read_str());
-        match line.parse() {
-            Ok(entry) => Ok(entry),
-            Err(_) => Err(d.error(&*format!(
-                "Could not decode '{}' as an entry.", line))),
-        }
-    }
+    named!(pub bus_configuration<&str, BusConfiguration>,
+        do_parse!(
+            tag!("BS_:")   >>
+            tag!(" ")   >>
+            data: map_res!(
+                take_until_either!("\r\n"),
+                FromStr::from_str) >>
+            line_ending >>
+            ( BusConfiguration(data) )
+        )
+    );
+
+    // FIXME: `space` isn't really correct since there should only be ONE (probably need alt)
+    named!(pub message_definition<&str, MessageDefinition>,
+        do_parse!(
+            tag!("BO_")   >>
+            space >>
+            id: digit >>
+            space >>
+            name: alphanumeric >>
+            space0 >>
+            tag!(":")   >>
+            space >>
+            len: map_res!(
+                digit,
+                FromStr::from_str) >>
+            space >>
+            sending_node: take_until_either!(" \t\r\n") >>
+            space0 >>
+            line_ending >>
+            ( MessageDefinition {
+                id: id.to_string(),
+                name: name.to_string(),
+                message_len: len,
+                sending_node: sending_node.to_string(),
+            } )
+        )
+    );
+
+    named!(pub message_description<&str, MessageDescription>,
+        do_parse!(
+            tag!("CM_")   >>
+            space >>
+            tag!("BO_")   >>
+            space >>
+            id: digit >>
+            space >>
+            description: quoted_str >>
+            tag!(";") >>
+            line_ending >>
+            ( MessageDescription {
+                id: id.to_string(),
+                signal_name: "".to_string(),
+                description: description.to_string(),
+            } )
+        )
+    );
+
+    named!(pub message_attribute<&str, MessageAttribute>,
+        do_parse!(
+            tag!("BA_")   >>
+            space >>
+            name: quoted_str >>
+            space >>
+            tag!("BO_")   >>
+            space >>
+            id: digit >>
+            space >>
+            value: digit >>
+            tag!(";") >>
+            line_ending >>
+            ( MessageAttribute {
+                name: name.to_string(),
+                signal_name: "".to_string(),
+                id: id.to_string(),
+                value: value.to_string()
+            } )
+        )
+    );
+
+    named!(pub signal_definition<&str, SignalDefinition>,
+        do_parse!(
+            space >>
+            tag!("SG_") >>
+            space >>
+            name: take_until_either!(" \t") >>
+            space >>
+            tag!(":") >>
+            space >>
+            start_bit: map_res!(
+                digit,
+                FromStr::from_str) >>
+            tag!("|") >>
+            bit_len: map_res!(
+                digit,
+                FromStr::from_str) >>
+            tag!("@") >>
+            little_endian: map!(digit, |d| d == "1") >>
+            signed: alt!(
+                tag!("+") => { |_| false } |
+                tag!("-") => { |_| true } ) >>
+            space >>
+            tag!("(") >>
+            scale: float >>
+            tag!(",") >>
+            offset: float >>
+            tag!(")") >>
+            space >>
+            tag!("[") >>
+            min_value: float >>
+            tag!("|") >>
+            max_value: float >>
+            tag!("]") >>
+            space >>
+            units: quoted_str >>
+            space >>
+            receiving_node: take_until_either!(" \t\r\n") >>
+            line_ending >>
+            ( SignalDefinition {
+                name: name.to_string(),
+                start_bit: start_bit,
+                bit_len: bit_len,
+                little_endian: little_endian,
+                signed: signed,
+                scale: scale,
+                offset: offset,
+                min_value: min_value,
+                max_value: max_value,
+                units: units.to_string(),
+                receiving_node: receiving_node.to_string(),
+            } )
+        )
+    );
+
+    named!(pub signal_description<&str, SignalDescription>,
+        do_parse!(
+            tag!("CM_")   >>
+            space >>
+            tag!("SG_")   >>
+            space >>
+            id: digit >>
+            space >>
+            signal_name: take_until_either!(" \t") >>
+            space >>
+            description: quoted_str >>
+            tag!(";") >>
+            line_ending >>
+            ( SignalDescription {
+                id: id.to_string(),
+                signal_name: signal_name.to_string(),
+                description: description.to_string()
+            } )
+        )
+    );
+
+    named!(pub signal_attribute<&str, SignalAttribute>,
+        do_parse!(
+            tag!("BA_")   >>
+            space >>
+            name: quoted_str >>
+            space >>
+            tag!("SG_")   >>
+            space >>
+            id: digit >>
+            space >>
+            signal_name: take_until_either!(" \t") >>
+            space >>
+            value: digit >>
+            tag!(";") >>
+            line_ending >>
+            ( SignalAttribute {
+                name: name.to_string(),
+                id: id.to_string(),
+                signal_name: signal_name.to_string(),
+                value: value.to_string()
+            } )
+        )
+    );
 }
 
 /// Error returned on failure to parse DBC `Entry`.
@@ -285,8 +466,10 @@ impl EntryErrorKind {
     pub fn __description(&self) -> &str {
         match *self {
             EntryErrorKind::RegexNoMatch => "could not find a regex match for input",
-            EntryErrorKind::UnknownEntryType(_) => "integer could not be converted into valid EntryType",
-            EntryErrorKind::RegexCapture => "failure to combine all values from regex capture"
+            EntryErrorKind::UnknownEntryType(_) => {
+                "integer could not be converted into valid EntryType"
+            }
+            EntryErrorKind::RegexCapture => "failure to combine all values from regex capture",
         }
     }
     #[doc(hidden)]
@@ -308,116 +491,17 @@ impl Display for EntryErrorKind {
 
 impl From<EntryErrorKind> for ParseEntryError {
     fn from(kind: EntryErrorKind) -> Self {
-        ParseEntryError {
-            kind
-        }
+        ParseEntryError { kind }
     }
 }
 
 impl FromStr for Entry {
     type Err = ParseEntryError;
 
-    fn from_str(line: &str) -> Result<Entry, Self::Err> {
-        let entry_idx = {
-            match Patterns.matches(line).iter().next() {
-                Some(idx) => idx,
-                None => return Err(EntryErrorKind::RegexNoMatch.into())
-            }
-        };
-
-        let entry_type = {
-            match EntryType::from_i32(entry_idx as i32) {
-                Some(ty) => ty,
-                None => return Err(EntryErrorKind::UnknownEntryType(entry_idx as i32).into())
-            }
-        };
-
-        let entry = match entry_type {
-
-            EntryType::Version => {
-                patterns[EntryType::Version as usize].captures(line).map(|caps| {
-                    Entry::Version(Version(caps.get(1).unwrap().as_str().to_string()))
-                })
-            },
-            EntryType::BusConfiguration => {
-                patterns[EntryType::BusConfiguration as usize].captures(line).map(|caps| {
-                    Entry::BusConfiguration(BusConfiguration(caps.get(1).unwrap().as_str().parse().unwrap()))
-                })
-            },
-            EntryType::MessageDefinition => {
-                patterns[EntryType::MessageDefinition as usize].captures(line).map(|caps| {
-                    Entry::MessageDefinition ( MessageDefinition {
-                        id: caps.get(1).unwrap().as_str().to_string(),
-                        name: caps.get(2).unwrap().as_str().to_string(),
-                        message_len: caps.get(3).unwrap().as_str().parse().unwrap(),
-                        sending_node: caps.get(4).unwrap().as_str().to_string()
-                    })
-                })
-            },
-            EntryType::MessageDescription => {
-                patterns[EntryType::MessageDescription as usize].captures(line).map(|caps| {
-                    Entry::MessageDescription ( MessageDescription {
-                        id: caps.get(1).unwrap().as_str().to_string(),
-                        signal_name: "".to_string(),
-                        description: caps.get(2).unwrap().as_str().to_string()
-                    })
-                })
-            },
-            EntryType::MessageAttribute => {
-                patterns[EntryType::MessageAttribute as usize].captures(line).map(|caps| {
-                    Entry::MessageAttribute ( MessageAttribute {
-                        name: caps.get(1).unwrap().as_str().to_string(),
-                        signal_name: "".to_string(),
-                        id: caps.get(2).unwrap().as_str().to_string(),
-                        value: caps.get(3).unwrap().as_str().to_string()
-                    })
-                })
-            }
-            EntryType::SignalDefinition => {
-                patterns[EntryType::SignalDefinition as usize].captures(line).map(|caps| {
-                    Entry::SignalDefinition ( SignalDefinition {
-                        name: caps.get(1).unwrap().as_str().to_string(),
-                        start_bit: caps.get(2).unwrap().as_str().parse().unwrap(),
-                        bit_len: caps.get(3).unwrap().as_str().parse().unwrap(),
-                        little_endian: caps.get(4).unwrap().as_str() == "1",
-                        signed: caps.get(5).unwrap().as_str() == "-",
-                        scale: caps.get(6).unwrap().as_str().parse().unwrap(),
-                        offset: caps.get(7).unwrap().as_str().parse().unwrap(),
-                        min_value: caps.get(8).unwrap().as_str().parse().unwrap(),
-                        max_value: caps.get(9).unwrap().as_str().parse().unwrap(),
-                        units: caps.get(10).unwrap().as_str().to_string(),
-                        receiving_node: caps.get(11).unwrap().as_str().to_string(),
-                    })
-                })
-
-            },
-            EntryType::SignalDescription => {
-                patterns[EntryType::SignalDescription as usize].captures(line).map(|caps| {
-                    Entry::SignalDescription ( SignalDescription {
-                        id: caps.get(1).unwrap().as_str().to_string(),
-                        signal_name: caps.get(2).unwrap().as_str().to_string(),
-                        description: caps.get(3).unwrap().as_str().to_string()
-                    })
-                })
-            },
-            EntryType::SignalAttribute => {
-                patterns[EntryType::SignalAttribute as usize].captures(line).map(|caps| {
-                    Entry::SignalAttribute ( SignalAttribute {
-                        name: caps.get(1).unwrap().as_str().to_string(),
-                        id: caps.get(2).unwrap().as_str().to_string(),
-                        signal_name: caps.get(3).unwrap().as_str().to_string().to_string(),
-                        value: caps.get(4).unwrap().as_str().to_string()
-                    })
-                })
-            },
-        };
-
-        if let Some(_entry) = entry {
-            Ok(_entry)
-        } else {
-            Err(EntryErrorKind::RegexCapture.into())
-        }
-
+    fn from_str(line: &str) -> Result<Self, Self::Err> {
+        nom::entry(line)
+            .map_err(|_e| EntryErrorKind::RegexNoMatch.into())
+            .map(|(_i, entry)| entry)
     }
 }
 
@@ -426,7 +510,7 @@ mod tests {
     use std::str::FromStr;
 
     macro_rules! test_entry {
-        ($test_name: ident, $entry_type: ident, $test_line: expr, $expected: expr) => (
+        ($test_name: ident, $entry_type: ident, $test_line: expr, $expected: expr) => {
             mod $test_name {
                 use dbc::*;
                 use std::str::FromStr;
@@ -435,41 +519,56 @@ mod tests {
                 fn from_str() {
                     assert_eq!(
                         Entry::from_str($test_line),
-                        Ok(Entry::$entry_type ( $expected ))
+                        Ok(Entry::$entry_type($expected))
                     );
                 }
 
+                /*
+                // FIXME: This test ends up failing because of `Entry::Unknown`
                 #[test]
                 fn from_str_err() {
-                    let failstr = format!("GONNAFAIL {}", $test_line);
+                    let failstr = format!("GONNAFAIL {}\n", $test_line);
+                    let res = Entry::from_str(&failstr);
+                    println!("res: {:?}", res);
                     assert!(
-                        Entry::from_str(&failstr).is_err(),
+                        res.is_err(),
                         "Result of entry parse failure should be Err"
                     );
                 }
+                */
 
                 #[test]
                 fn entry_type() {
-                    let entry = Entry::$entry_type( $expected );
+                    let entry = Entry::$entry_type($expected);
                     let entry_type = EntryType::$entry_type;
 
                     assert_eq!(entry.get_type(), entry_type);
+                    assert_eq!(format!("{}", entry), format!("{}", entry_type),);
+                }
+
+                #[test]
+                fn nom_parse() {
+                    assert_eq!(nom::$test_name($test_line).unwrap().1, $expected);
                     assert_eq!(
-                        format!("{}", entry),
-                        format!("{}", entry_type),
+                        nom::entry($test_line).unwrap().1,
+                        Entry::$entry_type($expected)
                     );
                 }
             }
-        )
+        };
     }
 
-    test_entry!( version, Version,
-        "VERSION \"A version string\"",
-        Version ( "A version string".to_string() )
+    test_entry!(
+        version,
+        Version,
+        "VERSION \"A version string\"\n",
+        Version("A version string".to_string())
     );
 
-    test_entry!( message_definition, MessageDefinition,
-        "BO_ 2364539904 EEC1 : 8 Vector__XXX",
+    test_entry!(
+        message_definition,
+        MessageDefinition,
+        "BO_ 2364539904 EEC1 : 8 Vector__XXX\n",
         MessageDefinition {
             id: "2364539904".to_string(),
             name: "EEC1".to_string(),
@@ -478,13 +577,21 @@ mod tests {
         }
     );
 
-    test_entry!( message_description, MessageDescription,
-        "CM_ BO_ 2364539904 \"Engine Controller\";",
-        MessageDescription { id: "2364539904".to_string(), signal_name: "".to_string(), description: "Engine Controller".to_string()}
+    test_entry!(
+        message_description,
+        MessageDescription,
+        "CM_ BO_ 2364539904 \"Engine Controller\";\n",
+        MessageDescription {
+            id: "2364539904".to_string(),
+            signal_name: "".to_string(),
+            description: "Engine Controller".to_string()
+        }
     );
 
-    test_entry!( message_attribute, MessageAttribute,
-        "BA_ \"SingleFrame\" BO_ 2364539904 0;",
+    test_entry!(
+        message_attribute,
+        MessageAttribute,
+        "BA_ \"SingleFrame\" BO_ 2364539904 0;\n",
         MessageAttribute {
             name: "SingleFrame".to_string(),
             signal_name: "".to_string(),
@@ -493,8 +600,10 @@ mod tests {
         }
     );
 
-    test_entry!( signal_definition, SignalDefinition,
-        " SG_ Engine_Speed : 24|16@1+ (0.125,0) [0|8031.88] \"rpm\" Vector__XXX",
+    test_entry!(
+        signal_definition,
+        SignalDefinition,
+        " SG_ Engine_Speed : 24|16@1+ (0.125,0) [0|8031.88] \"rpm\" Vector__XXX\n",
         SignalDefinition {
             name: "Engine_Speed".to_string(),
             start_bit: 24,
@@ -510,8 +619,10 @@ mod tests {
         }
     );
 
-    test_entry!( signal_description, SignalDescription,
-        "CM_ SG_ 2364539904 Engine_Speed \"A description for Engine speed.\";",
+    test_entry!(
+        signal_description,
+        SignalDescription,
+        "CM_ SG_ 2364539904 Engine_Speed \"A description for Engine speed.\";\n",
         SignalDescription {
             id: "2364539904".to_string(),
             signal_name: "Engine_Speed".to_string(),
@@ -519,21 +630,10 @@ mod tests {
         }
     );
 
-    test_entry!( signal_description_multiline, SignalDescription,
-        "CM_ SG_ 2364539904 Actual_Engine___Percent_Torque_High_Resolution \"A multi- \r \
-        \r \
-        line description for Engine torque.\";",
-        SignalDescription {
-            id: "2364539904".to_string(),
-            signal_name: "Actual_Engine___Percent_Torque_High_Resolution".to_string(),
-            description: "A multi- \r \
-            \r \
-            line description for Engine torque.".to_string()
-        }
-    );
-
-    test_entry!( signal_attribute, SignalAttribute,
-        "BA_ \"SPN\" SG_ 2364539904 Engine_Speed 190;",
+    test_entry!(
+        signal_attribute,
+        SignalAttribute,
+        "BA_ \"SPN\" SG_ 2364539904 Engine_Speed 190;\n",
         SignalAttribute {
             name: "SPN".to_string(),
             id: "2364539904".to_string(),
@@ -541,4 +641,22 @@ mod tests {
             value: "190".to_string()
         }
     );
+
+    mod multiline {
+        test_entry!(
+            signal_description,
+            SignalDescription,
+            "CM_ SG_ 2364539904 Actual_Engine___Percent_Torque_High_Resolution \"A multi- \r \
+             \r \
+             line description for Engine torque.\";\n",
+            SignalDescription {
+                id: "2364539904".to_string(),
+                signal_name: "Actual_Engine___Percent_Torque_High_Resolution".to_string(),
+                description: "A multi- \r \
+                              \r \
+                              line description for Engine torque."
+                    .to_string()
+            }
+        );
+    }
 }
diff --git a/src/lib.rs b/src/lib.rs
index 6db45ef..0d2b689 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -35,20 +35,22 @@
 //! ```
 
 #![cfg_attr(feature = "cargo-clippy", allow(redundant_field_names))]
-
 #![allow(non_snake_case, non_camel_case_types)]
 #![allow(unused_variables, dead_code)]
 #![allow(unused_imports, unused_mut)]
 #![allow(plugin_as_library)]
-
 #![crate_name = "canparse"]
 
 extern crate encoding;
-#[macro_use] extern crate enum_primitive;
-#[macro_use] extern crate lazy_static;
+#[macro_use]
+extern crate enum_primitive;
+#[macro_use]
+extern crate lazy_static;
+extern crate byteorder;
 extern crate regex;
 extern crate rustc_serialize;
-extern crate byteorder;
+#[macro_use]
+extern crate nom;
 
 #[cfg(feature = "use-socketcan")]
 extern crate socketcan;
diff --git a/src/pgn.rs b/src/pgn.rs
index 09cf99b..4e4fa14 100644
--- a/src/pgn.rs
+++ b/src/pgn.rs
@@ -1,21 +1,22 @@
 //! Signal processing using PGN/SPN definitions.
 
-use encoding::{CodecError, DecoderTrap, Encoding};
+use byteorder::{BigEndian, ByteOrder, LittleEndian};
+use dbc::{nom as nomparse, *};
 use encoding::all::ISO_8859_1;
+use encoding::{CodecError, DecoderTrap, Encoding};
+use nom;
 use regex::Regex;
 use std::collections::HashMap;
 use std::error::Error;
-use std::marker::Sized;
-use std::str::FromStr;
-use std::io::prelude::*;
+use std::fmt;
+use std::fmt::{Display, Formatter};
+use std::fs::File;
 use std::io;
+use std::io::prelude::*;
 use std::io::BufReader;
+use std::marker::Sized;
 use std::path::Path;
-use std::fmt::{Display, Formatter};
-use std::fmt;
-use std::fs::File;
-use dbc::*;
-use byteorder::{ByteOrder, BigEndian, LittleEndian};
+use std::str::FromStr;
 
 #[cfg(feature = "use-socketcan")]
 use socketcan::CANFrame;
@@ -25,7 +26,9 @@ pub trait FromDbc {
     type Err;
 
     /// Converts an `Entity` value from scratch.
-    fn from_entry(entry: Entry) -> Result<Self, Self::Err> where Self: Sized;
+    fn from_entry(entry: Entry) -> Result<Self, Self::Err>
+    where
+        Self: Sized;
 
     /// Merges the given `Entity` with a `mut` version of the library's entity.  Useful for when
     /// multiple `Entry` types contribute to various attributes within the same destination.
@@ -40,10 +43,12 @@ pub struct PgnLibrary {
 }
 
 impl PgnLibrary {
-
     /// Creates a new `PgnLibrary` instance given an existing lookup table.
     pub fn new(pgns: HashMap<u32, PgnDefinition>) -> Self {
-        PgnLibrary { last_id:0, pgns:pgns }
+        PgnLibrary {
+            last_id: 0,
+            pgns: pgns,
+        }
     }
 
     /// Convenience function for loading an entire DBC file into a returned `PgnLibrary`.  This
@@ -96,18 +101,29 @@ impl PgnLibrary {
         let data = File::open(path)
             .and_then(|mut f| {
                 let mut contents: Vec<u8> = Vec::new();
-                f.read_to_end(&mut contents)
-                    .map(|_bytes_read| contents)
+                f.read_to_end(&mut contents).map(|_bytes_read| contents)
             })
             .and_then(|mut contents| {
-                encoding.decode(contents.as_slice(), DecoderTrap::Replace)
+                encoding
+                    .decode(contents.as_slice(), DecoderTrap::Replace)
                     .map_err(|e| io::Error::new(io::ErrorKind::Other, e))
             })?;
 
-        for (idx, line) in data.lines().enumerate() {
-            if let Ok(entry) = Entry::from_str(line) {
-                if let Err(_e) = lib.add_entry(entry) {
-                    // TODO: Handle add_entry error
+        let mut i = data.as_str();
+        while i.len() > 0 {
+            match nomparse::entry(i) {
+                Ok((new_i, entry)) => {
+                    if let Err(_e) = lib.add_entry(entry) {
+                        // TODO: Handle add_entry error
+                    }
+                    i = new_i;
+                }
+                // FIXME: handling `IResult::Err`s could be better
+                Err(nom::Err::Incomplete(_)) => {
+                    break;
+                }
+                Err(_) => {
+                    i = &i[1..];
                 }
             }
         }
@@ -143,30 +159,35 @@ impl PgnLibrary {
     /// ```
     pub fn add_entry(&mut self, entry: Entry) -> Result<(), String> {
         let _id: u32 = match entry {
-            Entry::MessageDefinition (MessageDefinition  { ref id, .. }) => id.parse(),
+            Entry::MessageDefinition(MessageDefinition { ref id, .. }) => id.parse(),
             Entry::MessageDescription(MessageDescription { ref id, .. }) => id.parse(),
-            Entry::MessageAttribute( MessageAttribute  { ref id, .. }) => id.parse(),
-            Entry::SignalDefinition ( .. ) => {
-
-                 // no id, and by definition must follow MessageDefinition
+            Entry::MessageAttribute(MessageAttribute { ref id, .. }) => id.parse(),
+            Entry::SignalDefinition(..) => {
+                // no id, and by definition must follow MessageDefinition
                 if self.last_id == 0 {
                     return Err("Tried to add SignalDefinition without last ID.".to_string());
                 }
                 Ok(self.last_id)
-            },
-            Entry::SignalDescription (SignalDescription{ ref id, .. }) => id.parse(),
-            Entry::SignalAttribute (SignalAttribute { ref id, .. }) => id.parse(),
-            _ => { return Err(format!("Unsupported entry: {}.", entry).to_string()); }
-        }.unwrap();
+            }
+            Entry::SignalDescription(SignalDescription { ref id, .. }) => id.parse(),
+            Entry::SignalAttribute(SignalAttribute { ref id, .. }) => id.parse(),
+            _ => {
+                return Err(format!("Unsupported entry: {}.", entry).to_string());
+            }
+        }
+        .unwrap();
 
         // CanId{ DP, PF, PS, SA } => Pgn{ PF, PS }
         let pgn = (_id >> 8) & 0x1FFFF;
 
         self.last_id = _id;
         if self.pgns.contains_key(&pgn) {
-            (*self.pgns.get_mut(&pgn).unwrap()).merge_entry(entry).unwrap();
+            (*self.pgns.get_mut(&pgn).unwrap())
+                .merge_entry(entry)
+                .unwrap();
         } else {
-            self.pgns.insert(pgn, PgnDefinition::from_entry(entry).unwrap());
+            self.pgns
+                .insert(pgn, PgnDefinition::from_entry(entry).unwrap());
         }
         Ok(())
     }
@@ -178,15 +199,16 @@ impl PgnLibrary {
 
     /// Returns a `SpnDefinition` entry reference, if it exists.
     pub fn get_spn(&self, name: &str) -> Option<&SpnDefinition> {
-        self.pgns.iter().filter_map(|pgn| {
-            pgn.1.spns.get(name)
-        }).next()
+        self.pgns
+            .iter()
+            .filter_map(|pgn| pgn.1.spns.get(name))
+            .next()
     }
 }
 
 impl Default for PgnLibrary {
     fn default() -> Self {
-        PgnLibrary::new( HashMap::default() )
+        PgnLibrary::new(HashMap::default())
     }
 }
 
@@ -198,24 +220,25 @@ pub struct PgnDefinition {
     name_abbrev: String,
     description: String,
     length: u32,
-    spns: HashMap<String, SpnDefinition>
+    spns: HashMap<String, SpnDefinition>,
 }
 
 impl PgnDefinition {
-    pub fn new( pgn: u32,
-                pgn_long: u32,
-                name_abbrev: String,
-                description: String,
-                length: u32,
-                spns: HashMap<String,SpnDefinition> ) -> Self {
-
+    pub fn new(
+        pgn: u32,
+        pgn_long: u32,
+        name_abbrev: String,
+        description: String,
+        length: u32,
+        spns: HashMap<String, SpnDefinition>,
+    ) -> Self {
         PgnDefinition {
             pgn: pgn,
             pgn_long: pgn_long,
             name_abbrev: name_abbrev,
             description: description,
             length: length,
-            spns: spns
+            spns: spns,
         }
     }
 }
@@ -269,7 +292,9 @@ impl DefinitionErrorKind {
     pub fn __description(&self) -> &str {
         match self {
             DefinitionErrorKind::Entry(_) => "internal Entry parsing error",
-            DefinitionErrorKind::UnusedEntry(_) => "Entry type not applicable in constructing Definition",
+            DefinitionErrorKind::UnusedEntry(_) => {
+                "Entry type not applicable in constructing Definition"
+            }
         }
     }
 
@@ -291,9 +316,7 @@ impl Display for DefinitionErrorKind {
 
 impl From<DefinitionErrorKind> for ParseDefinitionError {
     fn from(kind: DefinitionErrorKind) -> Self {
-        ParseDefinitionError {
-            kind
-        }
+        ParseDefinitionError { kind }
     }
 }
 
@@ -302,7 +325,8 @@ impl FromStr for PgnDefinition {
 
     /// `&str` -> `PgnDefinition` via `dbc::Entry` (though probably won't be used).
     fn from_str(line: &str) -> Result<Self, Self::Err>
-        where Self: Sized + FromDbc
+    where
+        Self: Sized + FromDbc,
     {
         Entry::from_str(line)
             .map_err(|e| DefinitionErrorKind::Entry(e).into())
@@ -313,78 +337,148 @@ impl FromStr for PgnDefinition {
 impl FromDbc for PgnDefinition {
     type Err = ParseDefinitionError;
 
-    fn from_entry(entry: Entry) -> Result<Self, Self::Err> where Self: Sized {
+    fn from_entry(entry: Entry) -> Result<Self, Self::Err>
+    where
+        Self: Sized,
+    {
         match entry {
-            Entry::MessageDefinition ( MessageDefinition { id, name, message_len, sending_node }) => {
+            Entry::MessageDefinition(MessageDefinition {
+                id,
+                name,
+                message_len,
+                sending_node,
+            }) => {
                 let pgn_long = id.parse::<u32>().unwrap();
                 let pgn = pgn_long & 0x1FFFF;
-                Ok(PgnDefinition::new(pgn, pgn_long, name, "".to_string(), 0, HashMap::new()))
-            },
-            Entry::MessageDescription ( MessageDescription { id, signal_name, description }) => {
+                Ok(PgnDefinition::new(
+                    pgn,
+                    pgn_long,
+                    name,
+                    "".to_string(),
+                    0,
+                    HashMap::new(),
+                ))
+            }
+            Entry::MessageDescription(MessageDescription {
+                id,
+                signal_name,
+                description,
+            }) => {
                 let pgn_long = id.parse::<u32>().unwrap();
                 let pgn = pgn_long & 0x1FFFF;
-                Ok(PgnDefinition::new(pgn, pgn_long, "".to_string(), description, 0, HashMap::new()))
-            },
-            Entry::MessageAttribute ( MessageAttribute { name, signal_name, id, value }) => {
+                Ok(PgnDefinition::new(
+                    pgn,
+                    pgn_long,
+                    "".to_string(),
+                    description,
+                    0,
+                    HashMap::new(),
+                ))
+            }
+            Entry::MessageAttribute(MessageAttribute {
+                name,
+                signal_name,
+                id,
+                value,
+            }) => {
                 let pgn_long = id.parse::<u32>().unwrap();
                 let pgn = pgn_long & 0x1FFFF;
-                Ok(PgnDefinition::new(pgn, pgn_long, "".to_string(), "".to_string(), 0, HashMap::new()))
+                Ok(PgnDefinition::new(
+                    pgn,
+                    pgn_long,
+                    "".to_string(),
+                    "".to_string(),
+                    0,
+                    HashMap::new(),
+                ))
             }
-            _ => Err(DefinitionErrorKind::UnusedEntry(entry.get_type()).into())
+            _ => Err(DefinitionErrorKind::UnusedEntry(entry.get_type()).into()),
         }
     }
 
     fn merge_entry(&mut self, entry: Entry) -> Result<(), Self::Err> {
         match entry {
-            Entry::MessageDefinition ( MessageDefinition { id, name, message_len, sending_node }) => {
+            Entry::MessageDefinition(MessageDefinition {
+                id,
+                name,
+                message_len,
+                sending_node,
+            }) => {
                 let pgn_long = id.parse::<u32>().unwrap();
                 let pgn = pgn_long & 0x1FFFF;
-                self.pgn = pgn; self.pgn_long = pgn_long; self.length = message_len;
+                self.pgn = pgn;
+                self.pgn_long = pgn_long;
+                self.length = message_len;
                 Ok(())
-            },
-            Entry::MessageDescription ( MessageDescription {id, signal_name, description }) => {
+            }
+            Entry::MessageDescription(MessageDescription {
+                id,
+                signal_name,
+                description,
+            }) => {
                 let pgn_long = id.parse::<u32>().unwrap();
                 let pgn = pgn_long & 0x1FFFF;
-                self.pgn = pgn; self.pgn_long = pgn_long; self.description = description;
+                self.pgn = pgn;
+                self.pgn_long = pgn_long;
+                self.description = description;
                 Ok(())
-            },
-            Entry::MessageAttribute( MessageAttribute { name, signal_name, id, value }) => {
+            }
+            Entry::MessageAttribute(MessageAttribute {
+                name,
+                signal_name,
+                id,
+                value,
+            }) => {
                 let pgn_long = id.parse::<u32>().unwrap();
                 let pgn = pgn_long & 0x1FFFF;
-                self.pgn = pgn; self.pgn_long = pgn_long;
+                self.pgn = pgn;
+                self.pgn_long = pgn_long;
                 Ok(())
-            },
-            Entry::SignalDefinition ( wrapped ) => {
+            }
+            Entry::SignalDefinition(wrapped) => {
                 if self.spns.contains_key(&wrapped.name) {
                     (*self.spns.get_mut(&wrapped.name).unwrap())
-                        .merge_entry(Entry::SignalDefinition(wrapped)).unwrap();
+                        .merge_entry(Entry::SignalDefinition(wrapped))
+                        .unwrap();
                 } else {
-                    self.spns.insert(wrapped.name.clone(),
-                                     SpnDefinition::from_entry(Entry::SignalDefinition(wrapped)).unwrap());
+                    self.spns.insert(
+                        wrapped.name.clone(),
+                        SpnDefinition::from_entry(Entry::SignalDefinition(wrapped)).unwrap(),
+                    );
                 }
                 Ok(())
-            },
-            Entry::SignalDescription ( wrapped ) => {
+            }
+            Entry::SignalDescription(wrapped) => {
                 if self.spns.contains_key(&wrapped.signal_name) {
                     (*self.spns.get_mut(&wrapped.signal_name).unwrap())
-                        .merge_entry(Entry::SignalDescription(wrapped)).unwrap();
+                        .merge_entry(Entry::SignalDescription(wrapped))
+                        .unwrap();
                 } else {
-                    self.spns.insert(wrapped.signal_name.clone(),
-                                     SpnDefinition::from_entry(Entry::SignalDescription(wrapped)).unwrap());
+                    self.spns.insert(
+                        wrapped.signal_name.clone(),
+                        SpnDefinition::from_entry(Entry::SignalDescription(wrapped)).unwrap(),
+                    );
                 }
                 Ok(())
-            },
-            Entry::SignalAttribute ( wrapped ) => {
+            }
+            Entry::SignalAttribute(wrapped) => {
+                if wrapped.name != "SPN" {
+                    // Skip non-SPN attributes
+                    return Ok(());
+                }
                 if self.spns.contains_key(&wrapped.signal_name) {
                     (*self.spns.get_mut(&wrapped.signal_name).unwrap())
-                        .merge_entry(Entry::SignalAttribute(wrapped)).unwrap();
+                        .merge_entry(Entry::SignalAttribute(wrapped))
+                        .unwrap();
                 } else {
-                    self.spns.insert(wrapped.signal_name.clone(),
-                                     SpnDefinition::from_entry(Entry::SignalAttribute(wrapped)).unwrap());
+                    self.spns.insert(
+                        wrapped.signal_name.clone(),
+                        SpnDefinition::from_entry(Entry::SignalAttribute(wrapped)).unwrap(),
+                    );
                 }
                 Ok(())
-            },
-            _ => Err(DefinitionErrorKind::UnusedEntry(entry.get_type()).into())
+            }
+            _ => Err(DefinitionErrorKind::UnusedEntry(entry.get_type()).into()),
         }
     }
 }
@@ -404,48 +498,65 @@ pub struct SpnDefinition {
     offset: f32,
     min_value: f32,
     max_value: f32,
-    units: String
+    units: String,
 }
 
-const SHIFT_BYTE_LOOKUP: [u64; 8] = [ 1, 1<<(1*8), 1<<(2*8), 1<<(3*8), 1<<(4*8), 1<<(5*8), 1<<(6*8), 1<<(7*8) ];
+const SHIFT_BYTE_LOOKUP: [u64; 8] = [
+    1,
+    1 << (1 * 8),
+    1 << (2 * 8),
+    1 << (3 * 8),
+    1 << (4 * 8),
+    1 << (5 * 8),
+    1 << (6 * 8),
+    1 << (7 * 8),
+];
 
 /// Internal function for parsing CAN message arrays given the definition parameters.  This is where
 /// the real calculations happen.
-fn parse_array(bit_len: usize, start_bit: usize, little_endian: bool, scale: f32, offset: f32, msg: &[u8; 8]) -> Option<f32> {
+fn parse_array(
+    bit_len: usize,
+    start_bit: usize,
+    little_endian: bool,
+    scale: f32,
+    offset: f32,
+    msg: &[u8; 8],
+) -> Option<f32> {
     let msg64: u64 = match little_endian {
         true => LittleEndian::read_u64(msg),
-        false => BigEndian::read_u64(msg)
+        false => BigEndian::read_u64(msg),
     };
 
     let bit_mask: u64 = 2u64.pow(bit_len as u32) - 1;
 
-    Some(
-        ( ( ( msg64 >> start_bit ) & bit_mask ) as f32 ) * scale + offset
-    )
+    Some((((msg64 >> start_bit) & bit_mask) as f32) * scale + offset)
 }
 
 /// Internal function for parsing CAN message slices given the definition parameters.  This is where
 /// the real calculations happen.
-fn parse_message(bit_len: usize, start_bit: usize, little_endian: bool, scale: f32, offset: f32, msg: &[u8]) -> Option<f32> {
-
+fn parse_message(
+    bit_len: usize,
+    start_bit: usize,
+    little_endian: bool,
+    scale: f32,
+    offset: f32,
+    msg: &[u8],
+) -> Option<f32> {
     if msg.len() < 8 {
-        return None
+        return None;
     }
     let msg64: u64 = match little_endian {
         true => LittleEndian::read_u64(msg),
-        false => BigEndian::read_u64(msg)
+        false => BigEndian::read_u64(msg),
     };
 
     let bit_mask: u64 = 2u64.pow(bit_len as u32) - 1;
 
-    Some(
-        ( ( ( msg64 >> start_bit ) & bit_mask ) as f32 ) * scale + offset
-    )
+    Some((((msg64 >> start_bit) & bit_mask) as f32) * scale + offset)
 }
 
 /// The collection of functions for parsing CAN messages `N` into their defined signal values.
 pub trait ParseMessage<N> {
-
     /// Parses CAN message type `N` into generic `f32` signal value on success, or `None`
     /// on failure.
     fn parse_message(&self, msg: N) -> Option<f32>;
@@ -456,21 +567,22 @@ pub trait ParseMessage<N> {
 }
 
 impl SpnDefinition {
-
     /// Return new `SpnDefinition` given the definition parameters.
-    pub fn new(name: String,
-               number: usize,
-               id: String,
-               description: String,
-               start_bit: usize,
-               bit_len: usize,
-               little_endian: bool,
-               signed: bool,
-               scale: f32,
-               offset: f32,
-               min_value: f32,
-               max_value: f32,
-               units: String) -> Self {
+    pub fn new(
+        name: String,
+        number: usize,
+        id: String,
+        description: String,
+        start_bit: usize,
+        bit_len: usize,
+        little_endian: bool,
+        signed: bool,
+        scale: f32,
+        offset: f32,
+        min_value: f32,
+        max_value: f32,
+        units: String,
+    ) -> Self {
         SpnDefinition {
             name: name,
             number: number,
@@ -484,15 +596,21 @@ impl SpnDefinition {
             offset: offset,
             min_value: min_value,
             max_value: max_value,
-            units: units
+            units: units,
         }
     }
 }
 
-impl <'a> ParseMessage<&'a [u8; 8]> for SpnDefinition {
-
+impl<'a> ParseMessage<&'a [u8; 8]> for SpnDefinition {
     fn parse_message(&self, msg: &[u8; 8]) -> Option<f32> {
-        parse_array(self.bit_len, self.start_bit, self.little_endian, self.scale, self.offset, msg)
+        parse_array(
+            self.bit_len,
+            self.start_bit,
+            self.little_endian,
+            self.scale,
+            self.offset,
+            msg,
+        )
     }
 
     fn parser(&self) -> Box<Fn(&[u8; 8]) -> Option<f32>> {
@@ -502,18 +620,23 @@ impl <'a> ParseMessage<&'a [u8; 8]> for SpnDefinition {
         let offset = self.offset;
         let little_endian = self.little_endian;
 
-        let fun = move |msg: &[u8; 8]| {
-            parse_array(bit_len, start_bit, little_endian, scale, offset, msg)
-        };
+        let fun =
+            move |msg: &[u8; 8]| parse_array(bit_len, start_bit, little_endian, scale, offset, msg);
 
         Box::new(fun)
     }
 }
 
-impl <'a> ParseMessage<&'a [u8]> for SpnDefinition {
-
+impl<'a> ParseMessage<&'a [u8]> for SpnDefinition {
     fn parse_message(&self, msg: &[u8]) -> Option<f32> {
-        parse_message(self.bit_len, self.start_bit, self.little_endian, self.scale, self.offset, msg)
+        parse_message(
+            self.bit_len,
+            self.start_bit,
+            self.little_endian,
+            self.scale,
+            self.offset,
+            msg,
+        )
     }
 
     fn parser(&self) -> Box<Fn(&[u8]) -> Option<f32>> {
@@ -523,24 +646,28 @@ impl <'a> ParseMessage<&'a [u8]> for SpnDefinition {
         let offset = self.offset;
         let little_endian = self.little_endian;
 
-        let fun = move |msg: &[u8]| {
-            parse_message(bit_len, start_bit, little_endian, scale, offset, msg)
-        };
+        let fun =
+            move |msg: &[u8]| parse_message(bit_len, start_bit, little_endian, scale, offset, msg);
 
         Box::new(fun)
     }
 }
 
 #[cfg(feature = "use-socketcan")]
-impl <'a> ParseMessage<&'a CANFrame> for SpnDefinition {
-
+impl<'a> ParseMessage<&'a CANFrame> for SpnDefinition {
     fn parse_message(&self, frame: &CANFrame) -> Option<f32> {
         let ref msg = frame.data();
-        parse_message(self.bit_len, self.start_bit, self.little_endian, self.scale, self.offset, msg)
+        parse_message(
+            self.bit_len,
+            self.start_bit,
+            self.little_endian,
+            self.scale,
+            self.offset,
+            msg,
+        )
     }
 
     fn parser(&self) -> Box<Fn(&CANFrame) -> Option<f32>> {
-
         let bit_len = self.bit_len;
         let start_bit = self.start_bit;
         let scale = self.scale;
@@ -561,7 +688,8 @@ impl FromStr for SpnDefinition {
 
     /// `&str` -> `SpnDefinition` via `dbc::Entry` (though probably won't be used).
     fn from_str(line: &str) -> Result<Self, Self::Err>
-        where Self: Sized + FromDbc
+    where
+        Self: Sized + FromDbc,
     {
         Entry::from_str(line)
             .map_err(|e| DefinitionErrorKind::Entry(e).into())
@@ -572,97 +700,202 @@ impl FromStr for SpnDefinition {
 impl FromDbc for SpnDefinition {
     type Err = ParseDefinitionError;
 
-    fn from_entry(entry: Entry) -> Result<Self, Self::Err> where Self: Sized {
+    fn from_entry(entry: Entry) -> Result<Self, Self::Err>
+    where
+        Self: Sized,
+    {
         match entry {
-            Entry::SignalDefinition ( signal_definition ) =>
-                Ok(signal_definition.into()),
-            Entry::SignalDescription ( signal_description ) =>
-                Ok(signal_description.into()),
-            Entry::SignalAttribute ( signal_attribute ) =>
-                Ok(signal_attribute.into()),
-            _ => Err(DefinitionErrorKind::UnusedEntry(entry.get_type()).into())
+            Entry::SignalDefinition(signal_definition) => Ok(signal_definition.into()),
+            Entry::SignalDescription(signal_description) => Ok(signal_description.into()),
+            Entry::SignalAttribute(signal_attribute) => Ok(signal_attribute.into()),
+            _ => Err(DefinitionErrorKind::UnusedEntry(entry.get_type()).into()),
         }
     }
 
     fn merge_entry(&mut self, entry: Entry) -> Result<(), Self::Err> {
         match entry {
-            Entry::SignalDefinition ( SignalDefinition { name, start_bit, bit_len, little_endian, signed, scale, offset, min_value, max_value, units, .. }) => {
-                self.name = name; self.start_bit = start_bit; self.bit_len = bit_len; self.little_endian = little_endian; self.signed = signed; self.scale = scale; self.offset = offset; self.min_value = min_value; self.units = units;
+            Entry::SignalDefinition(SignalDefinition {
+                name,
+                start_bit,
+                bit_len,
+                little_endian,
+                signed,
+                scale,
+                offset,
+                min_value,
+                max_value,
+                units,
+                ..
+            }) => {
+                self.name = name;
+                self.start_bit = start_bit;
+                self.bit_len = bit_len;
+                self.little_endian = little_endian;
+                self.signed = signed;
+                self.scale = scale;
+                self.offset = offset;
+                self.min_value = min_value;
+                self.units = units;
                 Ok(())
-            },
-            Entry::SignalDescription ( SignalDescription {id, signal_name, description }) => {
-                self.name = signal_name; self.id = id; self.description = description;
+            }
+            Entry::SignalDescription(SignalDescription {
+                id,
+                signal_name,
+                description,
+            }) => {
+                self.name = signal_name;
+                self.id = id;
+                self.description = description;
                 Ok(())
-            },
-            Entry::SignalAttribute ( SignalAttribute { name, id, signal_name, value }) => {
-                self.name = signal_name; self.id = id; self.number = value.parse().unwrap();
+            }
+            Entry::SignalAttribute(SignalAttribute {
+                name,
+                id,
+                signal_name,
+                value,
+            }) => {
+                self.name = signal_name;
+                self.id = id;
+                self.number = value.parse().unwrap();
                 Ok(())
             }
-            _ => Err(DefinitionErrorKind::UnusedEntry(entry.get_type()).into())
+            _ => Err(DefinitionErrorKind::UnusedEntry(entry.get_type()).into()),
         }
     }
-
 }
 
 impl From<SignalDefinition> for SpnDefinition {
-    fn from(SignalDefinition { name, start_bit, bit_len, little_endian, signed, scale, offset, min_value, max_value, units, .. }: SignalDefinition) -> Self {
-        SpnDefinition::new(name, 0, "".to_string(), "".to_string(), start_bit, bit_len, little_endian, signed, scale, offset, min_value, max_value, units)
+    fn from(
+        SignalDefinition {
+            name,
+            start_bit,
+            bit_len,
+            little_endian,
+            signed,
+            scale,
+            offset,
+            min_value,
+            max_value,
+            units,
+            ..
+        }: SignalDefinition,
+    ) -> Self {
+        SpnDefinition::new(
+            name,
+            0,
+            "".to_string(),
+            "".to_string(),
+            start_bit,
+            bit_len,
+            little_endian,
+            signed,
+            scale,
+            offset,
+            min_value,
+            max_value,
+            units,
+        )
     }
 }
 impl From<SignalDescription> for SpnDefinition {
-    fn from(SignalDescription { id, signal_name, description }: SignalDescription) -> Self {
-        SpnDefinition::new(signal_name, 0, id, description, 0, 0, true, false, 0.0, 0.0, 0.0, 0.0, "".to_string() )
+    fn from(
+        SignalDescription {
+            id,
+            signal_name,
+            description,
+        }: SignalDescription,
+    ) -> Self {
+        SpnDefinition::new(
+            signal_name,
+            0,
+            id,
+            description,
+            0,
+            0,
+            true,
+            false,
+            0.0,
+            0.0,
+            0.0,
+            0.0,
+            "".to_string(),
+        )
     }
 }
 impl From<SignalAttribute> for SpnDefinition {
-    fn from(SignalAttribute { name, id, signal_name, value }: SignalAttribute) -> Self {
-        SpnDefinition::new(signal_name, value.parse().unwrap(), id, "".to_string(), 0, 0, true, false, 0.0, 0.0, 0.0, 0.0, "".to_string() )
+    fn from(
+        SignalAttribute {
+            name,
+            id,
+            signal_name,
+            value,
+        }: SignalAttribute,
+    ) -> Self {
+        SpnDefinition::new(
+            signal_name,
+            value.parse().unwrap(),
+            id,
+            "".to_string(),
+            0,
+            0,
+            true,
+            false,
+            0.0,
+            0.0,
+            0.0,
+            0.0,
+            "".to_string(),
+        )
     }
 }
 
 #[cfg(test)]
 mod tests {
 
-    use pgn::*;
     use dbc::*;
+    use pgn::*;
 
-    lazy_static!{
-    static ref PGNLIB_EMPTY: PgnLibrary = PgnLibrary::default();
-
-    static ref PGNLIB_ONE: PgnLibrary = PgnLibrary::from_dbc_file("./tests/data/sample.dbc")
-        .expect("Failed to create PgnLibrary from file");
-
-    static ref SPNDEF: SpnDefinition =
-        SpnDefinition::new("Engine_Speed".to_string(), 190, "2364539904".to_string(),
+    lazy_static! {
+        static ref PGNLIB_EMPTY: PgnLibrary = PgnLibrary::default();
+        static ref PGNLIB_ONE: PgnLibrary = PgnLibrary::from_dbc_file("./tests/data/sample.dbc")
+            .expect("Failed to create PgnLibrary from file");
+        static ref SPNDEF: SpnDefinition = SpnDefinition::new(
+            "Engine_Speed".to_string(),
+            190,
+            "2364539904".to_string(),
             "A description for Engine speed.".to_string(),
-            24, 16, true, false, 0.125, 0.0, 0.0, 8031.88, "rpm".to_string()
+            24,
+            16,
+            true,
+            false,
+            0.125,
+            0.0,
+            0.0,
+            8031.88,
+            "rpm".to_string()
         );
-
-    static ref SPNDEF_BE: SpnDefinition = {
-        let mut _spndef = SPNDEF.clone();
-        _spndef.little_endian = false;
-        _spndef
-    };
-
-    static ref MSG: [u8; 8] = [0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88];
-    static ref MSG_BE: [u8; 8] = [0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11];
-
+        static ref SPNDEF_BE: SpnDefinition = {
+            let mut _spndef = SPNDEF.clone();
+            _spndef.little_endian = false;
+            _spndef
+        };
+        static ref MSG: [u8; 8] = [0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88];
+        static ref MSG_BE: [u8; 8] = [0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11];
     }
 
     #[test]
     fn default_pgnlibrary() {
-        assert_eq!(
-            PGNLIB_EMPTY.pgns.len(),
-            0
-        );
+        assert_eq!(PGNLIB_EMPTY.pgns.len(), 0);
     }
 
     #[test]
     fn get_spndefinition() {
         assert_eq!(
-            *PGNLIB_ONE.get_pgn(0xF004)
+            *PGNLIB_ONE
+                .get_pgn(0xF004)
                 .expect("failed to get PgnDefinition from PgnLibrary")
-                .spns.get("Engine_Speed")
+                .spns
+                .get("Engine_Speed")
                 .expect("failed to get SpnDefinition from PgnDefinition"),
             *SPNDEF
         );
@@ -679,10 +912,7 @@ mod tests {
 
     #[test]
     fn test_parse_array() {
-        assert_eq!(
-            SPNDEF.parse_message(&MSG as &[u8; 8]).unwrap(),
-            2728.5
-        );
+        assert_eq!(SPNDEF.parse_message(&MSG as &[u8; 8]).unwrap(), 2728.5);
         assert_eq!(
             SPNDEF_BE.parse_message(&MSG_BE as &[u8; 8]).unwrap(),
             2728.5
@@ -691,31 +921,18 @@ mod tests {
 
     #[test]
     fn test_parse_message() {
-        assert_eq!(
-            SPNDEF.parse_message(&MSG[..]).unwrap(),
-            2728.5
-        );
-        assert_eq!(
-            SPNDEF_BE.parse_message(&MSG_BE[..]).unwrap(),
-            2728.5
-        );
+        assert_eq!(SPNDEF.parse_message(&MSG[..]).unwrap(), 2728.5);
+        assert_eq!(SPNDEF_BE.parse_message(&MSG_BE[..]).unwrap(), 2728.5);
         assert!(SPNDEF.parse_message(&MSG[..7]).is_none());
         assert!(SPNDEF_BE.parse_message(&MSG_BE[..7]).is_none());
     }
 
     #[test]
     fn parse_message_closure() {
-        assert_eq!(
-            SPNDEF.parser()(&MSG[..]).unwrap(),
-            2728.5
-        );
-        assert_eq!(
-            SPNDEF_BE.parser()(&MSG_BE[..]).unwrap(),
-            2728.5
-        );
+        assert_eq!(SPNDEF.parser()(&MSG[..]).unwrap(), 2728.5);
+        assert_eq!(SPNDEF_BE.parser()(&MSG_BE[..]).unwrap(), 2728.5);
     }
 
-
     #[cfg(feature = "use-socketcan")]
     mod socketcan {
         extern crate socketcan;
@@ -724,39 +941,20 @@ mod tests {
 
         use socketcan::CANFrame;
 
-        lazy_static!{
-        static ref FRAME: CANFrame = CANFrame::new(
-            0,
-            &MSG[..],
-            false,
-            false
-        ).unwrap();
-        static ref FRAME_BE: CANFrame = CANFrame::new(
-            0,
-            &MSG_BE[..],
-            false,
-            false
-        ).unwrap();
+        lazy_static! {
+            static ref FRAME: CANFrame = CANFrame::new(0, &MSG[..], false, false).unwrap();
+            static ref FRAME_BE: CANFrame = CANFrame::new(0, &MSG_BE[..], false, false).unwrap();
         }
 
         #[test]
         fn parse_canframe_closure() {
-            assert_eq!(
-                SPNDEF.parser()(&FRAME as &CANFrame).unwrap(),
-                2728.5
-            );
-            assert_eq!(
-                SPNDEF_BE.parser()(&FRAME_BE as &CANFrame).unwrap(),
-                2728.5
-            );
+            assert_eq!(SPNDEF.parser()(&FRAME as &CANFrame).unwrap(), 2728.5);
+            assert_eq!(SPNDEF_BE.parser()(&FRAME_BE as &CANFrame).unwrap(), 2728.5);
         }
 
         #[test]
         fn test_parse_canframe() {
-            assert_eq!(
-                SPNDEF.parse_message(&FRAME as &CANFrame).unwrap(),
-                2728.5
-            );
+            assert_eq!(SPNDEF.parse_message(&FRAME as &CANFrame).unwrap(), 2728.5);
             assert_eq!(
                 SPNDEF_BE.parse_message(&FRAME_BE as &CANFrame).unwrap(),
                 2728.5
@@ -765,4 +963,3 @@ mod tests {
 
     }
 }
-


Test patch:
diff --git a/tests/pgnlibrary.rs b/tests/pgnlibrary.rs
index f75f9e4..fee44f6 100644
--- a/tests/pgnlibrary.rs
+++ b/tests/pgnlibrary.rs
@@ -1,28 +1,13 @@
-
 extern crate canparse;
 
-use canparse::dbc::Entry;
-use canparse::pgn::{PgnLibrary, SpnDefinition, ParseMessage};
-use std::str::FromStr;
-use std::collections::HashMap;
+use canparse::pgn::{ParseMessage, PgnLibrary, SpnDefinition};
 
 #[test]
 fn pgnlib_build_parse() {
-    let mut lib = PgnLibrary::new( HashMap::default() );
-
-    let data: String = include_bytes!("./data/sample.dbc")
-        .iter().map(|b| *b as char).collect();
-
-    // Parse db lines into PgnLibrary
-    for line in data.lines() {
-        if let Ok(entry) = Entry::from_str(&line) {
-            lib.add_entry(entry).ok();
-        }
-    }
+    let lib = PgnLibrary::from_dbc_file("./tests/data/sample.dbc").unwrap();
 
     // Pull signal definition for engine speed
-    let enginespeed_def: &SpnDefinition = lib
-        .get_spn("Engine_Speed").unwrap();
+    let enginespeed_def: &SpnDefinition = lib.get_spn("Engine_Speed").unwrap();
 
     // Parse frame containing engine speed
     let msg: [u8; 8] = [0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88];
@@ -33,10 +18,12 @@ fn pgnlib_build_parse() {
 
 #[test]
 fn pgnlib_from_dbc_file() {
-
     let lib = PgnLibrary::from_dbc_file("./tests/data/sample.dbc");
     assert!(lib.is_ok(), "PgnLibrary should have built successfully.");
 
     let lib_fail = PgnLibrary::from_dbc_file("./tests/data/sample.dbc.fail");
-    assert_eq!(lib_fail.map_err(|e| e.kind()), Err(std::io::ErrorKind::NotFound))
+    assert_eq!(
+        lib_fail.map_err(|e| e.kind()),
+        Err(std::io::ErrorKind::NotFound)
+    )
 }