Skip to main content

zoe/data/arbitrary/
mod.rs

1//! A module providing implementations of
2//! [`Arbitrary`](https://docs.rs/arbitrary/latest/arbitrary/trait.Arbitrary.html)
3//! for many of *Zoe*'s types, as well as other tools for generating arbitrary
4//! testing data.
5//!
6//! *Zoe*'s implementations of [`Arbitrary`] generate the data with the least
7//! guarantees, excluding:
8//!
9//! - Safety guarantees, such as graphic ASCII for [`QualityScores`]
10//! - Some basic guarantees that would otherwise prevent any utility, such as
11//!   [`ThreeBitKmerSet`] containing the encoded k-mers of the proper size
12//!
13//! To generate data with stronger assumptions, *Zoe* uses the following
14//! framework:
15//!
16//! 1. In your fuzz test file, define an input struct containing the arbitrary
17//!    types you would like to generate. This could be a wrapper around a single
18//!    *Zoe* type, or it could include multiple types.
19//! 2. Begin implementing [`Arbitrary`] for the input struct. For any types not
20//!    requiring additional guarantees, generate these using
21//!    [`Arbitrary::arbitrary`].
22//! 3. For any types requiring more guarantees, find the corresponding
23//!    specifications struct in this module. For example, to specify the
24//!    guarantees for a CIGAR string, use [`CigarSpecs`].
25//! 4. Within the implementation, initialize the specifications struct. Any
26//!    guarantees that should be upheld will need to be specified, and then
27//!    `..Default::default()` can be used to initialize the rest of the fields
28//!    (the default for the specification structs is no guarantees).
29//! 5. Call [`ArbitrarySpecs::make_arbitrary`] on the specifications struct to
30//!    generate the arbitrary data.
31//!
32//! [`QualityScores`]: crate::prelude::QualityScores
33//! [`ThreeBitKmerSet`]: crate::kmer::encoders::three_bit::ThreeBitKmerSet
34
35use arbitrary::{Arbitrary, Result, Unstructured};
36
37mod alignment;
38mod byte;
39mod collections;
40mod kmer;
41mod math;
42mod records;
43mod string;
44mod types;
45
46pub use alignment::*;
47pub use byte::*;
48pub use collections::*;
49pub use math::*;
50pub use records::*;
51pub use string::*;
52pub use types::*;
53
54/// Implemented on structs which provide specifications for how to generate
55/// arbitrary data.
56///
57/// See [`arbitrary`] for more details.
58///
59/// [`arbitrary`]: crate::data::arbitrary
60pub trait ArbitrarySpecs<'a>: Sized {
61    /// The type of the arbitrary data that this specification generates.
62    type Output;
63
64    /// Generates arbitrary data conforming to the given specifications.
65    ///
66    /// ## Errors
67    ///
68    /// Any errors from the underlying [`arbitrary`] calls are propagated.
69    ///
70    /// [`arbitrary`]: Arbitrary::arbitrary
71    fn make_arbitrary(&self, u: &mut Unstructured<'a>) -> Result<Self::Output>;
72
73    /// Generates an iterator of arbitrary data, each item conforming to the
74    /// given specifications.
75    #[inline]
76    fn make_arbitrary_iter<'b>(&self, u: &'b mut Unstructured<'a>) -> MakeArbitraryIter<'a, 'b, '_, Self> {
77        MakeArbitraryIter { specs: self, u }
78    }
79}
80
81impl<'a, S> ArbitrarySpecs<'a> for Option<S>
82where
83    S: ArbitrarySpecs<'a, Output: Arbitrary<'a>>,
84{
85    type Output = S::Output;
86
87    #[inline]
88    fn make_arbitrary(&self, u: &mut Unstructured<'a>) -> Result<Self::Output> {
89        if let Some(specs) = self {
90            specs.make_arbitrary(u)
91        } else {
92            S::Output::arbitrary(u)
93        }
94    }
95}
96
97/// Utility iterator produced by [`ArbitrarySpecs::make_arbitrary_iter`].
98///
99/// Each call to [`next`] arbitrarily determines whether to continue the
100/// iterator or end it. The iterator is not fused.
101///
102/// [`next`]: Iterator::next
103#[must_use = "iterators are lazy and do nothing unless consumed"]
104pub struct MakeArbitraryIter<'a, 'b, 'c, Specs> {
105    specs: &'c Specs,
106    u:     &'b mut Unstructured<'a>,
107}
108
109impl<'a, Specs: ArbitrarySpecs<'a>> Iterator for MakeArbitraryIter<'a, '_, '_, Specs> {
110    type Item = Result<Specs::Output>;
111
112    #[inline]
113    fn next(&mut self) -> Option<Result<Specs::Output>> {
114        let keep_going = self.u.arbitrary().unwrap_or(false);
115        if keep_going {
116            Some(self.specs.make_arbitrary(self.u))
117        } else {
118            None
119        }
120    }
121}