Skip to content

Commit

Permalink
auto merge of rust-lang#16130 : apoelstra/rust/decode-error, r=alexcr…
Browse files Browse the repository at this point in the history
…ichton

A quick and dirty fix for rust-lang#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.
  • Loading branch information
bors committed Aug 1, 2014
2 parents b495933 + dac9a1c commit 9f0b919
Show file tree
Hide file tree
Showing 4 changed files with 40 additions and 7 deletions.
17 changes: 11 additions & 6 deletions src/librbml/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,8 @@ pub enum EbmlEncoderTag {
pub enum Error {
IntTooBig(uint),
Expected(String),
IoError(std::io::IoError)
IoError(std::io::IoError),
ApplicationError(String)
}
// --------------------------------------

Expand All @@ -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
Expand Down Expand Up @@ -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())
}
}
}

Expand Down
5 changes: 5 additions & 0 deletions src/libserialize/json.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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
Expand Down
3 changes: 3 additions & 0 deletions src/libserialize/serialize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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> {
Expand Down
22 changes: 21 additions & 1 deletion src/libuuid/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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"))
}
}
}

Expand Down Expand Up @@ -802,6 +805,23 @@ mod test {
assert_eq!(u, u2);
}

#[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;
Expand Down

0 comments on commit 9f0b919

Please sign in to comment.