Use autogenerated bindings for Lemmy 0.16.7

This commit is contained in:
2023-01-05 12:51:45 -08:00
parent c19f44c304
commit a355158d88
68 changed files with 3743 additions and 3083 deletions

62
cmd/gen/parser/impl.go Normal file
View File

@@ -0,0 +1,62 @@
package parser
import (
"bufio"
"errors"
"io"
"regexp"
)
var (
implRegex = regexp.MustCompile(`impl Perform.* for (.+) {`)
respTypeRegex = regexp.MustCompile(`type Response = (.+);`)
)
var ErrNoType = errors.New("type line not found")
type ImplParser struct {
r *bufio.Reader
}
func NewImpl(r io.Reader) *ImplParser {
return &ImplParser{
r: bufio.NewReader(r),
}
}
func (i *ImplParser) Parse() (map[string]string, error) {
out := map[string]string{}
for {
line, err := i.r.ReadString('\n')
if errors.Is(err, io.EOF) {
break
} else if err != nil {
return nil, err
}
if implRegex.MatchString(line) {
im := implRegex.FindStringSubmatch(line)
line, err := i.r.ReadString('\n')
if errors.Is(err, io.EOF) {
return nil, io.ErrUnexpectedEOF
} else if err != nil {
return nil, err
}
if !respTypeRegex.MatchString(line) {
return nil, ErrNoType
}
rtm := respTypeRegex.FindStringSubmatch(line)
out[im[1]] = rtm[1]
}
}
return out, nil
}
func (i *ImplParser) Reset(r io.Reader) {
i.r.Reset(r)
}

121
cmd/gen/parser/routes.go Normal file
View File

@@ -0,0 +1,121 @@
package parser
import (
"bufio"
"errors"
"io"
"net/url"
"regexp"
"strings"
)
var (
scopeRegex = regexp.MustCompile(`web::(?:scope|resource)\("(.*)"\)\n`)
routeRegex = regexp.MustCompile(`\.route\(\n?\s*(?:"(.*)",[ \n])?\s*web::(.+)\(\)\.to\(route_.*::<(.+)>`)
)
type Route struct {
Path string
Method string
Struct string
}
type RoutesParser struct {
r *bufio.Reader
}
func NewRoutes(r io.Reader) *RoutesParser {
return &RoutesParser{
r: bufio.NewReader(r),
}
}
func (r *RoutesParser) Parse() ([]Route, error) {
var out []Route
for {
line, err := r.r.ReadString('\n')
if errors.Is(err, io.EOF) {
break
} else if err != nil {
return nil, err
}
if scopeRegex.MatchString(line) {
scopePath := scopeRegex.FindStringSubmatch(line)[1]
if scopePath == "/api/v3" {
continue
}
routes, err := r.parseRoutes()
if err != nil {
return nil, err
}
for i := range routes {
path, err := url.JoinPath(scopePath, routes[i].Path)
if err != nil {
return nil, err
}
routes[i].Path = path
}
out = append(out, routes...)
}
}
return out, nil
}
func (r *RoutesParser) parseRoutes() ([]Route, error) {
var out []Route
for {
line, err := r.r.ReadString('\n')
if errors.Is(err, io.EOF) {
if strings.TrimSpace(line)[:1] == ")" {
return out, nil
} else {
return nil, io.ErrUnexpectedEOF
}
} else if err != nil {
return nil, err
}
if strings.TrimSpace(line) == ".route(" {
lines, err := r.readLines(3)
if err != nil {
return nil, err
}
line += lines
}
if strings.TrimSpace(line)[:1] == ")" {
return out, nil
} else if strings.HasPrefix(line, "//") {
continue
} else if !routeRegex.MatchString(line) {
continue
}
sm := routeRegex.FindStringSubmatch(line)
out = append(out, Route{
Path: sm[1],
Method: strings.ToUpper(sm[2]),
Struct: sm[3],
})
}
}
func (r *RoutesParser) readLines(n int) (string, error) {
out := ""
for i := 0; i < n; i++ {
line, err := r.r.ReadString('\n')
if err != nil {
return "", err
}
out += line
}
return out, nil
}
func (r *RoutesParser) Reset(rd io.Reader) {
r.r.Reset(rd)
}

259
cmd/gen/parser/struct.go Normal file
View File

@@ -0,0 +1,259 @@
package parser
import (
"bufio"
"errors"
"io"
"regexp"
"strings"
"golang.org/x/exp/slices"
)
var (
structRegex = regexp.MustCompile(`pub struct (.+) \{`)
fieldRegex = regexp.MustCompile(`(?U) {1,1}([^ ]+): (.+),`)
enumRegex = regexp.MustCompile(`pub enum (.+) \{`)
memberRegex = regexp.MustCompile(` ([^ #]+),\n`)
)
type Item struct {
Struct *Struct
Enum *Enum
}
type Struct struct {
Name string
Fields []Field
}
type Field struct {
OrigName string
Name string
Type string
}
type Enum struct {
Name string
Members []Member
}
type Member string
type StructParser struct {
r *bufio.Reader
Skip []string
TransformName func(string) string
TransformType func(string) string
}
func NewStruct(r io.Reader) *StructParser {
return &StructParser{
r: bufio.NewReader(r),
TransformName: TransformNameGo,
TransformType: TransformTypeGo,
}
}
func (s *StructParser) Parse() ([]Item, error) {
var out []Item
for {
line, err := s.r.ReadString('\n')
if errors.Is(err, io.EOF) {
break
} else if err != nil {
return nil, err
}
if structRegex.MatchString(line) {
structName := structRegex.FindStringSubmatch(line)[1]
if slices.Contains(s.Skip, structName) {
continue
}
// If the line ends with "}", this is a struct with no fields
if strings.HasSuffix(line, "}\n") {
out = append(out, Item{
Struct: &Struct{
Name: structRegex.FindStringSubmatch(line)[1],
},
})
continue
}
fields, err := s.parseStructFields()
if err != nil {
return nil, err
}
out = append(out, Item{
Struct: &Struct{
Name: structName,
Fields: fields,
},
})
} else if enumRegex.MatchString(line) {
enumName := enumRegex.FindStringSubmatch(line)[1]
if slices.Contains(s.Skip, enumName) {
continue
}
members, err := s.parseEnumMemebers()
if err != nil {
return nil, err
}
out = append(out, Item{
Enum: &Enum{
Name: enumName,
Members: members,
},
})
}
}
return out, nil
}
func (s *StructParser) parseStructFields() ([]Field, error) {
var out []Field
for {
line, err := s.r.ReadString('\n')
if errors.Is(err, io.EOF) {
if strings.HasPrefix(line, "}") {
return out, nil
} else {
return nil, io.ErrUnexpectedEOF
}
} else if err != nil {
return nil, err
}
if strings.HasPrefix(line, "}") {
return out, nil
} else if strings.HasPrefix(line, "//") {
continue
} else if !fieldRegex.MatchString(line) {
continue
}
sm := fieldRegex.FindStringSubmatch(line)
if sm[1] == "Example" {
continue
}
out = append(out, Field{
OrigName: sm[1],
Name: s.TransformName(sm[1]),
Type: s.TransformType(sm[2]),
})
}
}
func (s *StructParser) parseEnumMemebers() ([]Member, error) {
var out []Member
for {
line, err := s.r.ReadString('\n')
if errors.Is(err, io.EOF) {
if strings.HasPrefix(line, "}") {
return out, nil
} else {
return nil, io.ErrUnexpectedEOF
}
} else if err != nil {
return nil, err
}
if strings.HasPrefix(line, "}") {
return out, nil
} else if strings.HasPrefix(line, "//") {
continue
} else if !memberRegex.MatchString(line) {
continue
}
sm := memberRegex.FindStringSubmatch(line)
out = append(out, Member(sm[1]))
}
}
// TransformTypeGo transforms Rust types to Go
//
// Example: TransformTypeGo("Option<Vec<i64>>") // returns "Optional[[]int64]"
func TransformTypeGo(t string) string {
prefix := ""
suffix := ""
for strings.HasPrefix(t, "Option<") {
t = strings.TrimPrefix(strings.TrimSuffix(t, ">"), "Option<")
prefix += "Optional["
suffix += "]"
}
for strings.HasPrefix(t, "Vec<") {
t = strings.TrimPrefix(strings.TrimSuffix(t, ">"), "Vec<")
prefix += "[]"
}
for strings.HasPrefix(t, "Sensitive<") {
t = strings.TrimPrefix(strings.TrimSuffix(t, ">"), "Sensitive<")
}
if strings.HasSuffix(t, "Id") {
t = "int"
}
switch t {
case "String", "Url", "DbUrl", "Ltree":
t = "string"
case "usize":
t = "uint"
case "i64":
t = "int64"
case "i32":
t = "int32"
case "i16":
t = "int16"
case "i8":
t = "int8"
case "chrono::NaiveDateTime":
return "time.Time"
case "Value":
return "any"
}
return prefix + t + suffix
}
// TransformNameGo transforms conventional Rust naming to
// conventional Go naming.
//
// Example: TransformNameGo("post_id") // returns "PostID"
func TransformNameGo(s string) string {
out := ""
splitName := strings.Split(s, "_")
for _, segment := range splitName {
switch segment {
case "id":
out += "ID"
case "url":
out += "URL"
case "nsfw":
out += "NSFW"
default:
if len(segment) == 0 {
continue
}
out += strings.ToUpper(segment[:1]) + segment[1:]
}
}
return out
}
func (s *StructParser) Reset(r io.Reader) {
s.r.Reset(r)
}

View File

@@ -0,0 +1,49 @@
package parser
import "testing"
func TestTransformNameGo(t *testing.T) {
type testcase struct {
name string
expect string
}
cases := []testcase{
{"post_id", "PostID"},
{"nsfw", "NSFW"},
{"test_url", "TestURL"},
{"some_complex_name_with_id_and_nsfw_and_url", "SomeComplexNameWithIDAndNSFWAndURL"},
}
for _, testcase := range cases {
t.Run(testcase.name, func(t *testing.T) {
got := TransformNameGo(testcase.name)
if got != testcase.expect {
t.Errorf("Expected %s, got %s", testcase.expect, got)
}
})
}
}
func TestTransformTypeGo(t *testing.T) {
type testcase struct {
typeName string
expect string
}
cases := []testcase{
{"i16", "int16"},
{"Option<Vec<i64>>", "Optional[[]int64]"},
{"Url", "string"},
{"Sensitive<String>", "string"},
}
for _, testcase := range cases {
t.Run(testcase.typeName, func(t *testing.T) {
got := TransformTypeGo(testcase.typeName)
if got != testcase.expect {
t.Errorf("Expected %s, got %s", testcase.expect, got)
}
})
}
}