374 行
12 KiB
Go
374 行
12 KiB
Go
package exp
|
|
|
|
import (
|
|
"fmt"
|
|
"github.com/dave/jennifer/jen"
|
|
)
|
|
|
|
type FunctionalPropertyGenerator struct {
|
|
PropertyGenerator
|
|
}
|
|
|
|
func (p *FunctionalPropertyGenerator) Definition() jen.Code {
|
|
return p.def().Line().Line().Add(p.funcs()).Line().Line().Add(p.commonFuncs()).Line().Line()
|
|
}
|
|
|
|
func (p *FunctionalPropertyGenerator) def() *jen.Statement {
|
|
if len(p.Kinds) == 1 {
|
|
return p.singleTypeDef()
|
|
} else {
|
|
return p.multiTypeDef()
|
|
}
|
|
}
|
|
|
|
func (p *FunctionalPropertyGenerator) funcs() *jen.Statement {
|
|
memberFunc := jen.Func().Params(
|
|
jen.Id("t").Id(p.structName()),
|
|
)
|
|
kindIndexFns := make([]*jen.Statement, 0, len(p.Kinds))
|
|
for i, _ := range p.Kinds {
|
|
if len(p.Kinds) > 1 {
|
|
kindIndexFns = append(kindIndexFns, jen.If(
|
|
jen.Id("t").Dot(p.isMethodName(i)).Call(),
|
|
).Block(
|
|
jen.Return(jen.Lit(i)),
|
|
))
|
|
} else {
|
|
kindIndexFns = append(kindIndexFns, jen.If(
|
|
jen.Id("t").Dot(hasMethod).Call(),
|
|
).Block(
|
|
jen.Return(jen.Lit(i)),
|
|
))
|
|
}
|
|
}
|
|
kindIndexFn := jen.Commentf(
|
|
"%s computes an arbitrary value for indexing this kind of value.", kindIndexMethod,
|
|
).Line().Add(memberFunc.Clone().Id(kindIndexMethod).Params().Int().Block(
|
|
joinSingleLine(kindIndexFns),
|
|
jen.Return(jen.Lit(-1)),
|
|
))
|
|
funcs := jen.Empty()
|
|
funcs.Add(
|
|
p.serializationFuncs(),
|
|
).Line().Line().Add(
|
|
kindIndexFn,
|
|
).Line().Line()
|
|
if len(p.Kinds) == 1 {
|
|
return funcs.Add(p.singleTypeFuncs())
|
|
} else {
|
|
return funcs.Add(p.multiTypeFuncs())
|
|
}
|
|
}
|
|
|
|
func (p *FunctionalPropertyGenerator) serializationFuncs() *jen.Statement {
|
|
serializeFns := jen.Empty()
|
|
for i, kind := range p.Kinds {
|
|
if i > 0 {
|
|
serializeFns = serializeFns.Else()
|
|
}
|
|
if len(p.Kinds) == 1 {
|
|
serializeFns = serializeFns.If(
|
|
jen.Id("t").Dot(hasMethod).Call(),
|
|
)
|
|
} else {
|
|
serializeFns = serializeFns.If(
|
|
jen.Id("t").Dot(fmt.Sprintf("%s%s", isMethod, p.kindCamelName(i))).Call(),
|
|
)
|
|
}
|
|
serializeFns = serializeFns.Block(
|
|
jen.Return(
|
|
jen.Id(kind.SerializeFnName).Call(
|
|
jen.Id("t").Dot(p.getFnName(i)).Call(),
|
|
),
|
|
),
|
|
)
|
|
}
|
|
serialize := jen.Commentf(
|
|
"%s converts this into an interface representation suitable for marshalling into a text or binary format.", p.serializeFnName(),
|
|
).Line().Func().Params(
|
|
jen.Id("t").Id(p.structName()),
|
|
).Id(p.serializeFnName()).Params().Params(
|
|
jen.Interface(),
|
|
jen.Error(),
|
|
).Block(
|
|
serializeFns,
|
|
jen.Return(
|
|
jen.Nil(),
|
|
jen.Nil(),
|
|
),
|
|
)
|
|
deserializeFns := jen.Empty()
|
|
for i, kind := range p.Kinds {
|
|
if i > 0 {
|
|
deserializeFns = deserializeFns.Else()
|
|
}
|
|
values := jen.Dict{
|
|
jen.Id(p.memberName(i)): jen.Id("v"),
|
|
}
|
|
if !kind.Nilable {
|
|
values[jen.Id(p.hasMemberName(i))] = jen.True()
|
|
}
|
|
deserializeFns = deserializeFns.If(
|
|
jen.List(
|
|
jen.Id("v"),
|
|
jen.Id("handled"),
|
|
jen.Err(),
|
|
).Op(":=").Id(kind.DeserializeFnName).Call(
|
|
jen.Id("i"),
|
|
),
|
|
jen.Id("handled"),
|
|
).Block(
|
|
jen.Id("t").Op(":=").Op("&").Id(p.structName()).Values(
|
|
values,
|
|
),
|
|
jen.Return(
|
|
jen.Id("t"),
|
|
jen.Err(),
|
|
),
|
|
)
|
|
}
|
|
var deserialize jen.Code
|
|
if p.asIterator {
|
|
deserialize = jen.Commentf(
|
|
"%s creates an iterator from an element that has been unmarshalled from a text or binary format.", p.deserializeFnName(),
|
|
).Line().Func().Id(
|
|
p.deserializeFnName(),
|
|
).Params(
|
|
jen.Id("i").Interface(),
|
|
).Params(
|
|
jen.Op("*").Id(p.structName()),
|
|
jen.Error(),
|
|
).Block(
|
|
deserializeFns,
|
|
jen.Return(
|
|
jen.Nil(),
|
|
jen.Nil(),
|
|
),
|
|
)
|
|
} else {
|
|
deserialize = 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(),
|
|
).Line().Func().Id(
|
|
p.deserializeFnName(),
|
|
).Params(
|
|
jen.Id("m").Map(jen.String()).Interface(),
|
|
).Params(
|
|
jen.Op("*").Id(p.structName()),
|
|
jen.Error(),
|
|
).Block(
|
|
jen.If(
|
|
jen.List(
|
|
jen.Id("i"),
|
|
jen.Id("ok"),
|
|
).Op(":=").Id("m").Index(
|
|
jen.Lit(p.propertyName()),
|
|
),
|
|
jen.Id("ok"),
|
|
).Block(
|
|
deserializeFns,
|
|
),
|
|
jen.Return(
|
|
jen.Nil(),
|
|
jen.Nil(),
|
|
),
|
|
)
|
|
}
|
|
return serialize.Line().Line().Add(deserialize)
|
|
}
|
|
|
|
func (p *FunctionalPropertyGenerator) singleTypeDef() *jen.Statement {
|
|
if p.Kinds[0].Nilable {
|
|
comment := jen.Commentf("%s is the functional property %q. It is permitted to be a single nilable value type.", p.structName(), p.propertyName()).Line()
|
|
if p.asIterator {
|
|
comment = jen.Commentf("%s is an iterator for a property. It is permitted to be a single nilable value type.", p.structName()).Line()
|
|
}
|
|
return comment.Type().Id(p.structName()).Struct(
|
|
jen.Id(p.memberName(0)).Id(p.Kinds[0].ConcreteKind),
|
|
)
|
|
} else {
|
|
comment := jen.Commentf("%s is the functional property %q. It is permitted to be a single default-valued value type.", p.structName(), p.propertyName()).Line()
|
|
if p.asIterator {
|
|
comment = jen.Commentf("%s is an iterator for a property. It is permitted to be a single default-valued value type.", p.structName()).Line()
|
|
}
|
|
return comment.Type().Id(p.structName()).Struct(
|
|
jen.Id(p.memberName(0)).Id(p.Kinds[0].ConcreteKind),
|
|
jen.Id(p.hasMemberName(0)).Bool(),
|
|
)
|
|
}
|
|
}
|
|
|
|
func (p *FunctionalPropertyGenerator) singleTypeFuncs() *jen.Statement {
|
|
memberFunc := jen.Func().Params(
|
|
jen.Id("t").Id(p.structName()),
|
|
)
|
|
memberPtrFunc := jen.Func().Params(
|
|
jen.Id("t").Op("*").Id(p.structName()),
|
|
)
|
|
has := jen.Commentf("%s returns true if this property is set.", hasMethod).Line()
|
|
get := jen.Commentf("%s returns the value of this property. When %s returns false, %s will return any arbitrary value.", getMethod, hasMethod, getMethod).Line()
|
|
set := jen.Commentf("%s sets the value of this property. Calling %s afterwards will return true.", setMethod, hasMethod).Line()
|
|
clear := jen.Commentf("%s ensures no value of this property is set. Calling %s afterwards will return false.", p.clearMethodName(), hasMethod).Line()
|
|
if p.Kinds[0].Nilable {
|
|
has = has.Add(memberFunc.Clone().Id(hasMethod).Params().Params(
|
|
jen.Bool(),
|
|
).Block(
|
|
jen.Return(jen.Id("t").Dot(p.memberName(0)).Op("!=").Nil()),
|
|
))
|
|
get = get.Add(memberFunc.Clone().Id(p.getFnName(0)).Params().Params(
|
|
jen.Id(p.Kinds[0].ConcreteKind),
|
|
).Block(
|
|
jen.Return(jen.Id("t").Dot(p.memberName(0))),
|
|
))
|
|
set = set.Add(memberPtrFunc.Clone().Id(setMethod).Params(
|
|
jen.Id("v").Id(p.Kinds[0].ConcreteKind),
|
|
).Block(
|
|
jen.Id("t").Dot(p.memberName(0)).Op("=").Id("v"),
|
|
))
|
|
clear = clear.Add(memberPtrFunc.Clone().Id(p.clearMethodName()).Params().Block(
|
|
jen.Id("t").Dot(p.memberName(0)).Op("=").Nil(),
|
|
))
|
|
} else {
|
|
has = has.Add(memberFunc.Clone().Id(hasMethod).Params().Params(
|
|
jen.Bool(),
|
|
).Block(
|
|
jen.Return(jen.Id("t").Dot(p.hasMemberName(0))),
|
|
))
|
|
get = get.Add(memberFunc.Clone().Id(p.getFnName(0)).Params().Params(
|
|
jen.Id(p.Kinds[0].ConcreteKind),
|
|
).Block(
|
|
jen.Return(jen.Id("t").Dot(p.memberName(0))),
|
|
))
|
|
set = set.Add(memberPtrFunc.Clone().Id(setMethod).Params(
|
|
jen.Id("v").Id(p.Kinds[0].ConcreteKind),
|
|
).Block(
|
|
jen.Id("t").Dot(p.memberName(0)).Op("=").Id("v"),
|
|
jen.Id("t").Dot(p.hasMemberName(0)).Op("=").True(),
|
|
))
|
|
clear = clear.Add(memberPtrFunc.Clone().Id(p.clearMethodName()).Params().Block(
|
|
jen.Id("t").Dot(p.hasMemberName(0)).Op("=").False(),
|
|
))
|
|
}
|
|
return has.Line().Line().Add(get).Line().Line().Add(set).Line().Line().Add(clear)
|
|
}
|
|
|
|
func (p *FunctionalPropertyGenerator) multiTypeDef() *jen.Statement {
|
|
kindMembers := make([]*jen.Statement, 0, len(p.Kinds))
|
|
for i, kind := range p.Kinds {
|
|
if kind.Nilable {
|
|
kindMembers = append(kindMembers, jen.Id(p.memberName(i)).Id(p.Kinds[i].ConcreteKind))
|
|
} else {
|
|
kindMembers = append(kindMembers, jen.Id(p.memberName(i)).Id(p.Kinds[i].ConcreteKind).Line().Add(
|
|
jen.Id(p.hasMemberName(i)).Bool(),
|
|
))
|
|
}
|
|
}
|
|
explanation := jen.Commentf(
|
|
"At most, one type of value can be present, or none at all. Setting a value will",
|
|
).Line().Commentf(
|
|
"clear the other types of values so that only one of the 'Is' methods will return",
|
|
).Line().Commentf(
|
|
"true.",
|
|
).Line().Comment("").Line().Commentf(
|
|
"It is possible to clear all values, so that this property is empty.",
|
|
).Line()
|
|
comment := jen.Commentf(
|
|
"%s is the functional property %q. It is permitted to be one of multiple value types.", p.structName(), p.propertyName(),
|
|
).Line().Comment("").Line().Add(explanation)
|
|
if p.asIterator {
|
|
comment = jen.Commentf(
|
|
"%s is an iterator for a property. It is permitted to be one of multiple value types.", p.structName(),
|
|
).Line().Comment("").Line().Add(explanation)
|
|
}
|
|
return comment.Type().Id(p.structName()).Struct(joinSingleLine(kindMembers))
|
|
}
|
|
|
|
func (p *FunctionalPropertyGenerator) multiTypeFuncs() *jen.Statement {
|
|
funcs := jen.Empty()
|
|
memberFunc := jen.Func().Params(
|
|
jen.Id("t").Id(p.structName()),
|
|
)
|
|
memberPtrFunc := jen.Func().Params(
|
|
jen.Id("t").Op("*").Id(p.structName()),
|
|
)
|
|
clearLine := make([]*jen.Statement, len(p.Kinds))
|
|
isLine := make([]*jen.Statement, len(p.Kinds))
|
|
for i, kind := range p.Kinds {
|
|
if kind.Nilable {
|
|
clearLine[i] = jen.Id("t").Dot(p.memberName(i)).Op("=").Nil()
|
|
} else {
|
|
clearLine[i] = jen.Id("t").Dot(p.hasMemberName(i)).Op("=").False()
|
|
}
|
|
isLine[i] = jen.Id("t").Dot(fmt.Sprintf("%s%s", isMethod, p.kindCamelName(i))).Parens(nil)
|
|
if i != len(p.Kinds)-1 {
|
|
isLine[i].Op("||")
|
|
}
|
|
}
|
|
for i, kind := range p.Kinds {
|
|
setMethodName := fmt.Sprintf("%s%s", setMethod, p.kindCamelName(i))
|
|
is := jen.Commentf("%s returns true if this property has a type of value of %q.", p.isMethodName(i), kind.ConcreteKind).Line()
|
|
get := jen.Commentf("%s returns the value of this property. When %s returns false, %s will return an arbitrary value.", p.getFnName(i), p.isMethodName(i), p.getFnName(i)).Line()
|
|
set := jen.Commentf("%s sets the value of this property. Calling %s afterwards returns true.", setMethodName, p.isMethodName(i)).Line()
|
|
if kind.Nilable {
|
|
is = is.Add(memberFunc.Clone().Id(
|
|
p.isMethodName(i),
|
|
).Params().Params(
|
|
jen.Bool(),
|
|
).Block(
|
|
jen.Return(jen.Id("t").Dot(p.memberName(i)).Op("!=").Nil()),
|
|
))
|
|
set = set.Add(memberPtrFunc.Clone().Id(
|
|
setMethodName,
|
|
).Params(
|
|
jen.Id("v").Id(kind.ConcreteKind),
|
|
).Block(
|
|
jen.Id("t").Dot(p.clearMethodName()).Call(),
|
|
jen.Id("t").Dot(p.memberName(i)).Op("=").Id("v"),
|
|
))
|
|
} else {
|
|
is = is.Add(memberFunc.Clone().Id(
|
|
p.isMethodName(i),
|
|
).Params().Params(
|
|
jen.Bool(),
|
|
).Block(
|
|
jen.Return(jen.Id("t").Dot(p.hasMemberName(i))),
|
|
))
|
|
set = set.Add(memberPtrFunc.Clone().Id(
|
|
setMethodName,
|
|
).Params(
|
|
jen.Id("v").Id(kind.ConcreteKind),
|
|
).Block(
|
|
jen.Id("t").Dot(p.clearMethodName()).Call(),
|
|
jen.Id("t").Dot(p.memberName(i)).Op("=").Id("v"),
|
|
jen.Id("t").Dot(p.hasMemberName(i)).Op("=").True(),
|
|
))
|
|
}
|
|
get = get.Add(memberFunc.Clone().Id(
|
|
p.getFnName(i),
|
|
).Params().Params(
|
|
jen.Id(kind.ConcreteKind),
|
|
).Block(
|
|
jen.Return(jen.Id("t").Dot(p.memberName(i))),
|
|
))
|
|
funcs.Add(is).Line().Line()
|
|
funcs.Add(get).Line().Line()
|
|
funcs.Add(set).Line().Line()
|
|
}
|
|
hasAnyMethodName := fmt.Sprintf("%sAny", hasMethod)
|
|
clear := jen.Commentf(
|
|
"%s ensures no value of this property is set. Calling %s or any of the 'Is' methods afterwards will return false.", p.clearMethodName(), hasAnyMethodName,
|
|
).Line().Add(memberPtrFunc.Clone().Id(p.clearMethodName()).Params().Block(
|
|
joinSingleLine(clearLine),
|
|
))
|
|
hasAny := jen.Commentf(
|
|
"%s returns true if any of the different values is set.", hasAnyMethodName,
|
|
).Line().Add(memberFunc.Clone().Id(
|
|
hasAnyMethodName,
|
|
).Params().Params(
|
|
jen.Bool(),
|
|
).Block(
|
|
jen.Return(joinSingleLine(isLine)),
|
|
))
|
|
funcs.Add(clear).Line().Line().Add(hasAny)
|
|
return funcs
|
|
}
|