Files
mautrix-telegram/pkg/gotd/gen/make_bindings.go
T
2025-06-27 20:03:37 -07:00

130 lines
3.3 KiB
Go

package gen
import (
"strings"
"github.com/go-faster/errors"
"github.com/gotd/tl"
)
type typeBinding struct {
Namespace []string
Class string // user.Auth, class in TL
Name string // go type name, like in type Name struct{}
Interface string // go type interface, like UserAuthClass
InterfaceFunc string // go encoding/decoding function postfix, like UserAuth in DecodeUserAuth
Method string // go method name if function
}
type classBinding struct {
// Name as used in go interface type definition,
// i.e. type Name interface {}.
Name string
// Func is used as postfix for Decode and Encode functions.
Func string
Namespace []string
// Singular is special case for class where single constructor replaces
// class.
Singular bool
Vector bool
Constructors []string
// BaseName is "Auth" for user.Auth interface.
BaseName string
// RawType of class from TL.
RawType string
}
// namespacedName returns camel-case name with namespace prefix.
func namespacedName(name string, namespace []string) string {
goName := pascal(name)
if len(namespace) > 0 {
var b strings.Builder
for _, ns := range namespace {
b.WriteString(pascal(ns))
}
b.WriteString(goName)
goName = b.String()
}
return goName
}
// makeBindings fills classes and types fields of Generator.
func (g *Generator) makeBindings() error {
// 1) Searching for all classes with single constructor.
// If class has single constructor, it can be reduced to specific type.
constructors := map[string]int{}
for _, d := range g.schema.Definitions {
if d.Category != tl.CategoryType {
continue
}
constructors[d.Definition.Type.String()]++
}
// 2) Binding TL types to structures and interfaces.
for _, sd := range g.schema.Definitions {
var (
d = sd.Definition
classKey = d.Type.String()
typeKey = definitionType(d)
goName = namespacedName(d.Name, d.Namespace)
)
// Binding bare type.
tb := typeBinding{
Namespace: d.Namespace,
Class: classKey,
Name: goName,
}
switch sd.Category {
case tl.CategoryType:
constructorsCount, ok := constructors[classKey]
if constructorsCount == 0 || !ok {
return errors.Errorf("constructors[%s] not found", classKey)
}
if constructorsCount == 1 {
// Using this constructor instead of generic class for all definitions
// that depends on that class.
b := classBinding{
Namespace: d.Namespace,
Singular: true,
Name: goName,
BaseName: d.Type.Name,
}
g.classes[classKey] = b
g.types[typeKey] = tb
continue
}
if _, ok := g.classes[classKey]; !ok {
// interfaceDef has multiple constructors and is new.
className := namespacedName(d.Type.Name, d.Type.Namespace)
g.classes[classKey] = classBinding{
Namespace: d.Namespace,
Singular: false,
Func: className,
Name: className + "Class",
BaseName: d.Type.Name,
RawType: d.Type.String(),
}
}
c := g.classes[classKey]
c.Constructors = append(c.Constructors, goName)
g.classes[classKey] = c
tb.Interface = c.Name
tb.InterfaceFunc = c.Func
case tl.CategoryFunction:
// Just creating new bare type.
tb.Method = tb.Name
tb.Name += "Request"
}
g.types[typeKey] = tb
}
return nil
}