Skip to content

Commit 9f0b919

Browse files
committed
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.
2 parents b495933 + dac9a1c commit 9f0b919

File tree

4 files changed

+40
-7
lines changed

4 files changed

+40
-7
lines changed

src/librbml/lib.rs

+11-6
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,8 @@ pub enum EbmlEncoderTag {
105105
pub enum Error {
106106
IntTooBig(uint),
107107
Expected(String),
108-
IoError(std::io::IoError)
108+
IoError(std::io::IoError),
109+
ApplicationError(String)
109110
}
110111
// --------------------------------------
111112

@@ -119,11 +120,11 @@ pub mod reader {
119120

120121
use serialize;
121122

122-
use super::{ EsVec, EsMap, EsEnum, EsVecLen, EsVecElt, EsMapLen, EsMapKey,
123-
EsEnumVid, EsU64, EsU32, EsU16, EsU8, EsInt, EsI64, EsI32, EsI16, EsI8,
124-
EsBool, EsF64, EsF32, EsChar, EsStr, EsMapVal, EsEnumBody, EsUint,
125-
EsOpaque, EsLabel, EbmlEncoderTag, Doc, TaggedDoc, Error, IntTooBig,
126-
Expected };
123+
use super::{ ApplicationError, EsVec, EsMap, EsEnum, EsVecLen, EsVecElt,
124+
EsMapLen, EsMapKey, EsEnumVid, EsU64, EsU32, EsU16, EsU8, EsInt, EsI64,
125+
EsI32, EsI16, EsI8, EsBool, EsF64, EsF32, EsChar, EsStr, EsMapVal,
126+
EsEnumBody, EsUint, EsOpaque, EsLabel, EbmlEncoderTag, Doc, TaggedDoc,
127+
Error, IntTooBig, Expected };
127128

128129
pub type DecodeResult<T> = Result<T, Error>;
129130
// rbml reading
@@ -636,6 +637,10 @@ pub mod reader {
636637
debug!("read_map_elt_val(idx={})", idx);
637638
self.push_doc(EsMapVal, f)
638639
}
640+
641+
fn error(&mut self, err: &str) -> Error {
642+
ApplicationError(err.to_string())
643+
}
639644
}
640645
}
641646

src/libserialize/json.rs

+5
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,7 @@ pub enum DecoderError {
257257
ExpectedError(String, String),
258258
MissingFieldError(String),
259259
UnknownVariantError(String),
260+
ApplicationError(String)
260261
}
261262

262263
/// Returns a readable error string for a given error code.
@@ -2071,6 +2072,10 @@ impl ::Decoder<DecoderError> for Decoder {
20712072
debug!("read_map_elt_val(idx={})", idx);
20722073
f(self)
20732074
}
2075+
2076+
fn error(&mut self, err: &str) -> DecoderError {
2077+
ApplicationError(err.to_string())
2078+
}
20742079
}
20752080

20762081
/// A trait for converting values to JSON

src/libserialize/serialize.rs

+3
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,9 @@ pub trait Decoder<E> {
163163
fn read_map<T>(&mut self, f: |&mut Self, uint| -> Result<T, E>) -> Result<T, E>;
164164
fn read_map_elt_key<T>(&mut self, idx: uint, f: |&mut Self| -> Result<T, E>) -> Result<T, E>;
165165
fn read_map_elt_val<T>(&mut self, idx: uint, f: |&mut Self| -> Result<T, E>) -> Result<T, E>;
166+
167+
// Failure
168+
fn error(&mut self, err: &str) -> E;
166169
}
167170

168171
pub trait Encodable<S:Encoder<E>, E> {

src/libuuid/lib.rs

+21-1
Original file line numberDiff line numberDiff line change
@@ -501,7 +501,10 @@ impl<T: Encoder<E>, E> Encodable<T, E> for Uuid {
501501
impl<T: Decoder<E>, E> Decodable<T, E> for Uuid {
502502
/// Decode a UUID from a string
503503
fn decode(d: &mut T) -> Result<Uuid, E> {
504-
Ok(from_str(try!(d.read_str()).as_slice()).unwrap())
504+
match from_str(try!(d.read_str()).as_slice()) {
505+
Some(decode) => Ok(decode),
506+
None => Err(d.error("Unable to decode UUID"))
507+
}
505508
}
506509
}
507510

@@ -802,6 +805,23 @@ mod test {
802805
assert_eq!(u, u2);
803806
}
804807

808+
#[test]
809+
fn test_bad_decode() {
810+
use serialize::json;
811+
use serialize::{Encodable, Decodable};
812+
813+
let js_good = json::String("a1a2a3a4a5a6a7a8a1a2a3a4a5a6a7a8".to_string());
814+
let js_bad1 = json::String("a1a2a3a4a5a6a7a8a1a2a3a4a5a6a7ah".to_string());
815+
let js_bad2 = json::String("a1a2a3a4a5a6a7a8a1a2a3a4a5a6a7a".to_string());
816+
817+
let u_good: Result<Uuid, _> = Decodable::decode(&mut json::Decoder::new(js_good));
818+
let u_bad1: Result<Uuid, _> = Decodable::decode(&mut json::Decoder::new(js_bad1));
819+
let u_bad2: Result<Uuid, _> = Decodable::decode(&mut json::Decoder::new(js_bad2));
820+
assert!(u_good.is_ok());
821+
assert!(u_bad1.is_err());
822+
assert!(u_bad2.is_err());
823+
}
824+
805825
#[test]
806826
fn test_iterbytes_impl_for_uuid() {
807827
use std::collections::HashSet;

0 commit comments

Comments
 (0)