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}