1use std::str::FromStr;
23
24use rgb::{ContractId, SchemaId};
25use strict_types::FieldName;
26
27use crate::invoice::{Beneficiary, InvoiceState, RgbInvoice, RgbTransport, XChainNet};
28use crate::{Allocation, Amount, CoinAmount, NonFungible, Precision, TransportParseError};
29
30#[derive(Clone, Eq, PartialEq, Debug)]
31pub struct RgbInvoiceBuilder(RgbInvoice);
32
33#[allow(clippy::result_large_err)]
34impl RgbInvoiceBuilder {
35 pub fn new(beneficiary: impl Into<XChainNet<Beneficiary>>) -> Self {
36 Self(RgbInvoice {
37 transports: vec![],
38 contract: None,
39 schema: None,
40 assignment_name: None,
41 assignment_state: None,
42 beneficiary: beneficiary.into(),
43 expiry: None,
44 unknown_query: none!(),
45 })
46 }
47
48 pub fn with(contract_id: ContractId, beneficiary: impl Into<XChainNet<Beneficiary>>) -> Self {
49 Self::new(beneficiary).set_contract(contract_id)
50 }
51
52 pub fn set_contract(mut self, contract_id: ContractId) -> Self {
53 self.0.contract = Some(contract_id);
54 self
55 }
56
57 pub fn set_schema(mut self, schema_id: SchemaId) -> Self {
58 self.0.schema = Some(schema_id);
59 self
60 }
61
62 pub fn set_assignment_name(mut self, assignment_name: impl Into<FieldName>) -> Self {
63 self.0.assignment_name = Some(assignment_name.into());
64 self
65 }
66
67 pub fn set_void(mut self) -> Self {
68 self.0.assignment_state = Some(InvoiceState::Void);
69 self
70 }
71
72 pub fn set_amount_raw(mut self, amount: impl Into<Amount>) -> Self {
73 self.0.assignment_state = Some(InvoiceState::Amount(amount.into()));
74 self
75 }
76
77 pub fn set_amount(
78 mut self,
79 integer: u64,
80 decimals: u64,
81 precision: Precision,
82 ) -> Result<Self, Self> {
83 let amount = match CoinAmount::with(integer, decimals, precision) {
84 Ok(amount) => amount,
85 Err(_) => return Err(self),
86 }
87 .to_amount_unchecked();
88 self.0.assignment_state = Some(InvoiceState::Amount(amount));
89 Ok(self)
90 }
91
92 pub fn set_allocation_raw(mut self, allocation: impl Into<Allocation>) -> Self {
93 self.0.assignment_state =
94 Some(InvoiceState::Data(NonFungible::FractionedToken(allocation.into())));
95 self
96 }
97
98 pub fn set_allocation(self, token_index: u32, fraction: u64) -> Result<Self, Self> {
99 Ok(self.set_allocation_raw(Allocation::with(token_index, fraction)))
100 }
101
102 pub unsafe fn set_amount_approx(self, amount: f64, precision: Precision) -> Result<Self, Self> {
108 if amount <= 0.0 {
109 return Err(self);
110 }
111 let coins = amount.floor();
112 let cents = amount - coins;
113 self.set_amount(coins as u64, cents as u64, precision)
114 }
115
116 pub fn set_expiry_timestamp(mut self, expiry: i64) -> Self {
117 self.0.expiry = Some(expiry);
118 self
119 }
120
121 pub fn add_transport(self, transport: &str) -> Result<Self, (Self, TransportParseError)> {
122 let transport = match RgbTransport::from_str(transport) {
123 Err(err) => return Err((self, err)),
124 Ok(transport) => transport,
125 };
126 Ok(self.add_transport_raw(transport))
127 }
128
129 pub fn add_transport_raw(mut self, transport: RgbTransport) -> Self {
130 self.0.transports.push(transport);
131 self
132 }
133
134 pub fn add_transports<'a>(
135 self,
136 transports: impl IntoIterator<Item = &'a str>,
137 ) -> Result<Self, (Self, TransportParseError)> {
138 let res = transports
139 .into_iter()
140 .map(RgbTransport::from_str)
141 .collect::<Result<Vec<_>, TransportParseError>>();
142 let transports = match res {
143 Err(err) => return Err((self, err)),
144 Ok(transports) => transports,
145 };
146 Ok(self.add_transports_raw(transports))
147 }
148
149 pub fn add_transports_raw(
150 mut self,
151 transports: impl IntoIterator<Item = RgbTransport>,
152 ) -> Self {
153 self.0.transports.extend(transports);
154 self
155 }
156
157 pub fn finish(self) -> RgbInvoice { self.0 }
158}