cardano_sdk/cardano/output/
change_strategy.rs

1//  This Source Code Form is subject to the terms of the Mozilla Public
2//  License, v. 2.0. If a copy of the MPL was not distributed with this
3//  file, You can obtain one at http://mozilla.org/MPL/2.0/.
4
5use crate::{Address, Output, Value, address::kind::*};
6use anyhow::anyhow;
7use std::collections::VecDeque;
8
9/// Defines the behaviour of the transaction builder towards change outputs.
10///
11/// A _strategy_ is nothing more than a (faillible) function of the change and a mutable reference
12/// to the outputs. It can be defined explicitly using [`Self::new`]. Alternatively, we provide a
13/// handful of pre-defined common strategies:
14///
15/// - [`Self::as_first_output`]
16/// - [`Self::as_last_output`]
17pub struct ChangeStrategy(
18    #[allow(clippy::type_complexity)]
19    Box<dyn FnOnce(Value<u64>, &mut VecDeque<Output>) -> anyhow::Result<()>>,
20);
21
22// -------------------------------------------------------------------- Building
23
24impl Default for ChangeStrategy {
25    fn default() -> Self {
26        Self::new(|_change, _outputs| {
27            Err(anyhow!(
28                "no explicit change strategy defined; use 'with_change_strategy' to define one."
29            ))
30        })
31    }
32}
33
34impl ChangeStrategy {
35    /// A change strategy that creates a new output with all change sent at the given address, and
36    /// prepend it to the existing list of outputs.
37    pub fn as_first_output(change_address: Address<Any>) -> Self {
38        Self::new(move |change, outputs| {
39            outputs.push_front(Output::new(change_address, change));
40            Ok(())
41        })
42    }
43
44    /// A change strategy that creates a new output with all change sent at the given address, and
45    /// append it to the existing list of outputs.
46    pub fn as_last_output(change_address: Address<Any>) -> Self {
47        Self::new(move |change, outputs| {
48            outputs.push_back(Output::new(change_address, change));
49            Ok(())
50        })
51    }
52
53    /// Define a custom strategy manually. For example, we have:
54    ///
55    /// ```ignore
56    /// pub fn as_last_output(change_address: Address<Any>) -> Self {
57    ///     Self::new(move |change, outputs| {
58    ///         outputs.push_back(Output::new(change_address, change));
59    ///         Ok(())
60    ///     })
61    /// }
62    /// ```
63    ///
64    /// ```ignore
65    /// pub fn as_first_output(change_address: Address<Any>) -> Self {
66    ///     Self::new(move |change, outputs| {
67    ///         outputs.push_front(Output::new(change_address, change));
68    ///         Ok(())
69    ///     })
70    /// }
71    /// ```
72    pub fn new(
73        strategy: impl FnOnce(Value<u64>, &mut VecDeque<Output>) -> anyhow::Result<()> + 'static,
74    ) -> Self {
75        Self(Box::new(strategy))
76    }
77}
78
79// --------------------------------------------------------------------- Running
80
81impl ChangeStrategy {
82    /// Run the encapsulated (faillible) strategy on a mutable set of outputs. This is called
83    /// internally by the [`Transaction::build`](crate::Transaction::build) when necessary to
84    /// distribute change to the outputs according to the given strategy.
85    pub fn apply(self, change: Value<u64>, outputs: &mut VecDeque<Output>) -> anyhow::Result<()> {
86        self.0(change, outputs)
87    }
88}