syn_solidity/type/
array.rs

1use crate::{Expr, Lit, LitNumber, Spanned, Type};
2use proc_macro2::Span;
3use std::{
4    fmt,
5    hash::{Hash, Hasher},
6    num::NonZeroUsize,
7};
8use syn::{
9    bracketed,
10    parse::{discouraged::Speculative, Parse, ParseStream},
11    token::Bracket,
12    Result,
13};
14
15/// An array type.
16#[derive(Clone)]
17pub struct TypeArray {
18    pub ty: Box<Type>,
19    pub bracket_token: Bracket,
20    pub size: Option<Box<Expr>>,
21}
22
23impl PartialEq for TypeArray {
24    fn eq(&self, other: &Self) -> bool {
25        self.ty == other.ty && self.size() == other.size()
26    }
27}
28
29impl Eq for TypeArray {}
30
31impl Hash for TypeArray {
32    fn hash<H: Hasher>(&self, state: &mut H) {
33        self.ty.hash(state);
34        self.size().hash(state);
35    }
36}
37
38impl fmt::Display for TypeArray {
39    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
40        self.ty.fmt(f)?;
41        f.write_str("[")?;
42        if let Some(s) = self.size_lit() {
43            f.write_str(s.base10_digits())?;
44        }
45        f.write_str("]")
46    }
47}
48
49impl fmt::Debug for TypeArray {
50    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
51        f.debug_tuple("TypeArray").field(&self.ty).field(&self.size()).finish()
52    }
53}
54
55impl Parse for TypeArray {
56    fn parse(input: ParseStream<'_>) -> Result<Self> {
57        let ty = input.parse()?;
58        Self::parse_nested(Box::new(ty), input)
59    }
60}
61
62impl Spanned for TypeArray {
63    fn span(&self) -> Span {
64        let span = self.ty.span();
65        span.join(self.bracket_token.span.join()).unwrap_or(span)
66    }
67
68    fn set_span(&mut self, span: Span) {
69        self.ty.set_span(span);
70        self.bracket_token = Bracket(span);
71        if let Some(size) = &mut self.size {
72            size.set_span(span);
73        }
74    }
75}
76
77impl TypeArray {
78    /// Returns the size of the array, or None if dynamic.
79    pub fn size(&self) -> Option<usize> {
80        self.size_lit().map(|s| s.base10_parse().unwrap())
81    }
82
83    /// Returns the size of the array, or None if dynamic.
84    pub fn size_lit(&self) -> Option<&LitNumber> {
85        self.size.as_ref().map(|s| match &**s {
86            Expr::Lit(Lit::Number(n)) => n,
87            _ => panic!("unevaluated literal in array size"),
88        })
89    }
90
91    /// See [`Type::is_abi_dynamic`].
92    pub fn is_abi_dynamic(&self) -> bool {
93        match self.size {
94            Some(_) => self.ty.is_abi_dynamic(),
95            None => true,
96        }
97    }
98
99    /// Parses an array type from the given input stream, wrapping `ty` with it.
100    pub fn parse_nested(ty: Box<Type>, input: ParseStream<'_>) -> Result<Self> {
101        let content;
102        Ok(Self {
103            ty,
104            bracket_token: bracketed!(content in input),
105            size: {
106                if content.is_empty() {
107                    None
108                } else {
109                    let fork = content.fork();
110                    let sz = match fork.parse::<syn::LitInt>() {
111                        Ok(lit) => lit,
112                        Err(e) => {
113                            return Err(match fork.parse::<Expr>() {
114                                Ok(_) => syn::Error::new(
115                                    e.span(),
116                                    "generic expressions are not supported in array type sizes",
117                                ),
118                                Err(e) => e,
119                            })
120                        }
121                    };
122                    content.advance_to(&fork);
123                    // Validate the size
124                    sz.base10_parse::<NonZeroUsize>()?;
125                    Some(Box::new(Expr::Lit(Lit::Number(LitNumber::Int(sz)))))
126                }
127            },
128        })
129    }
130}