activity/tools/exp/nonfuncprop.go

313 行
8.1 KiB
Go

package exp
import (
"fmt"
"github.com/dave/jennifer/jen"
)
type NonFunctionalPropertyGenerator struct {
PropertyGenerator
}
func (p *NonFunctionalPropertyGenerator) Definition() jen.Code {
return p.def().Line().Line().Add(p.funcs()).Line().Line().Add(p.commonFuncs())
}
func (p *NonFunctionalPropertyGenerator) iteratorTypeName() Identifier {
return Identifier{
LowerName: fmt.Sprintf("%sPropertyIterator", p.Name.LowerName),
CamelName: fmt.Sprintf("%sPropertyIterator", p.Name.CamelName),
}
}
func (p *NonFunctionalPropertyGenerator) elementTypeGenerator() *FunctionalPropertyGenerator {
return &FunctionalPropertyGenerator{
PropertyGenerator{
Name: p.iteratorTypeName(),
Kinds: p.Kinds,
asIterator: true,
},
}
}
func (p *NonFunctionalPropertyGenerator) def() *jen.Statement {
comment := jen.Commentf("%s is the non-functional property %q. It is permitted to have one or more values, and of different value types.", p.structName(), p.propertyName()).Line()
return p.elementTypeGenerator().def().Line().Line().Add(
comment.Type().Id(p.structName()).Index().Id(p.iteratorTypeName().CamelName),
)
}
func (p *NonFunctionalPropertyGenerator) funcs() *jen.Statement {
funcs := p.elementTypeGenerator().funcs()
memberFunc := jen.Func().Params(
jen.Id("t").Id(p.structName()),
)
memberPtrFunc := jen.Func().Params(
jen.Id("t").Op("*").Id(p.structName()),
)
prepend := make([]*jen.Statement, 0, len(p.Kinds))
appendFns := make([]*jen.Statement, 0, len(p.Kinds))
less := jen.Empty()
for i, kind := range p.Kinds {
dict := jen.Dict{
jen.Id(p.memberName(i)): jen.Id("v"),
}
if !kind.Nilable {
dict[jen.Id(p.hasMemberName(i))] = jen.True()
}
prependMethodName := fmt.Sprintf("%s%s", prependMethod, p.kindCamelName(i))
prepend = append(prepend, jen.Commentf(
"%s prepends a %s value to the front of a list of the property %q.", prependMethodName, kind.ConcreteKind, p.propertyName(),
).Line().Add(memberPtrFunc.Clone().Id(
prependMethodName,
).Params(
jen.Id("v").Id(kind.ConcreteKind),
).Block(
jen.Op("*").Id("t").Op("=").Append(
jen.Index().Id(p.iteratorTypeName().CamelName).Values(
jen.Values(dict),
),
jen.Op("*").Id("t").Op("..."),
),
)))
appendMethodName := fmt.Sprintf("%s%s", appendMethod, p.kindCamelName(i))
appendFns = append(appendFns, jen.Commentf(
"%s appends a %s value to the back of a list of the property %q", appendMethodName, kind.ConcreteKind, p.propertyName(),
).Line().Add(memberPtrFunc.Clone().Id(
appendMethodName,
).Params(
jen.Id("v").Id(kind.ConcreteKind),
).Block(
jen.Op("*").Id("t").Op("=").Append(
jen.Op("*").Id("t"),
jen.Id(p.iteratorTypeName().CamelName).Values(
dict,
),
),
)))
if i > 0 {
less.Else()
}
less.If(
jen.Id("idx1").Op("==").Lit(i),
).Block(
jen.Id("lhs").Op(":=").Id("t").Index(jen.Id("i")).Dot(p.getFnName(i)).Call(),
jen.Id("rhs").Op(":=").Id("t").Index(jen.Id("j")).Dot(p.getFnName(i)).Call(),
jen.Return(jen.Id(kind.LessFnName).Call(
jen.Id("lhs"),
jen.Id("rhs"),
)),
)
}
remove := jen.Commentf(
"%s deletes an element at the specified index from a list of the property %q, regardless of its type.", removeMethod, p.propertyName(),
).Line().Add(memberPtrFunc.Clone().Id(removeMethod).Params(
jen.Id("idx").Int(),
).Block(
jen.Copy(
jen.Parens(
jen.Op("*").Id("t"),
).Index(
jen.Id("idx"),
jen.Empty(),
),
jen.Parens(
jen.Op("*").Id("t"),
).Index(
jen.Id("idx").Op("+").Lit(1),
jen.Empty(),
),
),
jen.Parens(
jen.Op("*").Id("t"),
).Index(
jen.Len(jen.Op("*").Id("t")).Op("-").Lit(1),
).Op("=").Id(p.iteratorTypeName().CamelName).Values(),
jen.Op("*").Id("t").Op("=").Parens(
jen.Op("*").Id("t"),
).Index(
jen.Empty(),
jen.Len(jen.Op("*").Id("t")).Op("-").Lit(1),
),
))
lenFn := jen.Commentf(
"%s returns the number of values that exist for the %q property.", lenMethod, p.propertyName(),
).Line().Add(memberFunc.Clone().Id(lenMethod).Params().Params(
jen.Id("length").Int(),
).Block(
jen.Return(
jen.Len(
jen.Id("t"),
),
),
))
swapFn := jen.Commentf(
"%s swaps the location of values at two indices for the %q property.", swapMethod, p.propertyName(),
).Line().Add(memberFunc.Clone().Id(swapMethod).Params(
jen.Id("i"),
jen.Id("j").Int(),
).Params().Block(
jen.List(
jen.Id("t").Index(jen.Id("i")),
jen.Id("t").Index(jen.Id("j")),
).Op("=").List(
jen.Id("t").Index(jen.Id("j")),
jen.Id("t").Index(jen.Id("i")),
),
))
lessFn := jen.Commentf(
"%s computes whether another property is less than this one. Mixing types results in a consistent but arbitrary ordering", lessMethod,
).Line().Add(memberFunc.Clone().Id(lessMethod).Params(
jen.Id("i"),
jen.Id("j").Int(),
).Bool().Block(
jen.Id("idx1").Op(":=").Id("t").Dot(kindIndexMethod).Call(jen.Id("i")),
jen.Id("idx2").Op(":=").Id("t").Dot(kindIndexMethod).Call(jen.Id("j")),
jen.If(jen.Id("idx1").Op("<").Id("idx2")).Block(
jen.Return(jen.True()),
).Else().If(jen.Id("idx1").Op("==").Id("idx2")).Block(
less,
),
jen.Return(jen.False()),
))
kindIndexFn := jen.Commentf(
"%s computes an arbitrary value for indexing this kind of value.", kindIndexMethod,
).Line().Add(memberFunc.Clone().Id(kindIndexMethod).Params(jen.Id("idx").Int()).Int().Block(
jen.Return(
jen.Id("t").Index(jen.Id("idx")).Dot(kindIndexMethod).Call(),
),
))
return funcs.Line().Line().Add(
join(appendFns),
).Line().Line().Add(
join(prepend),
).Line().Line().Add(
remove,
).Line().Line().Add(
lenFn,
).Line().Line().Add(
swapFn,
).Line().Line().Add(
lessFn,
).Line().Line().Add(
kindIndexFn,
).Line().Line().Add(
p.serializationFuncs(),
)
}
func (p *NonFunctionalPropertyGenerator) serializationFuncs() *jen.Statement {
serialize := jen.Commentf(
"%s converts this into an interface representation suitable for marshalling into a text or binary format.", p.serializeFnName(),
).Line().Add(jen.Func().Params(
jen.Id("t").Id(p.structName()),
).Id(p.serializeFnName()).Params().Params(
jen.Interface(),
jen.Error(),
).Block(
jen.Id("s").Op(":=").Make(
jen.Index().Interface(),
jen.Lit(0),
jen.Len(jen.Id("t")),
),
jen.For(
jen.List(
jen.Id("_"),
jen.Id("iterator"),
).Op(":=").Range().Id("t"),
).Block(
jen.If(
jen.List(
jen.Id("b"),
jen.Err(),
).Op(":=").Id("iterator").Dot(serializeIteratorMethod).Call(),
jen.Err().Op("!=").Nil(),
).Block(
jen.Return(
jen.Id("s"),
jen.Err(),
),
).Else().Block(
jen.Id("s").Op("=").Append(
jen.Id("s"),
jen.Id("b"),
),
),
),
jen.Return(
jen.Id("s"),
jen.Nil(),
),
))
deserializeFn := func(variable string) jen.Code {
return jen.If(
jen.List(
jen.Id("p"),
jen.Err(),
).Op(":=").Id(p.elementTypeGenerator().deserializeFnName()).Call(
jen.Id(variable),
),
jen.Err().Op("!=").Nil(),
).Block(
jen.Return(
jen.Id("t"),
jen.Err(),
),
).Else().If(
jen.Id("p").Op("!=").Nil(),
).Block(
jen.Id("t").Op("=").Append(
jen.Id("t"),
jen.Op("*").Id("p"),
),
)
}
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().Add(jen.Func().Id(
p.deserializeFnName(),
).Params(
jen.Id("m").Map(jen.String()).Interface(),
).Params(
jen.Id(p.structName()),
jen.Error(),
).Block(
jen.Var().Id("t").Index().Id(p.iteratorTypeName().CamelName),
jen.If(
jen.List(
jen.Id("i"),
jen.Id("ok"),
).Op(":=").Id("m").Index(
jen.Lit(p.propertyName()),
),
jen.Id("ok"),
).Block(
jen.If(
jen.List(
jen.Id("list"),
jen.Id("ok"),
).Op(":=").Id("i").Assert(
jen.Index().Interface(),
),
jen.Id("ok"),
).Block(
jen.For(
jen.List(
jen.Id("_"),
jen.Id("iterator"),
).Op(":=").Range().Id("list"),
).Block(
deserializeFn("iterator"),
),
).Else().Block(
deserializeFn("i"),
),
),
jen.Return(
jen.Id("t"),
jen.Nil(),
),
))
return serialize.Line().Line().Add(deserialize)
}