Files
go-winrt/internal/codegen/templates.go
Alex Savin 0a314257f8 Refactor import paths to use the new repository location for go-winrt
- Updated import paths in multiple files to point to the new repository at git.savin.nyc/alex/go-winrt.
- Removed old import paths referencing saltosystems/winrt-go.
- Ensured consistency across all affected files in the Bluetooth Generic Attribute Profile and Foundation packages.
2025-08-22 17:52:08 -04:00

312 lines
6.7 KiB
Go

package codegen
import (
"embed"
"strings"
"text/template"
"git.savin.nyc/alex/go-winrt/internal/winmd"
)
type genDataFile struct {
Filename string
Data genData
}
type genData struct {
Package string
Imports []string
Classes []*genClass
Enums []*genEnum
Interfaces []*genInterface
Structs []*genStruct
Delegates []*genDelegate
}
func (g *genData) ComputeImports(typeDef *winmd.TypeDef) {
// gather all imports
imports := make([]*genImport, 0)
if g.Classes != nil {
for _, c := range g.Classes {
imports = append(imports, c.GetRequiredImports()...)
}
}
if g.Interfaces != nil {
for _, i := range g.Interfaces {
imports = append(imports, i.GetRequiredImports()...)
}
}
for _, i := range imports {
if typeDef.TypeNamespace != i.Namespace {
g.Imports = append(g.Imports, i.ToGoImport())
}
}
}
type genInterface struct {
Name string
GUID string
Signature string
Funcs []*genFunc
}
func (g *genInterface) GetRequiredImports() []*genImport {
imports := make([]*genImport, 0)
for _, f := range g.Funcs {
imports = append(imports, f.RequiresImports...)
}
return imports
}
type genClass struct {
Name string
Signature string
RequiresImports []*genImport
FullyQualifiedName string
ImplInterfaces []*genInterface
ExclusiveInterfaces []*genInterface
HasEmptyConstructor bool
IsAbstract bool
}
func (g *genClass) GetRequiredImports() []*genImport {
imports := make([]*genImport, 0)
if g.RequiresImports != nil {
imports = append(imports, g.RequiresImports...)
}
if g.ExclusiveInterfaces != nil {
for _, i := range g.ExclusiveInterfaces {
imports = append(imports, i.GetRequiredImports()...)
}
}
return imports
}
type genDelegate struct {
Name string
GUID string
Signature string
InParams []*genParam
ReturnParam *genParam // this may be nil
}
type genEnum struct {
Name string
Type string
Signature string
Values []*genEnumValue
}
type genEnumValue struct {
Name string
Value string
}
type genFunc struct {
Name string
RequiresImports []*genImport
Implement bool
FuncOwner string
InParams []*genParam
ReturnParams []*genParam // this may be empty
// ExclusiveTo is the name of the class that this function is exclusive to.
// The funcion will be called statically using the RoGetActivationFactory function.
ExclusiveTo string
RequiresActivation bool
InheritedFrom winmd.QualifiedID
}
type genImport struct {
Namespace, Name string
}
func (i genImport) ToGoImport() string {
if !strings.Contains(i.Namespace, ".") && i.Namespace != "Windows" {
// This is probably a built-in package
return i.Namespace
}
folder := typeToFolder(i.Namespace, i.Name)
return "git.savin.nyc/alex/go-winrt/" + folder
}
// some of the variables are not public to avoid using them
// by mistake in the code.
type genDefaultValue struct {
value string
isPrimitive bool
}
// some of the variables are not public to avoid using them
// by mistake in the code.
type genParamType struct {
namespace string
name string
IsPointer bool
IsGeneric bool
IsArray bool
IsPrimitive bool
IsEnum bool
UnderlyingEnumType string
defaultValue genDefaultValue
}
// some of the variables are not public to avoid using them
// by mistake in the code.
type genParam struct {
callerPackage string
varName string
Type *genParamType
IsOut bool
}
func (g *genParam) GoVarName() string {
return typeNameToGoName(g.varName, true) // assume all are public
}
func (g *genParam) GoTypeName() string {
if g.Type.IsPrimitive {
return g.Type.name
}
name := typeNameToGoName(g.Type.name, true) // assume all are public
pkg := typePackage(g.Type.namespace, g.Type.name)
if g.callerPackage != pkg {
name = pkg + "." + name
}
return name
}
func (g *genParam) GoDefaultValue() string {
if g.Type.defaultValue.isPrimitive {
return g.Type.defaultValue.value
}
pkg := typePackage(g.Type.namespace, g.Type.name)
if g.callerPackage != pkg {
return pkg + "." + g.Type.defaultValue.value
}
return g.Type.defaultValue.value
}
type genStruct struct {
Name string
Signature string
Fields []*genParam
}
//go:embed templates/*
var templatesFS embed.FS
func getTemplates() (*template.Template, error) {
return template.New("").
Funcs(funcs()).
ParseFS(templatesFS, "templates/*")
}
func funcs() template.FuncMap {
return template.FuncMap{
"funcName": funcName,
"concat": func(a, b []*genParam) []*genParam {
return append(a, b...)
},
"toLower": func(s string) string {
return strings.ToLower(s[:1]) + s[1:]
},
}
}
// funcName is used to generate the name of a function.
func funcName(m genFunc) string {
// There are some special prefixes applied to methods that we need to replace
replacer := strings.NewReplacer(
"get_", "Get",
"put_", "Set",
"add_", "Add",
"remove_", "Remove",
)
name := replacer.Replace(m.Name)
// Add a prefix to static methods to include the owner class of the method.
// This is necessary to avoid conflicts with method names within the same package.
// Static methods are those that are exclusive to a class and require activation.
prefix := ""
if m.ExclusiveTo != "" && m.RequiresActivation {
nsAndName := strings.Split(m.ExclusiveTo, ".")
prefix = typeNameToGoName(nsAndName[len(nsAndName)-1], true)
}
return prefix + name
}
func typeToFolder(ns, name string) string {
fullName := ns
return strings.ToLower(strings.Replace(fullName, ".", "/", -1))
}
func typePackage(ns, name string) string {
sns := strings.Split(ns, ".")
return strings.ToLower(sns[len(sns)-1])
}
func enumName(typeName string, enumName string) string {
return typeName + enumName
}
func typeDefGoName(typeName string, public bool) string {
name := typeName
if isParameterizedName(typeName) {
name = strings.Split(name, "`")[0]
}
if !public {
name = strings.ToLower(name[0:1]) + name[1:]
}
return name
}
func isParameterizedName(typeName string) bool {
// parameterized types contain a '`' followed by the amount of generic parameters in their name.
return strings.Contains(typeName, "`")
}
func typeFilename(typeName string) string {
// public boolean is not relevant, we are going to lower everything
goname := typeDefGoName(typeName, true)
return strings.ToLower(goname)
}
// removes Go reserved words from param names
func cleanReservedWords(name string) string {
switch name {
case "type":
return "mType"
}
return name
}
func typeNameToGoName(typeName string, public bool) string {
name := typeName
if isParameterizedName(typeName) {
name = strings.Split(name, "`")[0]
}
if !public {
name = strings.ToLower(name[0:1]) + name[1:]
}
return name
}