Skip to content

Commit

Permalink
Utilize go/types for code generation (#47)
Browse files Browse the repository at this point in the history
  • Loading branch information
sunboyy authored May 15, 2024
1 parent 0c00401 commit f199cdd
Show file tree
Hide file tree
Showing 27 changed files with 299 additions and 308 deletions.
67 changes: 0 additions & 67 deletions internal/code/models.go
Original file line number Diff line number Diff line change
@@ -1,52 +1,16 @@
package code

import (
"fmt"
"go/types"
"reflect"
)

// Import is a model for package imports
type Import struct {
Name string
Path string
}

// LegacyStructField is a definition of the struct field
type LegacyStructField struct {
Name string
Type Type
Tag reflect.StructTag
}

// StructField is a definition of the struct field
type StructField struct {
Var *types.Var
Tag reflect.StructTag
}

// InterfaceType is a definition of the interface
type InterfaceType struct {
}

// Code returns token string in code format
func (intf InterfaceType) Code() string {
return `interface{}`
}

// Type is an interface for value types
type Type interface {
Code() string
}

// SimpleType is a type that can be called directly
type SimpleType string

// Code returns token string in code format
func (t SimpleType) Code() string {
return string(t)
}

var (
TypeBool = types.Typ[types.Bool]
TypeInt = types.Typ[types.Int]
Expand All @@ -55,34 +19,3 @@ var (
TypeString = types.Typ[types.String]
TypeError = types.Universe.Lookup("error").Type()
)

// ExternalType is a type that is called to another package
type ExternalType struct {
PackageAlias string
Name string
}

// Code returns token string in code format
func (t ExternalType) Code() string {
return fmt.Sprintf("%s.%s", t.PackageAlias, t.Name)
}

// PointerType is a model of pointer
type PointerType struct {
ContainedType Type
}

// Code returns token string in code format
func (t PointerType) Code() string {
return fmt.Sprintf("*%s", t.ContainedType.Code())
}

// ArrayType is a model of array
type ArrayType struct {
ContainedType Type
}

// Code returns token string in code format
func (t ArrayType) Code() string {
return fmt.Sprintf("[]%s", t.ContainedType.Code())
}
48 changes: 0 additions & 48 deletions internal/code/models_test.go

This file was deleted.

6 changes: 2 additions & 4 deletions internal/codegen/base.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@ package codegen
import (
"fmt"
"strings"

"github.com/sunboyy/repogen/internal/code"
)

const baseTemplate = `// Code generated by {{.Program}}. DO NOT EDIT.
Expand All @@ -18,7 +16,7 @@ import (
type baseTemplateData struct {
Program string
PackageName string
Imports [][]code.Import
Imports [][]Import
}

func (data baseTemplateData) GenImports() string {
Expand All @@ -33,7 +31,7 @@ func (data baseTemplateData) GenImports() string {
return strings.Join(sections, "\n\n")
}

func (data baseTemplateData) generateImportLine(imp code.Import) string {
func (data baseTemplateData) generateImportLine(imp Import) string {
if imp.Name == "" {
return fmt.Sprintf("\t\"%s\"", imp.Path)
}
Expand Down
32 changes: 26 additions & 6 deletions internal/codegen/body.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,8 @@ package codegen

import (
"fmt"
"go/types"
"strings"

"github.com/sunboyy/repogen/internal/code"
)

type FunctionBody []Statement
Expand Down Expand Up @@ -37,12 +36,21 @@ func (id Identifier) CodeLines() []string {
}

type DeclStatement struct {
Pkg *types.Package
Name string
Type code.Type
Type types.Type
}

func NewDeclStatement(pkg *types.Package, name string, typ types.Type) DeclStatement {
return DeclStatement{
Pkg: pkg,
Name: name,
Type: typ,
}
}

func (stmt DeclStatement) CodeLines() []string {
return []string{fmt.Sprintf("var %s %s", stmt.Name, stmt.Type.Code())}
return []string{fmt.Sprintf("var %s %s", stmt.Name, TypeToString(stmt.Pkg, stmt.Type))}
}

type DeclAssignStatement struct {
Expand Down Expand Up @@ -105,20 +113,32 @@ func (stmt CallStatement) CodeLines() []string {
}

type SliceStatement struct {
Type code.Type
Pkg *types.Package
Type types.Type
Values []Statement
}

func NewSliceStatement(pkg *types.Package, typ types.Type, values []Statement) SliceStatement {
return SliceStatement{
Pkg: pkg,
Type: typ,
Values: values,
}
}

func (stmt SliceStatement) CodeLines() []string {
lines := []string{stmt.Type.Code() + "{"}
lines := []string{TypeToString(stmt.Pkg, stmt.Type) + "{"}

for _, value := range stmt.Values {
stmtLines := value.CodeLines()
stmtLines[len(stmtLines)-1] += ","
for _, line := range stmtLines {
lines = append(lines, "\t"+line)
}
}

lines = append(lines, "}")

return lines
}

Expand Down
29 changes: 11 additions & 18 deletions internal/codegen/body_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package codegen_test

import (
"go/types"
"reflect"
"testing"

Expand All @@ -20,10 +21,7 @@ func TestIdentifier(t *testing.T) {
}

func TestDeclStatement(t *testing.T) {
stmt := codegen.DeclStatement{
Name: "arrs",
Type: code.ArrayType{ContainedType: code.SimpleType("int")},
}
stmt := codegen.NewDeclStatement(nil, "arrs", types.NewSlice(code.TypeInt))
expected := []string{"var arrs []int"}

actual := stmt.CodeLines()
Expand Down Expand Up @@ -139,23 +137,18 @@ func TestCallStatement(t *testing.T) {
}

func TestSliceStatement(t *testing.T) {
stmt := codegen.SliceStatement{
Type: code.ArrayType{
ContainedType: code.SimpleType("string"),
},
Values: []codegen.Statement{
codegen.Identifier(`"hello"`),
codegen.ChainStatement{
codegen.CallStatement{
FuncName: "GetUser",
Params: codegen.StatementList{
codegen.Identifier("userID"),
},
stmt := codegen.NewSliceStatement(nil, types.NewSlice(code.TypeString), []codegen.Statement{
codegen.Identifier(`"hello"`),
codegen.ChainStatement{
codegen.CallStatement{
FuncName: "GetUser",
Params: codegen.StatementList{
codegen.Identifier("userID"),
},
codegen.Identifier("Name"),
},
codegen.Identifier("Name"),
},
}
})
expected := []string{
"[]string{",
` "hello",`,
Expand Down
11 changes: 8 additions & 3 deletions internal/codegen/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,15 @@ import (
"bytes"
"text/template"

"github.com/sunboyy/repogen/internal/code"
"golang.org/x/tools/imports"
)

// Import is a model for package imports
type Import struct {
Name string
Path string
}

type Builder struct {
// Program defines generator program name in the generated file.
Program string
Expand All @@ -17,7 +22,7 @@ type Builder struct {

// Imports defines necessary imports to reduce ambiguity when generating
// formatting the raw-generated code.
Imports [][]code.Import
Imports [][]Import

implementers []Implementer
}
Expand All @@ -29,7 +34,7 @@ type Implementer interface {
}

// NewBuilder is a constructor of Builder struct.
func NewBuilder(program string, packageName string, imports [][]code.Import) *Builder {
func NewBuilder(program string, packageName string, imports [][]Import) *Builder {
return &Builder{
Program: program,
PackageName: packageName,
Expand Down
18 changes: 8 additions & 10 deletions internal/codegen/builder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ func (u User) IDHex() string {
`

func TestBuilderBuild(t *testing.T) {
builder := codegen.NewBuilder("repogen", "user", [][]code.Import{
builder := codegen.NewBuilder("repogen", "user", [][]codegen.Import{
{
{
Name: "_",
Expand All @@ -56,23 +56,20 @@ func TestBuilderBuild(t *testing.T) {
},
})
builder.AddImplementer(codegen.StructBuilder{
Pkg: testutils.Pkg,
Name: "User",
Fields: []code.LegacyStructField{
Fields: []code.StructField{
{
Name: "ID",
Type: code.ExternalType{
PackageAlias: "primitive",
Name: "ObjectID",
},
Var: types.NewVar(token.NoPos, nil, "ID", testutils.TypeObjectIDNamed),
Tag: `bson:"id" json:"id,omitempty"`,
},
{
Name: "Username",
Type: code.SimpleType("string"),
Var: types.NewVar(token.NoPos, nil, "Username", code.TypeString),
},
},
})
builder.AddImplementer(codegen.FunctionBuilder{
Pkg: testutils.Pkg,
Name: "NewUser",
Params: types.NewTuple(
types.NewVar(token.NoPos, nil, "username", code.TypeString),
Expand Down Expand Up @@ -104,7 +101,8 @@ func TestBuilderBuild(t *testing.T) {
},
})
builder.AddImplementer(codegen.MethodBuilder{
Receiver: codegen.MethodReceiver{Name: "u", Type: code.SimpleType("User")},
Pkg: testutils.Pkg,
Receiver: codegen.MethodReceiver{Name: "u", TypeName: "User"},
Name: "IDHex",
Params: nil,
Returns: []types.Type{code.TypeString},
Expand Down
Loading

0 comments on commit f199cdd

Please sign in to comment.