1use async_graphql::*;
6use iota_types::base_types::MoveObjectType;
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: TypeTag,
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}
105
106#[derive(Serialize, Deserialize)]
107pub(crate) struct MoveStructLayout {
108 #[serde(rename = "type")]
109 type_: String,
110 fields: Vec<MoveFieldLayout>,
111}
112
113#[derive(Serialize, Deserialize)]
114pub(crate) struct MoveEnumLayout {
115 variants: Vec<MoveVariantLayout>,
116}
117
118#[derive(Serialize, Deserialize)]
119pub(crate) struct MoveVariantLayout {
120 name: String,
121 layout: Vec<MoveFieldLayout>,
122}
123
124#[derive(Serialize, Deserialize)]
125pub(crate) struct MoveFieldLayout {
126 name: String,
127 layout: MoveTypeLayout,
128}
129
130#[Object]
132impl MoveType {
133 async fn repr(&self) -> String {
135 self.native.to_canonical_string(true)
136 }
137
138 async fn signature(&self) -> Result<MoveTypeSignature> {
140 self.signature_impl().extend()
143 }
144
145 async fn layout(&self, ctx: &Context<'_>) -> Result<MoveTypeLayout> {
147 let resolver: &PackageResolver = ctx
148 .data()
149 .map_err(|_| Error::Internal("Unable to fetch Package Cache.".to_string()))
150 .extend()?;
151
152 MoveTypeLayout::try_from(self.layout_impl(resolver).await.extend()?).extend()
153 }
154
155 async fn abilities(&self, ctx: &Context<'_>) -> Result<Vec<MoveAbility>> {
157 let resolver: &PackageResolver = ctx
158 .data()
159 .map_err(|_| Error::Internal("Unable to fetch Package Cache.".to_string()))
160 .extend()?;
161
162 Ok(self
163 .abilities_impl(resolver)
164 .await
165 .extend()?
166 .into_iter()
167 .map(MoveAbility::from)
168 .collect())
169 }
170}
171
172impl MoveType {
173 pub(crate) fn new(native: TypeTag) -> MoveType {
174 Self { native }
175 }
176
177 fn signature_impl(&self) -> Result<MoveTypeSignature, Error> {
178 MoveTypeSignature::try_from(self.native.clone())
179 }
180
181 pub(crate) async fn layout_impl(
182 &self,
183 resolver: &PackageResolver,
184 ) -> Result<A::MoveTypeLayout, Error> {
185 resolver
186 .type_layout(self.native.clone())
187 .await
188 .map_err(|e| {
189 Error::Internal(format!(
190 "Error calculating layout for {}: {e}",
191 self.native.to_canonical_display(true),
192 ))
193 })
194 }
195
196 pub(crate) async fn abilities_impl(
197 &self,
198 resolver: &PackageResolver,
199 ) -> Result<AbilitySet, Error> {
200 resolver.abilities(self.native.clone()).await.map_err(|e| {
201 Error::Internal(format!(
202 "Error calculating abilities for {}: {e}",
203 self.native.to_canonical_string(true),
204 ))
205 })
206 }
207}
208
209impl From<MoveObjectType> for MoveType {
210 fn from(obj: MoveObjectType) -> Self {
211 let tag: TypeTag = obj.into();
212 Self { native: tag }
213 }
214}
215
216impl From<TypeTag> for MoveType {
217 fn from(tag: TypeTag) -> Self {
218 Self { native: tag }
219 }
220}
221
222impl TryFrom<TypeTag> for MoveTypeSignature {
223 type Error = Error;
224
225 fn try_from(tag: TypeTag) -> Result<Self, Error> {
226 use TypeTag as T;
227
228 Ok(match tag {
229 T::Signer => return Err(unexpected_signer_error()),
230
231 T::U8 => Self::U8,
232 T::U16 => Self::U16,
233 T::U32 => Self::U32,
234 T::U64 => Self::U64,
235 T::U128 => Self::U128,
236 T::U256 => Self::U256,
237
238 T::Bool => Self::Bool,
239 T::Address => Self::Address,
240
241 T::Vector(v) => Self::Vector(Box::new(Self::try_from(*v)?)),
242
243 T::Struct(s) => Self::Datatype {
244 package: s.address.to_canonical_string(true),
245 module: s.module.to_string(),
246 type_: s.name.to_string(),
247 type_parameters: s
248 .type_params
249 .into_iter()
250 .map(Self::try_from)
251 .collect::<Result<Vec<_>, _>>()?,
252 },
253 })
254 }
255}
256
257impl TryFrom<A::MoveTypeLayout> for MoveTypeLayout {
258 type Error = Error;
259
260 fn try_from(layout: A::MoveTypeLayout) -> Result<Self, Error> {
261 use A::MoveTypeLayout as TL;
262
263 Ok(match layout {
264 TL::Signer => return Err(unexpected_signer_error()),
265
266 TL::U8 => Self::U8,
267 TL::U16 => Self::U16,
268 TL::U32 => Self::U32,
269 TL::U64 => Self::U64,
270 TL::U128 => Self::U128,
271 TL::U256 => Self::U256,
272
273 TL::Bool => Self::Bool,
274 TL::Address => Self::Address,
275
276 TL::Vector(v) => Self::Vector(Box::new(Self::try_from(*v)?)),
277 TL::Struct(s) => Self::Struct((*s).try_into()?),
278 TL::Enum(e) => Self::Enum((*e).try_into()?),
279 })
280 }
281}
282
283impl TryFrom<A::MoveEnumLayout> for MoveEnumLayout {
284 type Error = Error;
285
286 fn try_from(layout: A::MoveEnumLayout) -> Result<Self, Error> {
287 let A::MoveEnumLayout { variants, .. } = layout;
288 let mut variant_layouts = Vec::new();
289 for ((name, _), variant_fields) in variants {
290 let mut field_layouts = Vec::new();
291 for field in variant_fields {
292 field_layouts.push(MoveFieldLayout::try_from(field)?);
293 }
294 variant_layouts.push(MoveVariantLayout {
295 name: name.to_string(),
296 layout: field_layouts,
297 });
298 }
299
300 Ok(MoveEnumLayout {
301 variants: variant_layouts,
302 })
303 }
304}
305
306impl TryFrom<A::MoveStructLayout> for MoveStructLayout {
307 type Error = Error;
308
309 fn try_from(layout: A::MoveStructLayout) -> Result<Self, Error> {
310 Ok(Self {
311 type_: layout.type_.to_canonical_string(true),
312 fields: layout
313 .fields
314 .into_iter()
315 .map(MoveFieldLayout::try_from)
316 .collect::<Result<_, _>>()?,
317 })
318 }
319}
320
321impl TryFrom<A::MoveFieldLayout> for MoveFieldLayout {
322 type Error = Error;
323
324 fn try_from(layout: A::MoveFieldLayout) -> Result<Self, Error> {
325 Ok(Self {
326 name: layout.name.to_string(),
327 layout: layout.layout.try_into()?,
328 })
329 }
330}
331
332pub(crate) fn unexpected_signer_error() -> Error {
335 Error::Internal("Unexpected value of type: signer.".to_string())
336}
337
338#[cfg(test)]
339mod tests {
340 use std::str::FromStr;
341
342 use expect_test::expect;
343
344 use super::*;
345
346 fn signature(repr: impl Into<String>) -> Result<MoveTypeSignature, Error> {
347 let tag = TypeTag::from_str(repr.into().as_str()).unwrap();
348 MoveType::new(tag).signature_impl()
349 }
350
351 #[test]
352 fn complex_type() {
353 let sig = signature("vector<0x42::foo::Bar<address, u32, bool, u256>>").unwrap();
354 let expect = expect![[r#"
355 Vector(
356 Datatype {
357 package: "0x0000000000000000000000000000000000000000000000000000000000000042",
358 module: "foo",
359 type_: "Bar",
360 type_parameters: [
361 Address,
362 U32,
363 Bool,
364 U256,
365 ],
366 },
367 )"#]];
368 expect.assert_eq(&format!("{sig:#?}"));
369 }
370
371 #[test]
372 fn signer_type() {
373 let err = signature("signer").unwrap_err();
374 let expect = expect![[r#"Internal("Unexpected value of type: signer.")"#]];
375 expect.assert_eq(&format!("{err:?}"));
376 }
377
378 #[test]
379 fn nested_signer_type() {
380 let err = signature("0x42::baz::Qux<u32, vector<signer>>").unwrap_err();
381 let expect = expect![[r#"Internal("Unexpected value of type: signer.")"#]];
382 expect.assert_eq(&format!("{err:?}"));
383 }
384}