diff --git a/tools/exp/codegen/method.go b/tools/exp/codegen/method.go index 1bdc20a..31c6157 100644 --- a/tools/exp/codegen/method.go +++ b/tools/exp/codegen/method.go @@ -202,6 +202,12 @@ func (m Method) Call(on string, params ...jen.Code) jen.Code { return jen.Id(on).Dot(m.function.name).Call(params...) } +// On generates the Go code that determines the qualified method name on a +// specific variable. +func (m Method) On(on string) *jen.Statement { + return jen.Id(on).Dot(m.function.name) +} + // Name returns the identifier of this function. func (m Method) Name() string { return m.function.name diff --git a/tools/exp/props/funcprop.go b/tools/exp/props/funcprop.go index 1a3f4a6..7e1d44e 100644 --- a/tools/exp/props/funcprop.go +++ b/tools/exp/props/funcprop.go @@ -282,13 +282,24 @@ func (p *FunctionalPropertyGenerator) serializationFuncs() (*codegen.Method, *co jen.Id(codegen.This()).Dot(p.isMethodName(i)).Call(), ) } - serializeFns = serializeFns.Block( - jen.Return( - kind.SerializeFn.Clone().Call( - jen.Id(codegen.This()).Dot(p.getFnName(i)).Call(), + if kind.SerializeFn != nil { + // This is a value that has a function that must be + // called to serialize properly. + serializeFns = serializeFns.Block( + jen.Return( + kind.SerializeFn.Clone().Call( + jen.Id(codegen.This()).Dot(p.getFnName(i)).Call(), + ), ), - ), - ) + ) + } else { + // This is a type with a Serialize method. + serializeFns = serializeFns.Block( + jen.Return( + jen.Id(codegen.This()).Dot(p.getFnName(i)).Call().Dot(serializeMethodName).Call(), + ), + ) + } } serialize := codegen.NewCommentedValueMethod( p.Package.Path(), diff --git a/tools/exp/props/manager.go b/tools/exp/props/manager.go index 4111b13..28b1a25 100644 --- a/tools/exp/props/manager.go +++ b/tools/exp/props/manager.go @@ -57,7 +57,8 @@ type ManagerGenerator struct { // managedMethods caches the specific methods and interfaces mapped to specific // properties and types. type managedMethods struct { - deserializor *codegen.Method + deserializor *codegen.Method + // TODO: Delete these? publicDeserializor *codegen.Method // Interface for a manager mIface *codegen.Interface @@ -203,6 +204,7 @@ func (m *ManagerGenerator) PrivateManager() *codegen.Struct { func (m *ManagerGenerator) createPrivateDeserializationMethodForType(tg *TypeGenerator) *codegen.Method { dn := tg.deserializationFnName() pkg := m.pm.PrivatePackage() + // TODO: Better naming scheme from package name name := fmt.Sprintf("%s%s%s", dn, tg.PrivatePackage().Name(), strings.Title(pkg.Name())) return codegen.NewCommentedValueMethod( pkg.Path(), @@ -233,6 +235,7 @@ func (m *ManagerGenerator) createPrivateDeserializationMethodForType(tg *TypeGen func (m *ManagerGenerator) createPrivateDeserializationMethodForFuncProperty(fp *FunctionalPropertyGenerator) *codegen.Method { dn := fp.DeserializeFnName() pkg := m.pm.PrivatePackage() + // TODO: Better naming scheme from package name name := fmt.Sprintf("%s%s%s", dn, fp.Package.Name(), strings.Title(pkg.Name())) return codegen.NewCommentedValueMethod( pkg.Path(), @@ -263,6 +266,7 @@ func (m *ManagerGenerator) createPrivateDeserializationMethodForFuncProperty(fp func (m *ManagerGenerator) createPrivateDeserializationMethodForNonFuncProperty(nfp *NonFunctionalPropertyGenerator) *codegen.Method { dn := nfp.DeserializeFnName() pkg := m.pm.PrivatePackage() + // TODO: Better naming scheme from package name name := fmt.Sprintf("%s%s%s", dn, nfp.Package.Name(), strings.Title(pkg.Name())) return codegen.NewCommentedValueMethod( pkg.Path(), diff --git a/tools/exp/props/nonfuncprop.go b/tools/exp/props/nonfuncprop.go index ed89519..2651b5d 100644 --- a/tools/exp/props/nonfuncprop.go +++ b/tools/exp/props/nonfuncprop.go @@ -156,15 +156,21 @@ func (p *NonFunctionalPropertyGenerator) funcs() []*codegen.Method { if i > 0 { less.Else() } + // LessFn is nil case -- call Less method directly on the LHS. + lessCall := jen.Id("lhs").Dot(lessMethod).Call(jen.Id("rhs")) + if kind.LessFn != nil { + // LessFn is indeed a function -- call this function + lessCall = kind.LessFn.Clone().Call( + jen.Id("lhs"), + jen.Id("rhs"), + ) + } less.If( jen.Id("idx1").Op("==").Lit(i), ).Block( jen.Id("lhs").Op(":=").Id(codegen.This()).Index(jen.Id("i")).Dot(p.getFnName(i)).Call(), jen.Id("rhs").Op(":=").Id(codegen.This()).Index(jen.Id("j")).Dot(p.getFnName(i)).Call(), - jen.Return(kind.LessFn.Clone().Call( - jen.Id("lhs"), - jen.Id("rhs"), - )), + jen.Return(lessCall), ) } // Remove Method diff --git a/tools/exp/props/property.go b/tools/exp/props/property.go index d3e41e1..81c42e8 100644 --- a/tools/exp/props/property.go +++ b/tools/exp/props/property.go @@ -62,13 +62,15 @@ type Identifier struct { // Only represents values and other types. type Kind struct { Name Identifier - // ConcreteKind is expected to be a jen.Qual + // ConcreteKind is expected to be properly qualified. ConcreteKind *jen.Statement Nilable bool - // These functions are expected to be a jen.Qual - SerializeFn *jen.Statement + // Expected to always be non-nil: a function is needed to deserialize. DeserializeFn *jen.Statement - LessFn *jen.Statement + // If any of these are nil at generation time, assume to call the method + // on the object directly (instead of a qualified function). + SerializeFn *jen.Statement + LessFn *jen.Statement } // PropertyGenerator is a common base struct used in both Functional and @@ -100,16 +102,14 @@ func (p *PropertyGenerator) GetPackage() Package { // The name parameter must match the LowerName of an Identifier. // // This feels very hacky. -func (p *PropertyGenerator) SetKindFns(name string, qualKind, ser, deser, less *jen.Statement) error { +func (p *PropertyGenerator) SetKindFns(name string, qualKind, deser *jen.Statement) error { for i, kind := range p.Kinds { if kind.Name.LowerName == name { if kind.SerializeFn != nil || kind.DeserializeFn != nil || kind.LessFn != nil { return fmt.Errorf("property kind already has serialization functions set for %q", name) } kind.ConcreteKind = qualKind - kind.SerializeFn = ser kind.DeserializeFn = deser - kind.LessFn = less p.Kinds[i] = kind return nil } diff --git a/tools/exp/props/type.go b/tools/exp/props/type.go index af252be..e828d44 100644 --- a/tools/exp/props/type.go +++ b/tools/exp/props/type.go @@ -9,9 +9,6 @@ import ( "sync" ) -// TODO: Prevent circular dependency by somehow abstracting the requisite -// functions between props and types. - const ( typeInterfaceName = "Type" extendedByMethod = "IsExtendedBy" @@ -21,7 +18,7 @@ const ( typeNameMethod = "Name" serializeMethodName = "Serialize" deserializeFnName = "Deserialize" - lessFnName = "Less" + typeLessMethod = "LessThan" ) // TypeInterface returns the Type Interface that is needed for ActivityStream @@ -45,7 +42,7 @@ type Property interface { GetPackage() Package PropertyName() string StructName() string - SetKindFns(name string, kind, ser, deser, less *jen.Statement) error + SetKindFns(name string, kind, deser *jen.Statement) error DeserializeFnName() string } @@ -128,13 +125,12 @@ func NewTypeGenerator(pm *PackageManager, typeName, comment string, func (t *TypeGenerator) apply(m *ManagerGenerator) error { t.m = m // Set up Kind functions - // TODO: Call serialize on an object instead of using a function. - ser := jen.Qual(t.PrivatePackage().Path(), t.serializationFnName()) - deser := jen.Qual(m.privatePackage().Path(), m.getPrivateDeserializationMethodForType(t).Name()) - less := jen.Qual(t.PrivatePackage().Path(), t.lessFnName()) + // Note: this "i" must be the same as the "i" in the deserialization definition. + // TODO: Remove this kluge. + deser := m.getPrivateDeserializationMethodForType(t).On(managerInitName()) kind := jen.Qual(t.PublicPackage().Path(), t.InterfaceName()) for _, p := range t.rangeProperties { - if e := p.SetKindFns(t.TypeName(), kind, ser, deser, less); e != nil { + if e := p.SetKindFns(t.TypeName(), kind, deser); e != nil { return e } } @@ -220,17 +216,6 @@ func (t *TypeGenerator) deserializationFnName() string { return fmt.Sprintf("%s%s", deserializeFnName, t.TypeName()) } -// serializationFnName determines the name of the serialize function for this -// type. -func (t *TypeGenerator) serializationFnName() string { - return fmt.Sprintf("%s%s", serializeMethodName, t.TypeName()) -} - -// lessFnName determines the name of the less function for this type. -func (t *TypeGenerator) lessFnName() string { - return fmt.Sprintf("%s%s", lessFnName, t.TypeName()) -} - // toInterface creates the interface version of the definition generated. // // Requires apply to have already been called. @@ -254,8 +239,9 @@ func (t *TypeGenerator) InterfaceDefinition(pkg Package) *codegen.Interface { func (t *TypeGenerator) Definition() *codegen.Struct { t.cacheOnce.Do(func() { members := t.members() - m := t.serializationMethod() - ser, deser, less := t.kindSerializationFuncs() + ser := t.serializationMethod() + less := t.lessMethod() + deser := t.kindDeserializationFunc() extendsFn, extendsMethod := t.extendsDefinition() t.cachedStruct = codegen.NewStruct( jen.Commentf(t.Comments()), @@ -263,15 +249,14 @@ func (t *TypeGenerator) Definition() *codegen.Struct { []*codegen.Method{ t.nameDefinition(), extendsMethod, - m, + ser, + less, }, []*codegen.Function{ t.extendedByDefinition(), extendsFn, t.disjointWithDefinition(), - ser, deser, - less, }, members) }) @@ -322,6 +307,7 @@ func (t *TypeGenerator) members() (members []jen.Code) { // Convert to jen.Code members = make([]jen.Code, 0, len(p)) for _, property := range sortedMembers { + // TODO: Use interface instead members = append(members, jen.Id(strings.Title(property.PropertyName())).Qual(property.GetPackage().Path(), property.StructName())) } return @@ -508,20 +494,27 @@ func (t *TypeGenerator) serializationMethod() (ser *codegen.Method) { return } -// kindSerializationFuncs returns free function references that can be used to -// treat a TypeGenerator as another property's Kind. -func (t *TypeGenerator) kindSerializationFuncs() (ser, deser, less *codegen.Function) { - ser = codegen.NewCommentedFunction( +// lessMethod returns the method needed to compare a type with another type. +func (t *TypeGenerator) lessMethod() (less *codegen.Method) { + less = codegen.NewCommentedValueMethod( t.PrivatePackage().Path(), - t.serializationFnName(), - []jen.Code{jen.Id("s").Id(t.TypeName())}, - []jen.Code{jen.Interface(), jen.Error()}, + typeLessMethod, + t.TypeName(), []jen.Code{ - jen.Return( - jen.Id("s").Dot(serializeMethodName).Call(), - ), + jen.Id("o").Op("*").Id(t.TypeName()), }, - jen.Commentf("%s calls %s on the %s type.", t.serializationFnName(), serializeMethodName, t.TypeName())) + []jen.Code{jen.Bool()}, + []jen.Code{ + // TODO + jen.Commentf("TODO: Less code for %s", t.TypeName()), + }, + jen.Commentf("%s computes if this %s is lesser, with an arbitrary but stable determination", typeLessMethod, t.TypeName())) + return +} + +// kindDeserializationFunc returns free function reference that can be used to +// treat a TypeGenerator as another property's Kind. +func (t *TypeGenerator) kindDeserializationFunc() (deser *codegen.Function) { deserCode := jen.Empty() for name, prop := range t.allProperties() { deserMethod := t.m.getPrivateDeserializationMethodForProperty(prop) @@ -550,18 +543,5 @@ func (t *TypeGenerator) kindSerializationFuncs() (ser, deser, less *codegen.Func jen.Return(jen.Id(codegen.This()), jen.Nil()), }, jen.Commentf("%s creates a %s from a map representation that has been unmarshalled from a text or binary format.", t.deserializationFnName(), t.TypeName())) - less = codegen.NewCommentedFunction( - t.PrivatePackage().Path(), - t.lessFnName(), - []jen.Code{ - jen.Id("i"), - jen.Id("j").Op("*").Id(t.TypeName()), - }, - []jen.Code{jen.Bool()}, - []jen.Code{ - // TODO - jen.Commentf("TODO: Less code for %s", t.TypeName()), - }, - jen.Commentf("%s computes which %s is lesser, with an arbitrary but stable determination", t.lessFnName(), t.TypeName())) return }