Skip to content

Commit

Permalink
[Decoding] Rework the object unwrapping. (!API change)
Browse files Browse the repository at this point in the history
This should improve the error handling experience during decoding,
especially if unexpected object types appear as it allows to react
on the unexpected type.

Also changes the names of the related functions to express their
new behavior.
  • Loading branch information
0ndorio committed Feb 6, 2019
1 parent f65d272 commit fdacee9
Show file tree
Hide file tree
Showing 4 changed files with 218 additions and 143 deletions.
22 changes: 13 additions & 9 deletions examples/decode_torrent.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,31 +103,35 @@ struct Info {

/// Treats object as bencode integer.
fn decode_integer_as_string(field_name: &str, data: Object) -> Result<String, Error> {
let error = || Error::malformed_field("Integer", field_name);
let error = |_| Err(Error::malformed_field("Integer", field_name));

let number_string = data.integer_str_or_else_err(error)?;
let number_string = data.integer_or_else(error)?;
Ok(number_string.to_owned())
}

/// Treats object as byte string.
fn decode_bytes_as_string(field_name: &str, data: Object) -> Result<String, Error> {
let error = || Error::malformed_field("String", field_name);
let error = |_| Err(Error::malformed_field("String", field_name));

let bytes = data.bytes_or_else_err(error)?;
let text = str::from_utf8(bytes).map_err(|_| error())?;
let bytes = data.bytes_or_else(error)?;
let text = str::from_utf8(bytes).map_err(|_| Error::malformed_field("String", field_name))?;

Ok(text.to_owned())
}

/// Treats object as byte string.
fn decode_bytes_as_vec(field_name: &str, data: Object) -> Result<Vec<u8>, Error> {
let result = data.bytes_or_err(Error::malformed_field("String", field_name))?;
Ok(result.to_vec())
let error = |_| Err(Error::malformed_field("String", field_name));

let bytes = data.bytes_or_else(error)?;
Ok(bytes.to_vec())
}

/// Treats object as list of strings.
fn decode_list_of_strings(field_name: &str, data: Object) -> Result<Vec<String>, Error> {
let mut list_dec = data.list_or_err(Error::malformed_field("List", field_name))?;
let error = |_| Err(Error::malformed_field("List", field_name));

let mut list_dec = data.list_or_else(error)?;
let mut list = Vec::new();

while let Some(object) = list_dec.next_object()? {
Expand All @@ -149,7 +153,7 @@ fn decode_info(field_name: &str, data: Object) -> Result<Info, Error> {
let mut pieces = None;

let mut dict_dec =
data.dictionary_or_err(Error::malformed_field("Info Dictionary", field_name))?;
data.dictionary_or_else(|_| Err(Error::malformed_field("Info Dictionary", field_name)))?;

while let Some(pair) = dict_dec.next_pair()? {
match pair {
Expand Down
140 changes: 92 additions & 48 deletions src/decoding/decoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -514,117 +514,138 @@ mod test {

#[test]
fn bytes_or_should_work_on_bytes() {
assert_eq!(Ok(&b"foo"[..]), Object::Bytes(b"foo").bytes_or_err(0));
assert_eq!(
Ok(&b"foo"[..]),
Object::Bytes(b"foo").bytes_or(Err("failure"))
);
}

#[test]
fn bytes_or_should_not_work_on_other_types() {
assert_eq!(Err(0), Object::Integer("123").bytes_or_err(0));
assert_eq!(
Err("failure"),
Object::Integer("123").bytes_or(Err("failure"))
);

let mut list_decoder = Decoder::new(b"le");
assert_eq!(
Err(0),
list_decoder.next_object().unwrap().unwrap().bytes_or_err(0)
Err("failure"),
list_decoder
.next_object()
.unwrap()
.unwrap()
.bytes_or(Err("failure"))
);
let mut dict_decoder = Decoder::new(b"de");
assert_eq!(
Err(0),
dict_decoder.next_object().unwrap().unwrap().bytes_or_err(0)
Err("failure"),
dict_decoder
.next_object()
.unwrap()
.unwrap()
.bytes_or(Err("failure"))
);
}

#[test]
fn bytes_or_else_should_work_on_bytes() {
assert_eq!(
Ok(&b"foo"[..]),
Object::Bytes(b"foo").bytes_or_else_err(|| 0)
Object::Bytes(b"foo").bytes_or_else(|_| Err("failure"))
);
}

#[test]
fn bytes_or_else_should_not_work_on_other_types() {
assert_eq!(Err(0), Object::Integer("123").bytes_or_else_err(|| 0));
assert_eq!(
Err("failure"),
Object::Integer("123").bytes_or_else(|_| Err("failure"))
);
let mut list_decoder = Decoder::new(b"le");
assert_eq!(
Err(0),
Err("failure"),
list_decoder
.next_object()
.unwrap()
.unwrap()
.bytes_or_else_err(|| 0)
.bytes_or_else(|_| Err("failure"))
);
let mut dict_decoder = Decoder::new(b"de");
assert_eq!(
Err(0),
Err("failure"),
dict_decoder
.next_object()
.unwrap()
.unwrap()
.bytes_or_else_err(|| 0)
.bytes_or_else(|_| Err("failure"))
);
}

#[test]
fn integer_str_or_should_work_on_int() {
assert_eq!(
Ok(&"123"[..]),
Object::Integer("123").integer_str_or_err(-1)
Object::Integer("123").integer_or(Err("failure"))
);
}

#[test]
fn integer_str_or_should_not_work_on_other_types() {
assert_eq!(Err(-1), Object::Bytes(b"foo").integer_str_or_err(-1));
assert_eq!(
Err("failure"),
Object::Bytes(b"foo").integer_or(Err("failure"))
);
let mut list_decoder = Decoder::new(b"le");
assert_eq!(
Err(-1),
Err("failure"),
list_decoder
.next_object()
.unwrap()
.unwrap()
.integer_str_or_err(-1)
.integer_or(Err("failure"))
);
let mut dict_decoder = Decoder::new(b"de");
assert_eq!(
Err(-1),
Err("failure"),
dict_decoder
.next_object()
.unwrap()
.unwrap()
.integer_str_or_err(-1)
.integer_or(Err("failure"))
);
}

#[test]
fn integer_str_or_else_should_work_on_int() {
assert_eq!(
Ok(&"123"[..]),
Object::Integer("123").integer_str_or_else_err(|| -1)
Object::Integer("123").integer_or_else(|_| Err("failure"))
);
}

#[test]
fn integer_str_or_else_should_not_work_on_other_types() {
assert_eq!(
Err(-1),
Object::Bytes(b"foo").integer_str_or_else_err(|| -1)
Err("failure"),
Object::Bytes(b"foo").integer_or_else(|_| Err("failure"))
);
let mut list_decoder = Decoder::new(b"le");
assert_eq!(
Err(-1),
Err("failure"),
list_decoder
.next_object()
.unwrap()
.unwrap()
.integer_str_or_else_err(|| -1)
.integer_or_else(|_| Err("failure"))
);
let mut dict_decoder = Decoder::new(b"de");
assert_eq!(
Err(-1),
Err("failure"),
dict_decoder
.next_object()
.unwrap()
.unwrap()
.integer_str_or_else_err(|| -1)
.integer_or_else(|_| Err("failure"))
);
}

Expand All @@ -635,22 +656,28 @@ mod test {
.next_object()
.unwrap()
.unwrap()
.list_or_err(0)
.list_or(Err("failure"))
.is_ok());
}
#[test]
fn list_or_should_not_work_on_other_types() {
assert_eq!(0, Object::Bytes(b"foo").list_or_err(0).unwrap_err());
assert_eq!(0, Object::Integer("foo").list_or_err(0).unwrap_err());
assert_eq!(
"failure",
Object::Bytes(b"foo").list_or(Err("failure")).unwrap_err()
);
assert_eq!(
"failure",
Object::Integer("foo").list_or(Err("failure")).unwrap_err()
);

let mut dict_decoder = Decoder::new(b"de");
assert_eq!(
0,
"failure",
dict_decoder
.next_object()
.unwrap()
.unwrap()
.list_or_err(0)
.list_or(Err("failure"))
.unwrap_err()
);
}
Expand All @@ -662,25 +689,32 @@ mod test {
.next_object()
.unwrap()
.unwrap()
.list_or_else_err(|| 0)
.list_or_else(|_| Err("failure"))
.is_ok());
}
#[test]
fn list_or_else_should_not_work_on_other_types() {
assert_eq!(0, Object::Bytes(b"foo").list_or_else_err(|| 0).unwrap_err());
assert_eq!(
0,
Object::Integer("foo").list_or_else_err(|| 0).unwrap_err()
"failure",
Object::Bytes(b"foo")
.list_or_else(|_| Err("failure"))
.unwrap_err()
);
assert_eq!(
"failure",
Object::Integer("foo")
.list_or_else(|_| Err("failure"))
.unwrap_err()
);

let mut dict_decoder = Decoder::new(b"de");
assert_eq!(
0,
"failure",
dict_decoder
.next_object()
.unwrap()
.unwrap()
.list_or_else_err(|| 0)
.list_or_else(|_| Err("failure"))
.unwrap_err()
);
}
Expand All @@ -692,23 +726,33 @@ mod test {
.next_object()
.unwrap()
.unwrap()
.dictionary_or_err(0)
.dictionary_or(Err("failure"))
.is_ok());
}

#[test]
fn dictionary_or_should_not_work_on_other_types() {
assert_eq!(0, Object::Bytes(b"foo").dictionary_or_err(0).unwrap_err());
assert_eq!(0, Object::Integer("foo").dictionary_or_err(0).unwrap_err());
assert_eq!(
"failure",
Object::Bytes(b"foo")
.dictionary_or(Err("failure"))
.unwrap_err()
);
assert_eq!(
"failure",
Object::Integer("foo")
.dictionary_or(Err("failure"))
.unwrap_err()
);

let mut list_decoder = Decoder::new(b"le");
assert_eq!(
0,
"failure",
list_decoder
.next_object()
.unwrap()
.unwrap()
.dictionary_or_err(0)
.dictionary_or(Err("failure"))
.unwrap_err()
);
}
Expand All @@ -720,33 +764,33 @@ mod test {
.next_object()
.unwrap()
.unwrap()
.dictionary_or_else_err(|| 0)
.dictionary_or_else(|_| Err("failure"))
.is_ok());
}

#[test]
fn dictionary_or_else_should_not_work_on_other_types() {
assert_eq!(
0,
"failure",
Object::Bytes(b"foo")
.dictionary_or_else_err(|| 0)
.dictionary_or_else(|_| Err("failure"))
.unwrap_err()
);
assert_eq!(
0,
"failure",
Object::Integer("foo")
.dictionary_or_else_err(|| 0)
.dictionary_or_else(|_| Err("failure"))
.unwrap_err()
);

let mut list_decoder = Decoder::new(b"le");
assert_eq!(
0,
"failure",
list_decoder
.next_object()
.unwrap()
.unwrap()
.dictionary_or_else_err(|| 0)
.dictionary_or_else(|_| Err("failure"))
.unwrap_err()
);
}
Expand Down
Loading

0 comments on commit fdacee9

Please sign in to comment.