syn_solidity/type/
tuple.rs

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