Skip to content

Commit

Permalink
Add -mixedcaps option to edgeql-go (#262)
Browse files Browse the repository at this point in the history
The new `-mixedcaps` option changes snake_case names in shapes to
MixedCaps names in go structs.

```
select User {
    first_name,
    last_name,
}
```

Becomes:
```go
type MyQueryResult struct {
    FirstName string `edgedb:"first_name"`
    LastName  string `edgedb:"last_name"`
}
```

Without this option enabled names are used exactly as they appear in
shapes. For example the query above would produce the following struct:
```go
type MyQueryResult struct {
    first_name string `edgedb:"first_name"`
    last_name  string `edgedb:"last_name"`
}
```

working on #245
  • Loading branch information
fmoor authored May 1, 2023
1 parent 4301e5f commit 7043f73
Show file tree
Hide file tree
Showing 75 changed files with 1,319 additions and 85 deletions.
139 changes: 82 additions & 57 deletions cmd/edgeql-go/endtoend_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,23 @@ import (

var dsn string

var tests = []struct {
description string
directory string
args []string
}{
{
description: "invoke edgeql-go without args",
directory: "testdata/no-args",
args: []string{},
},
{
description: "invoke edgeql-go with -mixedcaps",
directory: "testdata/mixedcaps",
args: []string{"-mixedcaps"},
},
}

func TestMain(m *testing.M) {
o := edgedb.TestClientOptions()
pwd, ok := o.Password.Get()
Expand All @@ -54,69 +71,77 @@ func TestMain(m *testing.M) {
}

func TestEdgeQLGo(t *testing.T) {
dir, err := os.MkdirTemp("", "edgeql-go-*")
require.NoError(t, err)
defer func() {
assert.NoError(t, os.RemoveAll(dir))
}()
for _, test := range tests {
t.Run(test.description, runTest(test.directory, test.args))
}
}

t.Log("building edgeql-go")
edgeqlGo := filepath.Join(dir, "edgeql-go")
run(t, ".", "go", "build", "-o", edgeqlGo)
func runTest(dir string, args []string) func(*testing.T) {
return func(t *testing.T) {
tmpDir, err := os.MkdirTemp("", "edgeql-go-*")
require.NoError(t, err)
defer func() {
assert.NoError(t, os.RemoveAll(tmpDir))
}()

t.Log("building edgeql-go")
edgeqlGo := filepath.Join(tmpDir, "edgeql-go")
run(t, ".", "go", "build", "-o", edgeqlGo)

var wg sync.WaitGroup
err = filepath.WalkDir(
dir,
func(src string, d fs.DirEntry, e error) error {
require.NoError(t, e)
if src == dir {
return nil
}

var wg sync.WaitGroup
err = filepath.WalkDir(
"testdata",
func(src string, d fs.DirEntry, e error) error {
require.NoError(t, e)
if src == "testdata" {
dst := filepath.Join(tmpDir, strings.TrimPrefix(src, dir))
if d.IsDir() {
e = os.Mkdir(dst, os.ModePerm)
require.NoError(t, e)
} else {
wg.Add(1)
go func() {
defer wg.Done()
copyFile(t, dst, src)
}()
}
return nil
},
)
require.NoError(t, err)
wg.Wait()

entries, err := os.ReadDir(tmpDir)
require.NoError(t, err)
for _, entry := range entries {
if entry.Name() == "edgeql-go" {
continue
}

dst := filepath.Join(dir, strings.TrimPrefix(src, "testdata"))
if d.IsDir() {
e = os.Mkdir(dst, os.ModePerm)
require.NoError(t, e)
} else {
wg.Add(1)
go func() {
defer wg.Done()
copyFile(t, dst, src)
}()
}
return nil
},
)
require.NoError(t, err)
wg.Wait()

entries, err := os.ReadDir(dir)
require.NoError(t, err)
for _, entry := range entries {
if entry.Name() == "edgeql-go" {
continue
t.Run(entry.Name(), func(t *testing.T) {
projectDir := filepath.Join(tmpDir, entry.Name())
run(t, projectDir, edgeqlGo, args...)
run(t, projectDir, "go", "run", "./...")
er := filepath.WalkDir(
projectDir,
func(f string, d fs.DirEntry, e error) error {
require.NoError(t, e)
if strings.HasSuffix(f, ".go.assert") {
checkAssertFile(t, f)
}
if strings.HasSuffix(f, ".go") &&
!strings.HasSuffix(f, "ignore.go") {
checkGoFile(t, f)
}
return nil
},
)
require.NoError(t, er)
})
}

t.Run(entry.Name(), func(t *testing.T) {
projectDir := filepath.Join(dir, entry.Name())
run(t, projectDir, edgeqlGo)
run(t, projectDir, "go", "run", "./...")
er := filepath.WalkDir(
projectDir,
func(f string, d fs.DirEntry, e error) error {
require.NoError(t, e)
if strings.HasSuffix(f, ".go.assert") {
checkAssertFile(t, f)
}
if strings.HasSuffix(f, ".go") &&
!strings.HasSuffix(f, "ignore.go") {
checkGoFile(t, f)
}
return nil
},
)
require.NoError(t, er)
})
}
}

Expand Down
33 changes: 26 additions & 7 deletions cmd/edgeql-go/gen.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ func generateType(
desc descriptor.Descriptor,
required bool,
path []string,
mixedCaps bool,
) ([]goType, []string, error) {
var (
err error
Expand All @@ -37,11 +38,11 @@ func generateType(

switch desc.Type {
case descriptor.Set, descriptor.Array:
types, imports, err = generateSlice(desc, path)
types, imports, err = generateSlice(desc, path, mixedCaps)
case descriptor.Object, descriptor.NamedTuple:
types, imports, err = generateObject(desc, required, path)
types, imports, err = generateObject(desc, required, path, mixedCaps)
case descriptor.Tuple:
types, imports, err = generateTuple(desc, required, path)
types, imports, err = generateTuple(desc, required, path, mixedCaps)
case descriptor.BaseScalar, descriptor.Scalar, descriptor.Enum:
types, imports, err = generateBaseScalar(desc, required)
case descriptor.Range:
Expand Down Expand Up @@ -103,11 +104,13 @@ func generateRange(
func generateSlice(
desc descriptor.Descriptor,
path []string,
mixedCaps bool,
) ([]goType, []string, error) {
types, imports, err := generateType(
desc.Fields[0].Desc,
desc.Fields[0].Required,
path,
mixedCaps,
)
if err != nil {
return nil, nil, err
Expand All @@ -121,6 +124,7 @@ func generateObject(
desc descriptor.Descriptor,
required bool,
path []string,
mixedCaps bool,
) ([]goType, []string, error) {
var imports []string
typ := goStruct{Name: nameFromPath(path)}
Expand All @@ -131,14 +135,23 @@ func generateObject(
field.Desc,
field.Required,
append(path, field.Name),
mixedCaps,
)
if err != nil {
return nil, nil, err
}

tag := fmt.Sprintf(`edgedb:"%s"`, field.Name)
name := field.Name
if mixedCaps {
name = snakeToUpperMixedCase(name)
}

typ.Fields = append(typ.Fields, goStructField{
Name: field.Name,
Type: t[0].Reference(),
EQLName: field.Name,
GoName: name,
Type: t[0].Reference(),
Tag: tag,
})
types = append(types, t...)
imports = append(imports, i...)
Expand All @@ -151,6 +164,7 @@ func generateTuple(
desc descriptor.Descriptor,
required bool,
path []string,
mixedCaps bool,
) ([]goType, []string, error) {
var imports []string
typ := &goStruct{Name: nameFromPath(path)}
Expand All @@ -161,6 +175,7 @@ func generateTuple(
field.Desc,
field.Required,
append(path, field.Name),
mixedCaps,
)
if err != nil {
return nil, nil, err
Expand All @@ -169,11 +184,15 @@ func generateTuple(
name := field.Name
if name != "" && name[0] >= '0' && name[0] <= '9' {
name = fmt.Sprintf("Element%s", name)
} else if mixedCaps {
name = snakeToUpperMixedCase(name)
}

typ.Fields = append(typ.Fields, goStructField{
Name: name,
Type: t[0].Reference(),
EQLName: field.Name,
GoName: name,
Type: t[0].Reference(),
Tag: fmt.Sprintf(`edgedb:"%s"`, field.Name),
})
types = append(types, t...)
imports = append(imports, i...)
Expand Down
21 changes: 18 additions & 3 deletions cmd/edgeql-go/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,8 @@ func usage() {
"Generate go functions from edgeql files.\n"+
"\n"+
"USAGE:\n"+
" %s\n", os.Args[0])
" %s [OPTIONS]\n\n"+
"OPTIONS:\n", os.Args[0])
flag.PrintDefaults()
}

Expand All @@ -61,6 +62,9 @@ func main() {
log.SetPrefix("edgeql-go: ")

flag.Usage = usage
mixedCaps := flag.Bool("mixedcaps", false,
"Change snake_case names in shapes "+
"to MixedCaps names in go structs")
flag.Parse()

timer := time.AfterFunc(200*time.Millisecond, func() {
Expand All @@ -87,7 +91,7 @@ func main() {
go func(queryFile string) {
defer wg.Done()
outFile := getOutFile(queryFile)
q, e := newQuery(ctx, c, queryFile, outFile)
q, e := newQuery(ctx, c, queryFile, outFile, *mixedCaps)
if e != nil {
log.Fatalf("processing %s: %s", queryFile, e)
}
Expand Down Expand Up @@ -295,7 +299,18 @@ func isNumberedArgs(desc descriptor.Descriptor) bool {
return true
}

func snakeToLowerCamelCase(s string) string {
func snakeToUpperMixedCase(s string) string {
title := cases.Title(language.English)

parts := strings.Split(s, "_")
for i := 0; i < len(parts); i++ {
parts[i] = title.String(parts[i])
}

return strings.Join(parts, "")
}

func snakeToLowerMixedCase(s string) string {
title := cases.Title(language.English)
lower := cases.Lower(language.English)

Expand Down
20 changes: 14 additions & 6 deletions cmd/edgeql-go/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ func newQuery(
c *edgedb.Client,
qryFile,
outFile string,
mixedCaps bool,
) (*Query, error) {
var err error
qryFile, err = filepath.Abs(qryFile)
Expand All @@ -60,7 +61,7 @@ func newQuery(
}

qryName := queryName(qryFile)
rTypes, imports, err := resultTypes(qryName, description)
rTypes, imports, err := resultTypes(qryName, description, mixedCaps)
if err != nil {
log.Fatal(err)
}
Expand All @@ -72,7 +73,7 @@ func newQuery(
}
}

sTypes, i, err := signatureTypes(description)
sTypes, i, err := signatureTypes(description, mixedCaps)
if err != nil {
log.Fatal(err)
}
Expand Down Expand Up @@ -109,19 +110,20 @@ func cmdVarName(qryFile string) string {
name := filepath.Base(qryFile)
name = strings.TrimSuffix(name, ".edgeql")
name = fmt.Sprintf("%s_cmd", name)
return snakeToLowerCamelCase(name)
return snakeToLowerMixedCase(name)
}

func queryName(qryFile string) string {
name := filepath.Base(qryFile)
name = strings.TrimSuffix(name, ".edgeql")
return snakeToLowerCamelCase(name)
return snakeToLowerMixedCase(name)
}

func signatureTypes(
description *edgedb.CommandDescription,
mixedCaps bool,
) (*goStruct, []string, error) {
types, imports, err := generateType(description.In, true, nil)
types, imports, err := generateType(description.In, true, nil, mixedCaps)
if err != nil {
return &goStruct{}, nil, err
}
Expand All @@ -132,6 +134,7 @@ func signatureTypes(
func resultTypes(
qryName string,
description *edgedb.CommandDescription,
mixedCaps bool,
) ([]goType, []string, error) {
outDesc := description.Out
var required bool
Expand All @@ -154,7 +157,12 @@ func resultTypes(
required = true
}

return generateType(outDesc, required, []string{qryName + "Result"})
return generateType(
outDesc,
required,
[]string{qryName + "Result"},
mixedCaps,
)
}

func randomID() (edgedbtypes.UUID, error) {
Expand Down
Loading

0 comments on commit 7043f73

Please sign in to comment.