Added package empty

This package adds the code generation for package empty, which provides
structs that has no-op asserter methods for ease of use.

The package demonstrates one of the many possible use cases of having a
repository ready for code generation.
This commit is contained in:
diamondburned 2020-10-03 14:31:38 -07:00
parent 2d93bf62ea
commit 25980eb794
13 changed files with 262 additions and 37 deletions

View File

@ -0,0 +1,85 @@
package main
import (
"fmt"
"log"
"os"
"github.com/dave/jennifer/jen"
"github.com/diamondburned/cchat/cmd/internal/cchat-generator/genutils"
"github.com/diamondburned/cchat/repository"
)
const OutputDir = "."
func init() {
log.SetFlags(0)
}
var comment = repository.Comment{Raw: `
Package empty provides no-op asserter method implementations of interfaces
in cchat.
`}
func main() {
pk, ok := repository.Main[repository.RootPath]
if !ok {
log.Fatalf("Failed to find main namespace %q\n", repository.RootPath)
}
gen := jen.NewFile("empty")
gen.HeaderComment("DO NOT EDIT: THIS FILE IS GENERATED!")
gen.PackageComment(comment.GoString(1))
for _, iface := range pk.Interfaces {
// Skip structs without asserter methods.
if !hasAsserter(iface) {
continue
}
gen.Commentf("%[1]s provides no-op asserters for cchat.%[1]s.", iface.Name)
gen.Type().Id(iface.Name).Struct()
gen.Line()
for _, method := range iface.Methods {
am, ok := method.(repository.AsserterMethod)
if !ok {
continue
}
name := fmt.Sprintf("As%s", am.ChildType)
gen.Comment(fmt.Sprintf("%s returns nil.", name))
stmt := jen.Func()
stmt.Parens(jen.Id(iface.Name))
stmt.Id(fmt.Sprintf("As%s", am.ChildType))
stmt.Params()
stmt.Add(genutils.GenerateExternType(am))
stmt.Values(jen.Return(jen.Nil()))
gen.Add(stmt)
}
gen.Line()
}
f, err := os.Create("empty.go")
if err != nil {
log.Fatalln("Failed to create output file:", err)
}
defer f.Close()
if err := gen.Render(f); err != nil {
log.Fatalln("Failed to render output:", err)
}
}
func hasAsserter(iface repository.Interface) bool {
for _, method := range iface.Methods {
if _, isA := method.(repository.AsserterMethod); isA {
return true
}
}
return false
}

View File

@ -4,6 +4,7 @@ import (
"sort"
"github.com/dave/jennifer/jen"
"github.com/diamondburned/cchat/cmd/internal/cchat-generator/genutils"
"github.com/diamondburned/cchat/repository"
)
@ -45,7 +46,7 @@ func generateEnums(enums []repository.Enumeration) jen.Code {
stmt.Line()
stmt.Line()
var recv = recvName(enum.Name)
var recv = genutils.RecvName(enum.Name)
if enum.Bitwise {
fn := stmt.Func()

View File

@ -4,6 +4,7 @@ import (
"sort"
"github.com/dave/jennifer/jen"
"github.com/diamondburned/cchat/cmd/internal/cchat-generator/genutils"
"github.com/diamondburned/cchat/repository"
)
@ -71,7 +72,7 @@ func generateInterfaces(ifaces []repository.Interface) jen.Code {
stmt.Params(generateContainerFuncReturns(method)...)
case repository.AsserterMethod:
stmt.Params()
stmt.Params(generateType(method))
stmt.Params(genutils.GenerateType(method))
stmt.Comment("// Optional")
default:
continue
@ -108,9 +109,9 @@ func generateFuncParamErr(param repository.NamedType, genErr bool) []jen.Code {
func generateFuncParam(param repository.NamedType) jen.Code {
if param.Name == "" {
return generateType(param)
return genutils.GenerateType(param)
}
return jen.Id(param.Name).Add(generateType(param))
return jen.Id(param.Name).Add(genutils.GenerateType(param))
}
func generateFuncParams(params []repository.NamedType, withError bool) []jen.Code {
@ -151,7 +152,7 @@ func generateContainerFuncParams(method repository.ContainerMethod) []jen.Code {
if method.HasContext {
stmt.Qual("context", "Context")
}
stmt.Add(generateType(method))
stmt.Add(genutils.GenerateType(method))
return stmt
}

View File

@ -4,6 +4,7 @@ import (
"sort"
"github.com/dave/jennifer/jen"
"github.com/diamondburned/cchat/cmd/internal/cchat-generator/genutils"
"github.com/diamondburned/cchat/repository"
)
@ -35,7 +36,7 @@ func generateErrorStructs(errStructs []repository.ErrorStruct) jen.Code {
stmt.Line()
stmt.Line()
var recv = recvName(errStruct.Name)
var recv = genutils.RecvName(errStruct.Name)
stmt.Func()
stmt.Params(jen.Id(recv).Id(errStruct.Name))
@ -81,7 +82,7 @@ func generateStruct(s repository.Struct) jen.Code {
if field.Name != "" {
stmt.Id(field.Name)
}
stmt.Add(generateType(field))
stmt.Add(genutils.GenerateType(field))
group.Add(stmt)
}
})

View File

@ -4,6 +4,7 @@ import (
"sort"
"github.com/dave/jennifer/jen"
"github.com/diamondburned/cchat/cmd/internal/cchat-generator/genutils"
"github.com/diamondburned/cchat/repository"
)
@ -20,22 +21,10 @@ func generateTypeAlises(aliases []repository.TypeAlias) jen.Code {
stmt.Line()
}
stmt.Type().Id(alias.Name).Op("=").Add(generateType(alias))
stmt.Type().Id(alias.Name).Op("=").Add(genutils.GenerateType(alias))
stmt.Line()
stmt.Line()
}
return stmt
}
type qualer interface {
Qual() (path, name string)
}
func generateType(t qualer) jen.Code {
path, name := t.Qual()
if path == "" {
return jen.Id(name)
}
return jen.Qual(path, name)
}

View File

@ -0,0 +1,37 @@
package genutils
import (
"unicode"
"github.com/dave/jennifer/jen"
"github.com/diamondburned/cchat/repository"
)
type Qualer interface {
Qual() (path, name string)
}
// GenerateType generates a jen.Qual from the given Qualer.
func GenerateType(t Qualer) jen.Code {
path, name := t.Qual()
if path == "" {
return jen.Id(name)
}
return jen.Qual(path, name)
}
// GenerateExternType generates a jen.Qual from the given Qualer, except if
// the path is empty, RootPath is used instead.
func GenerateExternType(t Qualer) jen.Code {
path, name := t.Qual()
if path == "" {
return jen.Qual(repository.RootPath, name)
}
return jen.Qual(path, name)
}
// RecvName is used to get the receiver variable name. It returns the first
// letter lower-cased. It does NOT do length checking. It only works with ASCII.
func RecvName(name string) string {
return string(unicode.ToLower(rune(name[0])))
}

View File

@ -5,7 +5,6 @@ import (
"os"
"path/filepath"
"strings"
"unicode"
"github.com/dave/jennifer/jen"
"github.com/diamondburned/cchat/repository"
@ -48,12 +47,6 @@ func trimPrefix(rootPrefix, path string) string {
return strings.Trim(strings.TrimPrefix(path, rootPrefix), "/")
}
// recvName is used to get the receiver variable name. It returns the first
// letter lower-cased. It does NOT do length checking. It only works with ASCII.
func recvName(name string) string {
return string(unicode.ToLower(rune(name[0])))
}
func generate(pkgPath string, repo repository.Package) *jen.File {
gen := jen.NewFilePath(pkgPath)
gen.HeaderComment("DO NOT EDIT: THIS FILE IS GENERATED!")

View File

@ -1,3 +1,3 @@
package cchat
//go:generate go run ./cmd/cchat-generator
//go:generate go run ./cmd/internal/cchat-generator

View File

@ -1,3 +1,5 @@
// +build ignore
package main
//go:generate go run ./generator.go

View File

@ -5,7 +5,7 @@ package repository
const RootPath = "github.com/diamondburned/cchat"
var Main = Packages{
"github.com/diamondburned/cchat/text": {
MakePath("text"): {
Comment: Comment{`
Package text provides a rich text API for cchat interfaces to use.
`},
@ -210,7 +210,7 @@ var Main = Packages{
Name: "MentionInfo",
},
Returns: []NamedType{{
Type: "(github.com/diamondburned/cchat/text).Rich",
Type: MakeQual("text", "Rich"),
}},
},
},
@ -462,7 +462,7 @@ var Main = Packages{
`},
NamedType: NamedType{
Name: "Text",
Type: "(github.com/diamondburned/cchat/text).Rich",
Type: MakeQual("text", "Rich"),
},
}, {
Comment: Comment{`
@ -472,7 +472,7 @@ var Main = Packages{
`},
NamedType: NamedType{
Name: "Secondary",
Type: "(github.com/diamondburned/cchat/text).Rich",
Type: MakeQual("text", "Rich"),
},
}, {
Comment: Comment{`
@ -545,7 +545,7 @@ var Main = Packages{
GetterMethod{
method: method{Name: "Name"},
Returns: []NamedType{{
Type: "(github.com/diamondburned/cchat/text).Rich",
Type: MakeQual("text", "Rich"),
}},
},
AsserterMethod{
@ -1366,7 +1366,7 @@ var Main = Packages{
GetterMethod{
method: method{Name: "Content"},
Returns: []NamedType{{
Type: "(github.com/diamondburned/cchat/text).Rich",
Type: MakeQual("text", "Rich"),
}},
},
GetterMethod{
@ -1396,7 +1396,7 @@ var Main = Packages{
GetterMethod{
method: method{Name: "Content"},
Returns: []NamedType{{
Type: "(github.com/diamondburned/cchat/text).Rich",
Type: MakeQual("text", "Rich"),
}},
},
},
@ -1422,7 +1422,7 @@ var Main = Packages{
SetterMethod{
method: method{Name: "SetLabel"},
Parameters: []NamedType{{
Type: "(github.com/diamondburned/cchat/text).Rich",
Type: MakeQual("text", "Rich"),
}},
},
},
@ -1650,7 +1650,7 @@ var Main = Packages{
Name: "Secondary",
},
Returns: []NamedType{{
Type: "(github.com/diamondburned/cchat/text).Rich",
Type: MakeQual("text", "Rich"),
}},
},
},

View File

@ -1,9 +1,22 @@
package repository
import (
"fmt"
"path"
"strings"
)
// MakePath returns RootPath joined with relPath.
func MakePath(relPath string) string {
return path.Join(RootPath, relPath)
}
// MakeQual returns a qualified identifier that is the full path and name of
// something.
func MakeQual(relPath, name string) string {
return fmt.Sprintf("(%s).%s", MakePath(relPath), name)
}
// Packages maps Go module paths to packages.
type Packages map[string]Package

100
utils/empty/empty.go Normal file
View File

@ -0,0 +1,100 @@
// DO NOT EDIT: THIS FILE IS GENERATED!
// Package empty provides no-op asserter method implementations of interfaces in
// cchat.
package empty
import cchat "github.com/diamondburned/cchat"
// Namer provides no-op asserters for cchat.Namer.
type Namer struct{}
// AsIconer returns nil.
func (Namer) AsIconer() cchat.Iconer { return nil }
// Service provides no-op asserters for cchat.Service.
type Service struct{}
// AsConfigurator returns nil.
func (Service) AsConfigurator() cchat.Configurator { return nil }
// AsSessionRestorer returns nil.
func (Service) AsSessionRestorer() cchat.SessionRestorer { return nil }
// Session provides no-op asserters for cchat.Session.
type Session struct{}
// AsCommander returns nil.
func (Session) AsCommander() cchat.Commander { return nil }
// AsSessionSaver returns nil.
func (Session) AsSessionSaver() cchat.SessionSaver { return nil }
// Commander provides no-op asserters for cchat.Commander.
type Commander struct{}
// AsCompleter returns nil.
func (Commander) AsCompleter() cchat.Completer { return nil }
// Server provides no-op asserters for cchat.Server.
type Server struct{}
// AsLister returns nil.
func (Server) AsLister() cchat.Lister { return nil }
// AsMessenger returns nil.
func (Server) AsMessenger() cchat.Messenger { return nil }
// AsCommander returns nil.
func (Server) AsCommander() cchat.Commander { return nil }
// AsConfigurator returns nil.
func (Server) AsConfigurator() cchat.Configurator { return nil }
// Messenger provides no-op asserters for cchat.Messenger.
type Messenger struct{}
// AsSender returns nil.
func (Messenger) AsSender() cchat.Sender { return nil }
// AsEditor returns nil.
func (Messenger) AsEditor() cchat.Editor { return nil }
// AsActioner returns nil.
func (Messenger) AsActioner() cchat.Actioner { return nil }
// AsNicknamer returns nil.
func (Messenger) AsNicknamer() cchat.Nicknamer { return nil }
// AsBacklogger returns nil.
func (Messenger) AsBacklogger() cchat.Backlogger { return nil }
// AsMemberLister returns nil.
func (Messenger) AsMemberLister() cchat.MemberLister { return nil }
// AsUnreadIndicator returns nil.
func (Messenger) AsUnreadIndicator() cchat.UnreadIndicator { return nil }
// AsTypingIndicator returns nil.
func (Messenger) AsTypingIndicator() cchat.TypingIndicator { return nil }
// Sender provides no-op asserters for cchat.Sender.
type Sender struct{}
// AsCompleter returns nil.
func (Sender) AsCompleter() cchat.Completer { return nil }
// MemberSection provides no-op asserters for cchat.MemberSection.
type MemberSection struct{}
// AsMemberDynamicSection returns nil.
func (MemberSection) AsMemberDynamicSection() cchat.MemberDynamicSection { return nil }
// SendableMessage provides no-op asserters for cchat.SendableMessage.
type SendableMessage struct{}
// AsNoncer returns nil.
func (SendableMessage) AsNoncer() cchat.Noncer { return nil }
// AsAttachments returns nil.
func (SendableMessage) AsAttachments() cchat.Attachments { return nil }

3
utils/empty/generator.go Normal file
View File

@ -0,0 +1,3 @@
package main
//go:generate go run ../../cmd/internal/cchat-empty-gen