Skip to content

Commit

Permalink
feat(orm): codegen (#11033)
Browse files Browse the repository at this point in the history
## Description

adds orm code generation

Closes: #10737 



---

### Author Checklist

*All items are required. Please add a note to the item if the item is not applicable and
please add links to any relevant follow up issues.*

I have...

- [ ] included the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title
- [ ] added `!` to the type prefix if API or client breaking change
- [ ] targeted the correct branch (see [PR Targeting](https://github.com/cosmos/cosmos-sdk/blob/master/CONTRIBUTING.md#pr-targeting))
- [ ] provided a link to the relevant issue or specification
- [ ] followed the guidelines for [building modules](https://github.com/cosmos/cosmos-sdk/blob/master/docs/building-modules)
- [ ] included the necessary unit and integration [tests](https://github.com/cosmos/cosmos-sdk/blob/master/CONTRIBUTING.md#testing)
- [ ] added a changelog entry to `CHANGELOG.md`
- [ ] included comments for [documenting Go code](https://blog.golang.org/godoc)
- [ ] updated the relevant documentation or specification
- [ ] reviewed "Files changed" and left comments if necessary
- [ ] confirmed all CI checks have passed

### Reviewers Checklist

*All items are required. Please add a note if the item is not applicable and please add
your handle next to the items reviewed if you only reviewed selected items.*

I have...

- [ ] confirmed the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title
- [ ] confirmed `!` in the type prefix if API or client breaking change
- [ ] confirmed all author checklist items have been addressed 
- [ ] reviewed state machine logic
- [ ] reviewed API design and naming
- [ ] reviewed documentation is accurate
- [ ] reviewed tests and test coverage
- [ ] manually tested (if applicable)
  • Loading branch information
technicallyty committed Jan 28, 2022
1 parent 03dcc4f commit 20b2605
Show file tree
Hide file tree
Showing 28 changed files with 1,649 additions and 227 deletions.
3 changes: 3 additions & 0 deletions orm/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
codegen:
go install ./cmd/protoc-gen-go-cosmos-orm
(cd internal; buf generate)
11 changes: 11 additions & 0 deletions orm/cmd/protoc-gen-go-cosmos-orm/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package main

import (
"google.golang.org/protobuf/compiler/protogen"

"github.com/cosmos/cosmos-sdk/orm/internal/codegen"
)

func main() {
protogen.Options{}.Run(codegen.PluginRunner)
}
1 change: 1 addition & 0 deletions orm/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ require (
github.com/cosmos/cosmos-proto v1.0.0-alpha6
github.com/cosmos/cosmos-sdk/api v0.1.0-alpha3
github.com/cosmos/cosmos-sdk/errors v1.0.0-beta.2
github.com/iancoleman/strcase v0.2.0
github.com/stretchr/testify v1.7.0
github.com/tendermint/tm-db v0.6.6
google.golang.org/protobuf v1.27.1
Expand Down
2 changes: 2 additions & 0 deletions orm/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,8 @@ github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/iancoleman/strcase v0.2.0 h1:05I4QRnGpI0m37iZQRuskXh+w77mr6Z41lwQzuHLwW0=
github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/jmhodges/levigo v1.0.0 h1:q5EC36kV79HWeTBWsod3mG11EgStG3qArTKcvlksN1U=
github.com/jmhodges/levigo v1.0.0/go.mod h1:Q6Qx+uH3RAqyK4rFQroq9RL7mdkABMcfhEI+nNuzMJQ=
Expand Down
3 changes: 3 additions & 0 deletions orm/internal/buf.gen.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,6 @@ plugins:
- name: go-pulsar
out: .
opt: paths=source_relative
- name: go-cosmos-orm
out: .
opt: paths=source_relative
62 changes: 62 additions & 0 deletions orm/internal/codegen/codegen.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package codegen

import (
"fmt"

"google.golang.org/protobuf/proto"

v1alpha1 "github.com/cosmos/cosmos-sdk/api/cosmos/orm/v1alpha1"

"github.com/cosmos/cosmos-proto/generator"

"google.golang.org/protobuf/compiler/protogen"
)

const (
contextPkg = protogen.GoImportPath("context")
protoreflectPackage = protogen.GoImportPath("google.golang.org/protobuf/reflect/protoreflect")
ormListPkg = protogen.GoImportPath("github.com/cosmos/cosmos-sdk/orm/model/ormlist")
ormdbPkg = protogen.GoImportPath("github.com/cosmos/cosmos-sdk/orm/model/ormdb")
ormErrPkg = protogen.GoImportPath("github.com/cosmos/cosmos-sdk/orm/types/ormerrors")
fmtPkg = protogen.GoImportPath("fmt")
)

func PluginRunner(p *protogen.Plugin) error {
for _, f := range p.Files {
if !f.Generate {
continue
}

if !hasTables(f) {
continue
}

gen := p.NewGeneratedFile(fmt.Sprintf("%s.cosmos_orm.go", f.GeneratedFilenamePrefix), f.GoImportPath)
cgen := &generator.GeneratedFile{
GeneratedFile: gen,
LocalPackages: map[string]bool{},
}
f := fileGen{GeneratedFile: cgen, file: f}
err := f.gen()
if err != nil {
return err
}

}

return nil
}

func hasTables(file *protogen.File) bool {
for _, message := range file.Messages {
if proto.GetExtension(message.Desc.Options(), v1alpha1.E_Table).(*v1alpha1.TableDescriptor) != nil {
return true
}

if proto.GetExtension(message.Desc.Options(), v1alpha1.E_Singleton).(*v1alpha1.SingletonDescriptor) != nil {
return true
}
}

return false
}
162 changes: 162 additions & 0 deletions orm/internal/codegen/file.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
package codegen

import (
"path/filepath"
"strings"

"github.com/cosmos/cosmos-proto/generator"
"github.com/iancoleman/strcase"
"google.golang.org/protobuf/compiler/protogen"
"google.golang.org/protobuf/proto"

v1alpha1 "github.com/cosmos/cosmos-sdk/api/cosmos/orm/v1alpha1"
)

var (
tablePkg = protogen.GoImportPath("github.com/cosmos/cosmos-sdk/orm/model/ormtable")
)

type fileGen struct {
*generator.GeneratedFile
file *protogen.File
}

func (f fileGen) gen() error {
f.P("// Code generated by protoc-gen-go-cosmos-orm. DO NOT EDIT.")
f.P()
f.P("package ", f.file.GoPackageName)
stores := make([]*protogen.Message, 0)
for _, msg := range f.file.Messages {
tableDesc := proto.GetExtension(msg.Desc.Options(), v1alpha1.E_Table).(*v1alpha1.TableDescriptor)
if tableDesc != nil {
newTableGen(f, msg, tableDesc).gen()
}
singletonDesc := proto.GetExtension(msg.Desc.Options(), v1alpha1.E_Singleton).(*v1alpha1.SingletonDescriptor)
if singletonDesc != nil {
// do some singleton magic
newSingletonGen(f, msg, singletonDesc).gen()
}

if tableDesc != nil || singletonDesc != nil { // message is one of the tables,
stores = append(stores, msg)
}
}
f.genStoreInterfaces(stores)
f.genStoreStruct(stores)
f.genStoreMethods(stores)
f.genStoreInterfaceGuard()
f.genStoreConstructor(stores)
return nil
}

func (f fileGen) genStoreInterfaces(stores []*protogen.Message) {
f.P("type ", f.storeInterfaceName(), " interface {")
for _, store := range stores {
name := f.messageStoreInterfaceName(store)
f.P(name, "()", name)
}
f.P()
f.P("doNotImplement()")
f.P("}")
f.P()
}

func (f fileGen) genStoreStruct(stores []*protogen.Message) {
// struct
f.P("type ", f.storeStructName(), " struct {")
for _, message := range stores {
f.P(f.param(message.GoIdent.GoName), " ", f.messageStoreInterfaceName(message))
}
f.P("}")
}

func (f fileGen) storeAccessorName() string {
return f.storeInterfaceName()
}

func (f fileGen) storeInterfaceName() string {
return strcase.ToCamel(f.fileShortName()) + "Store"
}

func (f fileGen) storeStructName() string {
return strcase.ToLowerCamel(f.fileShortName()) + "Store"
}

func (f fileGen) fileShortName() string {
filename := f.file.Proto.GetName()
shortName := filepath.Base(filename)
i := strings.Index(shortName, ".")
if i > 0 {
return shortName[:i]
}
return strcase.ToCamel(shortName)
}

func (f fileGen) messageStoreInterfaceName(m *protogen.Message) string {
return m.GoIdent.GoName + "Store"
}

func (f fileGen) messageReaderInterfaceName(m *protogen.Message) string {
return m.GoIdent.GoName + "Reader"
}

func (f fileGen) messageTableVar(m *protogen.Message) string {
return f.param(m.GoIdent.GoName + "Table")
}

func (f fileGen) param(name string) string {
return strcase.ToLowerCamel(name)
}

func (f fileGen) messageStoreReceiverName(m *protogen.Message) string {
return f.param(f.messageStoreInterfaceName(m))
}

func (f fileGen) messageConstructorName(m *protogen.Message) string {
return "New" + f.messageStoreInterfaceName(m)
}

func (f fileGen) genStoreMethods(stores []*protogen.Message) {
// getters
for _, msg := range stores {
name := f.messageStoreInterfaceName(msg)
f.P("func(x ", f.storeStructName(), ") ", name, "() ", name, "{")
f.P("return x.", f.param(msg.GoIdent.GoName))
f.P("}")
f.P()
}
f.P("func(", f.storeStructName(), ") doNotImplement() {}")
f.P()
}

func (f fileGen) genStoreInterfaceGuard() {
f.P("var _ ", f.storeInterfaceName(), " = ", f.storeStructName(), "{}")
}

func (f fileGen) genStoreConstructor(stores []*protogen.Message) {
f.P("func New", f.storeInterfaceName(), "(db ", ormdbPkg.Ident("ModuleDB"), ") (", f.storeInterfaceName(), ", error) {")
for _, store := range stores {
f.P(f.messageStoreReceiverName(store), ", err := ", f.messageConstructorName(store), "(db)")
f.P("if err != nil {")
f.P("return nil, err")
f.P("}")
f.P()
}

f.P("return ", f.storeStructName(), "{")
for _, store := range stores {
f.P(f.messageStoreReceiverName(store), ",")
}
f.P("}, nil")
f.P("}")

}

func (f fileGen) fieldsToCamelCase(fields string) string {
splitFields := strings.Split(fields, ",")
camelFields := make([]string, len(splitFields))
for i, field := range splitFields {
camelFields[i] = strcase.ToCamel(field)
}
return strings.Join(camelFields, "")
}
Loading

0 comments on commit 20b2605

Please sign in to comment.