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.
このコミットが含まれているのは:
Cory Slep 2018-12-17 23:11:55 +01:00
コミット a3c3a7b5fc
5個のファイルの変更225行の追加118行の削除

ファイルの表示

@ -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

ファイルの表示

@ -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

ファイルの表示

@ -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

ファイルの表示

@ -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)
}

ファイルの表示

@ -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
}