initial commit

This commit is contained in:
astravexton 2021-10-22 15:24:12 +01:00
commit 6358d0754b
34 changed files with 82616 additions and 0 deletions

115
codegen/function.go Normal file
View file

@ -0,0 +1,115 @@
package codegen
import (
"bytes"
"fmt"
"github.com/astravexton/go-tdlib/tlparser"
)
func GenerateFunctions(schema *tlparser.Schema, packageName string) []byte {
buf := bytes.NewBufferString("")
buf.WriteString(fmt.Sprintf("%s\n\npackage %s\n\n", header, packageName))
buf.WriteString(`import (
"errors"
)`)
buf.WriteString("\n")
for _, function := range schema.Functions {
tdlibFunction := TdlibFunction(function.Name, schema)
tdlibFunctionReturn := TdlibFunctionReturn(function.Class, schema)
if len(function.Properties) > 0 {
buf.WriteString("\n")
buf.WriteString(fmt.Sprintf("type %sRequest struct { \n", tdlibFunction.ToGoName()))
for _, property := range function.Properties {
tdlibTypeProperty := TdlibTypeProperty(property.Name, property.Type, schema)
buf.WriteString(fmt.Sprintf(" // %s\n", property.Description))
buf.WriteString(fmt.Sprintf(" %s %s `json:\"%s\"`\n", tdlibTypeProperty.ToGoName(), tdlibTypeProperty.ToGoType(), property.Name))
}
buf.WriteString("}\n")
}
buf.WriteString("\n")
buf.WriteString("// " + function.Description)
buf.WriteString("\n")
requestArgument := ""
if len(function.Properties) > 0 {
requestArgument = fmt.Sprintf("req *%sRequest", tdlibFunction.ToGoName())
}
buf.WriteString(fmt.Sprintf("func (client *Client) %s(%s) (%s, error) {\n", tdlibFunction.ToGoName(), requestArgument, tdlibFunctionReturn.ToGoReturn()))
sendMethod := "Send"
if function.IsSynchronous {
sendMethod = "jsonClient.Execute"
}
if len(function.Properties) > 0 {
buf.WriteString(fmt.Sprintf(` result, err := client.%s(Request{
meta: meta{
Type: "%s",
},
Data: map[string]interface{}{
`, sendMethod, function.Name))
for _, property := range function.Properties {
tdlibTypeProperty := TdlibTypeProperty(property.Name, property.Type, schema)
buf.WriteString(fmt.Sprintf(" \"%s\": req.%s,\n", property.Name, tdlibTypeProperty.ToGoName()))
}
buf.WriteString(` },
})
`)
} else {
buf.WriteString(fmt.Sprintf(` result, err := client.%s(Request{
meta: meta{
Type: "%s",
},
Data: map[string]interface{}{},
})
`, sendMethod, function.Name))
}
buf.WriteString(` if err != nil {
return nil, err
}
if result.Type == "error" {
return nil, buildResponseError(result.Data)
}
`)
if tdlibFunctionReturn.IsClass() {
buf.WriteString(" switch result.Type {\n")
for _, subType := range tdlibFunctionReturn.GetClass().GetSubTypes() {
buf.WriteString(fmt.Sprintf(` case %s:
return Unmarshal%s(result.Data)
`, subType.ToTypeConst(), subType.ToGoType()))
}
buf.WriteString(` default:
return nil, errors.New("invalid type")
`)
buf.WriteString(" }\n")
} else {
buf.WriteString(fmt.Sprintf(` return Unmarshal%s(result.Data)
`, tdlibFunctionReturn.ToGoType()))
}
buf.WriteString("}\n")
}
return buf.Bytes()
}

3
codegen/header.go Normal file
View file

@ -0,0 +1,3 @@
package codegen
const header = "// AUTOGENERATED"

26
codegen/string.go Normal file
View file

@ -0,0 +1,26 @@
package codegen
import (
"strings"
"unicode"
)
func firstUpper(str string) string {
for i, r := range str {
return string(unicode.ToUpper(r)) + str[i+1:]
}
return str
}
func firstLower(str string) string {
for i, r := range str {
return string(unicode.ToLower(r)) + str[i+1:]
}
return str
}
func underscoreToCamelCase(s string) string {
return strings.Replace(strings.Title(strings.Replace(strings.ToLower(s), "_", " ", -1)), " ", "", -1)
}

487
codegen/tdlib.go Normal file
View file

@ -0,0 +1,487 @@
package codegen
import (
"log"
"strings"
"github.com/astravexton/go-tdlib/tlparser"
)
type tdlibFunction struct {
name string
schema *tlparser.Schema
}
func TdlibFunction(name string, schema *tlparser.Schema) *tdlibFunction {
return &tdlibFunction{
name: name,
schema: schema,
}
}
func (entity *tdlibFunction) ToGoName() string {
return firstUpper(entity.name)
}
type tdlibFunctionReturn struct {
name string
schema *tlparser.Schema
}
func TdlibFunctionReturn(name string, schema *tlparser.Schema) *tdlibFunctionReturn {
return &tdlibFunctionReturn{
name: name,
schema: schema,
}
}
func (entity *tdlibFunctionReturn) IsType() bool {
return isType(entity.name, func(entity *tlparser.Type) string {
return entity.Class
}, entity.schema)
}
func (entity *tdlibFunctionReturn) GetType() *tdlibType {
return getType(entity.name, func(entity *tlparser.Type) string {
return entity.Class
}, entity.schema)
}
func (entity *tdlibFunctionReturn) IsClass() bool {
return isClass(entity.name, func(entity *tlparser.Class) string {
return entity.Name
}, entity.schema)
}
func (entity *tdlibFunctionReturn) GetClass() *tdlibClass {
return getClass(entity.name, func(entity *tlparser.Class) string {
return entity.Name
}, entity.schema)
}
func (entity *tdlibFunctionReturn) ToGoReturn() string {
if strings.HasPrefix(entity.name, "vector<") {
log.Fatal("vectors are not supported")
}
if entity.IsClass() {
return entity.GetClass().ToGoType()
}
if entity.GetType().IsInternal() {
return entity.GetType().ToGoType()
}
return "*" + entity.GetType().ToGoType()
}
func (entity *tdlibFunctionReturn) ToGoType() string {
if strings.HasPrefix(entity.name, "vector<") {
log.Fatal("vectors are not supported")
}
if entity.IsClass() {
return entity.GetClass().ToGoType()
}
return entity.GetType().ToGoType()
}
type tdlibFunctionProperty struct {
name string
propertyType string
schema *tlparser.Schema
}
func TdlibFunctionProperty(name string, propertyType string, schema *tlparser.Schema) *tdlibFunctionProperty {
return &tdlibFunctionProperty{
name: name,
propertyType: propertyType,
schema: schema,
}
}
func (entity *tdlibFunctionProperty) GetPrimitive() string {
primitive := entity.propertyType
for strings.HasPrefix(primitive, "vector<") {
primitive = strings.TrimSuffix(strings.TrimPrefix(primitive, "vector<"), ">")
}
return primitive
}
func (entity *tdlibFunctionProperty) IsType() bool {
primitive := entity.GetPrimitive()
return isType(primitive, func(entity *tlparser.Type) string {
return entity.Name
}, entity.schema)
}
func (entity *tdlibFunctionProperty) GetType() *tdlibType {
primitive := entity.GetPrimitive()
return getType(primitive, func(entity *tlparser.Type) string {
return entity.Name
}, entity.schema)
}
func (entity *tdlibFunctionProperty) IsClass() bool {
primitive := entity.GetPrimitive()
return isClass(primitive, func(entity *tlparser.Class) string {
return entity.Name
}, entity.schema)
}
func (entity *tdlibFunctionProperty) GetClass() *tdlibClass {
primitive := entity.GetPrimitive()
return getClass(primitive, func(entity *tlparser.Class) string {
return entity.Name
}, entity.schema)
}
func (entity *tdlibFunctionProperty) ToGoName() string {
name := firstLower(underscoreToCamelCase(entity.name))
if name == "type" {
name += "Param"
}
return name
}
func (entity *tdlibFunctionProperty) ToGoType() string {
tdlibType := entity.propertyType
goType := ""
for strings.HasPrefix(tdlibType, "vector<") {
goType = goType + "[]"
tdlibType = strings.TrimSuffix(strings.TrimPrefix(tdlibType, "vector<"), ">")
}
if entity.IsClass() {
return goType + entity.GetClass().ToGoType()
}
if entity.GetType().IsInternal() {
return goType + entity.GetType().ToGoType()
}
return goType + "*" + entity.GetType().ToGoType()
}
type tdlibType struct {
name string
schema *tlparser.Schema
}
func TdlibType(name string, schema *tlparser.Schema) *tdlibType {
return &tdlibType{
name: name,
schema: schema,
}
}
func (entity *tdlibType) IsInternal() bool {
switch entity.name {
case "double":
return true
case "string":
return true
case "int32":
return true
case "int53":
return true
case "int64":
return true
case "bytes":
return true
case "boolFalse":
return true
case "boolTrue":
return true
case "vector<t>":
return true
}
return false
}
func (entity *tdlibType) GetType() *tlparser.Type {
name := normalizeEntityName(entity.name)
for _, typ := range entity.schema.Types {
if typ.Name == name {
return typ
}
}
return nil
}
func (entity *tdlibType) ToGoType() string {
if strings.HasPrefix(entity.name, "vector<") {
log.Fatal("vectors are not supported")
}
switch entity.name {
case "double":
return "float64"
case "string":
return "string"
case "int32":
return "int32"
case "int53":
return "int64"
case "int64":
return "JsonInt64"
case "bytes":
return "[]byte"
case "boolFalse":
return "bool"
case "boolTrue":
return "bool"
}
return firstUpper(entity.name)
}
func (entity *tdlibType) ToType() string {
return entity.ToGoType() + "Type"
}
func (entity *tdlibType) HasClass() bool {
className := entity.GetType().Class
for _, class := range entity.schema.Classes {
if class.Name == className {
return true
}
}
return false
}
func (entity *tdlibType) GetClass() *tlparser.Class {
className := entity.GetType().Class
for _, class := range entity.schema.Classes {
if class.Name == className {
return class
}
}
return nil
}
func (entity *tdlibType) HasClassProperties() bool {
for _, prop := range entity.GetType().Properties {
tdlibTypeProperty := TdlibTypeProperty(prop.Name, prop.Type, entity.schema)
if tdlibTypeProperty.IsClass() {
return true
}
}
return false
}
func (entity *tdlibType) IsList() bool {
return strings.HasPrefix(entity.name, "vector<")
}
func (entity *tdlibType) ToClassConst() string {
if entity.HasClass() {
return "Class" + TdlibClass(entity.GetType().Class, entity.schema).ToGoType()
}
return "Class" + entity.ToGoType()
}
func (entity *tdlibType) ToTypeConst() string {
return "Type" + entity.ToGoType()
}
type tdlibClass struct {
name string
schema *tlparser.Schema
}
func TdlibClass(name string, schema *tlparser.Schema) *tdlibClass {
return &tdlibClass{
name: name,
schema: schema,
}
}
func (entity *tdlibClass) ToGoType() string {
return firstUpper(entity.name)
}
func (entity *tdlibClass) ToType() string {
return entity.ToGoType() + "Type"
}
func (entity *tdlibClass) GetSubTypes() []*tdlibType {
types := []*tdlibType{}
for _, t := range entity.schema.Types {
if t.Class == entity.name {
types = append(types, TdlibType(t.Name, entity.schema))
}
}
return types
}
func (entity *tdlibClass) ToClassConst() string {
return "Class" + entity.ToGoType()
}
type tdlibTypeProperty struct {
name string
propertyType string
schema *tlparser.Schema
}
func TdlibTypeProperty(name string, propertyType string, schema *tlparser.Schema) *tdlibTypeProperty {
return &tdlibTypeProperty{
name: name,
propertyType: propertyType,
schema: schema,
}
}
func (entity *tdlibTypeProperty) IsList() bool {
return strings.HasPrefix(entity.propertyType, "vector<")
}
func (entity *tdlibTypeProperty) GetPrimitive() string {
primitive := entity.propertyType
for strings.HasPrefix(primitive, "vector<") {
primitive = strings.TrimSuffix(strings.TrimPrefix(primitive, "vector<"), ">")
}
return primitive
}
func (entity *tdlibTypeProperty) IsType() bool {
primitive := entity.GetPrimitive()
return isType(primitive, func(entity *tlparser.Type) string {
return entity.Name
}, entity.schema)
}
func (entity *tdlibTypeProperty) GetType() *tdlibType {
primitive := entity.GetPrimitive()
return getType(primitive, func(entity *tlparser.Type) string {
return entity.Name
}, entity.schema)
}
func (entity *tdlibTypeProperty) IsClass() bool {
primitive := entity.GetPrimitive()
return isClass(primitive, func(entity *tlparser.Class) string {
return entity.Name
}, entity.schema)
}
func (entity *tdlibTypeProperty) GetClass() *tdlibClass {
primitive := entity.GetPrimitive()
return getClass(primitive, func(entity *tlparser.Class) string {
return entity.Name
}, entity.schema)
}
func (entity *tdlibTypeProperty) ToGoName() string {
return firstUpper(underscoreToCamelCase(entity.name))
}
func (entity *tdlibTypeProperty) ToGoFunctionPropertyName() string {
name := firstLower(underscoreToCamelCase(entity.name))
if name == "type" {
name += "Param"
}
return name
}
func (entity *tdlibTypeProperty) ToGoType() string {
tdlibType := entity.propertyType
goType := ""
for strings.HasPrefix(tdlibType, "vector<") {
goType = goType + "[]"
tdlibType = strings.TrimSuffix(strings.TrimPrefix(tdlibType, "vector<"), ">")
}
if entity.IsClass() {
return goType + entity.GetClass().ToGoType()
}
if entity.GetType().IsInternal() {
return goType + entity.GetType().ToGoType()
}
return goType + "*" + entity.GetType().ToGoType()
}
func isType(name string, field func(entity *tlparser.Type) string, schema *tlparser.Schema) bool {
name = normalizeEntityName(name)
for _, entity := range schema.Types {
if name == field(entity) {
return true
}
}
return false
}
func getType(name string, field func(entity *tlparser.Type) string, schema *tlparser.Schema) *tdlibType {
name = normalizeEntityName(name)
for _, entity := range schema.Types {
if name == field(entity) {
return TdlibType(entity.Name, schema)
}
}
return nil
}
func isClass(name string, field func(entity *tlparser.Class) string, schema *tlparser.Schema) bool {
name = normalizeEntityName(name)
for _, entity := range schema.Classes {
if name == field(entity) {
return true
}
}
return false
}
func getClass(name string, field func(entity *tlparser.Class) string, schema *tlparser.Schema) *tdlibClass {
name = normalizeEntityName(name)
for _, entity := range schema.Classes {
if name == field(entity) {
return TdlibClass(entity.Name, schema)
}
}
return nil
}
func normalizeEntityName(name string) string {
if name == "Bool" {
name = "boolFalse"
}
return name
}

186
codegen/type.go Normal file
View file

@ -0,0 +1,186 @@
package codegen
import (
"bytes"
"fmt"
"github.com/astravexton/go-tdlib/tlparser"
)
func GenerateTypes(schema *tlparser.Schema, packageName string) []byte {
buf := bytes.NewBufferString("")
buf.WriteString(fmt.Sprintf("%s\n\npackage %s\n\n", header, packageName))
buf.WriteString(`import (
"encoding/json"
)
`)
buf.WriteString("const (\n")
for _, entity := range schema.Classes {
tdlibClass := TdlibClass(entity.Name, schema)
buf.WriteString(fmt.Sprintf(" %s = %q\n", tdlibClass.ToClassConst(), entity.Name))
}
for _, entity := range schema.Types {
tdlibType := TdlibType(entity.Name, schema)
if tdlibType.IsInternal() || tdlibType.HasClass() {
continue
}
buf.WriteString(fmt.Sprintf(" %s = %q\n", tdlibType.ToClassConst(), entity.Class))
}
buf.WriteString(")")
buf.WriteString("\n\n")
buf.WriteString("const (\n")
for _, entity := range schema.Types {
tdlibType := TdlibType(entity.Name, schema)
if tdlibType.IsInternal() {
continue
}
buf.WriteString(fmt.Sprintf(" %s = %q\n", tdlibType.ToTypeConst(), entity.Name))
}
buf.WriteString(")")
buf.WriteString("\n\n")
for _, class := range schema.Classes {
tdlibClass := TdlibClass(class.Name, schema)
buf.WriteString(fmt.Sprintf(`// %s
type %s interface {
%sType() string
}
`, class.Description, tdlibClass.ToGoType(), tdlibClass.ToGoType()))
}
for _, typ := range schema.Types {
tdlibType := TdlibType(typ.Name, schema)
if tdlibType.IsInternal() {
continue
}
buf.WriteString("// " + typ.Description + "\n")
if len(typ.Properties) > 0 {
buf.WriteString(`type ` + tdlibType.ToGoType() + ` struct {
meta
`)
for _, property := range typ.Properties {
tdlibTypeProperty := TdlibTypeProperty(property.Name, property.Type, schema)
buf.WriteString(fmt.Sprintf(" // %s\n", property.Description))
buf.WriteString(fmt.Sprintf(" %s %s `json:\"%s\"`\n", tdlibTypeProperty.ToGoName(), tdlibTypeProperty.ToGoType(), property.Name))
}
buf.WriteString("}\n\n")
} else {
buf.WriteString(`type ` + tdlibType.ToGoType() + ` struct{
meta
}
`)
}
buf.WriteString(fmt.Sprintf(`func (entity *%s) MarshalJSON() ([]byte, error) {
entity.meta.Type = entity.GetType()
type stub %s
return json.Marshal((*stub)(entity))
}
`, tdlibType.ToGoType(), tdlibType.ToGoType()))
buf.WriteString(fmt.Sprintf(`func (*%s) GetClass() string {
return %s
}
func (*%s) GetType() string {
return %s
}
`, tdlibType.ToGoType(), tdlibType.ToClassConst(), tdlibType.ToGoType(), tdlibType.ToTypeConst()))
if tdlibType.HasClass() {
tdlibClass := TdlibClass(tdlibType.GetClass().Name, schema)
buf.WriteString(fmt.Sprintf(`func (*%s) %sType() string {
return %s
}
`, tdlibType.ToGoType(), tdlibClass.ToGoType(), tdlibType.ToTypeConst()))
}
if tdlibType.HasClassProperties() {
buf.WriteString(fmt.Sprintf(`func (%s *%s) UnmarshalJSON(data []byte) error {
var tmp struct {
`, typ.Name, tdlibType.ToGoType()))
var countSimpleProperties int
for _, property := range typ.Properties {
tdlibTypeProperty := TdlibTypeProperty(property.Name, property.Type, schema)
if !tdlibTypeProperty.IsClass() {
buf.WriteString(fmt.Sprintf(" %s %s `json:\"%s\"`\n", tdlibTypeProperty.ToGoName(), tdlibTypeProperty.ToGoType(), property.Name))
countSimpleProperties++
} else {
if tdlibTypeProperty.IsList() {
buf.WriteString(fmt.Sprintf(" %s %s `json:\"%s\"`\n", tdlibTypeProperty.ToGoName(), "[]json.RawMessage", property.Name))
} else {
buf.WriteString(fmt.Sprintf(" %s %s `json:\"%s\"`\n", tdlibTypeProperty.ToGoName(), "json.RawMessage", property.Name))
}
}
}
buf.WriteString(` }
err := json.Unmarshal(data, &tmp)
if err != nil {
return err
}
`)
for _, property := range typ.Properties {
tdlibTypeProperty := TdlibTypeProperty(property.Name, property.Type, schema)
if !tdlibTypeProperty.IsClass() {
buf.WriteString(fmt.Sprintf(" %s.%s = tmp.%s\n", typ.Name, tdlibTypeProperty.ToGoName(), tdlibTypeProperty.ToGoName()))
}
}
if countSimpleProperties > 0 {
buf.WriteString("\n")
}
for _, property := range typ.Properties {
tdlibTypeProperty := TdlibTypeProperty(property.Name, property.Type, schema)
if tdlibTypeProperty.IsClass() && !tdlibTypeProperty.IsList() {
buf.WriteString(fmt.Sprintf(` field%s, _ := Unmarshal%s(tmp.%s)
%s.%s = field%s
`, tdlibTypeProperty.ToGoName(), tdlibTypeProperty.ToGoType(), tdlibTypeProperty.ToGoName(), typ.Name, tdlibTypeProperty.ToGoName(), tdlibTypeProperty.ToGoName()))
}
if tdlibTypeProperty.IsClass() && tdlibTypeProperty.IsList() {
buf.WriteString(fmt.Sprintf(` field%s, _ := UnmarshalListOf%s(tmp.%s)
%s.%s = field%s
`, tdlibTypeProperty.ToGoName(), tdlibTypeProperty.GetClass().ToGoType(), tdlibTypeProperty.ToGoName(), typ.Name, tdlibTypeProperty.ToGoName(), tdlibTypeProperty.ToGoName()))
}
}
buf.WriteString(` return nil
}
`)
}
}
return buf.Bytes()
}

120
codegen/unmarshaler.go Normal file
View file

@ -0,0 +1,120 @@
package codegen
import (
"bytes"
"fmt"
"github.com/astravexton/go-tdlib/tlparser"
)
func GenerateUnmarshalers(schema *tlparser.Schema, packageName string) []byte {
buf := bytes.NewBufferString("")
buf.WriteString(fmt.Sprintf("%s\n\npackage %s\n\n", header, packageName))
buf.WriteString(`import (
"encoding/json"
"fmt"
)
`)
for _, class := range schema.Classes {
tdlibClass := TdlibClass(class.Name, schema)
buf.WriteString(fmt.Sprintf(`func Unmarshal%s(data json.RawMessage) (%s, error) {
var meta meta
err := json.Unmarshal(data, &meta)
if err != nil {
return nil, err
}
switch meta.Type {
`, tdlibClass.ToGoType(), tdlibClass.ToGoType()))
for _, subType := range tdlibClass.GetSubTypes() {
buf.WriteString(fmt.Sprintf(` case %s:
return Unmarshal%s(data)
`, subType.ToTypeConst(), subType.ToGoType()))
}
buf.WriteString(` default:
return nil, fmt.Errorf("Error unmarshaling. Unknown type: " + meta.Type)
}
}
`)
buf.WriteString(fmt.Sprintf(`func UnmarshalListOf%s(dataList []json.RawMessage) ([]%s, error) {
list := []%s{}
for _, data := range dataList {
entity, err := Unmarshal%s(data)
if err != nil {
return nil, err
}
list = append(list, entity)
}
return list, nil
}
`, tdlibClass.ToGoType(), tdlibClass.ToGoType(), tdlibClass.ToGoType(), tdlibClass.ToGoType()))
}
for _, typ := range schema.Types {
tdlibType := TdlibType(typ.Name, schema)
if tdlibType.IsList() || tdlibType.IsInternal() {
continue
}
buf.WriteString(fmt.Sprintf(`func Unmarshal%s(data json.RawMessage) (*%s, error) {
var resp %s
err := json.Unmarshal(data, &resp)
return &resp, err
}
`, tdlibType.ToGoType(), tdlibType.ToGoType(), tdlibType.ToGoType()))
}
buf.WriteString(`func UnmarshalType(data json.RawMessage) (Type, error) {
var meta meta
err := json.Unmarshal(data, &meta)
if err != nil {
return nil, err
}
switch meta.Type {
`)
for _, typ := range schema.Types {
tdlibType := TdlibType(typ.Name, schema)
if tdlibType.IsList() || tdlibType.IsInternal() {
continue
}
buf.WriteString(fmt.Sprintf(` case %s:
return Unmarshal%s(data)
`, tdlibType.ToTypeConst(), tdlibType.ToGoType()))
}
buf.WriteString(` default:
return nil, fmt.Errorf("Error unmarshaling. Unknown type: " + meta.Type)
}
}
`)
return buf.Bytes()
}