syn_solidity/type/
tuple.rs

1use crate::{Spanned, Type, kw, utils::DebugPunctuated};
2use proc_macro2::Span;
3use std::{
4    fmt,
5    hash::{Hash, Hasher},
6};
7use syn::{
8    Error, Result, Token, parenthesized,
9    parse::{Parse, ParseStream},
10    punctuated::Punctuated,
11    token::Paren,
12};
13
14/// A tuple type.
15#[derive(Clone)]
16pub struct TypeTuple {
17    pub tuple_token: Option<kw::tuple>,
18    pub paren_token: Paren,
19    pub types: Punctuated<Type, Token![,]>,
20}
21
22impl PartialEq for TypeTuple {
23    fn eq(&self, other: &Self) -> bool {
24        self.types == other.types
25    }
26}
27
28impl Eq for TypeTuple {}
29
30impl Hash for TypeTuple {
31    fn hash<H: Hasher>(&self, state: &mut H) {
32        self.types.hash(state);
33    }
34}
35
36impl fmt::Display for TypeTuple {
37    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
38        f.write_str("(")?;
39        for (i, ty) in self.types.iter().enumerate() {
40            if i > 0 {
41                f.write_str(",")?;
42            }
43            ty.fmt(f)?;
44        }
45        if self.types.len() == 1 {
46            f.write_str(",")?;
47        }
48        f.write_str(")")
49    }
50}
51
52impl fmt::Debug for TypeTuple {
53    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
54        f.debug_tuple("TypeTuple").field(DebugPunctuated::new(&self.types)).finish()
55    }
56}
57
58impl Parse for TypeTuple {
59    fn parse(input: ParseStream<'_>) -> Result<Self> {
60        let content;
61        let this = Self {
62            tuple_token: input.parse()?,
63            paren_token: parenthesized!(content in input),
64            types: content.parse_terminated(Type::parse, Token![,])?,
65        };
66        match this.types.len() {
67            0 => Err(Error::new(this.paren_token.span.join(), "empty tuples are not allowed")),
68            1 if !this.types.trailing_punct() => Err(Error::new(
69                this.paren_token.span.close(),
70                "single element tuples must have a trailing comma",
71            )),
72            _ => Ok(this),
73        }
74    }
75}
76
77impl FromIterator<Type> for TypeTuple {
78    fn from_iter<T: IntoIterator<Item = Type>>(iter: T) -> Self {
79        Self {
80            tuple_token: None,
81            paren_token: Paren::default(),
82            types: {
83                let mut types = iter.into_iter().collect::<Punctuated<_, _>>();
84                // ensure trailing comma for single item tuple
85                if !types.trailing_punct() && types.len() == 1 {
86                    types.push_punct(Default::default());
87                }
88                types
89            },
90        }
91    }
92}
93
94impl Spanned for TypeTuple {
95    fn span(&self) -> Span {
96        let span = self.paren_token.span.join();
97        self.tuple_token.and_then(|tuple_token| tuple_token.span.join(span)).unwrap_or(span)
98    }
99
100    fn set_span(&mut self, span: Span) {
101        if let Some(tuple_token) = &mut self.tuple_token {
102            tuple_token.span = span;
103        }
104        self.paren_token = Paren(span);
105    }
106}
107
108impl TypeTuple {
109    /// See [`Type::is_abi_dynamic`].
110    pub fn is_abi_dynamic(&self) -> bool {
111        self.types.iter().any(Type::is_abi_dynamic)
112    }
113}