Use autogenerated bindings for Lemmy 0.16.7
This commit is contained in:
62
cmd/gen/parser/impl.go
Normal file
62
cmd/gen/parser/impl.go
Normal 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
121
cmd/gen/parser/routes.go
Normal 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
259
cmd/gen/parser/struct.go
Normal 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)
|
||||
}
|
||||
49
cmd/gen/parser/struct_test.go
Normal file
49
cmd/gen/parser/struct_test.go
Normal 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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user