From a3c3a7b5fc1993a11aacdfc34f26b6792e344c38 Mon Sep 17 00:00:00 2001 From: Cory Slep Date: Mon, 17 Dec 2018 23:11:55 +0100 Subject: [PATCH] Prepare TypeGenerator being a Kind for Properties. Right now the two-pass tooling system has issues with establishing doubly-linked data between Functiona/NonFunctional Properties (which have Kinds abstractions) and Types (which have Property abstraction). While the experimental tool compiles, it panics at runtime currently because the TypeGenerator needs to look at properties with Range of itself, but it is applying itself to properties with Domain of its type. Which is wrong. Will need to stew on this and think of how to avoid making even more shortcuts and hacky solutions in the name of progress. --- tools/exp/convert/convert.go | 112 +++++++++++------ tools/exp/props/funcprop.go | 8 +- tools/exp/props/nonfuncprop.go | 6 +- tools/exp/props/property.go | 25 +++- tools/exp/{types => props}/type.go | 192 ++++++++++++++++++----------- 5 files changed, 225 insertions(+), 118 deletions(-) rename tools/exp/{types => props}/type.go (75%) diff --git a/tools/exp/convert/convert.go b/tools/exp/convert/convert.go index 0986c68..c7994cd 100644 --- a/tools/exp/convert/convert.go +++ b/tools/exp/convert/convert.go @@ -4,7 +4,6 @@ import ( "fmt" "github.com/cjslep/activity/tools/exp/props" "github.com/cjslep/activity/tools/exp/rdf" - "github.com/cjslep/activity/tools/exp/types" "github.com/dave/jennifer/jen" "strings" ) @@ -19,7 +18,7 @@ type vocabulary struct { Kinds map[string]*props.Kind FProps map[string]*props.FunctionalPropertyGenerator NFProps map[string]*props.NonFunctionalPropertyGenerator - Types map[string]*types.TypeGenerator + Types map[string]*props.TypeGenerator } func newVocabulary() vocabulary { @@ -27,7 +26,7 @@ func newVocabulary() vocabulary { Kinds: make(map[string]*props.Kind, 0), FProps: make(map[string]*props.FunctionalPropertyGenerator, 0), NFProps: make(map[string]*props.NonFunctionalPropertyGenerator, 0), - Types: make(map[string]*types.TypeGenerator, 0), + Types: make(map[string]*props.TypeGenerator, 0), } } @@ -68,7 +67,7 @@ func (c Converter) Convert(p *rdf.ParsedVocabulary) (f []*File, e error) { func (c Converter) convertToFiles(v vocabulary) (f []*File, e error) { for _, _ = range v.Kinds { - // TODO + // TODO: Implement } for _, i := range v.FProps { var pkg string @@ -131,6 +130,16 @@ func (c Converter) convertToFiles(v vocabulary) (f []*File, e error) { return } +// convertVocabulary works in a two-pass system: first converting all known +// properties, and then the types. +// +// Due to the fact that properties rely on the Kind abstraction, and both +// properties and types can be Kinds, this introduces tight coupling between +// the two so that callbacks can fill in missing links in data that isn't known +// beforehand (ex: how to serialize, deserialize, and compare types). +// +// This feels very hacky and could be decoupled using standard design patterns, +// but since there is no need, it isn't addressed now. func (c Converter) convertVocabulary(p *rdf.ParsedVocabulary) (v vocabulary, e error) { v = newVocabulary() for k, val := range p.Vocab.Values { @@ -159,7 +168,7 @@ func (c Converter) convertVocabulary(p *rdf.ParsedVocabulary) (v vocabulary, e e stuck := true for i, t := range allTypes { if allExtendsAreIn(t, v.Types) { - var tg *types.TypeGenerator + var tg *props.TypeGenerator tg, e = c.convertType(t, p.Vocab, v.FProps, v.NFProps, v.Types) if e != nil { return @@ -173,7 +182,7 @@ func (c Converter) convertVocabulary(p *rdf.ParsedVocabulary) (v vocabulary, e e } } if stuck { - e = fmt.Errorf("converting types got stuck in dependency cycle") + e = fmt.Errorf("converting props got stuck in dependency cycle") return } } @@ -184,46 +193,70 @@ func (c Converter) convertType(t rdf.VocabularyType, v rdf.Vocabulary, existingFProps map[string]*props.FunctionalPropertyGenerator, existingNFProps map[string]*props.NonFunctionalPropertyGenerator, - existingTypes map[string]*types.TypeGenerator) (tg *types.TypeGenerator, e error) { - // Determine the types package name + existingTypes map[string]*props.TypeGenerator) (tg *props.TypeGenerator, e error) { + // Determine the props package name var pkg string pkg, e = c.typePackageName(t) if e != nil { return } // Determine the properties for this type - var p []types.Property - for _, prop := range v.Properties { - for _, ref := range prop.Domain { - if len(ref.Vocab) != 0 { - e = fmt.Errorf("unhandled use case: property domain outside its vocabulary") - return - } else if ref.Name == t.Name { - if prop.Functional { - p = append(p, existingFProps[prop.Name]) - } else { - p = append(p, existingNFProps[prop.Name]) + var p []props.Property + for _, prop := range t.Properties { + if len(prop.Vocab) != 0 { + e = fmt.Errorf("unhandled use case: property domain outside its vocabulary") + return + } else { + var property props.Property + var ok bool + property, ok = existingFProps[prop.Name] + if !ok { + property, ok = existingNFProps[prop.Name] + if !ok { + e = fmt.Errorf("cannot find property with name: %s", prop.Name) + return } - break } + p = append(p, property) + } + } + // Determine WithoutProperties for this type + var wop []props.Property + for _, prop := range t.WithoutProperties { + if len(prop.Vocab) != 0 { + e = fmt.Errorf("unhandled use case: withoutproperty domain outside its vocabulary") + return + } else { + var property props.Property + var ok bool + property, ok = existingFProps[prop.Name] + if !ok { + property, ok = existingNFProps[prop.Name] + if !ok { + e = fmt.Errorf("cannot find property with name: %s", prop.Name) + return + } + } + wop = append(wop, property) } } // Determine what this type extends - var ext []*types.TypeGenerator + var ext []*props.TypeGenerator for _, ex := range t.Extends { if len(ex.Vocab) != 0 { - // TODO: This should be fixed + // TODO: This should be fixed to handle references e = fmt.Errorf("unhandled use case: type extends another type outside its vocabulary") return } else { ext = append(ext, existingTypes[ex.Name]) } } - tg, e = types.NewTypeGenerator( + tg, e = props.NewTypeGenerator( pkg, c.convertTypeToName(t), t.Notes, p, + wop, ext, nil) if e != nil { @@ -232,7 +265,7 @@ func (c Converter) convertType(t rdf.VocabularyType, // Apply disjoint if both sides are available. for _, disj := range t.DisjointWith { if len(disj.Vocab) != 0 { - // TODO: This should be fixed + // TODO: This should be fixed to handle references e = fmt.Errorf("unhandled use case: type is disjoint with another type outside its vocabulary") return } else if disjointType, ok := existingTypes[disj.Name]; ok { @@ -300,19 +333,18 @@ func (c Converter) convertValue(v rdf.VocabularyValue) (k *props.Kind) { } func (c Converter) convertTypeToKind(v rdf.VocabularyType) (k *props.Kind, e error) { - var pkg string - pkg, e = c.typePackageName(v) - if e != nil { - return - } - s, d, l := types.KindSerializationFuncs(pkg, c.convertTypeToName(v)) k = &props.Kind{ - Name: c.toIdentifier(v), - ConcreteKind: c.convertTypeToConcreteKind(v), - Nilable: true, - SerializeFn: s, - DeserializeFn: d, - LessFn: l, + Name: c.toIdentifier(v), + ConcreteKind: c.convertTypeToConcreteKind(v), + Nilable: true, + // Instead of populating: + // - SerializeFn + // - DeserializeFn + // - LessFn + // + // The TypeGenerator is responsible for calling setKindFns on + // the properties, to property wire a Property's Kind back to + // the Type's implementation. } return } @@ -409,7 +441,7 @@ func (c Converter) propertyPackageName(v rdf.VocabularyProperty) (pkg string, e return } -func (c Converter) typePackageDirectory(v *types.TypeGenerator) (dir string, e error) { +func (c Converter) typePackageDirectory(v *props.TypeGenerator) (dir string, e error) { switch c.TypePackagePolicy { case TypeFlatUnderRoot: dir = fmt.Sprintf("%s/%s/", c.VocabularyRoot, c.TypePackageRoot) @@ -423,7 +455,7 @@ func (c Converter) typePackageDirectory(v *types.TypeGenerator) (dir string, e e return } -func (c Converter) typePackageFile(v *types.TypeGenerator) (pkg string, e error) { +func (c Converter) typePackageFile(v *props.TypeGenerator) (pkg string, e error) { switch c.TypePackagePolicy { case TypeFlatUnderRoot: pkg = fmt.Sprintf("%s/%s", c.VocabularyRoot, c.TypePackageRoot) @@ -485,10 +517,10 @@ func (c Converter) isNilable(goType string) bool { return goType[0] == '*' } -func allExtendsAreIn(t rdf.VocabularyType, v map[string]*types.TypeGenerator) bool { +func allExtendsAreIn(t rdf.VocabularyType, v map[string]*props.TypeGenerator) bool { for _, e := range t.Extends { if len(e.Vocab) != 0 { - // TODO: Handle references + // TODO: This should be fixed to handle references return false } else if _, ok := v[e.Name]; !ok { return false diff --git a/tools/exp/props/funcprop.go b/tools/exp/props/funcprop.go index fe19446..7b49bdc 100644 --- a/tools/exp/props/funcprop.go +++ b/tools/exp/props/funcprop.go @@ -320,7 +320,7 @@ func (p *FunctionalPropertyGenerator) serializationFuncs() ([]*codegen.Method, [ deserialize = append(deserialize, codegen.NewCommentedFunction( p.packageName(), - p.deserializeFnName(), + p.DeserializeFnName(), []jen.Code{jen.Id("i").Interface()}, []jen.Code{jen.Op("*").Id(p.StructName()), jen.Error()}, []jen.Code{ @@ -330,13 +330,13 @@ func (p *FunctionalPropertyGenerator) serializationFuncs() ([]*codegen.Method, [ jen.Nil(), ), }, - jen.Commentf("%s creates an iterator from an element that has been unmarshalled from a text or binary format.", p.deserializeFnName()), + jen.Commentf("%s creates an iterator from an element that has been unmarshalled from a text or binary format.", p.DeserializeFnName()), )) } else { deserialize = append(deserialize, codegen.NewCommentedFunction( p.packageName(), - p.deserializeFnName(), + p.DeserializeFnName(), []jen.Code{jen.Id("m").Map(jen.String()).Interface()}, []jen.Code{jen.Op("*").Id(p.StructName()), jen.Error()}, []jen.Code{ @@ -356,7 +356,7 @@ func (p *FunctionalPropertyGenerator) serializationFuncs() ([]*codegen.Method, [ jen.Nil(), ), }, - jen.Commentf("%s creates a %q property from an interface representation that has been unmarshalled from a text or binary format.", p.deserializeFnName(), p.PropertyName()), + jen.Commentf("%s creates a %q property from an interface representation that has been unmarshalled from a text or binary format.", p.DeserializeFnName(), p.PropertyName()), )) } return serialize, deserialize diff --git a/tools/exp/props/nonfuncprop.go b/tools/exp/props/nonfuncprop.go index e55a523..0f00337 100644 --- a/tools/exp/props/nonfuncprop.go +++ b/tools/exp/props/nonfuncprop.go @@ -303,7 +303,7 @@ func (p *NonFunctionalPropertyGenerator) serializationFuncs() ([]*codegen.Method jen.List( jen.Id("p"), jen.Err(), - ).Op(":=").Id(p.elementTypeGenerator().deserializeFnName()).Call( + ).Op(":=").Id(p.elementTypeGenerator().DeserializeFnName()).Call( jen.Id(variable), ), jen.Err().Op("!=").Nil(), @@ -324,7 +324,7 @@ func (p *NonFunctionalPropertyGenerator) serializationFuncs() ([]*codegen.Method deserialize := []*codegen.Function{ codegen.NewCommentedFunction( p.packageName(), - p.deserializeFnName(), + p.DeserializeFnName(), []jen.Code{jen.Id("m").Map(jen.String()).Interface()}, []jen.Code{jen.Id(p.StructName()), jen.Error()}, []jen.Code{ @@ -364,7 +364,7 @@ func (p *NonFunctionalPropertyGenerator) serializationFuncs() ([]*codegen.Method jen.Nil(), ), }, - jen.Commentf("%s creates a %q property from an interface representation that has been unmarshalled from a text or binary format.", p.deserializeFnName(), p.PropertyName()), + jen.Commentf("%s creates a %q property from an interface representation that has been unmarshalled from a text or binary format.", p.DeserializeFnName(), p.PropertyName()), ), } return serialize, deserialize diff --git a/tools/exp/props/property.go b/tools/exp/props/property.go index 7aff71f..574ced8 100644 --- a/tools/exp/props/property.go +++ b/tools/exp/props/property.go @@ -89,6 +89,27 @@ func (p *PropertyGenerator) packageName() string { return p.Package } +// SetKindFns allows TypeGenerators to later notify this Property what functions +// to use when generating the serialization code. +// +// The name parameter must match the LowerName of an Identifier. +// +// This feels very hacky. +func (p *PropertyGenerator) SetKindFns(name string, ser, deser, less *codegen.Function) error { + for _, 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.SerializeFn = ser + kind.DeserializeFn = deser + kind.LessFn = less + return nil + } + } + return fmt.Errorf("cannot find property kind %q", name) +} + // StructName returns the name of the type, which may or may not be a struct, // to generate. func (p *PropertyGenerator) StructName() string { @@ -105,9 +126,9 @@ func (p *PropertyGenerator) PropertyName() string { return p.Name.LowerName } -// deserializeFnName returns the identifier of the function that deserializes +// DeserializeFnName returns the identifier of the function that deserializes // raw JSON into the generated Go type. -func (p *PropertyGenerator) deserializeFnName() string { +func (p *PropertyGenerator) DeserializeFnName() string { if p.asIterator { return fmt.Sprintf("%s%s", deserializeIteratorMethod, p.Name.CamelName) } diff --git a/tools/exp/types/type.go b/tools/exp/props/type.go similarity index 75% rename from tools/exp/types/type.go rename to tools/exp/props/type.go index 82bfb56..6939a82 100644 --- a/tools/exp/types/type.go +++ b/tools/exp/props/type.go @@ -1,4 +1,4 @@ -package types +package props import ( "fmt" @@ -13,7 +13,7 @@ const ( extendedByMethod = "IsExtendedBy" extendsMethod = "Extends" disjointWithMethod = "IsDisjointWith" - nameMethod = "Name" + typeNameMethod = "Name" serializeMethodName = "Serialize" deserializeFnName = "Deserialize" lessFnName = "Less" @@ -26,73 +26,34 @@ func TypeInterface(pkg string) *codegen.Interface { comment := fmt.Sprintf("%s represents an ActivityStreams type.", typeInterfaceName) funcs := []codegen.FunctionSignature{ { - Name: nameMethod, + Name: typeNameMethod, Params: nil, Ret: []jen.Code{jen.String()}, - Comment: fmt.Sprintf("%s returns the ActivityStreams type name.", nameMethod), + Comment: fmt.Sprintf("%s returns the ActivityStreams type name.", typeNameMethod), }, } return codegen.NewInterface(pkg, typeInterfaceName, funcs, comment) } -// KindSerializationFuncs returns free function references that can be used to -// treat a TypeGenerator as another property's Kind. -func KindSerializationFuncs(pkg, typeName string) (ser, deser, less *codegen.Function) { - serName := fmt.Sprintf("%s%s", serializeMethodName, typeName) - ser = codegen.NewCommentedFunction( - pkg, - serName, - []jen.Code{jen.Id("s").Id(typeName)}, - []jen.Code{jen.Interface(), jen.Error()}, - []jen.Code{ - jen.Return( - jen.Id("s").Dot(serializeMethodName).Call(), - ), - }, - jen.Commentf("%s calls %s on the %s type.", serName, serializeMethodName, typeName)) - deserName := fmt.Sprintf("%s%s", deserializeFnName, typeName) - deser = codegen.NewCommentedFunction( - pkg, - deserName, - []jen.Code{jen.Id("m").Map(jen.String()).Interface()}, - []jen.Code{jen.Op("*").Id(typeName), jen.Error()}, - []jen.Code{ - // TODO - }, - jen.Commentf("%s creates a %s from a map representation that has been unmarshalled from a text or binary format.", deserName, typeName)) - lessName := fmt.Sprintf("%s%s", lessFnName, typeName) - less = codegen.NewCommentedFunction( - pkg, - lessName, - []jen.Code{ - jen.Id("i"), - jen.Id("j").Op("*").Id(typeName), - }, - []jen.Code{jen.Bool()}, - []jen.Code{ - // TODO - }, - jen.Commentf("%s computes which %s is lesser, with an arbitrary but stable determination", lessName, typeName)) - return -} - // Property represents a property of an ActivityStreams type. type Property interface { PropertyName() string StructName() string + SetKindFns(name string, ser, deser, less *codegen.Function) error } // TypeGenerator represents an ActivityStream type definition to generate in Go. type TypeGenerator struct { - packageName string - typeName string - comment string - properties map[string]Property - extends []*TypeGenerator - disjoint []*TypeGenerator - extendedBy []*TypeGenerator - cacheOnce sync.Once - cachedStruct *codegen.Struct + packageName string + typeName string + comment string + properties map[string]Property + withoutProperties map[string]Property + extends []*TypeGenerator + disjoint []*TypeGenerator + extendedBy []*TypeGenerator + cacheOnce sync.Once + cachedStruct *codegen.Struct } // NewTypeGenerator creates a new generator for a specific ActivityStreams Core @@ -105,15 +66,16 @@ type TypeGenerator struct { // All TypeGenerators must be created before the Definition method is called, to // ensure that type extension, in the inheritence sense, is properly set up. func NewTypeGenerator(packageName, typeName, comment string, - properties []Property, + properties, withoutProperties []Property, extends, disjoint []*TypeGenerator) (*TypeGenerator, error) { t := &TypeGenerator{ - packageName: packageName, - typeName: typeName, - comment: comment, - properties: make(map[string]Property, len(properties)), - extends: extends, - disjoint: disjoint, + packageName: packageName, + typeName: typeName, + comment: comment, + properties: make(map[string]Property, len(properties)), + withoutProperties: make(map[string]Property, len(withoutProperties)), + extends: extends, + disjoint: disjoint, } for _, property := range properties { if _, has := t.properties[property.PropertyName()]; has { @@ -121,10 +83,24 @@ func NewTypeGenerator(packageName, typeName, comment string, } t.properties[property.PropertyName()] = property } + for _, wop := range withoutProperties { + if _, has := t.withoutProperties[wop.PropertyName()]; has { + return nil, fmt.Errorf("type already has withoutproperty with name %q", wop.PropertyName()) + } + t.withoutProperties[wop.PropertyName()] = wop + } // Complete doubly-linked extends/extendedBy lists. for _, ext := range extends { ext.extendedBy = append(ext.extendedBy, t) } + // TODO: Fix: Only notify properties whose Range is this type, not the + // properties this type has (which is wrong and currently borken) + for _, property := range t.properties { + ser, deser, less := t.kindSerializationFuncs() + if err := property.SetKindFns(t.TypeName(), ser, deser, less); err != nil { + return nil, err + } + } return t, nil } @@ -167,6 +143,12 @@ func (t *TypeGenerator) Properties() map[string]Property { return t.properties } +// WithoutProperties returns the properties that do not apply to this type, +// mapped by their property name. +func (t *TypeGenerator) WithoutProperties() map[string]Property { + return t.withoutProperties +} + // extendsFnName determines the name of the Extends function, which // determines if this ActivityStreams type extends another one. func (t *TypeGenerator) extendsFnName() string { @@ -190,7 +172,7 @@ func (t *TypeGenerator) Definition() *codegen.Struct { t.cacheOnce.Do(func() { members := t.members() m := t.serializationMethod() - ser, deser, less := KindSerializationFuncs(t.packageName, t.TypeName()) + ser, deser, less := t.kindSerializationFuncs() t.cachedStruct = codegen.NewStruct( jen.Commentf(t.Comment()), t.TypeName(), @@ -211,7 +193,7 @@ func (t *TypeGenerator) Definition() *codegen.Struct { return t.cachedStruct } -func (t *TypeGenerator) members() (members []jen.Code) { +func (t *TypeGenerator) allProperties() map[string]Property { p := t.properties // Properties of parents that are extended, minus DoesNotApplyTo var extends []*TypeGenerator @@ -220,8 +202,17 @@ func (t *TypeGenerator) members() (members []jen.Code) { for k, v := range ext.Properties() { p[k] = v } - // TODO: DoesNotApplyTo } + for _, ext := range t.extends { + for k, _ := range ext.WithoutProperties() { + delete(p, k) + } + } + return p +} + +func (t *TypeGenerator) members() (members []jen.Code) { + p := t.allProperties() members = make([]jen.Code, 0, len(p)) for name, property := range p { members = append(members, jen.Id(strings.Title(name)).Id(property.StructName())) @@ -234,14 +225,14 @@ func (t *TypeGenerator) members() (members []jen.Code) { func (t *TypeGenerator) nameDefinition() *codegen.Method { return codegen.NewCommentedValueMethod( t.packageName, - nameMethod, + typeNameMethod, t.TypeName(), /*params=*/ nil, []jen.Code{jen.String()}, []jen.Code{ jen.Return(jen.Lit(t.TypeName())), }, - jen.Commentf("%s returns the name of this type.", nameMethod)) + jen.Commentf("%s returns the name of this type.", typeNameMethod)) } // getAllParentExtends recursively determines all the parent types that this @@ -275,7 +266,7 @@ func (t *TypeGenerator) extendsDefinition() *codegen.Method { jen.Id("ext"), ).Op(":=").Range().Id("extensions")).Block( jen.If( - jen.Id("ext").Op("==").Id("other").Dot(nameMethod).Call(), + jen.Id("ext").Op("==").Id("other").Dot(typeNameMethod).Call(), ).Block( jen.Return(jen.True()), ), @@ -319,7 +310,7 @@ func (t *TypeGenerator) extendedByDefinition() *codegen.Function { jen.Id("ext"), ).Op(":=").Range().Id("extensions")).Block( jen.If( - jen.Id("ext").Op("==").Id("other").Dot(nameMethod).Call(), + jen.Id("ext").Op("==").Id("other").Dot(typeNameMethod).Call(), ).Block( jen.Return(jen.True()), ), @@ -363,7 +354,7 @@ func (t *TypeGenerator) disjointWithDefinition() *codegen.Function { jen.Id("disjoint"), ).Op(":=").Range().Id("disjointWith")).Block( jen.If( - jen.Id("disjoint").Op("==").Id("other").Dot(nameMethod).Call(), + jen.Id("disjoint").Op("==").Id("other").Dot(typeNameMethod).Call(), ).Block( jen.Return(jen.True()), ), @@ -379,6 +370,8 @@ func (t *TypeGenerator) disjointWithDefinition() *codegen.Function { jen.Commentf("%s returns true if the other provided type is disjoint with the %s type.", t.disjointWithFnName(), t.TypeName())) } +// serializationMethod returns the method needed to serialize a TypeGenerator as +// a property. func (t *TypeGenerator) serializationMethod() (ser *codegen.Method) { ser = codegen.NewCommentedValueMethod( t.packageName, @@ -388,7 +381,68 @@ func (t *TypeGenerator) serializationMethod() (ser *codegen.Method) { []jen.Code{jen.Interface(), jen.Error()}, []jen.Code{ // TODO + jen.Commentf("TODO: Serialization code for %s", t.TypeName()), }, jen.Commentf("%s converts this into an interface representation suitable for marshalling into a text or binary format.", serializeMethodName)) 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) { + serName := fmt.Sprintf("%s%s", serializeMethodName, t.TypeName()) + ser = codegen.NewCommentedFunction( + t.packageName, + serName, + []jen.Code{jen.Id("s").Id(t.TypeName())}, + []jen.Code{jen.Interface(), jen.Error()}, + []jen.Code{ + jen.Return( + jen.Id("s").Dot(serializeMethodName).Call(), + ), + }, + jen.Commentf("%s calls %s on the %s type.", serName, serializeMethodName, t.TypeName())) + deserName := fmt.Sprintf("%s%s", deserializeFnName, t.TypeName()) + deserCode := jen.Empty() + for name := range t.allProperties() { + deserCode = deserCode.Add( + jen.If( + jen.List( + jen.Id("p"), + jen.Err(), + // TODO: Qual + ).Op(":=").Qual("", deserializeFnName).Call(jen.Id("m")), + jen.Err().Op("!=").Nil(), + ).Block( + jen.Return(jen.Nil(), jen.Err()), + ).Else().Block( + jen.Id(codegen.This()).Dot(strings.Title(name)).Op(":=").Op("*").Id("p"), + )) + } + deser = codegen.NewCommentedFunction( + t.packageName, + deserName, + []jen.Code{jen.Id("m").Map(jen.String()).Interface()}, + []jen.Code{jen.Op("*").Id(t.TypeName()), jen.Error()}, + []jen.Code{ + jen.Id(codegen.This()).Op(":=").Op("&").Id(t.TypeName()).Values(), + deserCode, + 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.", deserName, t.TypeName())) + lessName := fmt.Sprintf("%s%s", lessFnName, t.TypeName()) + less = codegen.NewCommentedFunction( + t.packageName, + lessName, + []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", lessName, t.TypeName())) + return +}