domain_key/
arbitrary_impls.rs1#[cfg(not(feature = "std"))]
17use alloc::vec::Vec;
18
19use core::num::NonZeroU64;
20
21use arbitrary::{Arbitrary, Unstructured};
22use smartstring::alias::String as SmartString;
23
24use crate::domain::{IdDomain, KeyDomain};
25use crate::id::Id;
26use crate::key::Key;
27
28impl<'a, D: IdDomain> Arbitrary<'a> for Id<D> {
31 fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
32 let raw: u64 = u.arbitrary()?;
33 Ok(Id::from_non_zero(
36 NonZeroU64::new(raw | 1).expect("raw | 1 is always non-zero"),
37 ))
38 }
39
40 fn size_hint(depth: usize) -> (usize, Option<usize>) {
41 u64::size_hint(depth)
42 }
43}
44
45#[cfg(feature = "uuid")]
54impl<'a, D: crate::domain::UuidDomain> Arbitrary<'a> for crate::uuid::Uuid<D> {
55 fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
56 let bytes: [u8; 16] = u.arbitrary()?;
57 Ok(Self::from_bytes(bytes))
58 }
59
60 fn size_hint(depth: usize) -> (usize, Option<usize>) {
61 <[u8; 16]>::size_hint(depth)
62 }
63}
64
65#[cfg(feature = "ulid")]
74impl<'a, D: crate::domain::UlidDomain> Arbitrary<'a> for crate::ulid::Ulid<D> {
75 fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
76 let bytes: [u8; 16] = u.arbitrary()?;
77 Ok(Self::from_bytes(bytes))
78 }
79
80 fn size_hint(depth: usize) -> (usize, Option<usize>) {
81 <[u8; 16]>::size_hint(depth)
82 }
83}
84
85impl<'a, D: KeyDomain> Arbitrary<'a> for Key<D> {
88 fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
100 if D::HAS_CUSTOM_VALIDATION {
102 let examples = D::examples();
103 if !examples.is_empty() {
104 let s: &&str = u.choose(examples)?;
105 return Ok(Key::from(SmartString::from(*s)));
107 }
108 }
109
110 let alphabet: Vec<char> = (' '..='~')
112 .filter(|&c| D::allowed_characters(c))
113 .collect();
114
115 if alphabet.is_empty() {
116 return Err(arbitrary::Error::EmptyChoose);
117 }
118
119 let start_chars: Vec<char> = alphabet
120 .iter()
121 .copied()
122 .filter(|&c| D::allowed_start_character(c))
123 .collect();
124
125 if start_chars.is_empty() {
126 return Err(arbitrary::Error::EmptyChoose);
127 }
128
129 let min_len = D::min_length().max(1);
130 let max_len = D::MAX_LENGTH.max(min_len);
131 let length: usize = u.int_in_range(min_len..=max_len)?;
132
133 let mut result = SmartString::new();
134 let mut prev: Option<char> = None;
135
136 for pos in 0..length {
137 let is_last = pos == length - 1;
138
139 let candidates: Vec<char> = match pos {
140 0 if is_last => start_chars
142 .iter()
143 .copied()
144 .filter(|&c| D::allowed_end_character(c))
145 .collect(),
146 0 => start_chars.clone(),
148 _ => {
150 let p = prev.expect("prev is set after position 0");
151 alphabet
152 .iter()
153 .copied()
154 .filter(|&c| D::allowed_consecutive_characters(p, c))
155 .filter(|&c| !is_last || D::allowed_end_character(c))
156 .collect()
157 }
158 };
159
160 if candidates.is_empty() {
161 return Err(arbitrary::Error::EmptyChoose);
162 }
163
164 let &c = u.choose(&candidates)?;
165 result.push(c);
166 prev = Some(c);
167 }
168
169 Key::new(result.as_str()).map_err(|_| arbitrary::Error::IncorrectFormat)
170 }
171
172 fn size_hint(_depth: usize) -> (usize, Option<usize>) {
173 (1, Some(D::MAX_LENGTH * 4 + 8))
175 }
176}
177
178#[cfg(test)]
179mod tests {
180 use super::*;
181
182 #[derive(Debug)]
184 struct SimpleDomain;
185
186 impl crate::domain::Domain for SimpleDomain {
187 const DOMAIN_NAME: &'static str = "simple";
188 }
189
190 impl KeyDomain for SimpleDomain {
191 const MAX_LENGTH: usize = 16;
192
193 fn allowed_characters(c: char) -> bool {
194 c.is_ascii_lowercase()
195 }
196 }
197
198 #[derive(Debug)]
200 struct ExamplesDomain;
201
202 impl crate::domain::Domain for ExamplesDomain {
203 const DOMAIN_NAME: &'static str = "examples";
204 }
205
206 impl KeyDomain for ExamplesDomain {
207 const MAX_LENGTH: usize = 32;
208 const HAS_CUSTOM_VALIDATION: bool = true;
209
210 fn examples() -> &'static [&'static str] {
211 &["foo", "bar", "baz"]
212 }
213
214 fn allowed_characters(c: char) -> bool {
215 c.is_ascii_lowercase()
216 }
217 }
218
219 fn arb_from_bytes(data: &[u8]) -> arbitrary::Unstructured<'_> {
221 arbitrary::Unstructured::new(data)
222 }
223
224 #[test]
225 fn id_arbitrary_is_non_zero() {
226 #[derive(Debug)]
227 struct TestId;
228 impl crate::domain::Domain for TestId {
229 const DOMAIN_NAME: &'static str = "test_id";
230 }
231 impl crate::domain::IdDomain for TestId {}
232
233 let data = [0u8; 64];
234 let mut u = arb_from_bytes(&data);
235 let id = Id::<TestId>::arbitrary(&mut u);
237 if let Ok(id) = id {
238 assert!(id.get() != 0);
239 }
240 }
241
242 #[test]
243 fn key_arbitrary_simple_domain_is_valid() {
244 let data: Vec<u8> = (0u8..=255).cycle().take(8192).collect();
246 let mut u = arb_from_bytes(&data);
247 let mut success_count = 0usize;
248 for _ in 0..100 {
249 match Key::<SimpleDomain>::arbitrary(&mut u) {
250 Ok(key) => {
251 assert!(
253 Key::<SimpleDomain>::new(key.as_str()).is_ok(),
254 "generated key failed re-validation: {key:?}"
255 );
256 success_count += 1;
257 }
258 Err(arbitrary::Error::NotEnoughData) => break,
259 Err(e) => panic!("unexpected error: {e:?}"),
260 }
261 }
262 assert!(success_count > 0, "no keys generated");
263 }
264
265 #[test]
266 fn key_arbitrary_examples_domain_draws_from_examples() {
267 let data: Vec<u8> = (0u8..=255).cycle().take(4096).collect();
268 let mut u = arb_from_bytes(&data);
269 let valid: &[&str] = ExamplesDomain::examples();
270 let mut count = 0usize;
271 for _ in 0..50 {
272 match Key::<ExamplesDomain>::arbitrary(&mut u) {
273 Ok(key) => {
274 assert!(
275 valid.contains(&key.as_str()),
276 "generated key not in examples: {key:?}"
277 );
278 count += 1;
279 }
280 Err(arbitrary::Error::NotEnoughData) => break,
281 Err(e) => panic!("unexpected error: {e:?}"),
282 }
283 }
284 assert!(count > 0);
285 }
286
287 #[test]
288 fn key_arbitrary_min_eq_max_produces_fixed_length() {
289 #[derive(Debug)]
290 struct FixedLen;
291 impl crate::domain::Domain for FixedLen {
292 const DOMAIN_NAME: &'static str = "fixed";
293 }
294 impl KeyDomain for FixedLen {
295 const MAX_LENGTH: usize = 4;
296 fn min_length() -> usize {
297 4
298 }
299 fn allowed_characters(c: char) -> bool {
300 c == 'a'
301 }
302 }
303
304 let data: Vec<u8> = (0u8..=255).cycle().take(4096).collect();
305 let mut u = arb_from_bytes(&data);
306 for _ in 0..10 {
307 match Key::<FixedLen>::arbitrary(&mut u) {
308 Ok(key) => assert_eq!(key.len(), 4),
309 Err(arbitrary::Error::NotEnoughData) => break,
310 Err(e) => panic!("{e:?}"),
311 }
312 }
313 }
314}
315