diff --git a/cmd/internal/cchat-empty-gen/main.go b/cmd/internal/cchat-empty-gen/main.go new file mode 100644 index 0000000..b43c124 --- /dev/null +++ b/cmd/internal/cchat-empty-gen/main.go @@ -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 +} diff --git a/cmd/cchat-generator/generate_enum.go b/cmd/internal/cchat-generator/generate_enum.go similarity index 92% rename from cmd/cchat-generator/generate_enum.go rename to cmd/internal/cchat-generator/generate_enum.go index 8fd31b7..42482fc 100644 --- a/cmd/cchat-generator/generate_enum.go +++ b/cmd/internal/cchat-generator/generate_enum.go @@ -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() diff --git a/cmd/cchat-generator/generate_interface.go b/cmd/internal/cchat-generator/generate_interface.go similarity index 92% rename from cmd/cchat-generator/generate_interface.go rename to cmd/internal/cchat-generator/generate_interface.go index 254d766..279da58 100644 --- a/cmd/cchat-generator/generate_interface.go +++ b/cmd/internal/cchat-generator/generate_interface.go @@ -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 } diff --git a/cmd/cchat-generator/generate_struct.go b/cmd/internal/cchat-generator/generate_struct.go similarity index 91% rename from cmd/cchat-generator/generate_struct.go rename to cmd/internal/cchat-generator/generate_struct.go index 349cc81..4caa0ea 100644 --- a/cmd/cchat-generator/generate_struct.go +++ b/cmd/internal/cchat-generator/generate_struct.go @@ -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) } }) diff --git a/cmd/cchat-generator/generate_type.go b/cmd/internal/cchat-generator/generate_type.go similarity index 65% rename from cmd/cchat-generator/generate_type.go rename to cmd/internal/cchat-generator/generate_type.go index 1e03536..e8ee470 100644 --- a/cmd/cchat-generator/generate_type.go +++ b/cmd/internal/cchat-generator/generate_type.go @@ -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) -} diff --git a/cmd/internal/cchat-generator/genutils/genutils.go b/cmd/internal/cchat-generator/genutils/genutils.go new file mode 100644 index 0000000..e4daa87 --- /dev/null +++ b/cmd/internal/cchat-generator/genutils/genutils.go @@ -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]))) +} diff --git a/cmd/cchat-generator/main.go b/cmd/internal/cchat-generator/main.go similarity index 85% rename from cmd/cchat-generator/main.go rename to cmd/internal/cchat-generator/main.go index c4cbbd6..5012647 100644 --- a/cmd/cchat-generator/main.go +++ b/cmd/internal/cchat-generator/main.go @@ -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!") diff --git a/generator.go b/generator.go index 44fe6f1..2bb0e04 100644 --- a/generator.go +++ b/generator.go @@ -1,3 +1,3 @@ package cchat -//go:generate go run ./cmd/cchat-generator +//go:generate go run ./cmd/internal/cchat-generator diff --git a/repository/gob/generator.go b/repository/gob/generator.go index 2ee880d..dd90a24 100644 --- a/repository/gob/generator.go +++ b/repository/gob/generator.go @@ -1,3 +1,5 @@ +// +build ignore + package main //go:generate go run ./generator.go diff --git a/repository/main.go b/repository/main.go index 2f35bee..d44e504 100644 --- a/repository/main.go +++ b/repository/main.go @@ -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"), }}, }, }, diff --git a/repository/repository.go b/repository/repository.go index a1feef0..f87bff2 100644 --- a/repository/repository.go +++ b/repository/repository.go @@ -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 diff --git a/utils/empty/empty.go b/utils/empty/empty.go new file mode 100644 index 0000000..1a520b6 --- /dev/null +++ b/utils/empty/empty.go @@ -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 } diff --git a/utils/empty/generator.go b/utils/empty/generator.go new file mode 100644 index 0000000..b079ea9 --- /dev/null +++ b/utils/empty/generator.go @@ -0,0 +1,3 @@ +package main + +//go:generate go run ../../cmd/internal/cchat-empty-gen