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(u16);
202
203impl Default for TypeSize {
204 #[inline]
205 fn default() -> Self {
206 Self::ZERO
207 }
208}
209
210impl fmt::Debug for TypeSize {
211 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
212 write!(f, "TypeSize({})", self.0)
213 }
214}
215
216impl TypeSize {
217 pub const ZERO: Self = Self(0);
219
220 pub const MAX: u16 = 256;
222
223 #[inline]
225 const fn new(bits: u16) -> Option<Self> {
226 if bits > Self::MAX { None } else { Some(Self(bits)) }
227 }
228
229 #[inline]
233 #[track_caller]
234 pub fn new_int_bits(bits: u16) -> Self {
235 Self::try_new_int_bits(bits).unwrap_or_else(|| panic!("invalid integer size: {bits}"))
236 }
237
238 #[inline]
242 pub fn try_new_int_bits(bits: u16) -> Option<Self> {
243 if bits.is_multiple_of(8) { Self::new(bits) } else { None }
244 }
245
246 #[inline]
250 #[track_caller]
251 pub fn new_literal_bits(bits: u16) -> Self {
252 Self::try_new_literal_bits(bits).unwrap_or_else(|| panic!("invalid literal size: {bits}"))
253 }
254
255 #[inline]
259 pub fn try_new_literal_bits(bits: u16) -> Option<Self> {
260 if bits == 0 { None } else { Self::new(bits) }
261 }
262
263 #[inline]
267 #[track_caller]
268 pub fn new_fb_bytes(bytes: u8) -> Self {
269 Self::try_new_fb_bytes(bytes).unwrap_or_else(|| panic!("invalid fixed-bytes size: {bytes}"))
270 }
271
272 #[inline]
276 pub fn try_new_fb_bytes(bytes: u8) -> Option<Self> {
277 if bytes == 0 {
278 return None;
279 }
280 Self::new(bytes as u16 * 8)
281 }
282
283 #[inline]
285 pub const fn bytes(self) -> u8 {
286 if self.0 == 0 { (Self::MAX / 8) as u8 } else { self.0.div_ceil(8) as u8 }
287 }
288
289 #[inline]
291 pub const fn bytes_raw(self) -> u8 {
292 self.0.div_ceil(8) as u8
293 }
294
295 #[inline]
297 pub const fn bits(self) -> u16 {
298 if self.0 == 0 { Self::MAX } else { self.0 }
299 }
300
301 #[inline]
303 pub const fn bits_raw(self) -> u16 {
304 self.0
305 }
306
307 #[inline]
309 pub const fn int_keyword(self) -> Symbol {
310 kw::int(self.bytes_raw())
311 }
312
313 #[inline]
315 pub const fn uint_keyword(self) -> Symbol {
316 kw::uint(self.bytes_raw())
317 }
318
319 #[inline]
325 #[track_caller]
326 pub const fn bytes_keyword(self) -> Symbol {
327 kw::fixed_bytes(self.bytes_raw())
328 }
329}
330
331#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
333pub struct TypeFixedSize(u8);
334
335impl fmt::Debug for TypeFixedSize {
336 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
337 write!(f, "TypeFixedSize({})", self.0)
338 }
339}
340
341impl TypeFixedSize {
342 pub const ZERO: Self = Self(0);
344
345 pub const MAX: u8 = 80;
347
348 #[inline]
350 pub const fn new(value: u8) -> Option<Self> {
351 if value > Self::MAX { None } else { Some(Self(value)) }
352 }
353
354 #[inline]
356 pub const fn get(self) -> u8 {
357 self.0
358 }
359}
360
361#[derive(Debug)]
363pub struct TypeArray<'ast> {
364 pub element: Type<'ast>,
365 pub size: Option<Box<'ast, Expr<'ast>>>,
366}
367
368#[derive(Debug)]
372pub struct TypeFunction<'ast> {
373 pub parameters: ParameterList<'ast>,
374 pub visibility: Option<Spanned<Visibility>>,
375 pub state_mutability: Option<Spanned<StateMutability>>,
376 pub returns: Option<ParameterList<'ast>>,
377}
378
379impl<'ast> TypeFunction<'ast> {
380 pub fn visibility(&self) -> Option<Visibility> {
381 self.visibility.map(Spanned::into_inner)
382 }
383
384 pub fn state_mutability(&self) -> StateMutability {
385 self.state_mutability.map(Spanned::into_inner).unwrap_or(StateMutability::NonPayable)
386 }
387
388 pub fn returns(&self) -> &[crate::VariableDefinition<'ast>] {
389 self.returns.as_ref().map(|pl| &pl.vars[..]).unwrap_or(&[])
390 }
391}
392
393#[derive(Debug)]
397pub struct TypeMapping<'ast> {
398 pub key: Type<'ast>,
399 pub key_name: Option<Ident>,
400 pub value: Type<'ast>,
401 pub value_name: Option<Ident>,
402}