sui_graphql_rpc/types/
display.rs

1// Copyright (c) Mysten Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4use async_graphql::*;
5
6use diesel::{ExpressionMethods, OptionalExtension, QueryDsl};
7use diesel_async::scoped_futures::ScopedFutureExt;
8use move_core_types::annotated_value as A;
9use sui_display::v1::Format;
10use sui_indexer::{models::display::StoredDisplay, schema::display};
11use sui_types::TypeTag;
12
13use crate::{
14    data::{Db, DbConnection, QueryExecutor},
15    error::Error,
16};
17
18pub(crate) struct Display {
19    pub stored: StoredDisplay,
20}
21
22/// Maximum depth of nested fields.
23const MAX_DEPTH: usize = 10;
24
25/// Maximum size of Display output.
26const MAX_OUTPUT_SIZE: usize = 1024 * 1024;
27
28/// The set of named templates defined on-chain for the type of this object,
29/// to be handled off-chain. The server substitutes data from the object
30/// into these templates to generate a display string per template.
31#[derive(Debug, SimpleObject)]
32pub(crate) struct DisplayEntry {
33    /// The identifier for a particular template string of the Display object.
34    pub key: String,
35    /// The template string for the key with placeholder values substituted.
36    pub value: Option<String>,
37    /// An error string describing why the template could not be rendered.
38    pub error: Option<String>,
39}
40
41impl Display {
42    /// Query for a `Display` object by the type that it is displaying
43    pub(crate) async fn query(db: &Db, type_: TypeTag) -> Result<Option<Display>, Error> {
44        let stored: Option<StoredDisplay> = db
45            .execute(move |conn| {
46                async move {
47                    conn.first(move || {
48                        use display::dsl;
49                        dsl::display.filter(
50                            dsl::object_type.eq(type_.to_canonical_string(/* with_prefix */ true)),
51                        )
52                    })
53                    .await
54                    .optional()
55                }
56                .scope_boxed()
57            })
58            .await?;
59
60        Ok(stored.map(|stored| Display { stored }))
61    }
62
63    /// Render the fields defined by this `Display` from the contents of `struct_`.
64    pub(crate) fn render(
65        &self,
66        bytes: &[u8],
67        layout: &A::MoveTypeLayout,
68    ) -> Result<Vec<DisplayEntry>, Error> {
69        let fields = self
70            .stored
71            .to_display_update_event()
72            .map_err(|e| Error::Internal(e.to_string()))?
73            .fields;
74
75        let mut rendered = vec![];
76        for (field, value) in Format::parse(MAX_DEPTH, &fields)
77            .map_err(|e| Error::Client(e.to_string()))?
78            .display(MAX_OUTPUT_SIZE, bytes, layout)
79            .map_err(|e| Error::Client(e.to_string()))?
80        {
81            rendered.push(match value {
82                Ok(v) => DisplayEntry::create_value(field, v),
83                Err(e) => DisplayEntry::create_error(field, e.to_string()),
84            });
85        }
86
87        Ok(rendered)
88    }
89}
90
91impl DisplayEntry {
92    pub(crate) fn create_value(key: String, value: String) -> Self {
93        Self {
94            key,
95            value: Some(value),
96            error: None,
97        }
98    }
99
100    pub(crate) fn create_error(key: String, error: String) -> Self {
101        Self {
102            key,
103            value: None,
104            error: Some(error),
105        }
106    }
107}