From 58e3d21e19f0eda28dedb4bb63f82b1e796f64cb Mon Sep 17 00:00:00 2001 From: Cory Slep Date: Sat, 5 Jan 2019 22:46:58 +0100 Subject: [PATCH] Add per-package files for type-based packages. This takes care of abstracting away the manager for the generated types, and also provides the hooks for the manager to inject itself at init time. --- tools/exp/convert/convert.go | 52 ++++++++++++++++++++++++++++++++ tools/exp/props/pkg.go | 57 ++++++++++++++++++++++++++++++++++++ tools/exp/props/type.go | 17 ++++++++--- 3 files changed, 122 insertions(+), 4 deletions(-) diff --git a/tools/exp/convert/convert.go b/tools/exp/convert/convert.go index 5cf4e14..091cd33 100644 --- a/tools/exp/convert/convert.go +++ b/tools/exp/convert/convert.go @@ -199,6 +199,12 @@ func (c Converter) convertToFiles(v vocabulary) (f []*File, e error) { Directory: pub.WriteDir(), }) } + typePkgFiles, err := c.typePackageFiles(v.Types) + if err != nil { + e = err + return + } + f = append(f, typePkgFiles...) // Manager pub := c.VocabularyRoot.PublicPackage() file := jen.NewFilePath(pub.Path()) @@ -565,6 +571,52 @@ func (c Converter) propertyPackageManager(v propertyNamer) (pkg *props.PackageMa return } +func (c Converter) typePackageFiles(t map[string]*props.TypeGenerator) (f []*File, e error) { + switch c.TypePackagePolicy { + case TypeFlatUnderRoot: + fallthrough + case TypeFlatUnderVocabularyRoot: + // Only need one for all types. + tgs := make([]*props.TypeGenerator, 0, len(t)) + for _, v := range t { + tgs = append(tgs, v) + } + tpg := props.NewTypePackageGenerator() + pubI := tpg.PublicDefinitions(tgs) + // Public + pub := tgs[0].PublicPackage() + file := jen.NewFilePath(pub.Path()) + file.Add(pubI.Definition()) + f = append(f, &File{ + F: file, + FileName: "gen_pkg.go", + Directory: pub.WriteDir(), + }) + // Private + s, i, fn := tpg.PrivateDefinitions(tgs) + priv := tgs[0].PrivatePackage() + file = jen.NewFilePath(priv.Path()) + file.Add( + s, + ).Line().Add( + i.Definition(), + ).Line().Add( + fn.Definition(), + ).Line() + f = append(f, &File{ + F: file, + FileName: "gen_pkg.go", + Directory: priv.WriteDir(), + }) + case TypeIndividualUnderRoot: + // Need individual files per type. + // TODO + default: + e = fmt.Errorf("unrecognized TypePackagePolicy: %v", c.TypePackagePolicy) + } + return +} + type typeNamer interface { TypeName() string } diff --git a/tools/exp/props/pkg.go b/tools/exp/props/pkg.go index 0680ba3..551dd1a 100644 --- a/tools/exp/props/pkg.go +++ b/tools/exp/props/pkg.go @@ -2,6 +2,8 @@ package props import ( "fmt" + "github.com/cjslep/activity/tools/exp/codegen" + "github.com/dave/jennifer/jen" "strings" ) @@ -86,3 +88,58 @@ func (p Package) IsPublic() bool { func (p Package) Parent() *PackageManager { return p.parent } + +const ( + managerInterfaceName = "privateManager" + setManagerFunctionName = "SetManager" +) + +// TypePackageGenerator manages generating one-time files needed for types. +type TypePackageGenerator struct{} + +// NewTypePackageGenerator creates a new TypePackageGenerator. +func NewTypePackageGenerator() *TypePackageGenerator { + return &TypePackageGenerator{} +} + +// PublicDefinitions creates the public-facing code generated definitions needed +// once per package. +// +// Precondition: The passed-in generators are the complete set of type +// generators within a package. +func (t *TypePackageGenerator) PublicDefinitions(tgs []*TypeGenerator) *codegen.Interface { + return TypeInterface(tgs[0].PublicPackage()) +} + +// PrivateDefinitions creates the private code generated definitions needed once +// per package. +// +// Precondition: The passed-in generators are the complete set of type +// generators within a package. +func (t *TypePackageGenerator) PrivateDefinitions(tgs []*TypeGenerator) (*jen.Statement, *codegen.Interface, *codegen.Function) { + var fns []codegen.FunctionSignature + for _, tg := range tgs { + for _, m := range tg.getAllManagerMethods() { + fns = append(fns, m.ToFunctionSignature()) + } + } + return jen.Var().Id(managerInitName()).Id(managerInterfaceName), + codegen.NewInterface(tgs[0].PrivatePackage().Path(), + managerInterfaceName, + fns, + fmt.Sprintf("%s abstracts the code-generated manager that provides access to concrete implementations.", managerInterfaceName)), + codegen.NewCommentedFunction(tgs[0].PrivatePackage().Path(), + setManagerFunctionName, + []jen.Code{ + jen.Id("m").Id(managerInterfaceName), + }, + /*ret=*/ nil, + []jen.Code{ + jen.Id(managerInitName()).Op("=").Id("m"), + }, + jen.Commentf("%s sets the manager package-global variable. For internal use only, do not use as part of Application behavior. Must be called at golang init time.", setManagerFunctionName)) +} + +// PropertyPackageGenerator manages generating one-time files needed for +// properties. +type PropertyPackageGenerator struct{} diff --git a/tools/exp/props/type.go b/tools/exp/props/type.go index ff9110d..0c8e5ce 100644 --- a/tools/exp/props/type.go +++ b/tools/exp/props/type.go @@ -377,7 +377,7 @@ func (t *TypeGenerator) extendsDefinition() (*codegen.Function, *codegen.Method) f := codegen.NewCommentedFunction( t.PrivatePackage().Path(), t.extendsFnName(), - []jen.Code{jen.Id("other").Id(typeInterfaceName)}, + []jen.Code{jen.Id("other").Qual(t.PublicPackage().Path(), typeInterfaceName)}, []jen.Code{jen.Bool()}, impl, jen.Commentf("%s returns true if the %s type extends from the other type.", t.extendsFnName(), t.TypeName())) @@ -385,7 +385,7 @@ func (t *TypeGenerator) extendsDefinition() (*codegen.Function, *codegen.Method) t.PrivatePackage().Path(), extendingMethod, t.TypeName(), - []jen.Code{jen.Id("other").Id(typeInterfaceName)}, + []jen.Code{jen.Id("other").Qual(t.PublicPackage().Path(), typeInterfaceName)}, []jen.Code{jen.Bool()}, []jen.Code{ jen.Return( @@ -433,7 +433,7 @@ func (t *TypeGenerator) extendedByDefinition() *codegen.Function { return codegen.NewCommentedFunction( t.PrivatePackage().Path(), t.extendedByFnName(), - []jen.Code{jen.Id("other").Id(typeInterfaceName)}, + []jen.Code{jen.Id("other").Qual(t.PublicPackage().Path(), typeInterfaceName)}, []jen.Code{jen.Bool()}, impl, jen.Commentf("%s returns true if the other provided type extends from the %s type.", t.extendedByFnName(), t.TypeName())) @@ -482,7 +482,7 @@ func (t *TypeGenerator) disjointWithDefinition() *codegen.Function { return codegen.NewCommentedFunction( t.PrivatePackage().Path(), t.disjointWithFnName(), - []jen.Code{jen.Id("other").Id(typeInterfaceName)}, + []jen.Code{jen.Id("other").Qual(t.PublicPackage().Path(), typeInterfaceName)}, []jen.Code{jen.Bool()}, impl, jen.Commentf("%s returns true if the other provided type is disjoint with the %s type.", t.disjointWithFnName(), t.TypeName())) @@ -681,3 +681,12 @@ implementation, but routine ActivityPub applications should not use this to bypa code generation tool.`, getUnknownMethod, t.TypeName())) return } + +// getAllManagerMethods returns all the manager methods used by this type. +func (t *TypeGenerator) getAllManagerMethods() (m []*codegen.Method) { + for _, prop := range t.allProperties() { + deserMethod := t.m.getDeserializationMethodForProperty(prop) + m = append(m, deserMethod) + } + return m +}