Skip to content

Commit

Permalink
Empty Unstructured::choose param should return Err, not panic.
Browse files Browse the repository at this point in the history
  • Loading branch information
frewsxcv committed Feb 6, 2021
1 parent 393edc3 commit 225d439
Show file tree
Hide file tree
Showing 3 changed files with 29 additions and 12 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -37,6 +37,7 @@ Released YYYY-MM-DD.
### Changed

* Rename `Unstructured#get_bytes` to `Unstructured#bytes`. [#70](https://github.com/rust-fuzz/arbitrary/pull/70)
* Passing an empty slice of choices to `Unstructured#choose` returns an error. Previously it would panic. [71](https://github.com/rust-fuzz/arbitrary/pull/71)

### Removed

Expand Down
6 changes: 6 additions & 0 deletions src/error.rs
Expand Up @@ -4,6 +4,8 @@ use std::{error, fmt};
#[derive(Debug, Clone, Copy)]
#[non_exhaustive]
pub enum Error {
/// No choices were provided to the Unstructured::choose call
EmptyChoose,
/// There was not enough underlying data to fulfill some request for raw
/// bytes.
NotEnoughData,
Expand All @@ -14,6 +16,10 @@ pub enum Error {
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Error::EmptyChoose => write!(
f,
"`arbitrary::Unstructured::choose` must be given a non-empty set of choices"
),
Error::NotEnoughData => write!(
f,
"There is not enough underlying raw data to construct an `Arbitrary` instance"
Expand Down
34 changes: 22 additions & 12 deletions src/unstructured.rs
Expand Up @@ -345,29 +345,39 @@ impl<'a> Unstructured<'a> {
/// This should only be used inside of `Arbitrary` implementations.
///
/// Returns an error if there is not enough underlying data to make a
/// choice.
/// choice or if no choices are provided.
///
/// # Panics
/// # Examples
///
/// Panics if `choices` is empty.
/// Selecting from an array of choices:
///
/// # Example
/// ```
/// use arbitrary::Unstructured;
///
/// let mut u = Unstructured::new(&[1, 2, 3, 4, 5, 6, 7, 8, 9, 0]);
/// let choices = ['a', 'b', 'c', 'd', 'e', 'f', 'g'];
///
/// let choice = u.choose(&choices).unwrap();
///
/// println!("chose {}", choice);
/// ```
///
/// An error is returned if no choices are provided:
///
/// ```
/// use arbitrary::Unstructured;
///
/// let mut u = Unstructured::new(&[1, 2, 3, 4, 5, 6, 7, 8, 9, 0]);
/// let choices: [char; 0] = [];
///
/// let choices = ['a', 'b', 'c', 'd', 'e', 'f', 'g'];
/// if let Ok(ch) = u.choose(&choices) {
/// println!("chose {}", ch);
/// }
/// let result = u.choose(&choices);
///
/// assert!(result.is_err());
/// ```
pub fn choose<'b, T>(&mut self, choices: &'b [T]) -> Result<&'b T> {
assert!(
!choices.is_empty(),
"`arbitrary::Unstructured::choose` must be given a non-empty set of choices"
);
if choices.is_empty() {
return Err(Error::EmptyChoose);
}
let idx = self.int_in_range(0..=choices.len() - 1)?;
Ok(&choices[idx])
}
Expand Down

0 comments on commit 225d439

Please sign in to comment.