evobench_tools/utillib/
lazy.rs1use std::{cell::UnsafeCell, fmt};
17
18pub enum Lazy<T, F: FnOnce() -> T> {
20 Thunk(F),
21 Poisoned,
22 Value(T),
23}
24
25impl<T, F: FnOnce() -> T> Lazy<T, F> {
26 pub fn new(f: F) -> Self {
27 Self::Thunk(f)
28 }
29
30 pub fn into_inner(self) -> Result<T, F> {
31 match self {
32 Lazy::Value(v) => Ok(v),
33 Lazy::Thunk(f) => Err(f),
34 Lazy::Poisoned => panic!("Lazy instance has previously been poisoned"),
35 }
36 }
37
38 pub fn into_value(self) -> T {
40 match self.into_inner() {
41 Ok(v) => v,
42 Err(f) => f(),
43 }
44 }
45
46 pub fn force(&mut self) -> &T {
47 match self {
48 Lazy::Thunk(_) => {
49 let mut thunk = Lazy::Poisoned;
50 std::mem::swap(self, &mut thunk);
51 match thunk {
52 Lazy::Thunk(t) => {
53 let mut new = Lazy::Value(t());
54 std::mem::swap(self, &mut new);
55 match self {
56 Lazy::Value(v) => v,
57 _ => panic!(),
58 }
59 }
60 _ => panic!(),
61 }
62 }
63 Lazy::Value(v) => v,
64 Lazy::Poisoned => panic!("Lazy instance has previously been poisoned"),
65 }
66 }
67
68 pub fn force_mut(&mut self) -> &mut T {
69 match self {
70 Lazy::Thunk(_) => {
71 let mut thunk = Lazy::Poisoned;
72 std::mem::swap(self, &mut thunk);
73 match thunk {
74 Lazy::Thunk(t) => {
75 let mut new = Lazy::Value(t());
76 std::mem::swap(self, &mut new);
77 match self {
78 Lazy::Value(v) => v,
79 _ => panic!(),
80 }
81 }
82 _ => panic!(),
83 }
84 }
85 Lazy::Value(v) => v,
86 Lazy::Poisoned => panic!("Lazy instance has previously been poisoned"),
87 }
88 }
89}
90
91enum InnerLazyResult<T, E, F: FnOnce() -> Result<T, E>> {
96 Thunk(F),
97 Poisoned,
98 Value(T),
99}
100
101pub struct LazyResult<T, E, F: FnOnce() -> Result<T, E>>(UnsafeCell<InnerLazyResult<T, E, F>>);
102
103impl<T, E, F: FnOnce() -> Result<T, E>> LazyResult<T, E, F> {
104 pub fn new(f: F) -> Self {
105 Self(UnsafeCell::new(InnerLazyResult::Thunk(f)))
106 }
107
108 pub fn into_inner(self) -> Result<T, F> {
109 match self.0.into_inner() {
110 InnerLazyResult::Value(v) => Ok(v),
111 InnerLazyResult::Thunk(f) => Err(f),
112 InnerLazyResult::Poisoned => panic!("Lazy instance has previously been poisoned"),
113 }
114 }
115
116 pub fn into_value(self) -> Result<T, E> {
118 match self.into_inner() {
119 Ok(v) => Ok(v),
120 Err(f) => f(),
121 }
122 }
123
124 pub fn force(&self) -> Result<&T, E> {
125 let rf = unsafe {
126 &mut *self.0.get()
131 };
132 match rf {
133 InnerLazyResult::Thunk(_) => {
134 let mut thunk = InnerLazyResult::Poisoned;
135 std::mem::swap(rf, &mut thunk);
136 match thunk {
137 InnerLazyResult::Thunk(t) => {
138 let mut new = InnerLazyResult::Value(t()?);
139 std::mem::swap(rf, &mut new);
140 match rf {
141 InnerLazyResult::Value(v) => Ok(v),
142 _ => panic!(),
143 }
144 }
145 _ => panic!(),
146 }
147 }
148 InnerLazyResult::Value(v) => Ok(v),
149 InnerLazyResult::Poisoned => panic!("Lazy instance has previously been poisoned"),
150 }
151 }
152
153 pub fn force_mut(&mut self) -> Result<&mut T, E> {
154 let rf = self.0.get_mut();
155 match rf {
156 InnerLazyResult::Thunk(_) => {
157 let mut thunk = InnerLazyResult::Poisoned;
158 std::mem::swap(rf, &mut thunk);
159 match thunk {
160 InnerLazyResult::Thunk(t) => {
161 let mut new = InnerLazyResult::Value(t()?);
162 std::mem::swap(rf, &mut new);
163 match rf {
164 InnerLazyResult::Value(v) => Ok(v),
165 _ => panic!(),
166 }
167 }
168 _ => panic!(),
169 }
170 }
171 InnerLazyResult::Value(v) => Ok(v),
172 InnerLazyResult::Poisoned => panic!("Lazy instance has previously been poisoned"),
173 }
174 }
175}
176
177impl<T: fmt::Debug, F: FnOnce() -> T> fmt::Debug for Lazy<T, F> {
178 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
179 let mut d = f.debug_tuple("Lazy");
180 match self {
181 Self::Thunk(_) => d.field(&format_args!("<unforced>")),
182 Self::Poisoned => d.field(&format_args!("<poisoned>")),
183 Self::Value(v) => d.field(v),
184 };
185 d.finish()
186 }
187}
188
189impl<T: fmt::Debug, E, F: FnOnce() -> Result<T, E>> fmt::Debug for LazyResult<T, E, F> {
190 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
191 let mut d = f.debug_tuple("LazyResult");
192 let rf = unsafe {
193 &*self.0.get()
195 };
196 match rf {
197 InnerLazyResult::Thunk(_) => d.field(&format_args!("<unforced>")),
198 InnerLazyResult::Poisoned => d.field(&format_args!("<poisoned>")),
199 InnerLazyResult::Value(v) => d.field(v),
200 };
201 d.finish()
202 }
203}
204
205#[cfg(test)]
206mod tests {
207 use std::cell::Cell;
208
209 use anyhow::Result;
210
211 use crate::lazyresult;
212
213 struct Foo<'t>(&'t Cell<i32>);
214
215 impl<'t> Drop for Foo<'t> {
216 fn drop(&mut self) {
217 self.0.set(self.0.get() + 1);
218 }
219 }
220
221 #[test]
222 fn t_() -> Result<()> {
223 let counter = Cell::new(0);
224 let l = lazyresult! {
225 anyhow::Ok(Foo(&counter))
226 };
227 assert_eq!(counter.get(), 0);
238 let foo = l.force()?;
239 assert_eq!(foo.0.get(), 0);
240 drop(l);
241 assert_eq!(counter.get(), 1);
242
243 Ok(())
244 }
245}
246
247#[macro_export]
262macro_rules! lazy {
263 { move $($body:tt)* } => {
264 $crate::lazy::Lazy::new(move || { $($body)* })
265 };
266 { $($body:tt)* } => {
267 $crate::lazy::Lazy::new(|| { $($body)* })
268 }
269}
270
271#[macro_export]
281macro_rules! lazyresult {
282 { move $($body:tt)* } => {
283 $crate::utillib::lazy::LazyResult::new(move || { $($body)* })
284 };
285 { $($body:tt)* } => {
286 $crate::utillib::lazy::LazyResult::new(|| { $($body)* })
287 }
288}