sui_graphql_rpc/types/
move_struct.rs

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