proptest/strategy/
lazy.rs

1//-
2// Copyright 2019 The proptest developers
3//
4// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
5// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
6// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
7// option. This file may not be copied, modified, or distributed
8// except according to those terms.
9
10use crate::std_facade::{fmt, Arc};
11use core::mem;
12
13use crate::strategy::traits::*;
14use crate::test_runner::*;
15
16/// Represents a value tree that is initialized on the first call to any
17/// methods.
18///
19/// This is used to defer potentially expensive generation to shrinking time. It
20/// is public only to allow APIs to expose it as an intermediate value.
21pub struct LazyValueTree<S: Strategy> {
22    state: LazyValueTreeState<S>,
23}
24
25enum LazyValueTreeState<S: Strategy> {
26    Initialized(S::Tree),
27    Uninitialized {
28        strategy: Arc<S>,
29        runner: TestRunner,
30    },
31    Failed,
32}
33
34impl<S: Strategy> LazyValueTree<S> {
35    /// Create a new value tree where initial generation is deferred until
36    /// `maybe_init` is called.
37    pub(crate) fn new(strategy: Arc<S>, runner: &mut TestRunner) -> Self {
38        let runner = runner.partial_clone();
39        Self {
40            state: LazyValueTreeState::Uninitialized { strategy, runner },
41        }
42    }
43
44    /// Create a new value tree that has already been initialized.
45    pub(crate) fn new_initialized(value_tree: S::Tree) -> Self {
46        Self {
47            state: LazyValueTreeState::Initialized(value_tree),
48        }
49    }
50
51    /// Returns a reference to the inner value tree if initialized.
52    pub(crate) fn as_inner(&self) -> Option<&S::Tree> {
53        match &self.state {
54            LazyValueTreeState::Initialized(v) => Some(v),
55            LazyValueTreeState::Uninitialized { .. }
56            | LazyValueTreeState::Failed => None,
57        }
58    }
59
60    /// Returns a mutable reference to the inner value tree if uninitialized.
61    pub(crate) fn as_inner_mut(&mut self) -> Option<&mut S::Tree> {
62        match &mut self.state {
63            LazyValueTreeState::Initialized(v) => Some(v),
64            LazyValueTreeState::Uninitialized { .. }
65            | LazyValueTreeState::Failed => None,
66        }
67    }
68
69    /// Try initializing the value tree.
70    pub(crate) fn maybe_init(&mut self) {
71        if !self.is_uninitialized() {
72            return;
73        }
74
75        let state = mem::replace(&mut self.state, LazyValueTreeState::Failed);
76        match state {
77            LazyValueTreeState::Uninitialized {
78                strategy,
79                mut runner,
80            } => {
81                match strategy.new_tree(&mut runner) {
82                    Ok(v) => {
83                        let _ = mem::replace(
84                            &mut self.state,
85                            LazyValueTreeState::Initialized(v),
86                        );
87                    }
88                    Err(_) => {
89                        // self.state is set to Failed above. Keep it that way.
90                    }
91                }
92            }
93            LazyValueTreeState::Initialized(_) | LazyValueTreeState::Failed => {
94                unreachable!("can only reach here if uninitialized")
95            }
96        }
97    }
98
99    /// Whether this value tree still needs to be initialized.
100    pub(crate) fn is_uninitialized(&self) -> bool {
101        match &self.state {
102            LazyValueTreeState::Uninitialized { .. } => true,
103            LazyValueTreeState::Initialized(_) | LazyValueTreeState::Failed => {
104                false
105            }
106        }
107    }
108
109    /// Whether the value tree was successfully initialized.
110    pub(crate) fn is_initialized(&self) -> bool {
111        match &self.state {
112            LazyValueTreeState::Initialized(_) => true,
113            LazyValueTreeState::Uninitialized { .. }
114            | LazyValueTreeState::Failed => false,
115        }
116    }
117}
118
119impl<S: Strategy> Clone for LazyValueTree<S>
120where
121    S::Tree: Clone,
122{
123    fn clone(&self) -> Self {
124        Self {
125            state: self.state.clone(),
126        }
127    }
128}
129
130impl<S: Strategy> fmt::Debug for LazyValueTree<S>
131where
132    S::Tree: fmt::Debug,
133{
134    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
135        f.debug_struct("LazyValueTree")
136            .field("state", &self.state)
137            .finish()
138    }
139}
140
141impl<S: Strategy> Clone for LazyValueTreeState<S>
142where
143    S::Tree: Clone,
144{
145    fn clone(&self) -> Self {
146        use LazyValueTreeState::*;
147
148        match self {
149            Initialized(v) => Initialized(v.clone()),
150            Uninitialized { strategy, runner } => Uninitialized {
151                strategy: Arc::clone(strategy),
152                runner: runner.clone(),
153            },
154            Failed => Failed,
155        }
156    }
157}
158
159impl<S: Strategy> fmt::Debug for LazyValueTreeState<S>
160where
161    S::Tree: fmt::Debug,
162{
163    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
164        match self {
165            LazyValueTreeState::Initialized(value_tree) => {
166                f.debug_tuple("Initialized").field(value_tree).finish()
167            }
168            LazyValueTreeState::Uninitialized { strategy, .. } => f
169                .debug_struct("Uninitialized")
170                .field("strategy", strategy)
171                .finish(),
172            LazyValueTreeState::Failed => write!(f, "Failed"),
173        }
174    }
175}