iota_graphql_rpc/types/
move_struct.rs

1// Copyright (c) Mysten Labs, Inc.
2// Modifications Copyright (c) 2024 IOTA Stiftung
3// SPDX-License-Identifier: Apache-2.0
4
5use async_graphql::*;
6use iota_package_resolver::{DataDef, MoveData};
7
8use crate::{
9    error::Error,
10    types::{
11        iota_address::IotaAddress,
12        move_module::MoveModule,
13        open_move_type::{MoveAbility, OpenMoveType, abilities},
14    },
15};
16
17pub(crate) struct MoveStruct {
18    defining_id: IotaAddress,
19    module: String,
20    name: String,
21    abilities: Vec<MoveAbility>,
22    type_parameters: Vec<MoveStructTypeParameter>,
23    fields: Vec<MoveField>,
24    checkpoint_viewed_at: u64,
25}
26
27#[derive(SimpleObject)]
28pub(crate) struct MoveStructTypeParameter {
29    pub(crate) constraints: Vec<MoveAbility>,
30    pub(crate) is_phantom: bool,
31}
32
33/// Information for a particular field on a Move struct.
34#[derive(SimpleObject)]
35#[graphql(complex)]
36pub(crate) struct MoveField {
37    pub(crate) name: String,
38    #[graphql(skip)]
39    pub(crate) type_: OpenMoveType,
40}
41
42/// Description of a struct type, defined in a Move module.
43#[Object]
44impl MoveStruct {
45    /// The module this struct was originally defined in.
46    pub(crate) async fn module(&self, ctx: &Context<'_>) -> Result<MoveModule> {
47        let Some(module) = MoveModule::query(
48            ctx,
49            self.defining_id,
50            &self.module,
51            self.checkpoint_viewed_at,
52        )
53        .await
54        .extend()?
55        else {
56            return Err(Error::Internal(format!(
57                "Failed to load module for struct: {}::{}::{}",
58                self.defining_id, self.module, self.name,
59            )))
60            .extend();
61        };
62
63        Ok(module)
64    }
65
66    /// The struct's (unqualified) type name.
67    pub(crate) async fn name(&self) -> &str {
68        &self.name
69    }
70
71    /// Abilities this struct has.
72    pub(crate) async fn abilities(&self) -> Option<&Vec<MoveAbility>> {
73        Some(&self.abilities)
74    }
75
76    /// Constraints on the struct's formal type parameters.  Move bytecode does
77    /// not name type parameters, so when they are referenced (e.g. in field
78    /// types) they are identified by their index in this list.
79    pub(crate) async fn type_parameters(&self) -> Option<&Vec<MoveStructTypeParameter>> {
80        Some(&self.type_parameters)
81    }
82
83    /// The names and types of the struct's fields.  Field types reference type
84    /// parameters, by their index in the defining struct's `typeParameters`
85    /// list.
86    pub(crate) async fn fields(&self) -> Option<&Vec<MoveField>> {
87        Some(&self.fields)
88    }
89}
90
91#[ComplexObject]
92impl MoveField {
93    #[graphql(name = "type")]
94    async fn type_(&self) -> Option<&OpenMoveType> {
95        Some(&self.type_)
96    }
97}
98
99impl MoveStruct {
100    pub(crate) fn new(
101        module: String,
102        name: String,
103        def: DataDef,
104        checkpoint_viewed_at: u64,
105    ) -> Result<Self, Error> {
106        let type_parameters = def
107            .type_params
108            .into_iter()
109            .map(|param| MoveStructTypeParameter {
110                constraints: abilities(param.constraints),
111                is_phantom: param.is_phantom,
112            })
113            .collect();
114
115        let MoveData::Struct(fields) = def.data else {
116            // This should never happen, as the data should always be a struct if we're
117            // calling this function. Signal an internal error if it does.
118            return Err(Error::Internal(format!(
119                "Expected struct data, but got: {:?}",
120                def.data
121            )));
122        };
123        let fields = fields
124            .into_iter()
125            .map(|(name, signature)| MoveField {
126                name,
127                type_: signature.into(),
128            })
129            .collect();
130
131        Ok(MoveStruct {
132            defining_id: IotaAddress::from(def.defining_id),
133            module,
134            name,
135            abilities: abilities(def.abilities),
136            type_parameters,
137            fields,
138            checkpoint_viewed_at,
139        })
140    }
141}