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. The difference between this macro and `cost_return_on_error` is that
202/// it is intended to be used on `Result` rather than `CostContext<Result<..>>`,
203/// so no costs will be added except previously accumulated.
204#[macro_export]
205macro_rules! cost_return_on_error_no_add {
206    ( $cost:ident, $($body:tt)+ ) => {
207        {
208            use $crate::CostsExt;
209            let result = { $($body)+ };
210            match result {
211                Ok(x) => x,
212                Err(e) => return Err(e).wrap_with_cost($cost),
213            }
214        }
215    };
216}
217
218/// Macro to achieve kind of what the `?` operator does, but with `CostContext`
219/// on top. The difference between this macro and `cost_return_on_error` is that
220/// it is intended to be used on `Result` rather than `CostContext<Result<..>>`,
221/// so no costs will be added except previously accumulated. The error case
222/// returns a default `OperationCost`.
223#[macro_export]
224macro_rules! cost_return_on_error_default {
225    ( $($body:tt)+ ) => {
226        {
227            use $crate::CostsExt;
228            let result = { $($body)+ };
229            match result {
230                Ok(x) => x,
231                Err(e) => return Err(e).wrap_with_cost(OperationCost::default()),
232            }
233        }
234    };
235}