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}