checkito/
boxed.rs

1use crate::{
2    generate::{Generate, State},
3    shrink::Shrink,
4};
5use core::{any::Any, fmt};
6
7pub struct Boxed<I> {
8    generator: Box<dyn Any>,
9    generate: fn(&dyn Any, &mut State) -> Shrinker<I>,
10    constant: fn(&dyn Any) -> bool,
11}
12
13pub struct Shrinker<I> {
14    shrinker: Box<dyn Any>,
15    clone: fn(&dyn Any) -> Box<dyn Any>,
16    item: fn(&dyn Any) -> I,
17    shrink: fn(&mut dyn Any) -> Option<Box<dyn Any>>,
18}
19
20impl<I> fmt::Debug for Boxed<I> {
21    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
22        f.debug_tuple("Boxed").field(&self.generator).finish()
23    }
24}
25
26impl<I> fmt::Debug for Shrinker<I> {
27    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
28        f.debug_tuple("Shrinker").field(&self.shrinker).finish()
29    }
30}
31
32impl<I> Generate for Boxed<I> {
33    type Item = I;
34    type Shrink = Shrinker<I>;
35
36    fn generate(&self, state: &mut State) -> Self::Shrink {
37        (self.generate)(self.generator.as_ref(), state)
38    }
39
40    fn constant(&self) -> bool {
41        (self.constant)(self.generator.as_ref())
42    }
43}
44
45impl<I> Boxed<I> {
46    #[rustversion::since(1.75)]
47    pub(crate) const fn new<G: Generate<Item = I> + 'static>(generator: Box<G>) -> Self
48    where
49        G::Shrink: 'static,
50    {
51        Self {
52            generator,
53            generate: generate::<G>,
54            constant: constant::<G>,
55        }
56    }
57
58    #[rustversion::before(1.75)]
59    pub(crate) fn new<G: Generate<Item = I> + 'static>(generator: Box<G>) -> Self
60    where
61        G::Shrink: 'static,
62    {
63        Self {
64            generator,
65            generate: generate::<G>,
66            constant: constant::<G>,
67        }
68    }
69
70    pub fn downcast<G: Generate + 'static>(self) -> Result<Box<G>, Self> {
71        match self.generator.downcast::<G>() {
72            Ok(generator) => Ok(generator),
73            Err(generator) => Err(Self {
74                generator,
75                generate: self.generate,
76                constant: self.constant,
77            }),
78        }
79    }
80}
81
82impl<I> Shrinker<I> {
83    pub(crate) fn new<S: Shrink<Item = I> + 'static>(shrinker: Box<S>) -> Self {
84        Self {
85            shrinker,
86            clone: clone::<S>,
87            item: item::<S>,
88            shrink: shrink::<S>,
89        }
90    }
91
92    pub fn downcast<S: Shrink + 'static>(self) -> Result<Box<S>, Self> {
93        match self.shrinker.downcast::<S>() {
94            Ok(shrinker) => Ok(shrinker),
95            Err(shrinker) => Err(Self {
96                shrinker,
97                clone: self.clone,
98                item: self.item,
99                shrink: self.shrink,
100            }),
101        }
102    }
103}
104
105impl<I> Clone for Shrinker<I> {
106    fn clone(&self) -> Self {
107        Self {
108            shrinker: (self.clone)(self.shrinker.as_ref()),
109            clone: self.clone,
110            item: self.item,
111            shrink: self.shrink,
112        }
113    }
114}
115
116impl<I> Shrink for Shrinker<I> {
117    type Item = I;
118
119    fn item(&self) -> Self::Item {
120        (self.item)(self.shrinker.as_ref())
121    }
122
123    fn shrink(&mut self) -> Option<Self> {
124        Some(Self {
125            shrinker: (self.shrink)(self.shrinker.as_mut())?,
126            clone: self.clone,
127            item: self.item,
128            shrink: self.shrink,
129        })
130    }
131}
132
133fn generate<G: Generate + 'static>(generator: &dyn Any, state: &mut State) -> Shrinker<G::Item>
134where
135    G::Shrink: 'static,
136{
137    Shrinker::new(Box::new(
138        generator.downcast_ref::<G>().unwrap().generate(state),
139    ))
140}
141
142fn constant<G: Generate + 'static>(generator: &dyn Any) -> bool {
143    generator.downcast_ref::<G>().unwrap().constant()
144}
145
146fn clone<S: Shrink + 'static>(shrinker: &dyn Any) -> Box<dyn Any> {
147    Box::new(shrinker.downcast_ref::<S>().unwrap().clone())
148}
149
150fn item<S: Shrink + 'static>(shrinker: &dyn Any) -> S::Item {
151    shrinker.downcast_ref::<S>().unwrap().item()
152}
153
154fn shrink<S: Shrink + 'static>(shrinker: &mut dyn Any) -> Option<Box<dyn Any>> {
155    Some(Box::new(shrinker.downcast_mut::<S>().unwrap().shrink()?))
156}