1use async_graphql::*;
6use iota_types::{base_types::MoveObjectType, type_input::TypeInput};
7use move_binary_format::file_format::AbilitySet;
8use move_core_types::{annotated_value as A, language_storage::TypeTag};
9use serde::{Deserialize, Serialize};
10
11use crate::{
12 data::package_resolver::PackageResolver, error::Error, types::open_move_type::MoveAbility,
13};
14
15#[derive(Clone, Debug, PartialEq, Eq)]
16pub(crate) struct MoveType {
17 pub native: TypeInput,
18}
19
20scalar!(
21 MoveTypeSignature,
22 "MoveTypeSignature",
23 "The signature of a concrete Move Type (a type with all its type parameters instantiated with \
24 concrete types, that contains no references), corresponding to the following recursive type:
25
26type MoveTypeSignature =
27 \"address\"
28 | \"bool\"
29 | \"u8\" | \"u16\" | ... | \"u256\"
30 | { vector: MoveTypeSignature }
31 | {
32 datatype: {
33 package: string,
34 module: string,
35 type: string,
36 typeParameters: [MoveTypeSignature],
37 }
38 }"
39);
40
41scalar!(
42 MoveTypeLayout,
43 "MoveTypeLayout",
44 "The shape of a concrete Move Type (a type with all its type parameters instantiated with \
45 concrete types), corresponding to the following recursive type:
46
47type MoveTypeLayout =
48 \"address\"
49 | \"bool\"
50 | \"u8\" | \"u16\" | ... | \"u256\"
51 | { vector: MoveTypeLayout }
52 | {
53 struct: {
54 type: string,
55 fields: [{ name: string, layout: MoveTypeLayout }],
56 }
57 }
58 | { enum: [{
59 type: string,
60 variants: [{
61 name: string,
62 fields: [{ name: string, layout: MoveTypeLayout }],
63 }]
64 }]
65 }"
66);
67
68#[derive(Serialize, Deserialize, Debug)]
69#[serde(rename_all = "camelCase")]
70pub(crate) enum MoveTypeSignature {
71 Address,
72 Bool,
73 U8,
74 U16,
75 U32,
76 U64,
77 U128,
78 U256,
79 Vector(Box<MoveTypeSignature>),
80 Datatype {
81 package: String,
82 module: String,
83 #[serde(rename = "type")]
84 type_: String,
85 #[serde(rename = "typeParameters")]
86 type_parameters: Vec<MoveTypeSignature>,
87 },
88}
89
90#[derive(Serialize, Deserialize)]
91#[serde(rename_all = "camelCase")]
92pub(crate) enum MoveTypeLayout {
93 Address,
94 Bool,
95 U8,
96 U16,
97 U32,
98 U64,
99 U128,
100 U256,
101 Vector(Box<MoveTypeLayout>),
102 Struct(MoveStructLayout),
103 Enum(MoveEnumLayout),
104 InvalidType,
105}
106
107#[derive(Serialize, Deserialize)]
108pub(crate) struct MoveStructLayout {
109 #[serde(rename = "type")]
110 type_: String,
111 fields: Vec<MoveFieldLayout>,
112}
113
114#[derive(Serialize, Deserialize)]
115pub(crate) struct MoveEnumLayout {
116 variants: Vec<MoveVariantLayout>,
117}
118
119#[derive(Serialize, Deserialize)]
120pub(crate) struct MoveVariantLayout {
121 name: String,
122 layout: Vec<MoveFieldLayout>,
123}
124
125#[derive(Serialize, Deserialize)]
126pub(crate) struct MoveFieldLayout {
127 name: String,
128 layout: MoveTypeLayout,
129}
130
131#[Object]
133impl MoveType {
134 async fn repr(&self) -> String {
136 self.native.to_canonical_string(true)
137 }
138
139 async fn signature(&self) -> Result<MoveTypeSignature> {
141 self.signature_impl().extend()
144 }
145
146 async fn layout(&self, ctx: &Context<'_>) -> Result<MoveTypeLayout> {
149 let resolver: &PackageResolver = ctx
150 .data()
151 .map_err(|_| Error::Internal("Unable to fetch Package Cache.".to_string()))
152 .extend()?;
153
154 let Some(layout) = self.layout_impl(resolver).await.extend()? else {
155 return Ok(MoveTypeLayout::InvalidType);
156 };
157
158 MoveTypeLayout::try_from(layout).extend()
159 }
160
161 async fn abilities(&self, ctx: &Context<'_>) -> Result<Vec<MoveAbility>> {
164 let resolver: &PackageResolver = ctx
165 .data()
166 .map_err(|_| Error::Internal("Unable to fetch Package Cache.".to_string()))
167 .extend()?;
168
169 let Some(abilities) = self.abilities_impl(resolver).await.extend()? else {
170 return Ok(vec![]);
171 };
172
173 Ok(abilities.into_iter().map(MoveAbility::from).collect())
174 }
175}
176
177impl MoveType {
178 fn signature_impl(&self) -> Result<MoveTypeSignature, Error> {
179 MoveTypeSignature::try_from(self.native.clone())
180 }
181
182 pub(crate) async fn layout_impl(
183 &self,
184 resolver: &PackageResolver,
185 ) -> Result<Option<A::MoveTypeLayout>, Error> {
186 let Ok(tag) = self.native.as_type_tag() else {
187 return Ok(None);
188 };
189
190 Ok(Some(resolver.type_layout(tag).await.map_err(|e| {
191 Error::Internal(format!(
192 "Error calculating layout for {}: {e}",
193 self.native.to_canonical_display(true),
194 ))
195 })?))
196 }
197
198 pub(crate) async fn abilities_impl(
199 &self,
200 resolver: &PackageResolver,
201 ) -> Result<Option<AbilitySet>, Error> {
202 let Ok(tag) = self.native.as_type_tag() else {
203 return Ok(None);
204 };
205
206 Ok(Some(resolver.abilities(tag).await.map_err(|e| {
207 Error::Internal(format!(
208 "Error calculating abilities for {}: {e}",
209 self.native.to_canonical_string(true),
210 ))
211 })?))
212 }
213}
214
215impl From<MoveObjectType> for MoveType {
216 fn from(obj: MoveObjectType) -> Self {
217 let tag: TypeTag = obj.into();
218 Self { native: tag.into() }
219 }
220}
221
222impl From<TypeTag> for MoveType {
223 fn from(tag: TypeTag) -> Self {
224 Self { native: tag.into() }
225 }
226}
227
228impl From<TypeInput> for MoveType {
229 fn from(native: TypeInput) -> Self {
230 Self { native }
231 }
232}
233
234impl TryFrom<TypeInput> for MoveTypeSignature {
235 type Error = Error;
236
237 fn try_from(tag: TypeInput) -> Result<Self, Error> {
238 use TypeInput as T;
239
240 Ok(match tag {
241 T::Signer => return Err(unexpected_signer_error()),
242
243 T::U8 => Self::U8,
244 T::U16 => Self::U16,
245 T::U32 => Self::U32,
246 T::U64 => Self::U64,
247 T::U128 => Self::U128,
248 T::U256 => Self::U256,
249
250 T::Bool => Self::Bool,
251 T::Address => Self::Address,
252
253 T::Vector(v) => Self::Vector(Box::new(Self::try_from(*v)?)),
254
255 T::Struct(s) => Self::Datatype {
256 package: s.address.to_canonical_string(true),
257 module: s.module,
258 type_: s.name,
259 type_parameters: s
260 .type_params
261 .into_iter()
262 .map(Self::try_from)
263 .collect::<Result<Vec<_>, _>>()?,
264 },
265 })
266 }
267}
268
269impl TryFrom<A::MoveTypeLayout> for MoveTypeLayout {
270 type Error = Error;
271
272 fn try_from(layout: A::MoveTypeLayout) -> Result<Self, Error> {
273 use A::MoveTypeLayout as TL;
274
275 Ok(match layout {
276 TL::Signer => return Err(unexpected_signer_error()),
277
278 TL::U8 => Self::U8,
279 TL::U16 => Self::U16,
280 TL::U32 => Self::U32,
281 TL::U64 => Self::U64,
282 TL::U128 => Self::U128,
283 TL::U256 => Self::U256,
284
285 TL::Bool => Self::Bool,
286 TL::Address => Self::Address,
287
288 TL::Vector(v) => Self::Vector(Box::new(Self::try_from(*v)?)),
289 TL::Struct(s) => Self::Struct((*s).try_into()?),
290 TL::Enum(e) => Self::Enum((*e).try_into()?),
291 })
292 }
293}
294
295impl TryFrom<A::MoveEnumLayout> for MoveEnumLayout {
296 type Error = Error;
297
298 fn try_from(layout: A::MoveEnumLayout) -> Result<Self, Error> {
299 let A::MoveEnumLayout { variants, .. } = layout;
300 let mut variant_layouts = Vec::new();
301 for ((name, _), variant_fields) in variants {
302 let mut field_layouts = Vec::new();
303 for field in variant_fields {
304 field_layouts.push(MoveFieldLayout::try_from(field)?);
305 }
306 variant_layouts.push(MoveVariantLayout {
307 name: name.to_string(),
308 layout: field_layouts,
309 });
310 }
311
312 Ok(MoveEnumLayout {
313 variants: variant_layouts,
314 })
315 }
316}
317
318impl TryFrom<A::MoveStructLayout> for MoveStructLayout {
319 type Error = Error;
320
321 fn try_from(layout: A::MoveStructLayout) -> Result<Self, Error> {
322 Ok(Self {
323 type_: layout.type_.to_canonical_string(true),
324 fields: layout
325 .fields
326 .into_iter()
327 .map(MoveFieldLayout::try_from)
328 .collect::<Result<_, _>>()?,
329 })
330 }
331}
332
333impl TryFrom<A::MoveFieldLayout> for MoveFieldLayout {
334 type Error = Error;
335
336 fn try_from(layout: A::MoveFieldLayout) -> Result<Self, Error> {
337 Ok(Self {
338 name: layout.name.to_string(),
339 layout: layout.layout.try_into()?,
340 })
341 }
342}
343
344pub(crate) fn unexpected_signer_error() -> Error {
347 Error::Internal("Unexpected value of type: signer.".to_string())
348}
349
350#[cfg(test)]
351mod tests {
352 use std::str::FromStr;
353
354 use expect_test::expect;
355
356 use super::*;
357
358 fn signature(repr: impl Into<String>) -> Result<MoveTypeSignature, Error> {
359 let tag = TypeTag::from_str(repr.into().as_str()).unwrap();
360 MoveType::from(tag).signature_impl()
361 }
362
363 #[test]
364 fn complex_type() {
365 let sig = signature("vector<0x42::foo::Bar<address, u32, bool, u256>>").unwrap();
366 let expect = expect![[r#"
367 Vector(
368 Datatype {
369 package: "0x0000000000000000000000000000000000000000000000000000000000000042",
370 module: "foo",
371 type_: "Bar",
372 type_parameters: [
373 Address,
374 U32,
375 Bool,
376 U256,
377 ],
378 },
379 )"#]];
380 expect.assert_eq(&format!("{sig:#?}"));
381 }
382
383 #[test]
384 fn signer_type() {
385 let err = signature("signer").unwrap_err();
386 let expect = expect![[r#"Internal("Unexpected value of type: signer.")"#]];
387 expect.assert_eq(&format!("{err:?}"));
388 }
389
390 #[test]
391 fn nested_signer_type() {
392 let err = signature("0x42::baz::Qux<u32, vector<signer>>").unwrap_err();
393 let expect = expect![[r#"Internal("Unexpected value of type: signer.")"#]];
394 expect.assert_eq(&format!("{err:?}"));
395 }
396}