1use internal::{
2 dirs::{
3 corpus_directory_from_args_type, generic_args_directory_from_args_type,
4 impl_generic_args_directory_from_args_type,
5 },
6 serde_format,
7};
8use serde::{Serialize, de::DeserializeOwned};
9use sha1::{Digest, Sha1};
10use std::{
11 any::type_name,
12 env,
13 fmt::{self, Debug, Formatter},
14 fs::{create_dir_all, write},
15 io::{self, Read, Write},
16 marker::PhantomData,
17 path::Path,
18 sync::Once,
19};
20
21pub use num_traits;
22
23pub mod traits;
24
25struct DebugUnimplemented<T>(PhantomData<T>);
29
30impl<T> Debug for DebugUnimplemented<T> {
31 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
34 const PAT: &str = "TryDebug<";
35 let type_name = type_name::<T>();
36 let pos = type_name.find(PAT).unwrap() + PAT.len();
37 write!(
38 f,
39 "<unknown of type {}>",
40 type_name[pos..].strip_suffix('>').unwrap()
41 )
42 }
43}
44
45pub trait TryDebugFallback {
46 fn apply<U>(&self, f: &mut dyn FnMut(&dyn Debug) -> U) -> U;
47}
48
49impl<T> TryDebugFallback for T {
50 fn apply<U>(&self, f: &mut dyn FnMut(&dyn Debug) -> U) -> U {
51 f(&DebugUnimplemented::<Self>(PhantomData))
52 }
53}
54
55pub struct TryDebug<'a, T>(pub &'a T);
56
57impl<T: Debug> TryDebug<'_, T> {
58 pub fn apply<U>(&self, f: &mut dyn FnMut(&dyn Debug) -> U) -> U {
59 f(self.0)
60 }
61}
62
63#[macro_export]
68macro_rules! auto_impl {
69 ($ty:ty, $trait:path, $expr:expr) => {{
70 trait AutoFallback<U> {
71 fn auto() -> Vec<U>;
72 }
73
74 impl<T, U> AutoFallback<U> for T {
75 #[must_use]
76 fn auto() -> Vec<U> {
77 vec![]
78 }
79 }
80
81 struct Auto<T>(std::marker::PhantomData<T>);
82
83 impl<T: $trait> Auto<T> {
84 #[must_use]
85 pub fn auto() -> Vec<T> {
86 $expr
87 }
88 }
89
90 Auto::<$ty>::auto() as Vec<$ty>
91 }};
92}
93
94#[macro_export]
95macro_rules! auto {
96 ($ty:ty) => {{
97 let xss = [
98 $crate::auto_impl!(
99 $ty,
100 $crate::num_traits::bounds::Bounded,
101 vec![T::min_value(), T::max_value()]
102 ),
103 $crate::auto_impl!($ty, Default, vec![T::default()]),
104 $crate::auto_impl!(
105 $ty,
106 $crate::traits::MaxValueSubOne,
107 vec![T::max_value_sub_one()]
108 ),
109 $crate::auto_impl!($ty, $crate::traits::Middle, vec![T::low(), T::high()]),
110 $crate::auto_impl!(
111 $ty,
112 $crate::traits::MinValueAddOne,
113 vec![T::min_value_add_one()]
114 ),
115 ];
116 IntoIterator::into_iter(xss).flatten()
117 }};
118}
119
120pub fn warn_if_test_fuzz_not_enabled() {
121 static ONCE: Once = Once::new();
122 ONCE.call_once(|| {
123 if !test_fuzz_enabled() {
124 #[allow(clippy::explicit_write)]
125 writeln!(
126 io::stderr(),
127 "If you are trying to run a test-fuzz-generated fuzzing harness, be sure to run \
128 with `TEST_FUZZ=1`."
129 )
130 .unwrap();
131 }
132 });
133}
134
135#[must_use]
136pub fn test_fuzz_enabled() -> bool {
137 enabled("")
138}
139
140#[must_use]
141pub fn display_enabled() -> bool {
142 enabled("DISPLAY")
143}
144
145#[must_use]
146pub fn pretty_print_enabled() -> bool {
147 enabled("PRETTY_PRINT")
148}
149
150#[must_use]
151pub fn replay_enabled() -> bool {
152 enabled("REPLAY")
153}
154
155#[must_use]
156pub fn write_enabled() -> bool {
157 enabled("WRITE")
158}
159
160#[must_use]
161fn enabled(opt: &str) -> bool {
162 let key = "TEST_FUZZ".to_owned() + if opt.is_empty() { "" } else { "_" } + opt;
163 env::var(key).is_ok_and(|value| value != "0")
164}
165
166pub fn write_impl_generic_args<T>(args: &[&str]) {
167 let impl_generic_args = impl_generic_args_directory_from_args_type::<T>();
168 let data = args.join(", ");
169 write_data(&impl_generic_args, data.as_bytes()).unwrap();
170}
171
172pub fn write_generic_args<T>(args: &[&str]) {
173 let generic_args = generic_args_directory_from_args_type::<T>();
174 let data = args.join(", ");
175 write_data(&generic_args, data.as_bytes()).unwrap();
176}
177
178pub fn write_args<T: Serialize>(args: &T) {
179 let corpus = corpus_directory_from_args_type::<T>();
180 let data = serde_format::serialize(args);
181 write_data(&corpus, &data).unwrap();
182}
183
184pub fn write_data(dir: &Path, data: &[u8]) -> io::Result<()> {
185 create_dir_all(dir).unwrap_or_default();
186 let hex = {
187 let digest = Sha1::digest(data);
188 hex::encode(digest)
189 };
190 let path_buf = dir.join(hex);
191 write(path_buf, data)
192}
193
194pub fn read_args<T: DeserializeOwned, R: Read>(reader: R) -> Option<T> {
195 serde_format::deserialize(reader)
196}