1use serde::{Deserialize, Serialize};
2use std::collections::HashMap;
3
4#[derive(Debug, Deserialize)]
5struct InitialJson {
6 imports: Option<HashMap<String, serde_json::Value>>,
7 program_scopes: HashMap<String, ProgramScope>,
8}
9
10#[derive(Debug, Deserialize)]
11struct ProgramScope {
12 structs: Vec<(String, StructDef)>,
13 mappings: Vec<(String, MappingDef)>,
14 functions: Vec<(String, FunctionDef)>,
15}
16
17#[derive(Debug, Deserialize)]
18struct Identifier {
19 name: String,
20}
21
22#[derive(Debug, Deserialize)]
23struct StructDef {
24 identifier: Identifier,
25 members: Vec<StructMember>,
26 is_record: bool,
27}
28
29#[derive(Debug, Deserialize)]
30struct StructMember {
31 identifier: Identifier,
32 #[serde(rename = "type_")]
33 type_info: TypeInfo,
34 mode: String, }
36
37#[derive(Debug, Deserialize)]
38struct FunctionDef {
39 identifier: Identifier,
40 variant: String,
41 input: Vec<Parameter>,
42 output: Vec<OutputParameter>,
43}
44
45#[derive(Debug, Deserialize)]
46struct Parameter {
47 identifier: Identifier,
48 #[serde(rename = "type_")]
49 type_info: TypeInfo,
50 mode: String,
51}
52
53#[derive(Debug, Deserialize)]
54struct OutputParameter {
55 #[serde(rename = "type_")]
56 type_info: TypeInfo,
57 mode: String,
58}
59
60#[derive(Debug, Deserialize)]
61#[serde(untagged)]
62enum TypeInfo {
63 Simple(String),
64 Integer {
65 #[serde(rename = "Integer")]
66 integer: String,
67 },
68 Composite {
69 #[serde(rename = "Composite")]
70 composite: CompositeType,
71 },
72 Array {
73 #[serde(rename = "Array")]
74 array: ArrayType,
75 },
76 Future {
77 #[serde(rename = "Future")]
78 #[allow(dead_code)]
79 future: FutureType,
80 },
81 Tuple {
82 #[serde(rename = "Tuple")]
83 tuple: TupleType,
84 },
85}
86
87#[derive(Debug, Deserialize)]
88struct CompositeType {
89 path: PathType,
90}
91
92#[derive(Debug, Deserialize)]
93struct PathType {
94 identifier: Identifier,
95}
96
97#[derive(Debug, Deserialize)]
98struct ArrayType {
99 element_type: Box<TypeInfo>,
100 length: serde_json::Value,
101}
102
103#[derive(Debug, Deserialize)]
104struct MappingDef {
105 identifier: Identifier,
106 key_type: TypeInfo,
107 value_type: TypeInfo,
108}
109
110#[derive(Debug, Deserialize)]
111struct FutureType {
112 #[allow(dead_code)]
113 inputs: Vec<serde_json::Value>,
114 #[allow(dead_code)]
115 location: Option<serde_json::Value>,
116 #[allow(dead_code)]
117 is_explicit: bool,
118}
119
120#[derive(Debug, Deserialize)]
121struct TupleType {
122 elements: Vec<TypeInfo>,
123}
124
125#[derive(Debug, Serialize, Deserialize)]
126pub struct SimplifiedBindings {
127 pub program_name: String,
128 pub imports: Vec<String>,
129 pub records: Vec<StructBinding>,
130 pub structs: Vec<StructBinding>,
131 pub mappings: Vec<MappingBinding>,
132 pub functions: Vec<FunctionBinding>,
133}
134
135#[derive(Debug, Serialize, Deserialize)]
136pub struct MemberDef {
137 pub name: String,
138 #[serde(rename = "type")]
139 pub type_name: String,
140 pub mode: String,
141}
142
143#[derive(Debug, Serialize, Deserialize)]
144pub struct FunctionBinding {
145 pub name: String,
146 pub inputs: Vec<InputParam>,
147 pub outputs: Vec<OutputType>,
148 pub is_async: bool,
149}
150
151#[derive(Debug, Serialize, Deserialize)]
152pub struct StructBinding {
153 pub name: String,
154 pub members: Vec<MemberDef>,
155 pub is_record: bool,
156}
157
158#[derive(Debug, Serialize, Deserialize)]
159pub struct MappingBinding {
160 pub name: String,
161 pub key_type: String,
162 pub value_type: String,
163}
164
165#[derive(Debug, Serialize, Deserialize)]
166pub struct InputParam {
167 pub name: String,
168 #[serde(rename = "type")]
169 pub type_name: String,
170 pub mode: String,
171}
172
173#[derive(Debug, Serialize, Deserialize)]
174pub struct OutputType {
175 #[serde(rename = "type")]
176 pub type_name: String,
177 pub mode: String,
178}
179
180pub fn get_signatures(input: String) -> String {
181 let initial_json: InitialJson = serde_json::from_str(&input).unwrap();
182
183 let imports: Vec<String> = initial_json
184 .imports
185 .as_ref()
186 .map(|imports_map| imports_map.keys().cloned().collect())
187 .unwrap_or_default();
188
189 let (program_name, program_scope) = initial_json.program_scopes.into_iter().next().unwrap();
190
191 let (records, structs): (Vec<StructBinding>, Vec<StructBinding>) = program_scope
192 .structs
193 .into_iter()
194 .map(|(_, struct_def)| {
195 let members = struct_def
196 .members
197 .into_iter()
198 .map(|member| MemberDef {
199 name: member.identifier.name,
200 type_name: normalize_type(&member.type_info),
201 mode: member.mode,
202 })
203 .collect();
204
205 StructBinding {
206 name: struct_def.identifier.name,
207 members,
208 is_record: struct_def.is_record,
209 }
210 })
211 .partition(|binding| binding.is_record);
212
213 let functions: Vec<FunctionBinding> = program_scope
214 .functions
215 .into_iter()
216 .filter_map(|(_, func_def)| {
217 if func_def.variant == "Transition" || func_def.variant == "AsyncTransition" {
218 let inputs = func_def
219 .input
220 .into_iter()
221 .map(|param| InputParam {
222 name: param.identifier.name,
223 type_name: normalize_type(¶m.type_info),
224 mode: param.mode,
225 })
226 .collect();
227
228 let outputs: Vec<OutputType> = func_def
229 .output
230 .into_iter()
231 .map(|output| OutputType {
232 type_name: normalize_type(&output.type_info),
233 mode: output.mode,
234 })
235 .collect();
236
237 let is_async = func_def.variant == "AsyncTransition";
238
239 if is_async {
240 if outputs.is_empty() {
241 panic!("Async function '{}' must have at least a Future output", func_def.identifier.name);
242 }
243 let last_output = &outputs[outputs.len() - 1];
244 if last_output.type_name != "Future" {
245 panic!("Async function '{}' must have Future as the last output, but found '{}'", func_def.identifier.name, last_output.type_name);
246 }
247 }
248
249 Some(FunctionBinding {
250 name: func_def.identifier.name,
251 inputs,
252 outputs,
253 is_async,
254 })
255 } else {
256 None
257 }
258 })
259 .collect();
260
261 let mappings: Vec<MappingBinding> = program_scope
262 .mappings
263 .into_iter()
264 .map(|(_, mapping_def)| MappingBinding {
265 name: mapping_def.identifier.name,
266 key_type: normalize_type(&mapping_def.key_type),
267 value_type: normalize_type(&mapping_def.value_type),
268 })
269 .collect();
270
271 let simplified = SimplifiedBindings {
272 program_name,
273 imports,
274 records,
275 structs,
276 mappings,
277 functions,
278 };
279
280 serde_json::to_string_pretty(&simplified).unwrap()
281}
282
283fn normalize_type(type_info: &TypeInfo) -> String {
284 match type_info {
285 TypeInfo::Simple(s) => s.clone(),
286 TypeInfo::Integer { integer: int_type } => match int_type.as_str() {
287 "U8" => "u8".to_string(),
288 "U16" => "u16".to_string(),
289 "U32" => "u32".to_string(),
290 "U64" => "u64".to_string(),
291 "U128" => "u128".to_string(),
292 "I8" => "i8".to_string(),
293 "I16" => "i16".to_string(),
294 "I32" => "i32".to_string(),
295 "I64" => "i64".to_string(),
296 "I128" => "i128".to_string(),
297 _ => format!("Unknown_Integer_{}", int_type),
298 },
299 TypeInfo::Composite { composite: comp } => comp.path.identifier.name.clone(),
300 TypeInfo::Array { array } => {
301 let element_type = normalize_type(&array.element_type);
302 let size = extract_array_size(&array.length);
303 format!("[{}; {}]", element_type, size)
304 }
305 TypeInfo::Future { .. } => "Future".to_string(),
306 TypeInfo::Tuple { tuple } => {
307 let element_types: Vec<String> = tuple.elements.iter().map(normalize_type).collect();
308 format!("({})", element_types.join(", "))
309 }
310 }
311}
312
313fn extract_array_size(length_json: &serde_json::Value) -> String {
314 if let Some(literal) = length_json.get("Literal") {
325 if let Some(variant) = literal.get("variant") {
326 if let Some(integer_array) = variant.get("Integer") {
327 if let Some(array) = integer_array.as_array() {
328 if array.len() == 2 {
329 if let Some(size_str) = array[1].as_str() {
330 let size = size_str
331 .trim_end_matches("u8")
332 .trim_end_matches("u16")
333 .trim_end_matches("u32")
334 .trim_end_matches("u64")
335 .trim_end_matches("u128")
336 .trim_end_matches("i8")
337 .trim_end_matches("i16")
338 .trim_end_matches("i32")
339 .trim_end_matches("i64")
340 .trim_end_matches("i128");
341 return size.to_string();
342 }
343 }
344 }
345 } else if let Some(unsuffixed_str) = variant.get("Unsuffixed") {
346 if let Some(size_str) = unsuffixed_str.as_str() {
347 return size_str.to_string();
348 }
349 }
350 }
351 }
352 "UNKNOWN_SIZE".to_string()
353}