about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2014-08-01 05:41:05 +0000
committerbors <bors@rust-lang.org>2014-08-01 05:41:05 +0000
commit9f0b91985fb99730f145b62f2d3b4c10f54e27f0 (patch)
tree25ecb89241b9081954eb2ea6e79d058650c25cfa
parentb495933a7fdc5e7b28ddbb058d1e2dab330ace7b (diff)
parentdac9a1c5207cb33a0b40813896b74d00bbbd1d36 (diff)
downloadrust-9f0b91985fb99730f145b62f2d3b4c10f54e27f0.tar.gz
rust-9f0b91985fb99730f145b62f2d3b4c10f54e27f0.zip
auto merge of #16130 : apoelstra/rust/decode-error, r=alexcrichton
A quick and dirty fix for #15036 until we get serious decoder reform.

Right now it is impossible for a `Decodable` to signal a decode error, for example if it has only finitely many allowed values, is a string which must be encoded a certain way, needs a valid checksum, etc. For example in the `libuuid` implementation of `Decodable` an `Option` is unwrapped, meaning that a decode of a malformed UUID will cause the task to fail.
-rw-r--r--src/librbml/lib.rs17
-rw-r--r--src/libserialize/json.rs5
-rw-r--r--src/libserialize/serialize.rs3
-rw-r--r--src/libuuid/lib.rs22
4 files changed, 40 insertions, 7 deletions
diff --git a/src/librbml/lib.rs b/src/librbml/lib.rs
index f77d36d1e06..4927a8293a4 100644
--- a/src/librbml/lib.rs
+++ b/src/librbml/lib.rs
@@ -105,7 +105,8 @@ pub enum EbmlEncoderTag {
 pub enum Error {
     IntTooBig(uint),
     Expected(String),
-    IoError(std::io::IoError)
+    IoError(std::io::IoError),
+    ApplicationError(String)
 }
 // --------------------------------------
 
@@ -119,11 +120,11 @@ pub mod reader {
 
     use serialize;
 
-    use super::{ EsVec, EsMap, EsEnum, EsVecLen, EsVecElt, EsMapLen, EsMapKey,
-        EsEnumVid, EsU64, EsU32, EsU16, EsU8, EsInt, EsI64, EsI32, EsI16, EsI8,
-        EsBool, EsF64, EsF32, EsChar, EsStr, EsMapVal, EsEnumBody, EsUint,
-        EsOpaque, EsLabel, EbmlEncoderTag, Doc, TaggedDoc, Error, IntTooBig,
-        Expected };
+    use super::{ ApplicationError, EsVec, EsMap, EsEnum, EsVecLen, EsVecElt,
+        EsMapLen, EsMapKey, EsEnumVid, EsU64, EsU32, EsU16, EsU8, EsInt, EsI64,
+        EsI32, EsI16, EsI8, EsBool, EsF64, EsF32, EsChar, EsStr, EsMapVal,
+        EsEnumBody, EsUint, EsOpaque, EsLabel, EbmlEncoderTag, Doc, TaggedDoc,
+        Error, IntTooBig, Expected };
 
     pub type DecodeResult<T> = Result<T, Error>;
     // rbml reading
@@ -636,6 +637,10 @@ pub mod reader {
             debug!("read_map_elt_val(idx={})", idx);
             self.push_doc(EsMapVal, f)
         }
+
+        fn error(&mut self, err: &str) -> Error {
+            ApplicationError(err.to_string())
+        }
     }
 }
 
diff --git a/src/libserialize/json.rs b/src/libserialize/json.rs
index 0752c68f4d0..58d69e38cc6 100644
--- a/src/libserialize/json.rs
+++ b/src/libserialize/json.rs
@@ -257,6 +257,7 @@ pub enum DecoderError {
     ExpectedError(String, String),
     MissingFieldError(String),
     UnknownVariantError(String),
+    ApplicationError(String)
 }
 
 /// Returns a readable error string for a given error code.
@@ -2071,6 +2072,10 @@ impl ::Decoder<DecoderError> for Decoder {
         debug!("read_map_elt_val(idx={})", idx);
         f(self)
     }
+
+    fn error(&mut self, err: &str) -> DecoderError {
+        ApplicationError(err.to_string())
+    }
 }
 
 /// A trait for converting values to JSON
diff --git a/src/libserialize/serialize.rs b/src/libserialize/serialize.rs
index 03d9445b9b9..95c677bcbd7 100644
--- a/src/libserialize/serialize.rs
+++ b/src/libserialize/serialize.rs
@@ -163,6 +163,9 @@ pub trait Decoder<E> {
     fn read_map<T>(&mut self, f: |&mut Self, uint| -> Result<T, E>) -> Result<T, E>;
     fn read_map_elt_key<T>(&mut self, idx: uint, f: |&mut Self| -> Result<T, E>) -> Result<T, E>;
     fn read_map_elt_val<T>(&mut self, idx: uint, f: |&mut Self| -> Result<T, E>) -> Result<T, E>;
+
+    // Failure
+    fn error(&mut self, err: &str) -> E;
 }
 
 pub trait Encodable<S:Encoder<E>, E> {
diff --git a/src/libuuid/lib.rs b/src/libuuid/lib.rs
index 2ffed792abd..d922dde6f85 100644
--- a/src/libuuid/lib.rs
+++ b/src/libuuid/lib.rs
@@ -501,7 +501,10 @@ impl<T: Encoder<E>, E> Encodable<T, E> for Uuid {
 impl<T: Decoder<E>, E> Decodable<T, E> for Uuid {
     /// Decode a UUID from a string
     fn decode(d: &mut T) -> Result<Uuid, E> {
-        Ok(from_str(try!(d.read_str()).as_slice()).unwrap())
+        match from_str(try!(d.read_str()).as_slice()) {
+            Some(decode) => Ok(decode),
+            None => Err(d.error("Unable to decode UUID"))
+        }
     }
 }
 
@@ -803,6 +806,23 @@ mod test {
     }
 
     #[test]
+    fn test_bad_decode() {
+        use serialize::json;
+        use serialize::{Encodable, Decodable};
+
+        let js_good = json::String("a1a2a3a4a5a6a7a8a1a2a3a4a5a6a7a8".to_string());
+        let js_bad1 = json::String("a1a2a3a4a5a6a7a8a1a2a3a4a5a6a7ah".to_string());
+        let js_bad2 = json::String("a1a2a3a4a5a6a7a8a1a2a3a4a5a6a7a".to_string());
+
+        let u_good: Result<Uuid, _> = Decodable::decode(&mut json::Decoder::new(js_good));
+        let u_bad1: Result<Uuid, _> = Decodable::decode(&mut json::Decoder::new(js_bad1));
+        let u_bad2: Result<Uuid, _> = Decodable::decode(&mut json::Decoder::new(js_bad2));
+        assert!(u_good.is_ok());
+        assert!(u_bad1.is_err());
+        assert!(u_bad2.is_err());
+    }
+
+    #[test]
     fn test_iterbytes_impl_for_uuid() {
         use std::collections::HashSet;
         let mut set = HashSet::new();