grovedb_costs/context.rs
1// MIT LICENSE
2//
3// Copyright (c) 2021 Dash Core Group
4//
5// Permission is hereby granted, free of charge, to any
6// person obtaining a copy of this software and associated
7// documentation files (the "Software"), to deal in the
8// Software without restriction, including without
9// limitation the rights to use, copy, modify, merge,
10// publish, distribute, sublicense, and/or sell copies of
11// the Software, and to permit persons to whom the Software
12// is furnished to do so, subject to the following
13// conditions:
14//
15// The above copyright notice and this permission notice
16// shall be included in all copies or substantial portions
17// of the Software.
18//
19// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
20// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
21// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
22// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
23// SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
24// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
26// IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
27// DEALINGS IN THE SOFTWARE.
28
29use crate::OperationCost;
30
31/// Wrapped operation result with associated cost.
32#[must_use]
33#[derive(Debug, Eq, PartialEq)]
34pub struct CostContext<T> {
35 /// Wrapped operation's return value.
36 pub value: T,
37 /// Cost of the operation.
38 pub cost: OperationCost,
39}
40
41/// General combinators for `CostContext`.
42impl<T> CostContext<T> {
43 /// Take wrapped value out adding its cost to provided accumulator.
44 pub fn unwrap_add_cost(self, acc_cost: &mut OperationCost) -> T {
45 *acc_cost += self.cost;
46 self.value
47 }
48
49 /// Take wrapped value out dropping cost data.
50 pub fn unwrap(self) -> T {
51 self.value
52 }
53
54 /// Borrow costs data.
55 pub fn cost(&self) -> &OperationCost {
56 &self.cost
57 }
58
59 /// Borrow wrapped data.
60 pub fn value(&self) -> &T {
61 &self.value
62 }
63
64 /// Applies function to wrapped value keeping cost the same as before.
65 pub fn map<B>(self, f: impl FnOnce(T) -> B) -> CostContext<B> {
66 let cost = self.cost;
67 let value = f(self.value);
68 CostContext { value, cost }
69 }
70
71 /// Applies function to wrapped value adding costs.
72 pub fn flat_map<B>(self, f: impl FnOnce(T) -> CostContext<B>) -> CostContext<B> {
73 let mut cost = self.cost;
74 let value = f(self.value).unwrap_add_cost(&mut cost);
75 CostContext { value, cost }
76 }
77
78 /// Adds previously accumulated cost
79 pub fn add_cost(mut self, cost: OperationCost) -> Self {
80 self.cost += cost;
81 self
82 }
83}
84
85/// Type alias for `Result` wrapped into `CostContext`.
86pub type CostResult<T, E> = CostContext<Result<T, E>>;
87
88/// Combinators to use with `Result` wrapped in `CostContext`.
89impl<T, E> CostResult<T, E> {
90 /// Applies function to wrapped value in case of `Ok` keeping cost the same
91 /// as before.
92 pub fn map_ok<B>(self, f: impl FnOnce(T) -> B) -> CostResult<B, E> {
93 self.map(|result| result.map(f))
94 }
95
96 /// Applies function to wrapped value in case of `Err` keeping cost the same
97 /// as before.
98 pub fn map_err<B>(self, f: impl FnOnce(E) -> B) -> CostResult<T, B> {
99 self.map(|result| result.map_err(f))
100 }
101
102 /// Applies function to wrapped result in case of `Ok` adding costs.
103 pub fn flat_map_ok<B>(self, f: impl FnOnce(T) -> CostResult<B, E>) -> CostResult<B, E> {
104 let mut cost = self.cost;
105 let result = match self.value {
106 Ok(x) => f(x).unwrap_add_cost(&mut cost),
107 Err(e) => Err(e),
108 };
109 CostContext {
110 value: result,
111 cost,
112 }
113 }
114
115 /// Gets the cost as a result
116 pub fn cost_as_result(self) -> Result<OperationCost, E> {
117 self.value.map(|_| self.cost)
118 }
119
120 /// Call the provided function on success without altering result or cost.
121 pub fn for_ok(self, f: impl FnOnce(&T)) -> CostResult<T, E> {
122 if let Ok(x) = &self.value {
123 f(x)
124 }
125
126 self
127 }
128}
129
130impl<T, E> CostResult<Result<T, E>, E> {
131 /// Flattens nested errors inside `CostContext`
132 pub fn flatten(self) -> CostResult<T, E> {
133 self.map(|value| match value {
134 Err(e) => Err(e),
135 Ok(Err(e)) => Err(e),
136 Ok(Ok(v)) => Ok(v),
137 })
138 }
139}
140
141impl<T> CostContext<CostContext<T>> {
142 /// Flattens nested `CostContext`s adding costs.
143 pub fn flatten(self) -> CostContext<T> {
144 let mut cost = OperationCost::default();
145 let inner = self.unwrap_add_cost(&mut cost);
146 inner.add_cost(cost)
147 }
148}
149
150/// Extension trait to add costs context to values.
151pub trait CostsExt {
152 /// Wraps any value into a `CostContext` object with provided costs.
153 fn wrap_with_cost(self, cost: OperationCost) -> CostContext<Self>
154 where
155 Self: Sized,
156 {
157 CostContext { value: self, cost }
158 }
159
160 /// Wraps any value into `CostContext` object with costs computed using the
161 /// value getting wrapped.
162 fn wrap_fn_cost(self, f: impl FnOnce(&Self) -> OperationCost) -> CostContext<Self>
163 where
164 Self: Sized,
165 {
166 CostContext {
167 cost: f(&self),
168 value: self,
169 }
170 }
171}
172
173impl<T> CostsExt for T {}
174
175/// Macro to achieve kind of what the `?` operator does, but with `CostContext`
176/// on top.
177///
178/// Main properties are:
179/// 1. Early termination on error;
180/// 2. Because of 1, `Result` is removed from the equation;
181/// 3. `CostContext` is removed too because it is added to external cost
182/// accumulator;
183/// 4. Early termination uses external cost accumulator so previous costs won't
184/// be lost.
185#[macro_export]
186macro_rules! cost_return_on_error {
187 ( &mut $cost:ident, $($body:tt)+ ) => {
188 {
189 use $crate::CostsExt;
190 let result_with_cost = { $($body)+ };
191 let result = result_with_cost.unwrap_add_cost(&mut $cost);
192 match result {
193 Ok(x) => x,
194 Err(e) => return Err(e).wrap_with_cost($cost),
195 }
196 }
197 };
198}
199
200/// Macro to achieve kind of what the `?` operator does, but with `CostContext`
201/// on top.
202///
203/// Main properties are:
204/// 1. Early termination on error;
205/// 2. Because of 1, `Result` is removed from the equation;
206/// 3. `CostContext` is removed too because it is added to external cost
207/// accumulator;
208/// 4. Early termination uses external cost accumulator so previous costs won't
209/// be lost.
210#[macro_export]
211macro_rules! cost_return_on_error_into {
212 ( &mut $cost:ident, $($body:tt)+ ) => {
213 {
214 use $crate::CostsExt;
215 let result_with_cost = { $($body)+ };
216 let result = result_with_cost.unwrap_add_cost(&mut $cost);
217 match result {
218 Ok(x) => x,
219 Err(e) => return Err(e.into()).wrap_with_cost($cost),
220 }
221 }
222 };
223}
224
225/// Macro to achieve kind of what the `?` operator does, but with `CostContext`
226/// on top. The difference between this macro and `cost_return_on_error` is that
227/// it is intended to be used on `Result` rather than `CostContext<Result<..>>`,
228/// so no costs will be added except previously accumulated.
229#[macro_export]
230macro_rules! cost_return_on_error_no_add {
231 ( $cost:ident, $($body:tt)+ ) => {
232 {
233 use $crate::CostsExt;
234 let result = { $($body)+ };
235 match result {
236 Ok(x) => x,
237 Err(e) => return Err(e).wrap_with_cost($cost),
238 }
239 }
240 };
241}
242
243/// Macro to achieve kind of what the `?` operator does, but with `CostContext`
244/// on top. The difference between this macro and `cost_return_on_error` is that
245/// it is intended to be used on `Result` rather than `CostContext<Result<..>>`,
246/// so no costs will be added except previously accumulated.
247#[macro_export]
248macro_rules! cost_return_on_error_into_no_add {
249 ( $cost:ident, $($body:tt)+ ) => {
250 {
251 use $crate::CostsExt;
252 let result = { $($body)+ };
253 match result {
254 Ok(x) => x,
255 Err(e) => return Err(e.into()).wrap_with_cost($cost),
256 }
257 }
258 };
259}
260
261/// Macro to achieve kind of what the `?` operator does, but with `CostContext`
262/// on top. The difference between this macro and `cost_return_on_error` is that
263/// it is intended to be used on `Result` rather than `CostContext<Result<..>>`,
264/// so no costs will be added except previously accumulated. The error case
265/// returns a default `OperationCost`.
266#[macro_export]
267macro_rules! cost_return_on_error_default {
268 ( $($body:tt)+ ) => {
269 {
270 use $crate::CostsExt;
271 let result = { $($body)+ };
272 match result {
273 Ok(x) => x,
274 Err(e) => return Err(e).wrap_with_cost(OperationCost::default()),
275 }
276 }
277 };
278}
279
280/// Macro to achieve kind of what the `?` operator does, but with `CostContext`
281/// on top. The difference between this macro and `cost_return_on_error` is that
282/// it is intended to be used on `Result` rather than `CostContext<Result<..>>`,
283/// so no costs will be added except previously accumulated. The error case
284/// returns a default `OperationCost`.
285#[macro_export]
286macro_rules! cost_return_on_error_into_default {
287 ( $($body:tt)+ ) => {
288 {
289 use $crate::CostsExt;
290 let result = { $($body)+ };
291 match result {
292 Ok(x) => x,
293 Err(e) => return Err(e.into()).wrap_with_cost(OperationCost::default()),
294 }
295 }
296 };
297}