1use std::collections::BTreeMap;
5
6use async_graphql::*;
7use once_cell::sync::Lazy;
8use serde::{Deserialize, Serialize};
9use serde_json as json;
10
11#[derive(Enum, Copy, Clone, Serialize, Deserialize, Debug, Eq, PartialEq, Ord, PartialOrd)]
14#[serde(rename_all = "kebab-case")]
15#[graphql(name = "Feature")]
16pub enum FunctionalGroup {
17 Analytics,
19
20 Coins,
22
23 DynamicFields,
25
26 NameService,
28
29 Subscriptions,
31
32 SystemState,
35
36 MoveRegistry,
38}
39
40impl FunctionalGroup {
41 pub(crate) fn name(&self) -> String {
44 json::ser::to_string(self).expect("Serializing `FunctionalGroup` cannot fail.")
45 }
46
47 pub(crate) fn all() -> &'static [FunctionalGroup] {
49 use FunctionalGroup as G;
50 static ALL: &[FunctionalGroup] = &[
51 G::Analytics,
52 G::Coins,
53 G::DynamicFields,
54 G::NameService,
55 G::Subscriptions,
56 G::SystemState,
57 G::MoveRegistry,
58 ];
59 ALL
60 }
61}
62
63fn functional_groups() -> &'static BTreeMap<(&'static str, &'static str), FunctionalGroup> {
65 use FunctionalGroup as G;
70 static GROUPS: Lazy<BTreeMap<(&str, &str), FunctionalGroup>> = Lazy::new(|| {
71 BTreeMap::from_iter([
72 (("Address", "balance"), G::Coins),
73 (("Address", "balances"), G::Coins),
74 (("Address", "coins"), G::Coins),
75 (("Address", "defaultSuinsName"), G::NameService),
76 (("Address", "suinsRegistrations"), G::NameService),
77 (("Checkpoint", "addressMetrics"), G::Analytics),
78 (("Checkpoint", "networkTotalTransactions"), G::Analytics),
79 (("Epoch", "protocolConfigs"), G::SystemState),
80 (("Epoch", "referenceGasPrice"), G::SystemState),
81 (("Epoch", "validatorSet"), G::SystemState),
82 (("Object", "balance"), G::Coins),
83 (("Object", "balances"), G::Coins),
84 (("Object", "coins"), G::Coins),
85 (("Object", "defaultSuinsName"), G::NameService),
86 (("Object", "dynamicField"), G::DynamicFields),
87 (("Object", "dynamicObjectField"), G::DynamicFields),
88 (("Object", "dynamicFields"), G::DynamicFields),
89 (("Object", "suinsRegistrations"), G::NameService),
90 (("Owner", "balance"), G::Coins),
91 (("Owner", "balances"), G::Coins),
92 (("Owner", "coins"), G::Coins),
93 (("Owner", "defaultSuinsName"), G::NameService),
94 (("Owner", "dynamicField"), G::DynamicFields),
95 (("Owner", "dynamicObjectField"), G::DynamicFields),
96 (("Owner", "dynamicFields"), G::DynamicFields),
97 (("Owner", "suinsRegistrations"), G::NameService),
98 (("Query", "coinMetadata"), G::Coins),
99 (("Query", "moveCallMetrics"), G::Analytics),
100 (("Query", "networkMetrics"), G::Analytics),
101 (("Query", "protocolConfig"), G::SystemState),
102 (("Query", "resolveSuinsAddress"), G::NameService),
103 (("Query", "packageByName"), G::MoveRegistry),
104 (("Query", "typeByName"), G::MoveRegistry),
105 (("Subscription", "events"), G::Subscriptions),
106 (("Subscription", "transactions"), G::Subscriptions),
107 (("SystemStateSummary", "safeMode"), G::SystemState),
108 (("SystemStateSummary", "storageFund"), G::SystemState),
109 (("SystemStateSummary", "systemParameters"), G::SystemState),
110 (("SystemStateSummary", "systemStateVersion"), G::SystemState),
111 ])
112 });
113
114 Lazy::force(&GROUPS)
115}
116
117pub(crate) fn functional_group(type_: &str, field: &str) -> Option<FunctionalGroup> {
120 functional_groups().get(&(type_, field)).copied()
121}
122
123#[cfg(test)]
124mod tests {
125 use std::collections::BTreeSet;
126
127 use async_graphql::registry::Registry;
128 use async_graphql::OutputType;
129
130 use crate::types::query::Query;
131
132 use super::*;
133
134 #[test]
135 fn test_groups_match_schema() {
139 let mut registry = Registry::default();
140 Query::create_type_info(&mut registry);
141
142 let unimplemented = BTreeSet::from_iter([
143 ("Checkpoint", "addressMetrics"),
144 ("Epoch", "protocolConfig"),
145 ("Query", "moveCallMetrics"),
146 ("Query", "networkMetrics"),
147 ("Subscription", "events"),
148 ("Subscription", "transactions"),
149 ]);
150
151 for (type_, field) in &unimplemented {
152 let Some(meta_type) = registry.concrete_type_by_name(type_) else {
153 continue;
154 };
155
156 let Some(_) = meta_type.field_by_name(field) else {
157 continue;
158 };
159
160 panic!(
161 "Field '{type_}.{field}' is marked as unimplemented in this test, but it's in the \
162 schema. Fix this by removing it from the `unimplemented` set."
163 );
164 }
165
166 for (type_, field) in functional_groups().keys() {
167 if unimplemented.contains(&(type_, field)) {
168 continue;
169 }
170
171 let Some(meta_type) = registry.concrete_type_by_name(type_) else {
172 panic!("Type '{type_}' from functional group configs does not appear in schema.");
173 };
174
175 let Some(_) = meta_type.field_by_name(field) else {
176 panic!(
177 "Field '{type_}.{field}' from functional group configs does not appear in \
178 schema."
179 );
180 };
181 }
182 }
183}