internals_generate.go 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211
  1. // Copyright (c) 2025 Tulir Asokan
  2. //
  3. // This Source Code Form is subject to the terms of the Mozilla Public
  4. // License, v. 2.0. If a copy of the MPL was not distributed with this
  5. // file, You can obtain one at http://mozilla.org/MPL/2.0/.
  6. //go:build ignore
  7. package main
  8. import (
  9. "fmt"
  10. "go/ast"
  11. "go/parser"
  12. "go/token"
  13. "os"
  14. "strings"
  15. "go.mau.fi/util/exerrors"
  16. )
  17. const header = `// GENERATED BY internals_generate.go; DO NOT EDIT
  18. //go:generate go run internals_generate.go
  19. //go:generate goimports -local git.bobomao.top/joey/testwh -w internals.go
  20. package whatsmeow
  21. `
  22. const postImportHeader = `
  23. type DangerousInternalClient struct {
  24. c *Client
  25. }
  26. // DangerousInternals allows access to all unexported methods in Client.
  27. //
  28. // Deprecated: dangerous
  29. func (cli *Client) DangerousInternals() *DangerousInternalClient {
  30. return &DangerousInternalClient{cli}
  31. }
  32. type DangerousInfoQuery = infoQuery
  33. type DangerousInfoQueryType = infoQueryType
  34. `
  35. func getTypeName(expr ast.Expr) string {
  36. switch e := expr.(type) {
  37. case *ast.Ident:
  38. return e.Name
  39. case *ast.StarExpr:
  40. return "*" + getTypeName(e.X)
  41. case *ast.ArrayType:
  42. if e.Len != nil {
  43. return fmt.Sprintf("[%s]%s", getTypeName(e.Len), getTypeName(e.Elt))
  44. }
  45. return "[]" + getTypeName(e.Elt)
  46. case *ast.MapType:
  47. return fmt.Sprintf("map[%s]%s", getTypeName(e.Key), getTypeName(e.Value))
  48. case *ast.ChanType:
  49. if e.Dir == ast.SEND {
  50. return fmt.Sprintf("chan<- %s", getTypeName(e.Value))
  51. } else if e.Dir == ast.RECV {
  52. return fmt.Sprintf("<-chan %s", getTypeName(e.Value))
  53. }
  54. return fmt.Sprintf("chan %s", getTypeName(e.Value))
  55. case *ast.FuncType:
  56. var params []string
  57. for _, param := range e.Params.List {
  58. params = append(params, getTypeName(param.Type))
  59. }
  60. var results []string
  61. if e.Results != nil {
  62. for _, result := range e.Results.List {
  63. results = append(results, getTypeName(result.Type))
  64. }
  65. }
  66. retVals := strings.Join(results, ", ")
  67. if len(results) > 1 {
  68. retVals = fmt.Sprintf("(%s)", retVals)
  69. }
  70. return fmt.Sprintf("func(%s) %s", strings.Join(params, ", "), retVals)
  71. case *ast.SelectorExpr:
  72. return fmt.Sprintf("%s.%s", getTypeName(e.X), e.Sel.Name)
  73. case *ast.StructType:
  74. // This isn't technically correct, but struct literals shouldn't be used for anything else
  75. return "struct{}"
  76. case *ast.Ellipsis:
  77. return fmt.Sprintf("...%s", getTypeName(e.Elt))
  78. case *ast.BasicLit:
  79. return e.Value
  80. default:
  81. panic(fmt.Errorf("unknown type %T", e))
  82. }
  83. }
  84. var write func(str string)
  85. var writef func(format string, args ...any)
  86. func main() {
  87. fset := token.NewFileSet()
  88. fileNames := []string{
  89. "appstate.go", "armadillomessage.go", "broadcast.go", "call.go", "client.go",
  90. "connectionevents.go", "download.go", "download-to-file.go", "group.go", "handshake.go",
  91. "keepalive.go", "mediaconn.go", "mediaretry.go", "message.go", "msgsecret.go",
  92. "newsletter.go", "notification.go", "pair-code.go", "pair.go", "prekeys.go",
  93. "presence.go", "privacysettings.go", "push.go", "qrchan.go", "receipt.go", "request.go",
  94. "retry.go", "sendfb.go", "send.go", "upload.go", "user.go", "reportingtoken.go",
  95. }
  96. files := make([]*ast.File, len(fileNames))
  97. for i, name := range fileNames {
  98. files[i] = exerrors.Must(parser.ParseFile(fset, name, nil, parser.SkipObjectResolution))
  99. }
  100. file := exerrors.Must(os.OpenFile("internals.go", os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0644))
  101. write = func(str string) {
  102. exerrors.Must(file.WriteString(str))
  103. }
  104. writef = func(format string, args ...any) {
  105. exerrors.Must(fmt.Fprintf(file, format, args...))
  106. }
  107. write(header)
  108. write("import (\n")
  109. for _, i := range files[0].Imports {
  110. write("\t")
  111. if i.Name != nil {
  112. writef("%s ", i.Name.Name)
  113. }
  114. writef("%s\n", i.Path.Value)
  115. }
  116. write(")\n")
  117. write(postImportHeader)
  118. for _, f := range files {
  119. processFile(f)
  120. }
  121. exerrors.PanicIfNotNil(file.Close())
  122. }
  123. func processFile(f *ast.File) {
  124. ast.Inspect(f, func(node ast.Node) (retVal bool) {
  125. retVal = true
  126. funcDecl, ok := node.(*ast.FuncDecl)
  127. if !ok || funcDecl.Name.IsExported() {
  128. return
  129. }
  130. if funcDecl.Recv == nil || len(funcDecl.Recv.List) == 0 || len(funcDecl.Recv.List[0].Names) == 0 ||
  131. funcDecl.Recv.List[0].Names[0].Name != "cli" {
  132. return
  133. }
  134. writef("\nfunc (int *DangerousInternalClient) %s%s(", strings.ToUpper(funcDecl.Name.Name[0:1]), funcDecl.Name.Name[1:])
  135. for i, param := range funcDecl.Type.Params.List {
  136. if i != 0 {
  137. write(", ")
  138. }
  139. for j, name := range param.Names {
  140. if j != 0 {
  141. write(", ")
  142. }
  143. write(name.Name)
  144. }
  145. if len(param.Names) > 0 {
  146. write(" ")
  147. }
  148. write(getTypeName(param.Type))
  149. }
  150. write(") ")
  151. if funcDecl.Type.Results != nil && len(funcDecl.Type.Results.List) > 0 {
  152. needsParentheses := len(funcDecl.Type.Results.List) > 1 || len(funcDecl.Type.Results.List[0].Names) > 0
  153. if needsParentheses {
  154. write("(")
  155. }
  156. for i, result := range funcDecl.Type.Results.List {
  157. if i != 0 {
  158. write(", ")
  159. }
  160. for j, name := range result.Names {
  161. if j != 0 {
  162. write(", ")
  163. }
  164. write(name.Name)
  165. }
  166. if len(result.Names) > 0 {
  167. write(" ")
  168. }
  169. write(getTypeName(result.Type))
  170. }
  171. if needsParentheses {
  172. write(")")
  173. }
  174. write(" ")
  175. }
  176. write("{\n\t")
  177. if funcDecl.Type.Results != nil {
  178. write("return ")
  179. }
  180. writef("int.c.%s(", funcDecl.Name.Name)
  181. for i, param := range funcDecl.Type.Params.List {
  182. for j, name := range param.Names {
  183. if i != 0 || j != 0 {
  184. write(", ")
  185. }
  186. write(name.Name)
  187. _, isEllipsis := param.Type.(*ast.Ellipsis)
  188. if isEllipsis {
  189. write("...")
  190. }
  191. }
  192. }
  193. write(")\n}\n")
  194. return
  195. })
  196. }