Files
whatsmeow/internals_generate.go

212 lines
5.6 KiB
Go

// Copyright (c) 2025 Tulir Asokan
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
//go:build ignore
package main
import (
"fmt"
"go/ast"
"go/parser"
"go/token"
"os"
"strings"
"go.mau.fi/util/exerrors"
)
const header = `// GENERATED BY internals_generate.go; DO NOT EDIT
//go:generate go run internals_generate.go
//go:generate goimports -local go.mau.fi/whatsmeow -w internals.go
package whatsmeow
`
const postImportHeader = `
type DangerousInternalClient struct {
c *Client
}
// DangerousInternals allows access to all unexported methods in Client.
//
// Deprecated: dangerous
func (cli *Client) DangerousInternals() *DangerousInternalClient {
return &DangerousInternalClient{cli}
}
type DangerousInfoQuery = infoQuery
type DangerousInfoQueryType = infoQueryType
`
func getTypeName(expr ast.Expr) string {
switch e := expr.(type) {
case *ast.Ident:
return e.Name
case *ast.StarExpr:
return "*" + getTypeName(e.X)
case *ast.ArrayType:
if e.Len != nil {
return fmt.Sprintf("[%s]%s", getTypeName(e.Len), getTypeName(e.Elt))
}
return "[]" + getTypeName(e.Elt)
case *ast.MapType:
return fmt.Sprintf("map[%s]%s", getTypeName(e.Key), getTypeName(e.Value))
case *ast.ChanType:
if e.Dir == ast.SEND {
return fmt.Sprintf("chan<- %s", getTypeName(e.Value))
} else if e.Dir == ast.RECV {
return fmt.Sprintf("<-chan %s", getTypeName(e.Value))
}
return fmt.Sprintf("chan %s", getTypeName(e.Value))
case *ast.FuncType:
var params []string
for _, param := range e.Params.List {
params = append(params, getTypeName(param.Type))
}
var results []string
if e.Results != nil {
for _, result := range e.Results.List {
results = append(results, getTypeName(result.Type))
}
}
retVals := strings.Join(results, ", ")
if len(results) > 1 {
retVals = fmt.Sprintf("(%s)", retVals)
}
return fmt.Sprintf("func(%s) %s", strings.Join(params, ", "), retVals)
case *ast.SelectorExpr:
return fmt.Sprintf("%s.%s", getTypeName(e.X), e.Sel.Name)
case *ast.StructType:
// This isn't technically correct, but struct literals shouldn't be used for anything else
return "struct{}"
case *ast.Ellipsis:
return fmt.Sprintf("...%s", getTypeName(e.Elt))
case *ast.BasicLit:
return e.Value
default:
panic(fmt.Errorf("unknown type %T", e))
}
}
var write func(str string)
var writef func(format string, args ...any)
func main() {
fset := token.NewFileSet()
fileNames := []string{
"appstate.go", "armadillomessage.go", "broadcast.go", "call.go", "client.go",
"connectionevents.go", "cstoken.go", "download.go", "download-to-file.go", "group.go", "handshake.go",
"keepalive.go", "mediaconn.go", "mediaretry.go", "message.go", "msgsecret.go",
"newsletter.go", "notification.go", "pair-code.go", "pair.go", "pair-passkey.go", "prekeys.go",
"presence.go", "privacysettings.go", "push.go", "qrchan.go", "receipt.go", "reportingtoken.go",
"request.go", "retry.go", "send.go", "sendfb.go", "tctoken.go", "upload.go", "user.go",
}
files := make([]*ast.File, len(fileNames))
for i, name := range fileNames {
files[i] = exerrors.Must(parser.ParseFile(fset, name, nil, parser.SkipObjectResolution))
}
file := exerrors.Must(os.OpenFile("internals.go", os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0644))
write = func(str string) {
exerrors.Must(file.WriteString(str))
}
writef = func(format string, args ...any) {
exerrors.Must(fmt.Fprintf(file, format, args...))
}
write(header)
write("import (\n")
for _, i := range files[0].Imports {
write("\t")
if i.Name != nil {
writef("%s ", i.Name.Name)
}
writef("%s\n", i.Path.Value)
}
write(")\n")
write(postImportHeader)
for _, f := range files {
processFile(f)
}
exerrors.PanicIfNotNil(file.Close())
}
func processFile(f *ast.File) {
ast.Inspect(f, func(node ast.Node) (retVal bool) {
retVal = true
funcDecl, ok := node.(*ast.FuncDecl)
if !ok || funcDecl.Name.IsExported() {
return
}
if funcDecl.Recv == nil || len(funcDecl.Recv.List) == 0 || len(funcDecl.Recv.List[0].Names) == 0 ||
funcDecl.Recv.List[0].Names[0].Name != "cli" {
return
}
writef("\nfunc (int *DangerousInternalClient) %s%s(", strings.ToUpper(funcDecl.Name.Name[0:1]), funcDecl.Name.Name[1:])
for i, param := range funcDecl.Type.Params.List {
if i != 0 {
write(", ")
}
for j, name := range param.Names {
if j != 0 {
write(", ")
}
write(name.Name)
}
if len(param.Names) > 0 {
write(" ")
}
write(getTypeName(param.Type))
}
write(") ")
if funcDecl.Type.Results != nil && len(funcDecl.Type.Results.List) > 0 {
needsParentheses := len(funcDecl.Type.Results.List) > 1 || len(funcDecl.Type.Results.List[0].Names) > 0
if needsParentheses {
write("(")
}
for i, result := range funcDecl.Type.Results.List {
if i != 0 {
write(", ")
}
for j, name := range result.Names {
if j != 0 {
write(", ")
}
write(name.Name)
}
if len(result.Names) > 0 {
write(" ")
}
write(getTypeName(result.Type))
}
if needsParentheses {
write(")")
}
write(" ")
}
write("{\n\t")
if funcDecl.Type.Results != nil {
write("return ")
}
writef("int.c.%s(", funcDecl.Name.Name)
for i, param := range funcDecl.Type.Params.List {
for j, name := range param.Names {
if i != 0 || j != 0 {
write(", ")
}
write(name.Name)
_, isEllipsis := param.Type.(*ast.Ellipsis)
if isEllipsis {
write("...")
}
}
}
write(")\n}\n")
return
})
}