sway_core/abi_generation/
abi_str.rs

1use sway_types::{integer_bits::IntegerBits, Named};
2
3use crate::{language::CallPath, Engines, GenericArgument, TypeId, TypeInfo};
4
5#[derive(Clone)]
6pub struct AbiStrContext {
7    pub program_name: String,
8    pub abi_with_callpaths: bool,
9    pub abi_with_fully_specified_types: bool,
10    pub abi_root_type_without_generic_type_parameters: bool,
11}
12
13impl TypeId {
14    /// Gives back a string that represents the type, considering what it resolves to
15    pub fn get_abi_type_str(
16        &self,
17        ctx: &AbiStrContext,
18        engines: &Engines,
19        resolved_type_id: TypeId,
20    ) -> String {
21        let type_engine = engines.te();
22        let self_abi_str = type_engine.get(*self).abi_str(ctx, engines, true);
23        if self.is_generic_parameter(engines, resolved_type_id) {
24            format!("generic {}", self_abi_str)
25        } else {
26            match (
27                &*type_engine.get(*self),
28                &*type_engine.get(resolved_type_id),
29            ) {
30                (TypeInfo::Custom { .. }, TypeInfo::Struct { .. })
31                | (TypeInfo::Custom { .. }, TypeInfo::Enum { .. }) => type_engine
32                    .get(resolved_type_id)
33                    .abi_str(ctx, engines, true),
34                (_, TypeInfo::Alias { ty, .. }) => {
35                    ty.type_id().get_abi_type_str(ctx, engines, ty.type_id())
36                }
37                (TypeInfo::Tuple(fields), TypeInfo::Tuple(resolved_fields)) => {
38                    assert_eq!(fields.len(), resolved_fields.len());
39                    let field_strs = resolved_fields
40                        .iter()
41                        .map(|f| {
42                            if ctx.abi_with_fully_specified_types {
43                                type_engine.get(f.type_id()).abi_str(ctx, engines, false)
44                            } else {
45                                "_".to_string()
46                            }
47                        })
48                        .collect::<Vec<String>>();
49                    format!("({})", field_strs.join(", "))
50                }
51                (TypeInfo::Array(_, length), TypeInfo::Array(type_arg, resolved_length)) => {
52                    assert_eq!(
53                        length.as_literal_val().unwrap(),
54                        resolved_length.as_literal_val().unwrap()
55                    );
56                    let inner_type = if ctx.abi_with_fully_specified_types {
57                        type_engine
58                            .get(type_arg.type_id())
59                            .abi_str(ctx, engines, false)
60                    } else {
61                        "_".to_string()
62                    };
63                    format!("[{}; {:?}]", inner_type, engines.help_out(length))
64                }
65                (TypeInfo::Slice(type_arg), TypeInfo::Slice(_)) => {
66                    let inner_type = if ctx.abi_with_fully_specified_types {
67                        type_engine
68                            .get(type_arg.type_id())
69                            .abi_str(ctx, engines, false)
70                    } else {
71                        "_".to_string()
72                    };
73                    format!("[{}]", inner_type)
74                }
75                (TypeInfo::Custom { .. }, _) => {
76                    format!("generic {}", self_abi_str)
77                }
78                _ => type_engine
79                    .get(resolved_type_id)
80                    .abi_str(ctx, engines, true),
81            }
82        }
83    }
84}
85
86impl TypeInfo {
87    pub fn abi_str(&self, ctx: &AbiStrContext, engines: &Engines, is_root: bool) -> String {
88        use TypeInfo::*;
89        let decl_engine = engines.de();
90        match self {
91            Unknown => "unknown".into(),
92            Never => "never".into(),
93            UnknownGeneric { name, .. } => name.to_string(),
94            Placeholder(_) => "_".to_string(),
95            TypeParam(param) => format!("typeparam({})", param.name()),
96            StringSlice => "str".into(),
97            StringArray(length) => format!("str[{}]", length.val()),
98            UnsignedInteger(x) => match x {
99                IntegerBits::Eight => "u8",
100                IntegerBits::Sixteen => "u16",
101                IntegerBits::ThirtyTwo => "u32",
102                IntegerBits::SixtyFour => "u64",
103                IntegerBits::V256 => "u256",
104            }
105            .into(),
106            Boolean => "bool".into(),
107            Custom {
108                qualified_call_path: call_path,
109                ..
110            } => call_path.call_path.suffix.to_string(),
111            Tuple(fields) => {
112                let field_strs = fields
113                    .iter()
114                    .map(|field| field.abi_str(ctx, engines, false))
115                    .collect::<Vec<String>>();
116                format!("({})", field_strs.join(", "))
117            }
118            B256 => "b256".into(),
119            Numeric => "u64".into(), // u64 is the default
120            Contract => "contract".into(),
121            ErrorRecovery(_) => "unknown due to error".into(),
122            UntypedEnum(decl_id) => {
123                let decl = engines.pe().get_enum(decl_id);
124                format!("untyped enum {}", decl.name)
125            }
126            UntypedStruct(decl_id) => {
127                let decl = engines.pe().get_struct(decl_id);
128                format!("untyped struct {}", decl.name)
129            }
130            Enum(decl_ref) => {
131                let decl = decl_engine.get_enum(decl_ref);
132                let type_params = if (ctx.abi_root_type_without_generic_type_parameters && is_root)
133                    || decl.type_parameters.is_empty()
134                {
135                    "".into()
136                } else {
137                    format!(
138                        "<{}>",
139                        decl.type_parameters
140                            .iter()
141                            .map(|p| p.abi_str(engines, ctx, false))
142                            .collect::<Vec<_>>()
143                            .join(",")
144                    )
145                };
146                format!(
147                    "enum {}{}",
148                    call_path_display(ctx, &decl.call_path),
149                    type_params
150                )
151            }
152            Struct(decl_ref) => {
153                let decl = decl_engine.get_struct(decl_ref);
154                let type_params = if (ctx.abi_root_type_without_generic_type_parameters && is_root)
155                    || decl.type_parameters.is_empty()
156                {
157                    "".into()
158                } else {
159                    format!(
160                        "<{}>",
161                        decl.type_parameters
162                            .iter()
163                            .map(|p| p.abi_str(engines, ctx, false))
164                            .collect::<Vec<_>>()
165                            .join(",")
166                    )
167                };
168                format!(
169                    "struct {}{}",
170                    call_path_display(ctx, &decl.call_path),
171                    type_params
172                )
173            }
174            ContractCaller { abi_name, .. } => {
175                format!("contract caller {abi_name}")
176            }
177            Array(elem_ty, length) => {
178                format!(
179                    "[{}; {:?}]",
180                    elem_ty.abi_str(ctx, engines, false),
181                    engines.help_out(length)
182                )
183            }
184            RawUntypedPtr => "raw untyped ptr".into(),
185            RawUntypedSlice => "raw untyped slice".into(),
186            Ptr(ty) => {
187                format!("__ptr {}", ty.abi_str(ctx, engines, false))
188            }
189            Slice(ty) => {
190                format!("__slice {}", ty.abi_str(ctx, engines, false))
191            }
192            Alias { ty, .. } => ty.abi_str(ctx, engines, false),
193            TraitType {
194                name,
195                trait_type_id: _,
196            } => format!("trait type {}", name),
197            Ref {
198                to_mutable_value,
199                referenced_type,
200            } => {
201                format!(
202                    "__ref {}{}", // TODO-IG: No references in ABIs according to the RFC. Or we want to have them?
203                    if *to_mutable_value { "mut " } else { "" },
204                    referenced_type.abi_str(ctx, engines, false)
205                )
206            }
207        }
208    }
209}
210
211/// `call_path_display`  returns the provided `call_path` without the first prefix in case it is equal to the program name.
212/// If the program name is `my_program` and the `call_path` is `my_program::MyStruct` then this function returns only `MyStruct`.
213fn call_path_display(ctx: &AbiStrContext, call_path: &CallPath) -> String {
214    if !ctx.abi_with_callpaths {
215        return call_path.suffix.as_str().to_string();
216    }
217    let mut buf = String::new();
218    for (index, prefix) in call_path.prefixes.iter().enumerate() {
219        if index == 0 && prefix.as_str() == ctx.program_name {
220            continue;
221        }
222        buf.push_str(prefix.as_str());
223        buf.push_str("::");
224    }
225    buf.push_str(&call_path.suffix.to_string());
226
227    buf
228}
229
230impl GenericArgument {
231    pub(self) fn abi_str(&self, ctx: &AbiStrContext, engines: &Engines, is_root: bool) -> String {
232        engines
233            .te()
234            .get(self.type_id())
235            .abi_str(ctx, engines, is_root)
236    }
237}