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