1use crate::{Parser, ParserError};
4use amalgam_core::{
5 ir::{IRBuilder, IR},
6 types::{Field, Type},
7};
8use std::collections::BTreeMap;
9
10#[derive(Debug)]
12pub struct GoFile {
13 pub package: String,
14 pub imports: Vec<String>,
15 pub types: Vec<GoTypeDecl>,
16}
17
18#[derive(Debug)]
19pub struct GoTypeDecl {
20 pub name: String,
21 pub ty: GoType,
22}
23
24#[derive(Debug)]
25pub enum GoType {
26 Struct {
27 fields: Vec<GoField>,
28 },
29 Interface {
30 methods: Vec<GoMethod>,
31 },
32 Alias(Box<GoType>),
33 Basic(String),
34 Array(Box<GoType>),
35 Slice(Box<GoType>),
36 Map {
37 key: Box<GoType>,
38 value: Box<GoType>,
39 },
40 Pointer(Box<GoType>),
41}
42
43#[derive(Debug)]
44pub struct GoField {
45 pub name: String,
46 pub ty: GoType,
47 pub tag: Option<String>,
48}
49
50#[derive(Debug)]
51pub struct GoMethod {
52 pub name: String,
53 pub params: Vec<GoType>,
54 pub returns: Vec<GoType>,
55}
56
57pub struct GoParser;
58
59impl Parser for GoParser {
60 type Input = GoFile;
61
62 fn parse(&self, input: Self::Input) -> Result<IR, ParserError> {
63 let mut builder = IRBuilder::new().module(&input.package);
64
65 for import in input.imports {
67 builder = builder.add_import(import);
68 }
69
70 for type_decl in input.types {
72 let ty = self.go_type_to_type(&type_decl.ty)?;
73 builder = builder.add_type(type_decl.name, ty);
74 }
75
76 Ok(builder.build())
77 }
78}
79
80impl GoParser {
81 pub fn new() -> Self {
82 Self
83 }
84
85 fn go_type_to_type(&self, go_type: &GoType) -> Result<Type, ParserError> {
86 match go_type {
87 GoType::Basic(name) => match name.as_str() {
88 "string" => Ok(Type::String),
89 "int" | "int8" | "int16" | "int32" | "int64" | "uint" | "uint8" | "uint16"
90 | "uint32" | "uint64" => Ok(Type::Integer),
91 "float32" | "float64" => Ok(Type::Number),
92 "bool" => Ok(Type::Bool),
93 "byte" => Ok(Type::Integer), "rune" => Ok(Type::Integer), "interface{}" | "any" => Ok(Type::Any),
96 _ => Ok(Type::Reference(name.clone())),
97 },
98 GoType::Struct { fields } => {
99 let mut record_fields = BTreeMap::new();
100 for field in fields {
101 let field_type = self.go_type_to_type(&field.ty)?;
102 let (name, required) = self.parse_field_tag(&field.name, &field.tag);
103 record_fields.insert(
104 name,
105 Field {
106 ty: field_type,
107 required,
108 description: None,
109 default: None,
110 },
111 );
112 }
113 Ok(Type::Record {
114 fields: record_fields,
115 open: false,
116 })
117 }
118 GoType::Interface { methods: _ } => {
119 Ok(Type::Contract {
121 base: Box::new(Type::Any),
122 predicate: "interface".to_string(),
123 })
124 }
125 GoType::Alias(inner) => self.go_type_to_type(inner),
126 GoType::Array(elem) | GoType::Slice(elem) => {
127 let elem_type = self.go_type_to_type(elem)?;
128 Ok(Type::Array(Box::new(elem_type)))
129 }
130 GoType::Map { key, value } => {
131 let key_type = self.go_type_to_type(key)?;
132 let value_type = self.go_type_to_type(value)?;
133 Ok(Type::Map {
134 key: Box::new(key_type),
135 value: Box::new(value_type),
136 })
137 }
138 GoType::Pointer(inner) => {
139 let inner_type = self.go_type_to_type(inner)?;
140 Ok(Type::Optional(Box::new(inner_type)))
141 }
142 }
143 }
144
145 fn parse_field_tag(&self, field_name: &str, tag: &Option<String>) -> (String, bool) {
146 if let Some(tag_str) = tag {
147 if let Some(json_tag) = self.extract_json_tag(tag_str) {
149 let parts: Vec<&str> = json_tag.split(',').collect();
150 if let Some(name) = parts.first() {
151 if *name != "-" {
152 let required = !parts.contains(&"omitempty");
153 return (name.to_string(), required);
154 }
155 }
156 }
157 }
158 (field_name.to_string(), true)
159 }
160
161 fn extract_json_tag(&self, tag: &str) -> Option<String> {
162 if let Some(start) = tag.find("json:\"") {
164 let tag_content = &tag[start + 6..];
165 if let Some(end) = tag_content.find('"') {
166 return Some(tag_content[..end].to_string());
167 }
168 }
169 None
170 }
171}
172
173impl Default for GoParser {
174 fn default() -> Self {
175 Self::new()
176 }
177}