1use std::cmp::Ordering;
23use std::fmt::{self, Display, Formatter};
24use std::str::FromStr;
25
26use amplify::confinement::{TinyOrdMap, TinyOrdSet};
27use amplify::{ByteArray, Bytes32};
28use baid58::{Baid58ParseError, Chunking, FromBaid58, ToBaid58, CHUNKING_32};
29use commit_verify::{CommitStrategy, CommitmentId};
30use rgb::Occurrences;
31use strict_encoding::{
32 FieldName, StrictDecode, StrictDeserialize, StrictDumb, StrictEncode, StrictSerialize,
33 StrictType, TypeName,
34};
35use strict_types::{SemId, TypeSystem};
36
37use crate::interface::VerNo;
38use crate::LIB_NAME_RGB_STD;
39
40#[derive(Wrapper, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, From)]
44#[wrapper(Deref, BorrowSlice, Hex, Index, RangeOps)]
45#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)]
46#[strict_type(lib = LIB_NAME_RGB_STD)]
47#[cfg_attr(
48 feature = "serde",
49 derive(Serialize, Deserialize),
50 serde(crate = "serde_crate", transparent)
51)]
52pub struct IfaceId(
53 #[from]
54 #[from([u8; 32])]
55 Bytes32,
56);
57
58impl ToBaid58<32> for IfaceId {
59 const HRI: &'static str = "if";
60 const CHUNKING: Option<Chunking> = CHUNKING_32;
61 fn to_baid58_payload(&self) -> [u8; 32] { self.to_byte_array() }
62 fn to_baid58_string(&self) -> String { self.to_string() }
63}
64impl FromBaid58<32> for IfaceId {}
65impl Display for IfaceId {
66 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
67 if !f.alternate() {
68 f.write_str("urn:lnp-bp:if:")?;
69 }
70 if f.sign_minus() {
71 write!(f, "{:.2}", self.to_baid58())
72 } else {
73 write!(f, "{:#.2}", self.to_baid58())
74 }
75 }
76}
77impl FromStr for IfaceId {
78 type Err = Baid58ParseError;
79 fn from_str(s: &str) -> Result<Self, Self::Err> {
80 Self::from_baid58_maybe_chunked_str(s.trim_start_matches("urn:lnp-bp:"), ':', '#')
81 }
82}
83impl IfaceId {
84 pub fn to_mnemonic(&self) -> String { self.to_baid58().mnemonic() }
85}
86
87#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug)]
88pub enum Req {
89 Optional,
90 Required,
91 NoneOrMore,
92 OneOrMore,
93}
94
95impl Req {
96 pub fn is_required(self) -> bool { self == Req::Required || self == Req::OneOrMore }
97 pub fn is_multiple(self) -> bool { self == Req::NoneOrMore || self == Req::OneOrMore }
98}
99
100#[derive(Clone, PartialEq, Eq, Hash, Debug)]
101#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)]
102#[strict_type(lib = LIB_NAME_RGB_STD)]
103#[cfg_attr(
104 feature = "serde",
105 derive(Serialize, Deserialize),
106 serde(crate = "serde_crate", rename_all = "camelCase")
107)]
108pub struct ValencyIface {
109 pub required: bool,
110 pub multiple: bool,
111}
112
113#[derive(Clone, PartialEq, Eq, Hash, Debug)]
114#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)]
115#[strict_type(lib = LIB_NAME_RGB_STD)]
116#[cfg_attr(
117 feature = "serde",
118 derive(Serialize, Deserialize),
119 serde(crate = "serde_crate", rename_all = "camelCase")
120)]
121pub struct GlobalIface {
122 pub sem_id: Option<SemId>,
123 pub required: bool,
124 pub multiple: bool,
125}
126
127impl GlobalIface {
128 pub fn any(req: Req) -> Self {
129 GlobalIface {
130 sem_id: None,
131 required: req.is_required(),
132 multiple: req.is_multiple(),
133 }
134 }
135 pub fn optional(sem_id: SemId) -> Self {
136 GlobalIface {
137 sem_id: Some(sem_id),
138 required: false,
139 multiple: false,
140 }
141 }
142 pub fn required(sem_id: SemId) -> Self {
143 GlobalIface {
144 sem_id: Some(sem_id),
145 required: true,
146 multiple: false,
147 }
148 }
149 pub fn none_or_many(sem_id: SemId) -> Self {
150 GlobalIface {
151 sem_id: Some(sem_id),
152 required: false,
153 multiple: true,
154 }
155 }
156 pub fn one_or_many(sem_id: SemId) -> Self {
157 GlobalIface {
158 sem_id: Some(sem_id),
159 required: true,
160 multiple: true,
161 }
162 }
163}
164
165#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
166#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)]
167#[strict_type(lib = LIB_NAME_RGB_STD, tags = order)]
168#[cfg_attr(
169 feature = "serde",
170 derive(Serialize, Deserialize),
171 serde(crate = "serde_crate", rename_all = "camelCase")
172)]
173pub struct AssignIface {
174 pub owned_state: OwnedIface,
175 pub public: bool,
176 pub required: bool,
177 pub multiple: bool,
178}
179
180impl AssignIface {
181 pub fn public(owned_state: OwnedIface, req: Req) -> Self {
182 AssignIface {
183 owned_state,
184 public: true,
185 required: req.is_required(),
186 multiple: req.is_multiple(),
187 }
188 }
189
190 pub fn private(owned_state: OwnedIface, req: Req) -> Self {
191 AssignIface {
192 owned_state,
193 public: false,
194 required: req.is_required(),
195 multiple: req.is_multiple(),
196 }
197 }
198}
199
200#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
201#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)]
202#[strict_type(lib = LIB_NAME_RGB_STD, tags = order)]
203#[cfg_attr(
204 feature = "serde",
205 derive(Serialize, Deserialize),
206 serde(crate = "serde_crate", rename_all = "camelCase")
207)]
208pub enum OwnedIface {
209 #[strict_type(dumb)]
210 Any,
211 Rights,
212 Amount,
213 AnyData,
214 AnyAttach,
215 Data(SemId),
216}
217
218pub type ArgMap = TinyOrdMap<FieldName, ArgSpec>;
219
220#[derive(Clone, PartialEq, Eq, Debug, Default)]
223#[derive(StrictType, StrictEncode, StrictDecode)]
224#[strict_type(lib = LIB_NAME_RGB_STD)]
225#[cfg_attr(
226 feature = "serde",
227 derive(Serialize, Deserialize),
228 serde(crate = "serde_crate", rename_all = "camelCase")
229)]
230pub struct ArgSpec {
231 pub name: Option<FieldName>,
235 pub req: Occurrences,
237}
238
239impl ArgSpec {
240 pub fn new(req: Occurrences) -> Self { ArgSpec { name: None, req } }
241
242 pub fn required() -> Self { ArgSpec::new(Occurrences::Once) }
243
244 pub fn optional() -> Self { ArgSpec::new(Occurrences::NoneOrOnce) }
245
246 pub fn non_empty() -> Self { ArgSpec::new(Occurrences::OnceOrMore) }
247
248 pub fn many() -> Self { ArgSpec::new(Occurrences::NoneOrMore) }
249
250 pub fn with(name: &'static str, req: Occurrences) -> Self {
251 ArgSpec {
252 name: Some(FieldName::from(name)),
253 req,
254 }
255 }
256
257 pub fn from_required(name: &'static str) -> Self { ArgSpec::with(name, Occurrences::Once) }
258
259 pub fn from_optional(name: &'static str) -> Self {
260 ArgSpec::with(name, Occurrences::NoneOrOnce)
261 }
262
263 pub fn from_non_empty(name: &'static str) -> Self {
264 ArgSpec::with(name, Occurrences::OnceOrMore)
265 }
266
267 pub fn from_many(name: &'static str) -> Self { ArgSpec::with(name, Occurrences::NoneOrMore) }
268}
269
270#[derive(Clone, PartialEq, Eq, Debug)]
271#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)]
272#[strict_type(lib = LIB_NAME_RGB_STD)]
273#[cfg_attr(
274 feature = "serde",
275 derive(Serialize, Deserialize),
276 serde(crate = "serde_crate", rename_all = "camelCase")
277)]
278pub struct GenesisIface {
279 pub metadata: Option<SemId>,
280 pub global: ArgMap,
281 pub assignments: ArgMap,
282 pub valencies: ArgMap,
283 pub errors: TinyOrdSet<u8>,
284}
285
286#[derive(Clone, PartialEq, Eq, Debug)]
287#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)]
288#[strict_type(lib = LIB_NAME_RGB_STD)]
289#[cfg_attr(
290 feature = "serde",
291 derive(Serialize, Deserialize),
292 serde(crate = "serde_crate", rename_all = "camelCase")
293)]
294pub struct ExtensionIface {
295 pub metadata: Option<SemId>,
296 pub globals: ArgMap,
297 pub redeems: ArgMap,
298 pub assignments: ArgMap,
299 pub valencies: ArgMap,
300 pub errors: TinyOrdSet<u8>,
301}
302
303#[derive(Clone, PartialEq, Eq, Debug)]
304#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)]
305#[strict_type(lib = LIB_NAME_RGB_STD)]
306#[cfg_attr(
307 feature = "serde",
308 derive(Serialize, Deserialize),
309 serde(crate = "serde_crate", rename_all = "camelCase")
310)]
311pub struct TransitionIface {
312 pub optional: bool,
314 pub metadata: Option<SemId>,
315 pub globals: ArgMap,
316 pub inputs: ArgMap,
317 pub assignments: ArgMap,
318 pub valencies: ArgMap,
319 pub errors: TinyOrdSet<u8>,
320 pub default_assignment: Option<FieldName>,
321}
322
323#[derive(Clone, Eq, Debug)]
325#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)]
326#[strict_type(lib = LIB_NAME_RGB_STD)]
327#[cfg_attr(
328 feature = "serde",
329 derive(Serialize, Deserialize),
330 serde(crate = "serde_crate", rename_all = "camelCase")
331)]
332pub struct Iface {
333 pub version: VerNo,
334 pub name: TypeName,
335 pub global_state: TinyOrdMap<FieldName, GlobalIface>,
336 pub assignments: TinyOrdMap<FieldName, AssignIface>,
337 pub valencies: TinyOrdMap<FieldName, ValencyIface>,
338 pub genesis: GenesisIface,
339 pub transitions: TinyOrdMap<TypeName, TransitionIface>,
340 pub extensions: TinyOrdMap<TypeName, ExtensionIface>,
341 pub error_type: SemId,
342 pub default_operation: Option<TypeName>,
343 pub type_system: TypeSystem,
344}
345
346impl PartialEq for Iface {
347 fn eq(&self, other: &Self) -> bool { self.iface_id() == other.iface_id() }
348}
349
350impl Ord for Iface {
351 fn cmp(&self, other: &Self) -> Ordering { self.iface_id().cmp(&other.iface_id()) }
352}
353
354impl PartialOrd for Iface {
355 fn partial_cmp(&self, other: &Self) -> Option<Ordering> { Some(self.cmp(other)) }
356}
357
358impl CommitStrategy for Iface {
359 type Strategy = commit_verify::strategies::Strict;
360}
361
362impl CommitmentId for Iface {
363 const TAG: [u8; 32] = *b"urn:lnpbp:rgb:interface:v01#2303";
364 type Id = IfaceId;
365}
366
367impl StrictSerialize for Iface {}
368impl StrictDeserialize for Iface {}
369
370impl Iface {
371 #[inline]
372 pub fn iface_id(&self) -> IfaceId { self.commitment_id() }
373}