1use super::{AstPath, Box, Expr, ParameterList, StateMutability, Visibility};
2use solar_interface::{Ident, Span, Spanned, Symbol, kw};
3use std::{borrow::Cow, fmt};
4
5#[derive(Debug)]
9pub struct Type<'ast> {
10 pub span: Span,
11 pub kind: TypeKind<'ast>,
12}
13
14impl Type<'_> {
15 #[inline]
17 pub fn is_elementary(&self) -> bool {
18 self.kind.is_elementary()
19 }
20
21 #[inline]
23 pub fn is_custom(&self) -> bool {
24 self.kind.is_custom()
25 }
26
27 #[inline]
29 pub fn is_function(&self) -> bool {
30 matches!(self.kind, TypeKind::Function(_))
31 }
32}
33
34pub enum TypeKind<'ast> {
36 Elementary(ElementaryType),
38
39 Array(Box<'ast, TypeArray<'ast>>),
41 Function(Box<'ast, TypeFunction<'ast>>),
43 Mapping(Box<'ast, TypeMapping<'ast>>),
45
46 Custom(AstPath<'ast>),
48}
49
50impl fmt::Debug for TypeKind<'_> {
51 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
52 match self {
53 Self::Elementary(ty) => ty.fmt(f),
54 Self::Array(ty) => ty.fmt(f),
55 Self::Function(ty) => ty.fmt(f),
56 Self::Mapping(ty) => ty.fmt(f),
57 Self::Custom(path) => write!(f, "Custom({path:?})"),
58 }
59 }
60}
61
62impl TypeKind<'_> {
63 pub fn is_elementary(&self) -> bool {
67 matches!(self, Self::Elementary(_))
68 }
69
70 pub fn is_custom(&self) -> bool {
72 matches!(self, Self::Custom(_))
73 }
74}
75
76#[derive(Clone, Copy, PartialEq, Eq, Hash)]
80pub enum ElementaryType {
81 Address(bool),
84 Bool,
87 String,
90 Bytes,
93
94 Fixed(TypeSize, TypeFixedSize),
97 UFixed(TypeSize, TypeFixedSize),
100
101 Int(TypeSize),
106 UInt(TypeSize),
111 FixedBytes(TypeSize),
115}
116
117impl fmt::Debug for ElementaryType {
118 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
119 match self {
120 Self::Address(false) => f.write_str("Address"),
121 Self::Address(true) => f.write_str("AddressPayable"),
122 Self::Bool => f.write_str("Bool"),
123 Self::String => f.write_str("String"),
124 Self::Bytes => f.write_str("Bytes"),
125 Self::Fixed(size, fixed) => write!(f, "Fixed({}, {})", size.bytes_raw(), fixed.get()),
126 Self::UFixed(size, fixed) => write!(f, "UFixed({}, {})", size.bytes_raw(), fixed.get()),
127 Self::Int(size) => write!(f, "Int({})", size.bits_raw()),
128 Self::UInt(size) => write!(f, "UInt({})", size.bits_raw()),
129 Self::FixedBytes(size) => write!(f, "FixedBytes({})", size.bytes_raw()),
130 }
131 }
132}
133
134impl fmt::Display for ElementaryType {
135 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
136 self.write_abi_str(f)?;
137 if let Self::Address(true) = self {
138 f.write_str(" payable")?;
139 }
140 Ok(())
141 }
142}
143
144impl ElementaryType {
145 pub fn to_abi_str(self) -> Cow<'static, str> {
147 match self {
148 Self::Address(_) => "address".into(),
149 Self::Bool => "bool".into(),
150 Self::String => "string".into(),
151 Self::Bytes => "bytes".into(),
152 Self::Fixed(_size, _fixed) => "fixed".into(),
153 Self::UFixed(_size, _fixed) => "ufixed".into(),
154 Self::Int(size) => format!("int{}", size.bits()).into(),
155 Self::UInt(size) => format!("uint{}", size.bits()).into(),
156 Self::FixedBytes(size) => format!("bytes{}", size.bytes()).into(),
157 }
158 }
159
160 pub fn write_abi_str<W: fmt::Write + ?Sized>(self, f: &mut W) -> fmt::Result {
162 f.write_str(match self {
163 Self::Address(_) => "address",
164 Self::Bool => "bool",
165 Self::String => "string",
166 Self::Bytes => "bytes",
167 Self::Fixed(m, n) => return write!(f, "fixed{}x{}", m.bits(), n.get()),
168 Self::UFixed(m, n) => return write!(f, "ufixed{}x{}", m.bits(), n.get()),
169 Self::Int(size) => return write!(f, "int{}", size.bits()),
170 Self::UInt(size) => return write!(f, "uint{}", size.bits()),
171 Self::FixedBytes(size) => return write!(f, "bytes{}", size.bytes()),
172 })
173 }
174
175 #[inline]
179 pub const fn is_value_type(self) -> bool {
180 matches!(
181 self,
182 Self::Address(_)
183 | Self::Bool
184 | Self::Fixed(..)
185 | Self::UFixed(..)
186 | Self::Int(..)
187 | Self::UInt(..)
188 | Self::FixedBytes(..)
189 )
190 }
191
192 #[inline]
194 pub const fn is_reference_type(self) -> bool {
195 matches!(self, Self::String | Self::Bytes)
196 }
197}
198
199#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
201pub struct TypeSize(u8);
202
203impl fmt::Debug for TypeSize {
204 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
205 write!(f, "TypeSize({})", self.0)
206 }
207}
208
209impl TypeSize {
210 pub const ZERO: Self = Self(0);
212
213 pub const MAX: u8 = 32;
215
216 #[inline]
218 pub const fn new(bytes: u8) -> Option<Self> {
219 if bytes > Self::MAX { None } else { Some(Self(bytes)) }
220 }
221
222 #[inline]
226 #[track_caller]
227 pub fn new_int_bits(bits: u16) -> Self {
228 Self::try_new_int_bits(bits).unwrap_or_else(|| panic!("invalid integer size: {bits}"))
229 }
230
231 #[inline]
235 pub fn try_new_int_bits(bits: u16) -> Option<Self> {
236 if bits.is_multiple_of(8) { Self::new((bits / 8).try_into().ok()?) } else { None }
237 }
238
239 #[inline]
243 #[track_caller]
244 pub fn new_fb_bytes(bytes: u8) -> Self {
245 Self::try_new_fb_bytes(bytes).unwrap_or_else(|| panic!("invalid fixed-bytes size: {bytes}"))
246 }
247
248 #[inline]
252 pub fn try_new_fb_bytes(bytes: u8) -> Option<Self> {
253 if bytes == 0 {
254 return None;
255 }
256 Self::new(bytes)
257 }
258
259 #[inline]
261 pub const fn bytes(self) -> u8 {
262 if self.0 == 0 { Self::MAX } else { self.0 }
263 }
264
265 #[inline]
267 pub const fn bytes_raw(self) -> u8 {
268 self.0
269 }
270
271 #[inline]
273 pub const fn bits(self) -> u16 {
274 self.bytes() as u16 * 8
275 }
276
277 #[inline]
279 pub const fn bits_raw(self) -> u16 {
280 self.0 as u16 * 8
281 }
282
283 #[inline]
285 pub const fn int_keyword(self) -> Symbol {
286 kw::int(self.0)
287 }
288
289 #[inline]
291 pub const fn uint_keyword(self) -> Symbol {
292 kw::uint(self.0)
293 }
294
295 #[inline]
301 #[track_caller]
302 pub const fn bytes_keyword(self) -> Symbol {
303 kw::fixed_bytes(self.0)
304 }
305}
306
307#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
309pub struct TypeFixedSize(u8);
310
311impl fmt::Debug for TypeFixedSize {
312 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
313 write!(f, "TypeFixedSize({})", self.0)
314 }
315}
316
317impl TypeFixedSize {
318 pub const ZERO: Self = Self(0);
320
321 pub const MAX: u8 = 80;
323
324 #[inline]
326 pub const fn new(value: u8) -> Option<Self> {
327 if value > Self::MAX { None } else { Some(Self(value)) }
328 }
329
330 #[inline]
332 pub const fn get(self) -> u8 {
333 self.0
334 }
335}
336
337#[derive(Debug)]
339pub struct TypeArray<'ast> {
340 pub element: Type<'ast>,
341 pub size: Option<Box<'ast, Expr<'ast>>>,
342}
343
344#[derive(Debug)]
348pub struct TypeFunction<'ast> {
349 pub parameters: ParameterList<'ast>,
350 pub visibility: Option<Spanned<Visibility>>,
351 pub state_mutability: Option<Spanned<StateMutability>>,
352 pub returns: Option<ParameterList<'ast>>,
353}
354
355impl<'ast> TypeFunction<'ast> {
356 pub fn visibility(&self) -> Option<Visibility> {
357 self.visibility.map(Spanned::into_inner)
358 }
359
360 pub fn state_mutability(&self) -> StateMutability {
361 self.state_mutability.map(Spanned::into_inner).unwrap_or(StateMutability::NonPayable)
362 }
363
364 pub fn returns(&self) -> &[crate::VariableDefinition<'ast>] {
365 self.returns.as_ref().map(|pl| &pl.vars[..]).unwrap_or(&[])
366 }
367}
368
369#[derive(Debug)]
373pub struct TypeMapping<'ast> {
374 pub key: Type<'ast>,
375 pub key_name: Option<Ident>,
376 pub value: Type<'ast>,
377 pub value_name: Option<Ident>,
378}