GitRoot
Craft your forge, Build your project, Grow your community freely
1// SPDX-FileCopyrightText: 2026 Romain Maneschi <romain@gitroot.dev>
2//
3// SPDX-License-Identifier: EUPL-1.2
4
5package main
6
7import (
8 "fmt"
9 "go/types"
10 "path/filepath"
11 "slices"
12 "strings"
13
14 "github.com/gobeam/stringy"
15)
16
17type Zig struct{}
18
19func (Zig) Extension() string {
20 return ".zig"
21}
22
23func (t Zig) TemplateStruct(g *Generate, f *FileToGen, st Struct) (string, []string) {
24 fieldStrings := []string{}
25 imports := []string{}
26 for _, field := range st.Fields {
27 typeStr, imp := t.TypeToLang(field.Type(), f.OriginFilePath, g.structLocations)
28 if imp != "" && !slices.Contains(imports, imp) {
29 imports = append(imports, imp)
30 }
31 fieldName := stringy.New(field.Name()).SnakeCase().CamelCase().Get()
32 fieldStrings = append(fieldStrings, fmt.Sprintf(" %s: %s,", fieldName, typeStr))
33 }
34 structName := stringy.New(st.TypeName).PascalCase().Get()
35
36 return fmt.Sprintf(`pub const %s = struct {
37%s
38};
39`, structName, strings.Join(fieldStrings, "\n")), imports
40}
41
42func (Zig) TemplateEnum(en Enum, variants []string) string {
43 enumName := stringy.New(en.TypeName)
44 enumBuilder := strings.Builder{}
45
46 enumBuilder.WriteString(fmt.Sprintf("pub const %s = enum {\n", enumName.PascalCase().Get()))
47 for _, e := range variants {
48 estringy := stringy.New(e)
49 enumBuilder.WriteString(fmt.Sprintf(" %s,\n", estringy.ToLower()))
50 }
51 enumBuilder.WriteString("};\n")
52
53 return enumBuilder.String()
54}
55
56func (ts Zig) FormatFuncName(fn Func) string {
57 return stringy.New(fn.Name).SnakeCase().CamelCase().Get()
58}
59
60func (ts Zig) FormatParams(sig *types.Signature, isMethod bool, currentFile string, locations map[string]string) (string, []string) {
61 var params []string
62 var imports []string
63
64 p := sig.Params()
65 for i := 0; i < p.Len(); i++ {
66 v := p.At(i)
67 typeStr, imp := ts.TypeToLang(v.Type(), currentFile, locations)
68 if imp != "" {
69 imports = append(imports, imp)
70 }
71
72 name := v.Name()
73 if name == "" {
74 name = fmt.Sprintf("arg%d", i)
75 } else {
76 name = stringy.New(name).PascalCase().Get()
77 }
78
79 params = append(params, fmt.Sprintf("%s: %s", name, typeStr))
80 }
81
82 return strings.Join(params, ", "), imports
83}
84
85func (ts Zig) FormatReturnType(sig *types.Signature, currentFile string, locations map[string]string) string {
86 res := sig.Results()
87 if res.Len() == 0 {
88 return " void "
89 } else {
90 typeStr, _ := ts.TypeToLang(res.At(0).Type(), currentFile, locations)
91 return fmt.Sprintf(" %s ", typeStr)
92 }
93}
94
95func (ts Zig) FormatExportFunc(wasmName string, funcName string) string {
96 return fmt.Sprintf(`pub export fn %s(message: [*]const u8, size: u32): void {
97 ??
98}
99
100`, funcName)
101}
102
103func (Zig) FormatImportFunc(wasmName string, funcName string, paramsList string, returnType string) string {
104 return fmt.Sprintf(`pub extern "gitroot" fn %s(%s) %s;
105`, funcName, paramsList, returnType)
106}
107
108func (ts Zig) TypeToLang(t types.Type, currentFile string, locations map[string]string) (string, string) {
109 if sig, ok := t.(*types.Signature); ok && sig.Recv() == nil {
110 var cbParams []string
111 var cbImports []string
112
113 p := sig.Params()
114 for i := 0; i < p.Len(); i++ {
115 pType, pImp := ts.TypeToLang(p.At(i).Type(), currentFile, locations)
116 if pImp != "" {
117 cbImports = append(cbImports, pImp)
118 }
119 cbParams = append(cbParams, pType)
120 }
121
122 callbackType := fmt.Sprintf("*const fn(%s) void", strings.Join(cbParams, ", "))
123 return callbackType, strings.Join(cbImports, "\n")
124 }
125
126 if slice, ok := t.(*types.Slice); ok {
127 elemType, imp := ts.TypeToLang(slice.Elem(), currentFile, locations)
128 return fmt.Sprintf("[]%s", elemType), imp
129 }
130
131 if ptr, ok := t.(*types.Pointer); ok {
132 return ts.TypeToLang(ptr.Elem(), currentFile, locations)
133 }
134
135 if named, ok := t.(*types.Named); ok {
136 obj := named.Obj()
137 typeName := stringy.New(obj.Name()).PascalCase().Get()
138
139 originFile, exists := locations[obj.Name()]
140 if exists {
141 if originFile == currentFile {
142 return typeName, ""
143 }
144
145 moduleName := strings.TrimSuffix(filepath.Base(originFile), ".go")
146 rustImport := fmt.Sprintf(`const %s = @import("%s.zig");`, typeName, moduleName)
147 return typeName, rustImport
148 }
149 }
150
151 switch t.String() {
152 case "string":
153 return "[]const u8", ""
154 case "map[string]string":
155 return "std.StringHashMap([]const u8)", "const std = @import(\"std\");"
156 case "map[string]any":
157 return "std.StringHashMap([]const u8)", "const std = @import(\"std\");"
158 case "uint64":
159 return "u64", ""
160 case "uint32":
161 return "u32", ""
162 case "int32":
163 return "u32", ""
164 case "int":
165 return "u32", ""
166 case "bool":
167 return "bool", ""
168 case "time.Time":
169 return "datetime.DateTime", "const datetime = @import(\"datetime\");"
170 }
171
172 fmt.Printf("🐞 Type not found %s\n", t.String())
173
174 return t.String(), ""
175}