gsdk 2.0.0

Rust SDK of the Gear network
Documentation
// Copyright (C) Gear Technologies Inc.
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0

//! This module provides [`TxOutput`] helper type.

use crate::{AsGear, Error, Event, GearConfig, Result, TxInBlock};
use subxt::{blocks::ExtrinsicEvents, utils::H256};

/// Transaction with its output value.
#[derive(Debug, derive_more::AsRef, derive_more::AsMut)]
pub struct TxOutput<T = ()> {
    /// The hash of the block that the transaction has made it into.
    pub block_hash: H256,

    /// Events associated with the transaction.
    pub events: ExtrinsicEvents<GearConfig>,

    /// Output value of the transaction.
    #[as_ref]
    #[as_mut]
    pub value: T,
}

/// [`TxOutput`] without value.
impl TxOutput {
    /// Constructs a [`TxOutput`] without value from a [`TxInBlock`].
    pub async fn new(tx: TxInBlock) -> Result<Self> {
        Ok(Self {
            block_hash: tx.block_hash(),
            events: tx.wait_for_success().await?,
            value: (),
        })
    }

    /// Finds an event with an output value and extracts the value
    /// from the event.
    ///
    /// Essentially just an [`Iterator::filter_map`] on events.
    pub fn find_map<T, F>(self, mut f: F) -> Result<TxOutput<Option<T>>>
    where
        F: FnMut(Event) -> Option<T>,
    {
        let value = self
            .events()
            .map(move |event| event.map(&mut f))
            .find_map(|res| res.transpose())
            .transpose()?;

        Ok(self.with_value(value))
    }

    /// Filters and maps events and collects results into [`Vec`] as a value.
    ///
    /// Essentially just a [`Iterator::filter_map`] on events.
    pub fn filter_map<T, F>(self, mut f: F) -> Result<TxOutput<Vec<T>>>
    where
        F: FnMut(Event) -> Option<T>,
    {
        let values = self
            .events()
            .map(move |event| event.map(&mut f))
            .filter_map(|res| res.transpose())
            .collect::<Result<Vec<_>>>()?;

        Ok(self.with_value(values))
    }

    /// Ensures that there's an event matching given predicate.
    ///
    /// Essentially just an [`Iterator::any`] on events.
    pub fn any<F>(self, mut f: F) -> Result<TxOutput<bool>>
    where
        F: FnMut(Event) -> bool,
    {
        Ok(self
            .find_map(move |event| f(event).then_some(()))?
            .map(|opt| opt.is_some()))
    }

    /// Calls given fallible function on each event, returning
    /// `Err` if any of then fails.
    pub fn try_for_each<E, F>(self, mut f: F) -> Result<Self, E>
    where
        E: From<Error>,
        F: FnMut(Event) -> Result<(), E>,
    {
        self.events().try_for_each(move |event| f(event?))?;

        Ok(self)
    }
}

/// [`TxOutput`] without value, but after some kind of check.
impl TxOutput<bool> {
    /// Applies logical `||` on the value and `f()`.
    pub fn or<F: FnOnce() -> bool>(self, b: F) -> Self {
        self.map(move |opt| opt || b())
    }

    /// Applies [`bool::then`] to the inner value.
    pub fn then<T, F>(self, f: F) -> TxOutput<Option<T>>
    where
        F: FnOnce() -> T,
    {
        self.map(move |opt| opt.then(f))
    }

    /// Returns `Ok(tx_output)` if `value` is `true`
    /// and `Err(Error::EventNotFound)` if `value` is `false`.
    ///
    /// Useful for final part of [`TxOutput::any`]-based event
    /// validation.
    pub fn then_ok(self) -> Result<TxOutput> {
        if self.value {
            Ok(self.with_value(()))
        } else {
            Err(Error::EventNotFound)
        }
    }
}

impl<T> TxOutput<T> {
    /// Returns an iterator over events.
    ///
    /// Does event casting inside.
    pub fn events(&self) -> impl Iterator<Item = Result<Event>> {
        self.events.iter().map(|event| event?.as_gear())
    }

    /// Returns hash of the extrinsic.
    pub fn extrinsic_hash(&self) -> H256 {
        self.events.extrinsic_hash()
    }

    /// Replaces the inner value.
    pub fn with_value<O>(self, value: O) -> TxOutput<O> {
        TxOutput {
            block_hash: self.block_hash,
            events: self.events,
            value,
        }
    }

    /// Separates the value and the transaction details.
    ///
    /// Useful for taking the value ownership out of the [`TxOutput`].
    pub fn split(self) -> (TxOutput, T) {
        (
            TxOutput {
                block_hash: self.block_hash,
                events: self.events,
                value: (),
            },
            self.value,
        )
    }

    /// Maps inner value of the [`TxOutput`].
    pub fn map<O, F>(self, f: F) -> TxOutput<O>
    where
        F: FnOnce(T) -> O,
    {
        let (tx_output, value) = self.split();
        tx_output.with_value(f(value))
    }
}

impl<T> TxOutput<Option<T>> {
    /// Applies [`Option::or_else`] to the inner [`Option`].
    pub fn or_else<F>(self, f: F) -> Self
    where
        F: FnOnce() -> Option<T>,
    {
        self.map(move |opt| opt.or_else(f))
    }

    /// Unwraps the inner `Option`.
    ///
    /// Returns `Err(Error::EventNotFound)` if it's `None`.
    pub fn ok_or_err(self) -> Result<TxOutput<T>> {
        let (tx_output, option) = self.split();
        match option {
            Some(value) => Ok(tx_output.with_value(value)),
            None => Err(Error::EventNotFound),
        }
    }
}