Skip to main content

tx3_resolver/inputs/
canonical.rs

1//! Canonical representation of input queries.
2
3use std::collections::HashSet;
4
5use tx3_tir::model::v1beta0 as tir;
6use tx3_tir::model::{assets::CanonicalAssets, core::UtxoRef, core::UtxoSet};
7
8use crate::Error;
9
10macro_rules! data_or_bail {
11    ($expr:expr, bytes) => {
12        $expr
13            .as_bytes()
14            .ok_or(Error::ExpectedData("bytes".to_string(), $expr.clone()))
15    };
16
17    ($expr:expr, number) => {
18        $expr
19            .as_number()
20            .ok_or(Error::ExpectedData("number".to_string(), $expr.clone()))?
21    };
22
23    ($expr:expr, assets) => {
24        $expr
25            .as_assets()
26            .ok_or(Error::ExpectedData("assets".to_string(), $expr.clone()))
27    };
28
29    ($expr:expr, utxo_refs) => {
30        $expr
31            .as_utxo_refs()
32            .ok_or(Error::ExpectedData("utxo refs".to_string(), $expr.clone()))
33    };
34}
35
36pub struct Diagnostic {
37    pub query: tir::InputQuery,
38    pub utxos: UtxoSet,
39    pub selected: UtxoSet,
40}
41
42#[derive(Debug, Clone)]
43pub struct CanonicalQuery {
44    pub address: Option<Vec<u8>>,
45    pub min_amount: Option<CanonicalAssets>,
46    pub refs: HashSet<UtxoRef>,
47    pub support_many: bool,
48    pub collateral: bool,
49}
50
51impl std::fmt::Display for CanonicalQuery {
52    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
53        write!(f, "CanonicalQuery {{")?;
54
55        if let Some(address) = &self.address {
56            write!(f, "address: {}", hex::encode(address))?;
57        }
58
59        if let Some(min_amount) = &self.min_amount {
60            write!(f, "min_amount: {}", min_amount)?;
61        }
62
63        for (i, ref_) in self.refs.iter().enumerate() {
64            write!(f, "ref[{}]:{}#{}", i, hex::encode(&ref_.txid), ref_.index)?;
65        }
66
67        write!(f, "support_many: {:?}", self.support_many)?;
68        write!(f, "for_collateral: {:?}", self.collateral)?;
69        write!(f, "}}")
70    }
71}
72
73impl TryFrom<tir::InputQuery> for CanonicalQuery {
74    type Error = Error;
75
76    fn try_from(query: tir::InputQuery) -> Result<Self, Self::Error> {
77        let address = query
78            .address
79            .as_option()
80            .map(|x| data_or_bail!(x, bytes))
81            .transpose()?
82            .map(Vec::from);
83
84        let min_amount = query
85            .min_amount
86            .as_option()
87            .map(|x| data_or_bail!(x, assets))
88            .transpose()?
89            .map(|x| CanonicalAssets::from(Vec::from(x)));
90
91        let refs = query
92            .r#ref
93            .as_option()
94            .map(|x| data_or_bail!(x, utxo_refs))
95            .transpose()?
96            .map(|x| HashSet::from_iter(x.iter().cloned()))
97            .unwrap_or_default();
98
99        Ok(Self {
100            address,
101            min_amount,
102            refs,
103            support_many: query.many,
104            collateral: query.collateral,
105        })
106    }
107}