1use indexmap::IndexMap;
6use serde::{Deserialize, Serialize};
7use sha2::{Digest, Sha256};
8
9#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
11pub struct WesleyIR {
12 pub version: String,
14 #[serde(skip_serializing_if = "Option::is_none")]
16 pub metadata: Option<Metadata>,
17 pub types: Vec<TypeDefinition>,
19}
20
21#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
23#[serde(rename_all = "camelCase")]
24pub struct Metadata {
25 pub source_hash: Option<String>,
27 pub generated_at: Option<String>,
29 #[serde(default, skip_serializing_if = "Vec::is_empty")]
31 pub units: Vec<UnitMeta>,
32}
33
34#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
36pub struct UnitMeta {
37 pub id: String,
39 pub package: String,
41 pub hash: String,
43}
44
45#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
47#[serde(rename_all = "camelCase")]
48pub struct TypeDefinition {
49 pub name: String,
51 pub kind: TypeKind,
53 #[serde(skip_serializing_if = "Option::is_none")]
55 pub description: Option<String>,
56 pub directives: IndexMap<String, serde_json::Value>,
58 #[serde(default, skip_serializing_if = "Vec::is_empty")]
60 pub implements: Vec<String>,
61 #[serde(default, skip_serializing_if = "Vec::is_empty")]
63 pub fields: Vec<Field>,
64 #[serde(default, skip_serializing_if = "Vec::is_empty")]
66 pub enum_values: Vec<String>,
67 #[serde(default, skip_serializing_if = "Vec::is_empty")]
69 pub union_members: Vec<String>,
70}
71
72#[derive(Debug, Serialize, Deserialize, Clone, Copy, PartialEq, Eq)]
74#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
75pub enum TypeKind {
76 Object,
78 Interface,
80 Union,
82 Enum,
84 Scalar,
86 InputObject,
88}
89
90#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
92#[serde(rename_all = "camelCase")]
93pub struct Field {
94 pub name: String,
96 #[serde(skip_serializing_if = "Option::is_none")]
98 pub description: Option<String>,
99 pub r#type: TypeReference,
101 #[serde(default, skip_serializing_if = "Vec::is_empty")]
103 pub arguments: Vec<FieldArgument>,
104 #[serde(default, skip_serializing_if = "Option::is_none")]
106 pub default_value: Option<serde_json::Value>,
107 pub directives: IndexMap<String, serde_json::Value>,
109}
110
111#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
113#[serde(rename_all = "camelCase")]
114pub struct FieldArgument {
115 pub name: String,
117 #[serde(skip_serializing_if = "Option::is_none")]
119 pub description: Option<String>,
120 pub r#type: TypeReference,
122 #[serde(skip_serializing_if = "Option::is_none")]
124 pub default_value: Option<serde_json::Value>,
125 pub directives: IndexMap<String, serde_json::Value>,
127}
128
129#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
131#[serde(rename_all = "camelCase")]
132pub struct TypeReference {
133 pub base: String,
135 pub nullable: bool,
137 pub is_list: bool,
139 #[serde(skip_serializing_if = "Option::is_none")]
141 pub list_item_nullable: Option<bool>,
142 #[serde(default, skip_serializing_if = "Vec::is_empty")]
144 pub list_wrappers: Vec<TypeListWrapper>,
145 #[serde(skip_serializing_if = "Option::is_none")]
147 pub leaf_nullable: Option<bool>,
148}
149
150#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
152#[serde(rename_all = "camelCase")]
153pub struct TypeListWrapper {
154 pub nullable: bool,
156}
157
158pub fn compute_registry_hash(ir: &WesleyIR) -> Result<String, serde_json::Error> {
160 let mut parity_ir = ir.clone();
161 parity_ir.metadata = None;
162
163 let json = to_canonical_json(&parity_ir)?;
164 Ok(compute_content_hash(&json))
165}
166
167pub fn compute_content_hash(content: &str) -> String {
169 compute_content_hash_bytes(content.as_bytes())
170}
171
172pub fn compute_content_hash_bytes(content: &[u8]) -> String {
174 let mut hasher = Sha256::new();
175 hasher.update(content);
176 let result = hasher.finalize();
177
178 hex::encode(result)
179}
180
181pub fn to_canonical_json<T: Serialize>(value: &T) -> Result<String, serde_json::Error> {
183 let val = serde_json::to_value(value)?;
184 let sorted_val = sort_json_value(val);
185 serde_json::to_string(&sorted_val)
186}
187
188fn sort_json_value(value: serde_json::Value) -> serde_json::Value {
189 match value {
190 serde_json::Value::Object(map) => {
191 let mut sorted_map = serde_json::Map::new();
192 let mut keys: Vec<String> = map.keys().cloned().collect();
193 keys.sort();
194 for key in keys {
195 if let Some(val) = map.get(&key) {
196 sorted_map.insert(key, sort_json_value(val.clone()));
197 }
198 }
199 serde_json::Value::Object(sorted_map)
200 }
201 serde_json::Value::Array(arr) => {
202 serde_json::Value::Array(arr.into_iter().map(sort_json_value).collect())
203 }
204 _ => value,
205 }
206}