2018-10-20 05:44:13 +09:00
package props
2018-09-26 06:21:07 +09:00
import (
"fmt"
2018-12-09 07:05:02 +09:00
"github.com/cjslep/activity/tools/exp/codegen"
2018-09-26 06:21:07 +09:00
"github.com/dave/jennifer/jen"
2018-11-05 03:05:56 +09:00
"sync"
2018-09-26 06:21:07 +09:00
)
2018-10-10 07:32:37 +09:00
// NonFunctionalPropertyGenerator produces Go code for properties that can have
// more than one value. The resulting property is a type that is a list of
// iterators; each iterator is a concrete struct type. The property can be
// sorted and iterated over so individual elements can be inspected.
2018-09-26 06:21:07 +09:00
type NonFunctionalPropertyGenerator struct {
PropertyGenerator
2018-11-05 03:05:56 +09:00
cacheOnce sync . Once
cachedStruct * codegen . Struct
cachedTypedef * codegen . Typedef
}
// NewNonFunctionalPropertyGenerator is a convenience constructor to create
// NonFunctionalPropertyGenerators.
func NewNonFunctionalPropertyGenerator ( pkg string ,
name Identifier ,
kinds [ ] Kind ,
hasNaturalLanguageMap bool ) * NonFunctionalPropertyGenerator {
return & NonFunctionalPropertyGenerator {
PropertyGenerator : PropertyGenerator {
Package : pkg ,
HasNaturalLanguageMap : hasNaturalLanguageMap ,
2018-11-29 05:39:11 +09:00
Name : name ,
Kinds : kinds ,
2018-11-05 03:05:56 +09:00
} ,
}
2018-09-26 06:21:07 +09:00
}
2018-10-10 07:32:37 +09:00
// Definitions produces the Go code definitions, which can generate their Go
2018-11-04 00:56:09 +09:00
// implementations. The struct is the iterator for various values of the
// property, which is defined by the type definition.
2018-10-20 05:44:13 +09:00
func ( p * NonFunctionalPropertyGenerator ) Definitions ( ) ( * codegen . Struct , * codegen . Typedef ) {
2018-11-05 03:05:56 +09:00
p . cacheOnce . Do ( func ( ) {
methods , funcs := p . serializationFuncs ( )
methods = append ( methods , p . funcs ( ) ... )
property := codegen . NewTypedef (
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 ( ) ) ,
p . StructName ( ) ,
jen . Index ( ) . Id ( p . iteratorTypeName ( ) . CamelName ) ,
methods ,
funcs )
iterator := p . elementTypeGenerator ( ) . Definition ( )
p . cachedStruct , p . cachedTypedef = iterator , property
} )
return p . cachedStruct , p . cachedTypedef
2018-09-26 06:21:07 +09:00
}
2018-10-10 07:32:37 +09:00
// iteratorTypeName determines the identifier to use for the iterator type.
2018-09-26 06:21:07 +09:00
func ( p * NonFunctionalPropertyGenerator ) iteratorTypeName ( ) Identifier {
return Identifier {
LowerName : fmt . Sprintf ( "%sPropertyIterator" , p . Name . LowerName ) ,
CamelName : fmt . Sprintf ( "%sPropertyIterator" , p . Name . CamelName ) ,
}
}
2018-10-10 07:32:37 +09:00
// elementTypeGenerator produces a FunctionalPropertyGenerator for the iterator
// type.
2018-09-26 06:21:07 +09:00
func ( p * NonFunctionalPropertyGenerator ) elementTypeGenerator ( ) * FunctionalPropertyGenerator {
return & FunctionalPropertyGenerator {
2018-11-05 03:05:56 +09:00
PropertyGenerator : PropertyGenerator {
2018-11-29 05:39:11 +09:00
Package : p . PropertyGenerator . Package ,
Name : p . iteratorTypeName ( ) ,
Kinds : p . Kinds ,
2018-10-17 05:00:18 +09:00
HasNaturalLanguageMap : p . PropertyGenerator . HasNaturalLanguageMap ,
asIterator : true ,
2018-09-26 06:21:07 +09:00
} ,
}
}
2018-10-10 07:32:37 +09:00
// funcs produces the methods needed for the NonFunctional property.
2018-10-20 05:44:13 +09:00
func ( p * NonFunctionalPropertyGenerator ) funcs ( ) [ ] * codegen . Method {
var methods [ ] * codegen . Method
2018-09-26 06:21:07 +09:00
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 ( )
}
2018-10-09 05:19:10 +09:00
// Prepend Method
2018-09-26 06:21:07 +09:00
prependMethodName := fmt . Sprintf ( "%s%s" , prependMethod , p . kindCamelName ( i ) )
2018-10-09 05:19:10 +09:00
methods = append ( methods ,
2018-10-20 05:44:13 +09:00
codegen . NewCommentedPointerMethod (
2018-12-19 17:44:57 +09:00
p . PackageName ( ) ,
2018-10-09 05:19:10 +09:00
prependMethodName ,
2018-11-04 00:56:09 +09:00
p . StructName ( ) ,
2018-10-09 05:19:10 +09:00
[ ] jen . Code { jen . Id ( "v" ) . Id ( kind . ConcreteKind ) } ,
/*ret=*/ nil ,
[ ] jen . Code {
2018-10-20 05:44:13 +09:00
jen . Op ( "*" ) . Id ( codegen . This ( ) ) . Op ( "=" ) . Append (
2018-10-09 05:19:10 +09:00
jen . Index ( ) . Id ( p . iteratorTypeName ( ) . CamelName ) . Values (
jen . Values ( dict ) ,
) ,
2018-10-20 05:44:13 +09:00
jen . Op ( "*" ) . Id ( codegen . This ( ) ) . Op ( "..." ) ,
2018-10-09 05:19:10 +09:00
) ,
} ,
2018-11-04 00:56:09 +09:00
jen . Commentf ( "%s prepends a %s value to the front of a list of the property %q." , prependMethodName , kind . ConcreteKind , p . PropertyName ( ) ) ) )
2018-10-09 05:19:10 +09:00
// Append Method
2018-09-26 06:21:07 +09:00
appendMethodName := fmt . Sprintf ( "%s%s" , appendMethod , p . kindCamelName ( i ) )
2018-10-09 05:19:10 +09:00
methods = append ( methods ,
2018-10-20 05:44:13 +09:00
codegen . NewCommentedPointerMethod (
2018-12-19 17:44:57 +09:00
p . PackageName ( ) ,
2018-10-09 05:19:10 +09:00
appendMethodName ,
2018-11-04 00:56:09 +09:00
p . StructName ( ) ,
2018-10-09 05:19:10 +09:00
[ ] jen . Code { jen . Id ( "v" ) . Id ( kind . ConcreteKind ) } ,
/*ret=*/ nil ,
[ ] jen . Code {
2018-10-20 05:44:13 +09:00
jen . Op ( "*" ) . Id ( codegen . This ( ) ) . Op ( "=" ) . Append (
jen . Op ( "*" ) . Id ( codegen . This ( ) ) ,
2018-10-09 05:19:10 +09:00
jen . Id ( p . iteratorTypeName ( ) . CamelName ) . Values (
dict ,
) ,
) ,
} ,
2018-11-04 00:56:09 +09:00
jen . Commentf ( "%s appends a %s value to the back of a list of the property %q" , appendMethodName , kind . ConcreteKind , p . PropertyName ( ) ) ) )
2018-10-09 05:19:10 +09:00
// Less logic
2018-09-26 06:21:07 +09:00
if i > 0 {
less . Else ( )
}
less . If (
jen . Id ( "idx1" ) . Op ( "==" ) . Lit ( i ) ,
) . Block (
2018-10-20 05:44:13 +09:00
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 . Call (
2018-09-26 06:21:07 +09:00
jen . Id ( "lhs" ) ,
jen . Id ( "rhs" ) ,
) ) ,
)
}
2018-10-09 05:19:10 +09:00
// Remove Method
methods = append ( methods ,
2018-10-20 05:44:13 +09:00
codegen . NewCommentedPointerMethod (
2018-12-19 17:44:57 +09:00
p . PackageName ( ) ,
2018-10-09 05:19:10 +09:00
removeMethod ,
2018-11-04 00:56:09 +09:00
p . StructName ( ) ,
2018-10-09 05:19:10 +09:00
[ ] jen . Code { jen . Id ( "idx" ) . Int ( ) } ,
/*ret=*/ nil ,
[ ] jen . Code {
jen . Copy (
jen . Parens (
2018-10-20 05:44:13 +09:00
jen . Op ( "*" ) . Id ( codegen . This ( ) ) ,
2018-10-09 05:19:10 +09:00
) . Index (
jen . Id ( "idx" ) ,
jen . Empty ( ) ,
) ,
jen . Parens (
2018-10-20 05:44:13 +09:00
jen . Op ( "*" ) . Id ( codegen . This ( ) ) ,
2018-10-09 05:19:10 +09:00
) . Index (
jen . Id ( "idx" ) . Op ( "+" ) . Lit ( 1 ) ,
jen . Empty ( ) ,
) ,
) ,
jen . Parens (
2018-10-20 05:44:13 +09:00
jen . Op ( "*" ) . Id ( codegen . This ( ) ) ,
2018-10-09 05:19:10 +09:00
) . Index (
2018-10-20 05:44:13 +09:00
jen . Len ( jen . Op ( "*" ) . Id ( codegen . This ( ) ) ) . Op ( "-" ) . Lit ( 1 ) ,
2018-10-09 05:19:10 +09:00
) . Op ( "=" ) . Id ( p . iteratorTypeName ( ) . CamelName ) . Values ( ) ,
2018-10-20 05:44:13 +09:00
jen . Op ( "*" ) . Id ( codegen . This ( ) ) . Op ( "=" ) . Parens (
jen . Op ( "*" ) . Id ( codegen . This ( ) ) ,
2018-10-09 05:19:10 +09:00
) . Index (
jen . Empty ( ) ,
2018-10-20 05:44:13 +09:00
jen . Len ( jen . Op ( "*" ) . Id ( codegen . This ( ) ) ) . Op ( "-" ) . Lit ( 1 ) ,
2018-10-09 05:19:10 +09:00
) ,
} ,
2018-11-04 00:56:09 +09:00
jen . Commentf ( "%s deletes an element at the specified index from a list of the property %q, regardless of its type." , removeMethod , p . PropertyName ( ) ) ) )
2018-10-09 05:19:10 +09:00
// Len Method
methods = append ( methods ,
2018-10-20 05:44:13 +09:00
codegen . NewCommentedValueMethod (
2018-12-19 17:44:57 +09:00
p . PackageName ( ) ,
2018-10-09 05:19:10 +09:00
lenMethod ,
2018-11-04 00:56:09 +09:00
p . StructName ( ) ,
2018-10-09 05:19:10 +09:00
/*params=*/ nil ,
[ ] jen . Code { jen . Id ( "length" ) . Int ( ) } ,
[ ] jen . Code {
jen . Return (
jen . Len (
2018-10-20 05:44:13 +09:00
jen . Id ( codegen . This ( ) ) ,
2018-10-09 05:19:10 +09:00
) ,
) ,
} ,
2018-11-04 00:56:09 +09:00
jen . Commentf ( "%s returns the number of values that exist for the %q property." , lenMethod , p . PropertyName ( ) ) ) )
2018-10-09 05:19:10 +09:00
// Swap Method
methods = append ( methods ,
2018-10-20 05:44:13 +09:00
codegen . NewCommentedValueMethod (
2018-12-19 17:44:57 +09:00
p . PackageName ( ) ,
2018-10-09 05:19:10 +09:00
swapMethod ,
2018-11-04 00:56:09 +09:00
p . StructName ( ) ,
2018-10-09 05:19:10 +09:00
[ ] jen . Code {
jen . Id ( "i" ) ,
jen . Id ( "j" ) . Int ( ) ,
} ,
/*ret=*/ nil ,
[ ] jen . Code {
2018-09-26 06:21:07 +09:00
jen . List (
2018-10-20 05:44:13 +09:00
jen . Id ( codegen . This ( ) ) . Index ( jen . Id ( "i" ) ) ,
jen . Id ( codegen . This ( ) ) . Index ( jen . Id ( "j" ) ) ,
2018-10-09 05:19:10 +09:00
) . Op ( "=" ) . List (
2018-10-20 05:44:13 +09:00
jen . Id ( codegen . This ( ) ) . Index ( jen . Id ( "j" ) ) ,
jen . Id ( codegen . This ( ) ) . Index ( jen . Id ( "i" ) ) ,
2018-10-09 05:19:10 +09:00
) ,
} ,
2018-11-04 00:56:09 +09:00
jen . Commentf ( "%s swaps the location of values at two indices for the %q property." , swapMethod , p . PropertyName ( ) ) ) )
2018-10-09 05:19:10 +09:00
// Less Method
methods = append ( methods ,
2018-10-20 05:44:13 +09:00
codegen . NewCommentedValueMethod (
2018-12-19 17:44:57 +09:00
p . PackageName ( ) ,
2018-10-09 05:19:10 +09:00
lessMethod ,
2018-11-04 00:56:09 +09:00
p . StructName ( ) ,
2018-10-09 05:19:10 +09:00
[ ] jen . Code {
jen . Id ( "i" ) ,
jen . Id ( "j" ) . Int ( ) ,
} ,
[ ] jen . Code { jen . Bool ( ) } ,
[ ] jen . Code {
2018-10-20 05:44:13 +09:00
jen . Id ( "idx1" ) . Op ( ":=" ) . Id ( codegen . This ( ) ) . Dot ( kindIndexMethod ) . Call ( jen . Id ( "i" ) ) ,
jen . Id ( "idx2" ) . Op ( ":=" ) . Id ( codegen . This ( ) ) . Dot ( kindIndexMethod ) . Call ( jen . Id ( "j" ) ) ,
2018-10-09 05:19:10 +09:00
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 ( ) ) ,
} ,
jen . Commentf ( "%s computes whether another property is less than this one. Mixing types results in a consistent but arbitrary ordering" , lessMethod ) ) )
// Kind Method
methods = append ( methods ,
2018-10-20 05:44:13 +09:00
codegen . NewCommentedValueMethod (
2018-12-19 17:44:57 +09:00
p . PackageName ( ) ,
2018-10-09 05:19:10 +09:00
kindIndexMethod ,
2018-11-04 00:56:09 +09:00
p . StructName ( ) ,
2018-10-09 05:19:10 +09:00
[ ] jen . Code { jen . Id ( "idx" ) . Int ( ) } ,
[ ] jen . Code { jen . Int ( ) } ,
[ ] jen . Code {
2018-09-26 06:21:07 +09:00
jen . Return (
2018-10-20 05:44:13 +09:00
jen . Id ( codegen . This ( ) ) . Index ( jen . Id ( "idx" ) ) . Dot ( kindIndexMethod ) . Call ( ) ,
2018-09-26 06:21:07 +09:00
) ,
2018-10-09 05:19:10 +09:00
} ,
jen . Commentf ( "%s computes an arbitrary value for indexing this kind of value." , kindIndexMethod ) ) )
return methods
}
2018-10-10 07:32:37 +09:00
// serializationFuncs produces the Methods and Functions needed for a
// NonFunctional property to be serialized and deserialized to and from an
// encoding.
2018-10-20 05:44:13 +09:00
func ( p * NonFunctionalPropertyGenerator ) serializationFuncs ( ) ( [ ] * codegen . Method , [ ] * codegen . Function ) {
serialize := [ ] * codegen . Method {
codegen . NewCommentedValueMethod (
2018-12-19 17:44:57 +09:00
p . PackageName ( ) ,
2018-10-09 05:19:10 +09:00
p . serializeFnName ( ) ,
2018-11-04 00:56:09 +09:00
p . StructName ( ) ,
2018-10-09 05:19:10 +09:00
/*params=*/ nil ,
[ ] jen . Code { jen . Interface ( ) , jen . Error ( ) } ,
[ ] jen . Code {
jen . Id ( "s" ) . Op ( ":=" ) . Make (
jen . Index ( ) . Interface ( ) ,
jen . Lit ( 0 ) ,
2018-10-20 05:44:13 +09:00
jen . Len ( jen . Id ( codegen . This ( ) ) ) ,
2018-10-09 05:19:10 +09:00
) ,
jen . For (
jen . List (
jen . Id ( "_" ) ,
jen . Id ( "iterator" ) ,
2018-10-20 05:44:13 +09:00
) . Op ( ":=" ) . Range ( ) . Id ( codegen . This ( ) ) ,
2018-10-09 05:19:10 +09:00
) . 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 (
2018-09-26 06:21:07 +09:00
jen . Id ( "s" ) ,
2018-10-09 05:19:10 +09:00
jen . Nil ( ) ,
2018-09-26 06:21:07 +09:00
) ,
2018-10-09 05:19:10 +09:00
} ,
jen . Commentf ( "%s converts this into an interface representation suitable for marshalling into a text or binary format." , p . serializeFnName ( ) ) ,
2018-09-26 06:21:07 +09:00
) ,
2018-10-09 05:19:10 +09:00
}
2018-09-26 06:21:07 +09:00
deserializeFn := func ( variable string ) jen . Code {
return jen . If (
jen . List (
jen . Id ( "p" ) ,
jen . Err ( ) ,
2018-12-18 07:11:55 +09:00
) . Op ( ":=" ) . Id ( p . elementTypeGenerator ( ) . DeserializeFnName ( ) ) . Call (
2018-09-26 06:21:07 +09:00
jen . Id ( variable ) ,
) ,
jen . Err ( ) . Op ( "!=" ) . Nil ( ) ,
) . Block (
jen . Return (
2018-10-20 05:44:13 +09:00
jen . Id ( codegen . This ( ) ) ,
2018-09-26 06:21:07 +09:00
jen . Err ( ) ,
) ,
) . Else ( ) . If (
jen . Id ( "p" ) . Op ( "!=" ) . Nil ( ) ,
) . Block (
2018-10-20 05:44:13 +09:00
jen . Id ( codegen . This ( ) ) . Op ( "=" ) . Append (
jen . Id ( codegen . This ( ) ) ,
2018-09-26 06:21:07 +09:00
jen . Op ( "*" ) . Id ( "p" ) ,
) ,
)
}
2018-10-20 05:44:13 +09:00
deserialize := [ ] * codegen . Function {
codegen . NewCommentedFunction (
2018-12-19 17:44:57 +09:00
p . PackageName ( ) ,
2018-12-18 07:11:55 +09:00
p . DeserializeFnName ( ) ,
2018-10-09 05:19:10 +09:00
[ ] jen . Code { jen . Id ( "m" ) . Map ( jen . String ( ) ) . Interface ( ) } ,
2018-11-04 00:56:09 +09:00
[ ] jen . Code { jen . Id ( p . StructName ( ) ) , jen . Error ( ) } ,
2018-10-09 05:19:10 +09:00
[ ] jen . Code {
2018-10-20 05:44:13 +09:00
jen . Var ( ) . Id ( codegen . This ( ) ) . Index ( ) . Id ( p . iteratorTypeName ( ) . CamelName ) ,
2018-10-09 05:19:10 +09:00
jen . If (
2018-09-26 06:21:07 +09:00
jen . List (
2018-10-09 05:19:10 +09:00
jen . Id ( "i" ) ,
jen . Id ( "ok" ) ,
) . Op ( ":=" ) . Id ( "m" ) . Index (
2018-11-04 00:56:09 +09:00
jen . Lit ( p . PropertyName ( ) ) ,
2018-10-09 05:19:10 +09:00
) ,
jen . Id ( "ok" ) ,
2018-09-26 06:21:07 +09:00
) . Block (
2018-10-09 05:19:10 +09:00
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" ) ,
) ,
2018-09-26 06:21:07 +09:00
) ,
2018-10-09 05:19:10 +09:00
jen . Return (
2018-10-20 05:44:13 +09:00
jen . Id ( codegen . This ( ) ) ,
2018-10-09 05:19:10 +09:00
jen . Nil ( ) ,
) ,
} ,
2018-12-18 07:11:55 +09:00
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 ( ) ) ,
2018-09-26 06:21:07 +09:00
) ,
2018-10-09 05:19:10 +09:00
}
return serialize , deserialize
2018-09-26 06:21:07 +09:00
}