tycho_types/abi/value/
mod.rs

1use std::collections::BTreeMap;
2use std::num::NonZeroU8;
3use std::sync::Arc;
4
5use anyhow::Result;
6use bytes::Bytes;
7use num_bigint::{BigInt, BigUint};
8
9use super::ty::*;
10use super::{IntoAbi, IntoPlainAbi, WithAbiType, WithPlainAbiType, WithoutName};
11use crate::abi::error::AbiError;
12use crate::cell::{Cell, CellFamily};
13use crate::models::IntAddr;
14use crate::num::Tokens;
15
16mod de;
17pub(crate) mod ser;
18
19/// ABI value with name.
20#[derive(Debug, Clone, Eq, PartialEq)]
21pub struct NamedAbiValue {
22    /// Item name.
23    pub name: Arc<str>,
24    /// ABI value.
25    pub value: AbiValue,
26}
27
28impl NamedAbiValue {
29    /// Ensures that all values satisfy the provided types.
30    pub fn check_types(items: &[Self], types: &[NamedAbiType]) -> Result<()> {
31        anyhow::ensure!(Self::have_types(items, types), AbiError::TypeMismatch {
32            expected: DisplayTupleType(types).to_string().into(),
33            ty: DisplayTupleValueType(items).to_string().into(),
34        });
35        Ok(())
36    }
37
38    /// Returns whether all values satisfy the provided types.
39    pub fn have_types(items: &[Self], types: &[NamedAbiType]) -> bool {
40        items.len() == types.len()
41            && items
42                .iter()
43                .zip(types.iter())
44                .all(|(item, t)| item.value.has_type(&t.ty))
45    }
46
47    /// Creates a named ABI value with an index name (e.g. `value123`).
48    pub fn from_index(index: usize, value: AbiValue) -> Self {
49        Self {
50            name: format!("value{index}").into(),
51            value,
52        }
53    }
54
55    /// Ensures that value satisfies the type.
56    pub fn check_type<T: AsRef<AbiType>>(&self, ty: T) -> Result<()> {
57        fn type_mismatch(this: &NamedAbiValue, expected: &AbiType) -> AbiError {
58            AbiError::TypeMismatch {
59                expected: expected.to_string().into(),
60                ty: this.value.display_type().to_string().into(),
61            }
62        }
63        let ty = ty.as_ref();
64        anyhow::ensure!(self.value.has_type(ty), type_mismatch(self, ty));
65        Ok(())
66    }
67}
68
69impl From<(String, AbiValue)> for NamedAbiValue {
70    #[inline]
71    fn from((name, value): (String, AbiValue)) -> Self {
72        Self {
73            name: name.into(),
74            value,
75        }
76    }
77}
78
79impl<'a> From<(&'a str, AbiValue)> for NamedAbiValue {
80    #[inline]
81    fn from((name, value): (&'a str, AbiValue)) -> Self {
82        Self {
83            name: Arc::from(name),
84            value,
85        }
86    }
87}
88
89impl From<(usize, AbiValue)> for NamedAbiValue {
90    #[inline]
91    fn from((index, value): (usize, AbiValue)) -> Self {
92        Self::from_index(index, value)
93    }
94}
95
96impl NamedAbiType {
97    /// Returns a default value corresponding to the this type.
98    pub fn make_default_value(&self) -> NamedAbiValue {
99        NamedAbiValue {
100            name: self.name.clone(),
101            value: self.ty.make_default_value(),
102        }
103    }
104}
105
106impl PartialEq for WithoutName<NamedAbiValue> {
107    #[inline]
108    fn eq(&self, other: &Self) -> bool {
109        WithoutName::wrap(&self.0.value).eq(WithoutName::wrap(&other.0.value))
110    }
111}
112
113impl std::borrow::Borrow<WithoutName<AbiValue>> for WithoutName<NamedAbiValue> {
114    fn borrow(&self) -> &WithoutName<AbiValue> {
115        WithoutName::wrap(&self.0.value)
116    }
117}
118
119/// ABI encoded value.
120#[derive(Debug, Clone, Eq, PartialEq)]
121pub enum AbiValue {
122    /// Unsigned integer of n bits.
123    Uint(u16, BigUint),
124    /// Signed integer of n bits.
125    Int(u16, BigInt),
126    /// Variable-length unsigned integer of maximum n bytes.
127    VarUint(NonZeroU8, BigUint),
128    /// Variable-length signed integer of maximum n bytes.
129    VarInt(NonZeroU8, BigInt),
130    /// Boolean.
131    Bool(bool),
132    /// Tree of cells ([`Cell`]).
133    ///
134    /// [`Cell`]: crate::cell::Cell
135    Cell(Cell),
136    /// Internal address ([`IntAddr`]).
137    ///
138    /// [`IntAddr`]: crate::models::message::IntAddr
139    Address(Box<IntAddr>),
140    /// Byte array.
141    Bytes(Bytes),
142    /// Byte array of fixed length.
143    FixedBytes(Bytes),
144    /// Utf8-encoded string.
145    String(String),
146    /// Variable length 120-bit integer ([`Tokens`]).
147    ///
148    /// [`Tokens`]: crate::num::Tokens
149    Token(Tokens),
150    /// Product type.
151    Tuple(Vec<NamedAbiValue>),
152    /// Array of ABI values.
153    Array(Arc<AbiType>, Vec<Self>),
154    /// Fixed-length array of ABI values.
155    FixedArray(Arc<AbiType>, Vec<Self>),
156    /// Sorted dictionary of ABI values.
157    Map(
158        PlainAbiType,
159        Arc<AbiType>,
160        BTreeMap<PlainAbiValue, AbiValue>,
161    ),
162    /// Optional value.
163    Optional(Arc<AbiType>, Option<Box<Self>>),
164    /// Value stored in a new cell.
165    Ref(Box<Self>),
166}
167
168impl AbiValue {
169    /// Returns a named ABI value.
170    pub fn named<T: Into<String>>(self, name: T) -> NamedAbiValue {
171        NamedAbiValue {
172            name: Arc::from(name.into()),
173            value: self,
174        }
175    }
176
177    /// Ensures that value satisfies the type.
178    pub fn check_type<T: AsRef<AbiType>>(&self, ty: T) -> Result<()> {
179        fn type_mismatch(value: &AbiValue, expected: &AbiType) -> AbiError {
180            AbiError::TypeMismatch {
181                expected: expected.to_string().into(),
182                ty: value.display_type().to_string().into(),
183            }
184        }
185        let ty = ty.as_ref();
186        anyhow::ensure!(self.has_type(ty), type_mismatch(self, ty));
187        Ok(())
188    }
189
190    /// Returns whether this value has the same type as the provided one.
191    pub fn has_type(&self, ty: &AbiType) -> bool {
192        match (self, ty) {
193            (Self::Uint(n, _), AbiType::Uint(t)) => n == t,
194            (Self::Int(n, _), AbiType::Int(t)) => n == t,
195            (Self::VarUint(n, _), AbiType::VarUint(t)) => n == t,
196            (Self::VarInt(n, _), AbiType::VarInt(t)) => n == t,
197            (Self::FixedBytes(bytes), AbiType::FixedBytes(len)) => bytes.len() == *len,
198            (Self::Tuple(items), AbiType::Tuple(types)) => NamedAbiValue::have_types(items, types),
199            (Self::Array(ty, _), AbiType::Array(t)) => ty == t,
200            (Self::FixedArray(ty, items), AbiType::FixedArray(t, len)) => {
201                items.len() == *len && ty == t
202            }
203            (Self::Map(key_ty, value_ty, _), AbiType::Map(k, v)) => key_ty == k && value_ty == v,
204            (Self::Optional(ty, _), AbiType::Optional(t)) => ty == t,
205            (Self::Ref(value), AbiType::Ref(t)) => value.has_type(t),
206            (Self::Bool(_), AbiType::Bool)
207            | (Self::Cell(_), AbiType::Cell)
208            | (Self::Address(_), AbiType::Address)
209            | (Self::Bytes(_), AbiType::Bytes)
210            | (Self::String(_), AbiType::String)
211            | (Self::Token(_), AbiType::Token) => true,
212            _ => false,
213        }
214    }
215
216    /// Returns an ABI type of the value.
217    pub fn get_type(&self) -> AbiType {
218        match self {
219            AbiValue::Uint(n, _) => AbiType::Uint(*n),
220            AbiValue::Int(n, _) => AbiType::Int(*n),
221            AbiValue::VarUint(n, _) => AbiType::VarUint(*n),
222            AbiValue::VarInt(n, _) => AbiType::VarInt(*n),
223            AbiValue::Bool(_) => AbiType::Bool,
224            AbiValue::Cell(_) => AbiType::Cell,
225            AbiValue::Address(_) => AbiType::Address,
226            AbiValue::Bytes(_) => AbiType::Bytes,
227            AbiValue::FixedBytes(bytes) => AbiType::FixedBytes(bytes.len()),
228            AbiValue::String(_) => AbiType::String,
229            AbiValue::Token(_) => AbiType::Token,
230            AbiValue::Tuple(items) => AbiType::Tuple(
231                items
232                    .iter()
233                    .map(|item| NamedAbiType::new(item.name.clone(), item.value.get_type()))
234                    .collect(),
235            ),
236            AbiValue::Array(ty, _) => AbiType::Array(ty.clone()),
237            AbiValue::FixedArray(ty, items) => AbiType::FixedArray(ty.clone(), items.len()),
238            AbiValue::Map(key_ty, value_ty, _) => AbiType::Map(*key_ty, value_ty.clone()),
239            AbiValue::Optional(ty, _) => AbiType::Optional(ty.clone()),
240            AbiValue::Ref(value) => AbiType::Ref(Arc::new(value.get_type())),
241        }
242    }
243
244    /// Returns a printable object which will display a value type signature.
245    #[inline]
246    pub fn display_type(&self) -> impl std::fmt::Display + '_ {
247        DisplayValueType(self)
248    }
249
250    /// Simple `uintN` constructor.
251    #[inline]
252    pub fn uint<T>(bits: u16, value: T) -> Self
253    where
254        BigUint: From<T>,
255    {
256        Self::Uint(bits, BigUint::from(value))
257    }
258
259    /// Simple `intN` constructor.
260    #[inline]
261    pub fn int<T>(bits: u16, value: T) -> Self
262    where
263        BigInt: From<T>,
264    {
265        Self::Int(bits, BigInt::from(value))
266    }
267
268    /// Simple `varuintN` constructor.
269    #[inline]
270    pub fn varuint<T>(size: u8, value: T) -> Self
271    where
272        BigUint: From<T>,
273    {
274        Self::VarUint(NonZeroU8::new(size).unwrap(), BigUint::from(value))
275    }
276
277    /// Simple `varintN` constructor.
278    #[inline]
279    pub fn varint<T>(size: u8, value: T) -> Self
280    where
281        BigInt: From<T>,
282    {
283        Self::VarInt(NonZeroU8::new(size).unwrap(), BigInt::from(value))
284    }
285
286    /// Simple `address` constructor.
287    #[inline]
288    pub fn address<T>(value: T) -> Self
289    where
290        IntAddr: From<T>,
291    {
292        Self::Address(Box::new(IntAddr::from(value)))
293    }
294
295    /// Simple `bytes` constructor.
296    #[inline]
297    pub fn bytes<T>(value: T) -> Self
298    where
299        Bytes: From<T>,
300    {
301        Self::Bytes(Bytes::from(value))
302    }
303
304    /// Simple `bytes` constructor.
305    #[inline]
306    pub fn fixedbytes<T>(value: T) -> Self
307    where
308        Bytes: From<T>,
309    {
310        Self::FixedBytes(Bytes::from(value))
311    }
312
313    /// Simple `tuple` constructor.
314    #[inline]
315    pub fn tuple<I, T>(values: I) -> Self
316    where
317        I: IntoIterator<Item = T>,
318        NamedAbiValue: From<T>,
319    {
320        Self::Tuple(values.into_iter().map(NamedAbiValue::from).collect())
321    }
322
323    /// Simple `tuple` constructor.
324    #[inline]
325    pub fn unnamed_tuple<I>(values: I) -> Self
326    where
327        I: IntoIterator<Item = AbiValue>,
328    {
329        Self::Tuple(
330            values
331                .into_iter()
332                .enumerate()
333                .map(|(i, value)| NamedAbiValue::from_index(i, value))
334                .collect(),
335        )
336    }
337
338    /// Simple `array` constructor.
339    #[inline]
340    pub fn array<T, I>(values: I) -> Self
341    where
342        T: WithAbiType + IntoAbi,
343        I: IntoIterator<Item = T>,
344    {
345        Self::Array(
346            Arc::new(T::abi_type()),
347            values.into_iter().map(IntoAbi::into_abi).collect(),
348        )
349    }
350
351    /// Simple `fixedarray` constructor.
352    #[inline]
353    pub fn fixedarray<T, I>(values: I) -> Self
354    where
355        T: WithAbiType + IntoAbi,
356        I: IntoIterator<Item = T>,
357    {
358        Self::FixedArray(
359            Arc::new(T::abi_type()),
360            values.into_iter().map(IntoAbi::into_abi).collect(),
361        )
362    }
363
364    /// Simple `map` constructor.
365    #[inline]
366    pub fn map<K, V, I>(entries: I) -> Self
367    where
368        K: WithPlainAbiType + IntoPlainAbi,
369        V: WithAbiType + IntoAbi,
370        I: IntoIterator<Item = (K, V)>,
371    {
372        Self::Map(
373            K::plain_abi_type(),
374            Arc::new(V::abi_type()),
375            entries
376                .into_iter()
377                .map(|(key, value)| (key.into_plain_abi(), value.into_abi()))
378                .collect(),
379        )
380    }
381
382    /// Simple `optional` constructor.
383    #[inline]
384    pub fn optional<T>(value: Option<T>) -> Self
385    where
386        T: WithAbiType + IntoAbi,
387    {
388        Self::Optional(
389            Arc::new(T::abi_type()),
390            value.map(T::into_abi).map(Box::new),
391        )
392    }
393
394    /// Simple `optional` constructor.
395    #[inline]
396    pub fn reference<T>(value: T) -> Self
397    where
398        T: IntoAbi,
399    {
400        Self::Ref(Box::new(value.into_abi()))
401    }
402}
403
404impl AbiType {
405    /// Returns a default value corresponding to the this type.
406    pub fn make_default_value(&self) -> AbiValue {
407        match self {
408            AbiType::Uint(bits) => AbiValue::Uint(*bits, BigUint::default()),
409            AbiType::Int(bits) => AbiValue::Int(*bits, BigInt::default()),
410            AbiType::VarUint(size) => AbiValue::VarUint(*size, BigUint::default()),
411            AbiType::VarInt(size) => AbiValue::VarInt(*size, BigInt::default()),
412            AbiType::Bool => AbiValue::Bool(false),
413            AbiType::Cell => AbiValue::Cell(Cell::empty_cell()),
414            AbiType::Address => AbiValue::Address(Box::default()),
415            AbiType::Bytes => AbiValue::Bytes(Bytes::default()),
416            AbiType::FixedBytes(len) => AbiValue::FixedBytes(Bytes::from(vec![0u8; *len])),
417            AbiType::String => AbiValue::String(String::default()),
418            AbiType::Token => AbiValue::Token(Tokens::ZERO),
419            AbiType::Tuple(items) => {
420                let mut tuple = Vec::with_capacity(items.len());
421                for item in items.as_ref() {
422                    tuple.push(item.make_default_value());
423                }
424                AbiValue::Tuple(tuple)
425            }
426            AbiType::Array(ty) => AbiValue::Array(ty.clone(), Vec::new()),
427            AbiType::FixedArray(ty, items) => {
428                AbiValue::FixedArray(ty.clone(), vec![ty.make_default_value(); *items])
429            }
430            AbiType::Map(key_ty, value_ty) => {
431                AbiValue::Map(*key_ty, value_ty.clone(), BTreeMap::default())
432            }
433            AbiType::Optional(ty) => AbiValue::Optional(ty.clone(), None),
434            AbiType::Ref(ty) => AbiValue::Ref(Box::new(ty.make_default_value())),
435        }
436    }
437}
438
439impl PartialEq for WithoutName<AbiValue> {
440    fn eq(&self, other: &Self) -> bool {
441        match (&self.0, &other.0) {
442            (AbiValue::Uint(an, a), AbiValue::Uint(bn, b)) => an.eq(bn) && a.eq(b),
443            (AbiValue::Int(an, a), AbiValue::Int(bn, b)) => an.eq(bn) && a.eq(b),
444            (AbiValue::VarUint(an, a), AbiValue::VarUint(bn, b)) => an.eq(bn) && a.eq(b),
445            (AbiValue::VarInt(an, a), AbiValue::VarInt(bn, b)) => an.eq(bn) && a.eq(b),
446            (AbiValue::Bool(a), AbiValue::Bool(b)) => a.eq(b),
447            (AbiValue::Cell(a), AbiValue::Cell(b)) => a.eq(b),
448            (AbiValue::Address(a), AbiValue::Address(b)) => a.eq(b),
449            (AbiValue::Bytes(a), AbiValue::Bytes(b)) => a.eq(b),
450            (AbiValue::FixedBytes(a), AbiValue::FixedBytes(b)) => a.eq(b),
451            (AbiValue::String(a), AbiValue::String(b)) => a.eq(b),
452            (AbiValue::Token(a), AbiValue::Token(b)) => a.eq(b),
453            (AbiValue::Tuple(a), AbiValue::Tuple(b)) => {
454                WithoutName::wrap_slice(a.as_slice()).eq(WithoutName::wrap_slice(b.as_slice()))
455            }
456            (AbiValue::Array(at, av), AbiValue::Array(bt, bv))
457            | (AbiValue::FixedArray(at, av), AbiValue::FixedArray(bt, bv)) => {
458                WithoutName::wrap(at.as_ref()).eq(WithoutName::wrap(bt.as_ref()))
459                    && WithoutName::wrap_slice(av.as_slice())
460                        .eq(WithoutName::wrap_slice(bv.as_slice()))
461            }
462            (AbiValue::Map(akt, avt, a), AbiValue::Map(bkt, bvt, b)) => {
463                akt.eq(bkt)
464                    && WithoutName::wrap(avt.as_ref()).eq(WithoutName::wrap(bvt.as_ref()))
465                    && WithoutName::wrap(a).eq(WithoutName::wrap(b))
466            }
467            (AbiValue::Optional(at, a), AbiValue::Optional(bt, b)) => {
468                WithoutName::wrap(at.as_ref()).eq(WithoutName::wrap(bt.as_ref()))
469                    && a.as_deref()
470                        .map(WithoutName::wrap)
471                        .eq(&b.as_deref().map(WithoutName::wrap))
472            }
473            (AbiValue::Ref(a), AbiValue::Ref(b)) => {
474                WithoutName::wrap(a.as_ref()).eq(WithoutName::wrap(b.as_ref()))
475            }
476            _unused => false,
477        }
478    }
479}
480
481/// ABI value which has a fixed bits representation
482/// and therefore can be used as a map key.
483#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd)]
484pub enum PlainAbiValue {
485    /// Unsigned integer of n bits.
486    Uint(u16, BigUint),
487    /// Signed integer of n bits.
488    Int(u16, BigInt),
489    /// Boolean.
490    Bool(bool),
491    /// Internal address ([`IntAddr`]).
492    ///
493    /// [`IntAddr`]: crate::models::message::IntAddr
494    Address(Box<IntAddr>),
495}
496
497impl PlainAbiValue {
498    /// Returns whether this value has the same type as the provided one.
499    pub fn has_type(&self, ty: &PlainAbiType) -> bool {
500        match (self, ty) {
501            (Self::Uint(n, _), PlainAbiType::Uint(t)) => n == t,
502            (Self::Int(n, _), PlainAbiType::Int(t)) => n == t,
503            (Self::Bool(_), PlainAbiType::Bool) | (Self::Address(_), PlainAbiType::Address) => true,
504            _ => false,
505        }
506    }
507
508    /// Returns a printable object which will display a value type signature.
509    #[inline]
510    pub fn display_type(&self) -> impl std::fmt::Display + '_ {
511        DisplayPlainValueType(self)
512    }
513}
514
515impl From<PlainAbiValue> for AbiValue {
516    fn from(value: PlainAbiValue) -> Self {
517        match value {
518            PlainAbiValue::Uint(n, value) => Self::Uint(n, value),
519            PlainAbiValue::Int(n, value) => Self::Int(n, value),
520            PlainAbiValue::Bool(value) => Self::Bool(value),
521            PlainAbiValue::Address(value) => Self::Address(value),
522        }
523    }
524}
525
526/// Contract header value.
527#[derive(Debug, Clone, Eq, PartialEq)]
528pub enum AbiHeader {
529    /// `time` header.
530    Time(u64),
531    /// `expire` header.
532    Expire(u32),
533    /// `pubkey` header.
534    PublicKey(Option<Box<ed25519_dalek::VerifyingKey>>),
535}
536
537impl AbiHeader {
538    /// Returns whether this value has the same type as the provided one.
539    pub fn has_type(&self, ty: &AbiHeaderType) -> bool {
540        matches!(
541            (self, ty),
542            (Self::Time(_), AbiHeaderType::Time)
543                | (Self::Expire(_), AbiHeaderType::Expire)
544                | (Self::PublicKey(_), AbiHeaderType::PublicKey)
545        )
546    }
547
548    /// Returns a printable object which will display a header type signature.
549    #[inline]
550    pub fn display_type(&self) -> impl std::fmt::Display + '_ {
551        DisplayHeaderType(self)
552    }
553}
554
555struct DisplayHeaderType<'a>(&'a AbiHeader);
556
557impl std::fmt::Display for DisplayHeaderType<'_> {
558    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
559        f.write_str(match self.0 {
560            AbiHeader::Time(_) => "time",
561            AbiHeader::Expire(_) => "expire",
562            AbiHeader::PublicKey(_) => "pubkey",
563        })
564    }
565}
566
567struct DisplayPlainValueType<'a>(&'a PlainAbiValue);
568
569impl std::fmt::Display for DisplayPlainValueType<'_> {
570    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
571        f.write_str(match self.0 {
572            PlainAbiValue::Uint(n, _) => return write!(f, "uint{n}"),
573            PlainAbiValue::Int(n, _) => return write!(f, "int{n}"),
574            PlainAbiValue::Bool(_) => "bool",
575            PlainAbiValue::Address(_) => "address",
576        })
577    }
578}
579
580struct DisplayValueType<'a>(&'a AbiValue);
581
582impl std::fmt::Display for DisplayValueType<'_> {
583    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
584        let s = match self.0 {
585            AbiValue::Uint(n, _) => return write!(f, "uint{n}"),
586            AbiValue::Int(n, _) => return write!(f, "int{n}"),
587            AbiValue::VarUint(n, _) => return write!(f, "varuint{n}"),
588            AbiValue::VarInt(n, _) => return write!(f, "varint{n}"),
589            AbiValue::Bool(_) => "bool",
590            AbiValue::Cell(_) => "cell",
591            AbiValue::Address(_) => "address",
592            AbiValue::Bytes(_) => "bytes",
593            AbiValue::FixedBytes(bytes) => return write!(f, "fixedbytes{}", bytes.len()),
594            AbiValue::String(_) => "string",
595            AbiValue::Token(_) => "gram",
596            AbiValue::Tuple(items) => {
597                return std::fmt::Display::fmt(&DisplayTupleValueType(items), f)
598            }
599            AbiValue::Array(ty, _) => return write!(f, "{ty}[]"),
600            AbiValue::FixedArray(ty, items) => return write!(f, "{ty}[{}]", items.len()),
601            AbiValue::Map(key_ty, value_ty, _) => return write!(f, "map({key_ty},{value_ty})"),
602            AbiValue::Optional(ty, _) => return write!(f, "optional({ty})"),
603            AbiValue::Ref(val) => return write!(f, "ref({})", val.display_type()),
604        };
605        f.write_str(s)
606    }
607}
608
609struct DisplayTupleValueType<'a>(&'a [NamedAbiValue]);
610
611impl std::fmt::Display for DisplayTupleValueType<'_> {
612    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
613        let s = if self.0.is_empty() {
614            "()"
615        } else {
616            let mut first = true;
617            ok!(f.write_str("("));
618            for item in self.0 {
619                if !std::mem::take(&mut first) {
620                    ok!(f.write_str(","));
621                }
622                ok!(write!(f, "{}", item.value.display_type()));
623            }
624            ")"
625        };
626        f.write_str(s)
627    }
628}
629
630struct DisplayTupleType<'a>(&'a [NamedAbiType]);
631
632impl std::fmt::Display for DisplayTupleType<'_> {
633    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
634        let s = if self.0.is_empty() {
635            "()"
636        } else {
637            let mut first = true;
638            ok!(f.write_str("("));
639            for item in self.0 {
640                if !std::mem::take(&mut first) {
641                    ok!(f.write_str(","));
642                }
643                ok!(write!(f, "{}", item.ty));
644            }
645            ")"
646        };
647        f.write_str(s)
648    }
649}