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
// FunctionalPropertyGenerator produces Go code for properties that can have
// only one value. The resulting property is a struct type that can have one
// value that could be from multiple Kinds of values. If there is only one
// allowed Kind, then a smaller API is generated as a special case.
2018-09-26 06:21:07 +09:00
type FunctionalPropertyGenerator struct {
PropertyGenerator
2018-11-05 03:05:56 +09:00
cacheOnce sync . Once
cachedStruct * codegen . Struct
}
// NewFunctionalPropertyGenerator is a convenience constructor to create
// FunctionalPropertyGenerators.
func NewFunctionalPropertyGenerator ( pkg string ,
name Identifier ,
kinds [ ] Kind ,
hasNaturalLanguageMap bool ) * FunctionalPropertyGenerator {
return & FunctionalPropertyGenerator {
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-17 05:00:18 +09:00
// isSingleTypeDef determines whether a special-case API can be generated for
// one allowed Kind.
func ( p * FunctionalPropertyGenerator ) isSingleTypeDef ( ) bool {
return len ( p . Kinds ) == 1
}
2018-10-10 07:32:37 +09:00
// Definition produces the Go Struct code definition, which can generate its Go
// implementations.
2018-10-20 05:44:13 +09:00
func ( p * FunctionalPropertyGenerator ) Definition ( ) * codegen . Struct {
2018-11-05 03:05:56 +09:00
p . cacheOnce . Do ( func ( ) {
if p . isSingleTypeDef ( ) {
p . cachedStruct = p . singleTypeDef ( )
} else {
p . cachedStruct = p . multiTypeDef ( )
}
} )
return p . cachedStruct
2018-09-26 06:21:07 +09:00
}
2018-10-17 05:00:18 +09:00
// clearNonLanguageMapMembers generates the code required to clear all values,
// including unknown values, from this property except for the natural language
// map. If this property can handle a natural language map, then it is up to the
// calling code to determine whether to set the 'langMapMember' to nil.
func ( p * FunctionalPropertyGenerator ) clearNonLanguageMapMembers ( ) [ ] jen . Code {
if p . isSingleTypeDef ( ) {
return p . singleTypeClearNonLanguageMapMembers ( )
} else {
return p . multiTypeClearNonLanguageMapMembers ( )
}
}
// singleTypeClearNonLanguageMapMembers generates code to clear all members for
// the special case single-Kind property.
func ( p * FunctionalPropertyGenerator ) singleTypeClearNonLanguageMapMembers ( ) [ ] jen . Code {
clearCode := [ ] jen . Code {
2018-10-20 05:44:13 +09:00
jen . Id ( codegen . This ( ) ) . Dot ( unknownMemberName ) . Op ( "=" ) . Nil ( ) ,
2018-10-17 05:00:18 +09:00
}
if p . Kinds [ 0 ] . Nilable {
2018-10-20 05:44:13 +09:00
clearCode = append ( clearCode , jen . Id ( codegen . This ( ) ) . Dot ( p . memberName ( 0 ) ) . Op ( "=" ) . Nil ( ) )
2018-10-17 05:00:18 +09:00
} else {
2018-10-20 05:44:13 +09:00
clearCode = append ( clearCode , jen . Id ( codegen . This ( ) ) . Dot ( p . hasMemberName ( 0 ) ) . Op ( "=" ) . False ( ) )
2018-10-17 05:00:18 +09:00
}
return clearCode
}
// multiTypeClearNonLanguageMapMembers generates code to clear all members for
// a property with multiple Kinds.
func ( p * FunctionalPropertyGenerator ) multiTypeClearNonLanguageMapMembers ( ) [ ] jen . Code {
clearLine := make ( [ ] jen . Code , len ( p . Kinds ) + 2 ) // +2 for the unknown, and maybe language map
for i , kind := range p . Kinds {
if kind . Nilable {
2018-10-20 05:44:13 +09:00
clearLine [ i ] = jen . Id ( codegen . This ( ) ) . Dot ( p . memberName ( i ) ) . Op ( "=" ) . Nil ( )
2018-10-17 05:00:18 +09:00
} else {
2018-10-20 05:44:13 +09:00
clearLine [ i ] = jen . Id ( codegen . This ( ) ) . Dot ( p . hasMemberName ( i ) ) . Op ( "=" ) . False ( )
2018-10-17 05:00:18 +09:00
}
}
2018-10-20 05:44:13 +09:00
clearLine = append ( clearLine , jen . Id ( codegen . This ( ) ) . Dot ( unknownMemberName ) . Op ( "=" ) . Nil ( ) )
2018-10-17 05:00:18 +09:00
return clearLine
}
2018-10-10 07:32:37 +09:00
// funcs produces the methods needed for the functional property.
2018-10-20 05:44:13 +09:00
func ( p * FunctionalPropertyGenerator ) funcs ( ) [ ] * codegen . Method {
2018-10-09 05:19:10 +09:00
kindIndexFns := make ( [ ] jen . Code , 0 , len ( p . Kinds ) )
2018-09-26 06:21:07 +09:00
for i , _ := range p . Kinds {
2018-10-17 05:00:18 +09:00
if p . isSingleTypeDef ( ) {
2018-09-26 06:21:07 +09:00
kindIndexFns = append ( kindIndexFns , jen . If (
2018-10-20 05:44:13 +09:00
jen . Id ( codegen . This ( ) ) . Dot ( hasMethod ) . Call ( ) ,
2018-09-26 06:21:07 +09:00
) . Block (
jen . Return ( jen . Lit ( i ) ) ,
) )
} else {
kindIndexFns = append ( kindIndexFns , jen . If (
2018-10-20 05:44:13 +09:00
jen . Id ( codegen . This ( ) ) . Dot ( p . isMethodName ( i ) ) . Call ( ) ,
2018-09-26 06:21:07 +09:00
) . Block (
jen . Return ( jen . Lit ( i ) ) ,
) )
}
}
2018-10-20 05:44:13 +09:00
methods := [ ] * codegen . Method {
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
/*params=*/ nil ,
[ ] jen . Code { jen . Int ( ) } ,
[ ] jen . Code {
2018-10-10 07:32:37 +09:00
join ( kindIndexFns ) ,
2018-10-09 05:19:10 +09:00
jen . Return ( jen . Lit ( - 1 ) ) ,
} ,
jen . Commentf ( "%s computes an arbitrary value for indexing this kind of value." , kindIndexMethod ) ,
) ,
2018-09-26 06:21:07 +09:00
}
2018-10-17 05:00:18 +09:00
if p . HasNaturalLanguageMap {
// IsLanguageMap 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-17 05:00:18 +09:00
isLanguageMapMethod ,
2018-11-04 00:56:09 +09:00
p . StructName ( ) ,
2018-10-17 05:00:18 +09:00
/*params=*/ nil ,
[ ] jen . Code { jen . Bool ( ) } ,
[ ] jen . Code {
2018-10-20 05:44:13 +09:00
jen . Return ( jen . Id ( codegen . This ( ) ) . Dot ( langMapMember ) . Op ( "!=" ) . Nil ( ) ) ,
2018-10-17 05:00:18 +09:00
} ,
jen . Commentf (
"%s determines if this property is represented by a natural language map." ,
isLanguageMapMethod ,
) . Line ( ) . Commentf ( "" ) . Line ( ) . Commentf (
"When true, use %s, %s, and %s methods to access and mutate the natural language map." ,
hasLanguageMethod ,
getLanguageMethod ,
setLanguageMethod ,
) . Line ( ) . Commentf (
"The %s method can be used to clear the natural language map." ,
p . clearMethodName ( ) ,
) . Line ( ) . Commentf ( "" ) . Line ( ) . Commentf (
"Note that this method is only used for natural language representations, and does not determine the presence nor absence of other values for this property." ,
) ) )
// HasLanguage 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-17 05:00:18 +09:00
hasLanguageMethod ,
2018-11-04 00:56:09 +09:00
p . StructName ( ) ,
2018-10-17 05:00:18 +09:00
[ ] jen . Code { jen . Id ( "bcp47" ) . String ( ) } ,
[ ] jen . Code { jen . Bool ( ) } ,
[ ] jen . Code {
jen . If (
2018-10-20 05:44:13 +09:00
jen . Id ( codegen . This ( ) ) . Dot ( langMapMember ) . Op ( "==" ) . Nil ( ) ,
2018-10-17 05:00:18 +09:00
) . Block (
jen . Return ( jen . False ( ) ) ,
) . Else ( ) . Block (
jen . List (
jen . Id ( "_" ) ,
jen . Id ( "ok" ) ,
2018-10-20 05:44:13 +09:00
) . Op ( ":=" ) . Id ( codegen . This ( ) ) . Dot ( langMapMember ) . Index (
2018-10-17 05:00:18 +09:00
jen . Id ( "bcp47" ) ,
) ,
jen . Return ( jen . Id ( "ok" ) ) ,
) ,
} ,
jen . Commentf (
"%s returns true if the natural language map has an entry for the specified BCP47 language code." ,
hasLanguageMethod ,
) ,
) )
// GetLanguage 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-17 05:00:18 +09:00
getLanguageMethod ,
2018-11-04 00:56:09 +09:00
p . StructName ( ) ,
2018-10-17 05:00:18 +09:00
[ ] jen . Code { jen . Id ( "bcp47" ) . String ( ) } ,
[ ] jen . Code { jen . String ( ) } ,
[ ] jen . Code {
jen . If (
2018-10-20 05:44:13 +09:00
jen . Id ( codegen . This ( ) ) . Dot ( langMapMember ) . Op ( "==" ) . Nil ( ) ,
2018-10-17 05:00:18 +09:00
) . Block (
jen . Return ( jen . Lit ( "" ) ) ,
) . Else ( ) . If (
jen . List (
jen . Id ( "v" ) ,
jen . Id ( "ok" ) ,
2018-10-20 05:44:13 +09:00
) . Op ( ":=" ) . Id ( codegen . This ( ) ) . Dot ( langMapMember ) . Index (
2018-10-17 05:00:18 +09:00
jen . Id ( "bcp47" ) ,
) ,
jen . Id ( "ok" ) ,
) . Block (
jen . Return ( jen . Id ( "v" ) ) ,
) . Else ( ) . Block (
jen . Return ( jen . Lit ( "" ) ) ,
) ,
} ,
jen . Commentf (
"%s returns the value for the specified BCP47 language code, or an empty string if it is either not a language map or no value is present." ,
getLanguageMethod ,
) ,
) )
// SetLanguage 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-17 05:00:18 +09:00
setLanguageMethod ,
2018-11-04 00:56:09 +09:00
p . StructName ( ) ,
2018-10-17 05:00:18 +09:00
[ ] jen . Code {
jen . Id ( "bcp47" ) ,
jen . Id ( "value" ) . String ( ) ,
} ,
/*ret=*/ nil ,
append ( p . clearNonLanguageMapMembers ( ) ,
[ ] jen . Code {
jen . If (
2018-10-20 05:44:13 +09:00
jen . Id ( codegen . This ( ) ) . Dot ( langMapMember ) . Op ( "==" ) . Nil ( ) ,
2018-10-17 05:00:18 +09:00
) . Block (
2018-10-20 05:44:13 +09:00
jen . Id ( codegen . This ( ) ) . Dot ( langMapMember ) . Op ( "=" ) . Make (
2018-10-17 05:00:18 +09:00
jen . Map ( jen . String ( ) ) . String ( ) ,
) ,
) ,
2018-10-20 05:44:13 +09:00
jen . Id ( codegen . This ( ) ) . Dot ( langMapMember ) . Index (
2018-10-17 05:00:18 +09:00
jen . Id ( "bcp47" ) ,
) . Op ( "=" ) . Id ( "value" ) ,
} ... ,
) ,
jen . Commentf (
"%s sets the value for the specified BCP47 language code." ,
setLanguageMethod ,
) ,
) )
}
return methods
2018-09-26 06:21:07 +09:00
}
2018-10-10 07:32:37 +09:00
// serializationFuncs produces the Methods and Functions needed for a
// functional property to be serialized and deserialized to and from an
// encoding.
2018-10-20 05:44:13 +09:00
func ( p * FunctionalPropertyGenerator ) serializationFuncs ( ) ( [ ] * codegen . Method , [ ] * codegen . Function ) {
2018-09-26 06:21:07 +09:00
serializeFns := jen . Empty ( )
for i , kind := range p . Kinds {
if i > 0 {
serializeFns = serializeFns . Else ( )
}
2018-10-17 05:00:18 +09:00
if p . isSingleTypeDef ( ) {
2018-09-26 06:21:07 +09:00
serializeFns = serializeFns . If (
2018-10-20 05:44:13 +09:00
jen . Id ( codegen . This ( ) ) . Dot ( hasMethod ) . Call ( ) ,
2018-09-26 06:21:07 +09:00
)
} else {
serializeFns = serializeFns . If (
2018-10-20 05:44:13 +09:00
jen . Id ( codegen . This ( ) ) . Dot ( p . isMethodName ( i ) ) . Call ( ) ,
2018-09-26 06:21:07 +09:00
)
}
serializeFns = serializeFns . Block (
jen . Return (
2018-10-20 05:44:13 +09:00
kind . SerializeFn . Call (
jen . Id ( codegen . This ( ) ) . Dot ( p . getFnName ( i ) ) . Call ( ) ,
2018-09-26 06:21:07 +09:00
) ,
) ,
)
}
2018-10-20 05:44:13 +09:00
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 { serializeFns , jen . Return (
2018-10-20 05:44:13 +09:00
jen . Id ( codegen . This ( ) ) . Dot ( unknownMemberName ) ,
2018-10-09 05:19:10 +09:00
jen . Nil ( ) ,
) } ,
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
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 ( ) ,
2018-10-20 05:44:13 +09:00
) . Op ( ":=" ) . Add ( kind . DeserializeFn . Call (
2018-09-26 06:21:07 +09:00
jen . Id ( "i" ) ,
2018-10-20 05:44:13 +09:00
) ) ,
2018-09-26 06:21:07 +09:00
jen . Id ( "handled" ) ,
) . Block (
2018-11-04 00:56:09 +09:00
jen . Id ( codegen . This ( ) ) . Op ( ":=" ) . Op ( "&" ) . Id ( p . StructName ( ) ) . Values (
2018-09-26 06:21:07 +09:00
values ,
) ,
jen . Return (
2018-10-20 05:44:13 +09:00
jen . Id ( codegen . This ( ) ) ,
2018-09-26 06:21:07 +09:00
jen . Err ( ) ,
) ,
)
}
2018-10-20 05:44:13 +09:00
var deserialize [ ] * codegen . Function
2018-09-26 06:21:07 +09:00
if p . asIterator {
2018-10-09 05:19:10 +09:00
deserialize = append ( deserialize ,
2018-10-20 05:44:13 +09:00
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 ( "i" ) . Interface ( ) } ,
2018-11-04 00:56:09 +09:00
[ ] jen . Code { jen . Op ( "*" ) . Id ( p . StructName ( ) ) , jen . Error ( ) } ,
2018-10-09 05:19:10 +09:00
[ ] jen . Code {
2018-12-10 05:23:32 +09:00
p . addUnknownDeserializeCode ( deserializeFns ) ,
2018-10-09 05:19:10 +09:00
jen . Return (
jen . Nil ( ) ,
jen . Nil ( ) ,
) ,
} ,
2018-12-18 07:11:55 +09:00
jen . Commentf ( "%s creates an iterator from an element that has been unmarshalled from a text or binary format." , p . DeserializeFnName ( ) ) ,
2018-10-09 05:19:10 +09:00
) )
2018-09-26 06:21:07 +09:00
} else {
2018-10-09 05:19:10 +09:00
deserialize = append ( deserialize ,
2018-10-20 05:44:13 +09:00
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 . Op ( "*" ) . Id ( p . StructName ( ) ) , jen . Error ( ) } ,
2018-10-09 05:19:10 +09:00
[ ] jen . Code {
jen . If (
jen . List (
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" ) ,
) . Block (
2018-12-10 05:23:32 +09:00
p . addUnknownDeserializeCode ( deserializeFns ) ,
2018-10-09 05:19:10 +09:00
) ,
jen . Return (
jen . Nil ( ) ,
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-10-09 05:19:10 +09:00
) )
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
}
2018-10-10 07:32:37 +09:00
// singleTypeDef generates a special-case simplified API for a functional
// property that can only be a single Kind of value.
2018-10-20 05:44:13 +09:00
func ( p * FunctionalPropertyGenerator ) singleTypeDef ( ) * codegen . Struct {
2018-10-09 05:19:10 +09:00
var comment jen . Code
var kindMembers [ ] jen . Code
2018-09-26 06:21:07 +09:00
if p . Kinds [ 0 ] . Nilable {
2018-11-04 00:56:09 +09:00
comment = jen . Commentf ( "%s is the functional property %q. It is permitted to be a single nilable value type." , p . StructName ( ) , p . PropertyName ( ) )
2018-09-26 06:21:07 +09:00
if p . asIterator {
2018-11-04 00:56:09 +09:00
comment = jen . Commentf ( "%s is an iterator for a property. It is permitted to be a single nilable value type." , p . StructName ( ) )
2018-09-26 06:21:07 +09:00
}
2018-10-11 19:15:55 +09:00
kindMembers = [ ] jen . Code {
jen . Id ( p . memberName ( 0 ) ) . Id ( p . Kinds [ 0 ] . ConcreteKind ) ,
}
2018-09-26 06:21:07 +09:00
} else {
2018-11-04 00:56:09 +09:00
comment = jen . Commentf ( "%s is the functional property %q. It is permitted to be a single default-valued value type." , p . StructName ( ) , p . PropertyName ( ) )
2018-09-26 06:21:07 +09:00
if p . asIterator {
2018-11-04 00:56:09 +09:00
comment = jen . Commentf ( "%s is an iterator for a property. It is permitted to be a single default-valued value type." , p . StructName ( ) )
2018-09-26 06:21:07 +09:00
}
2018-10-09 05:19:10 +09:00
kindMembers = [ ] jen . Code {
2018-09-26 06:21:07 +09:00
jen . Id ( p . memberName ( 0 ) ) . Id ( p . Kinds [ 0 ] . ConcreteKind ) ,
jen . Id ( p . hasMemberName ( 0 ) ) . Bool ( ) ,
2018-10-09 05:19:10 +09:00
}
2018-09-26 06:21:07 +09:00
}
2018-10-17 05:00:18 +09:00
kindMembers = append ( kindMembers , p . unknownMemberDef ( ) )
if p . HasNaturalLanguageMap {
kindMembers = append ( kindMembers , jen . Id ( langMapMember ) . Map ( jen . String ( ) ) . String ( ) )
}
2018-10-09 05:19:10 +09:00
methods , funcs := p . serializationFuncs ( )
methods = append ( methods , p . singleTypeFuncs ( ) ... )
methods = append ( methods , p . funcs ( ) ... )
methods = append ( methods , p . commonMethods ( ) ... )
2018-10-20 05:44:13 +09:00
return codegen . NewStruct ( comment ,
2018-11-04 00:56:09 +09:00
p . StructName ( ) ,
2018-10-09 05:19:10 +09:00
methods ,
funcs ,
kindMembers )
2018-09-26 06:21:07 +09:00
}
2018-10-10 07:32:37 +09:00
// singleTypeFuncs generates the special-case simplified methods for a
// functional property with exactly one Kind of value.
2018-10-20 05:44:13 +09:00
func ( p * FunctionalPropertyGenerator ) singleTypeFuncs ( ) [ ] * codegen . Method {
var methods [ ] * codegen . Method
2018-10-09 05:19:10 +09:00
// Has Method
hasComment := jen . Commentf ( "%s returns true if this property is set." , hasMethod )
2018-10-17 05:00:18 +09:00
if p . HasNaturalLanguageMap {
hasComment = jen . Commentf (
"%s returns true if this property is set and is not a natural language map." ,
hasMethod ,
) . Line ( ) . Commentf ( "" ) . Line ( ) . Commentf (
"When true, the %s and %s methods may be used to access and set this property." ,
getMethod ,
p . setFnName ( 0 ) ,
) . Line ( ) . Commentf ( "" ) . Line ( ) . Commentf (
"To determine if the property was set as a natural language map, use the %s method instead." ,
isLanguageMapMethod ,
)
}
2018-09-26 06:21:07 +09:00
if p . Kinds [ 0 ] . Nilable {
2018-10-20 05:44:13 +09:00
methods = append ( methods , codegen . NewCommentedValueMethod (
2018-12-19 17:44:57 +09:00
p . PackageName ( ) ,
2018-10-09 05:19:10 +09:00
hasMethod ,
2018-11-04 00:56:09 +09:00
p . StructName ( ) ,
2018-10-09 05:19:10 +09:00
/*params=*/ nil ,
[ ] jen . Code { jen . Bool ( ) } ,
2018-10-20 05:44:13 +09:00
[ ] jen . Code { jen . Return ( jen . Id ( codegen . This ( ) ) . Dot ( p . memberName ( 0 ) ) . Op ( "!=" ) . Nil ( ) ) } ,
2018-10-09 05:19:10 +09:00
hasComment ,
2018-09-26 06:21:07 +09:00
) )
2018-10-09 05:19:10 +09:00
} else {
2018-10-20 05:44:13 +09:00
methods = append ( methods , codegen . NewCommentedValueMethod (
2018-12-19 17:44:57 +09:00
p . PackageName ( ) ,
2018-10-09 05:19:10 +09:00
hasMethod ,
2018-11-04 00:56:09 +09:00
p . StructName ( ) ,
2018-10-09 05:19:10 +09:00
/*params=*/ nil ,
[ ] jen . Code { jen . Bool ( ) } ,
2018-10-20 05:44:13 +09:00
[ ] jen . Code { jen . Return ( jen . Id ( codegen . This ( ) ) . Dot ( p . hasMemberName ( 0 ) ) ) } ,
2018-10-09 05:19:10 +09:00
hasComment ,
2018-09-26 06:21:07 +09:00
) )
2018-10-09 05:19:10 +09:00
}
// Get Method
getComment := jen . Commentf ( "%s returns the value of this property. When %s returns false, %s will return any arbitrary value." , getMethod , hasMethod , getMethod )
2018-10-20 05:44:13 +09:00
methods = append ( methods , codegen . NewCommentedValueMethod (
2018-12-19 17:44:57 +09:00
p . PackageName ( ) ,
2018-10-09 05:19:10 +09:00
p . getFnName ( 0 ) ,
2018-11-04 00:56:09 +09:00
p . StructName ( ) ,
2018-10-09 05:19:10 +09:00
/*params=*/ nil ,
[ ] jen . Code { jen . Id ( p . Kinds [ 0 ] . ConcreteKind ) } ,
2018-10-20 05:44:13 +09:00
[ ] jen . Code { jen . Return ( jen . Id ( codegen . This ( ) ) . Dot ( p . memberName ( 0 ) ) ) } ,
2018-10-09 05:19:10 +09:00
getComment ,
) )
// Set Method
2018-10-17 05:00:18 +09:00
setComment := jen . Commentf ( "%s sets the value of this property. Calling %s afterwards will return true." , p . setFnName ( 0 ) , hasMethod )
if p . HasNaturalLanguageMap {
setComment = jen . Commentf (
"%s sets the value of this property and clears the natural language map." ,
p . setFnName ( 0 ) ,
) . Line ( ) . Commentf ( "" ) . Line ( ) . Commentf (
"Calling %s afterwards will return true. Calling %s afterwards returns false." ,
hasMethod ,
isLanguageMapMethod ,
)
}
2018-10-09 05:19:10 +09:00
if p . Kinds [ 0 ] . Nilable {
2018-10-20 05:44:13 +09:00
methods = append ( methods , codegen . NewCommentedPointerMethod (
2018-12-19 17:44:57 +09:00
p . PackageName ( ) ,
2018-10-17 05:00:18 +09:00
p . setFnName ( 0 ) ,
2018-11-04 00:56:09 +09:00
p . StructName ( ) ,
2018-10-09 05:19:10 +09:00
[ ] jen . Code { jen . Id ( "v" ) . Id ( p . Kinds [ 0 ] . ConcreteKind ) } ,
/*ret=*/ nil ,
2018-10-17 05:00:18 +09:00
[ ] jen . Code {
2018-10-20 05:44:13 +09:00
jen . Id ( codegen . This ( ) ) . Dot ( p . clearMethodName ( ) ) . Call ( ) ,
jen . Id ( codegen . This ( ) ) . Dot ( p . memberName ( 0 ) ) . Op ( "=" ) . Id ( "v" ) ,
2018-10-17 05:00:18 +09:00
} ,
2018-10-09 05:19:10 +09:00
setComment ,
2018-09-26 06:21:07 +09:00
) )
} else {
2018-10-20 05:44:13 +09:00
methods = append ( methods , codegen . NewCommentedPointerMethod (
2018-12-19 17:44:57 +09:00
p . PackageName ( ) ,
2018-10-17 05:00:18 +09:00
p . setFnName ( 0 ) ,
2018-11-04 00:56:09 +09:00
p . StructName ( ) ,
2018-10-09 05:19:10 +09:00
[ ] jen . Code { jen . Id ( "v" ) . Id ( p . Kinds [ 0 ] . ConcreteKind ) } ,
/*ret=*/ nil ,
[ ] jen . Code {
2018-10-20 05:44:13 +09:00
jen . Id ( codegen . This ( ) ) . Dot ( p . clearMethodName ( ) ) . Call ( ) ,
jen . Id ( codegen . This ( ) ) . Dot ( p . memberName ( 0 ) ) . Op ( "=" ) . Id ( "v" ) ,
jen . Id ( codegen . This ( ) ) . Dot ( p . hasMemberName ( 0 ) ) . Op ( "=" ) . True ( ) ,
2018-10-09 05:19:10 +09:00
} ,
setComment ,
2018-09-26 06:21:07 +09:00
) )
2018-10-09 05:19:10 +09:00
}
// Clear Method
2018-10-17 05:00:18 +09:00
clearComment := jen . Commentf ( "%s ensures no value of this property is set. Calling %s afterwards will return false." , p . clearMethodName ( ) , hasMethod )
clearCode := p . singleTypeClearNonLanguageMapMembers ( )
if p . HasNaturalLanguageMap {
clearComment = jen . Commentf (
"%s ensures no value and no language map for this property is set." ,
p . clearMethodName ( ) ,
) . Line ( ) . Commentf ( "" ) . Line ( ) . Commentf (
"Calling %s or %s afterwards will return false." ,
hasMethod ,
isLanguageMapMethod ,
)
2018-10-20 05:44:13 +09:00
clearCode = append ( clearCode , jen . Id ( codegen . This ( ) ) . Dot ( langMapMember ) . Op ( "=" ) . Nil ( ) )
2018-10-17 05:00:18 +09:00
}
2018-10-09 05:19:10 +09:00
if p . Kinds [ 0 ] . Nilable {
2018-10-20 05:44:13 +09:00
methods = append ( methods , codegen . NewCommentedPointerMethod (
2018-12-19 17:44:57 +09:00
p . PackageName ( ) ,
2018-10-09 05:19:10 +09:00
p . clearMethodName ( ) ,
2018-11-04 00:56:09 +09:00
p . StructName ( ) ,
2018-10-09 05:19:10 +09:00
/*params=*/ nil ,
/*ret=*/ nil ,
2018-10-17 05:00:18 +09:00
clearCode ,
2018-10-09 05:19:10 +09:00
clearComment ,
2018-09-26 06:21:07 +09:00
) )
2018-10-09 05:19:10 +09:00
} else {
2018-10-20 05:44:13 +09:00
methods = append ( methods , codegen . NewCommentedPointerMethod (
2018-12-19 17:44:57 +09:00
p . PackageName ( ) ,
2018-10-09 05:19:10 +09:00
p . clearMethodName ( ) ,
2018-11-04 00:56:09 +09:00
p . StructName ( ) ,
2018-10-09 05:19:10 +09:00
/*params=*/ nil ,
/*ret=*/ nil ,
2018-10-17 05:00:18 +09:00
clearCode ,
2018-10-09 05:19:10 +09:00
clearComment ,
2018-09-26 06:21:07 +09:00
) )
}
2018-10-09 05:19:10 +09:00
return methods
2018-09-26 06:21:07 +09:00
}
2018-10-10 07:32:37 +09:00
// multiTypeDef generates an API for a functional property that can be multiple
// Kinds of value.
2018-10-20 05:44:13 +09:00
func ( p * FunctionalPropertyGenerator ) multiTypeDef ( ) * codegen . Struct {
2018-10-09 05:19:10 +09:00
kindMembers := make ( [ ] jen . Code , 0 , len ( p . Kinds ) )
2018-09-26 06:21:07 +09:00
for i , kind := range p . Kinds {
if kind . Nilable {
kindMembers = append ( kindMembers , jen . Id ( p . memberName ( i ) ) . Id ( p . Kinds [ i ] . ConcreteKind ) )
} else {
2018-10-09 05:19:10 +09:00
kindMembers = append ( kindMembers , jen . Id ( p . memberName ( i ) ) . Id ( p . Kinds [ i ] . ConcreteKind ) )
kindMembers = append ( kindMembers , jen . Id ( p . hasMemberName ( i ) ) . Bool ( ) )
2018-09-26 06:21:07 +09:00
}
}
2018-10-11 19:15:55 +09:00
kindMembers = append ( kindMembers , p . unknownMemberDef ( ) )
2018-10-17 05:00:18 +09:00
if p . HasNaturalLanguageMap {
kindMembers = append ( kindMembers , jen . Id ( langMapMember ) . Map ( jen . String ( ) ) . String ( ) )
}
2018-09-26 06:21:07 +09:00
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." ,
2018-10-11 18:38:46 +09:00
)
2018-09-26 06:21:07 +09:00
comment := jen . Commentf (
2018-11-04 00:56:09 +09:00
"%s is the functional property %q. It is permitted to be one of multiple value types." , p . StructName ( ) , p . PropertyName ( ) ,
2018-09-26 06:21:07 +09:00
) . Line ( ) . Comment ( "" ) . Line ( ) . Add ( explanation )
if p . asIterator {
comment = jen . Commentf (
2018-11-04 00:56:09 +09:00
"%s is an iterator for a property. It is permitted to be one of multiple value types." , p . StructName ( ) ,
2018-09-26 06:21:07 +09:00
) . Line ( ) . Comment ( "" ) . Line ( ) . Add ( explanation )
}
2018-10-09 05:19:10 +09:00
methods , funcs := p . serializationFuncs ( )
methods = append ( methods , p . multiTypeFuncs ( ) ... )
methods = append ( methods , p . funcs ( ) ... )
methods = append ( methods , p . commonMethods ( ) ... )
2018-10-20 05:44:13 +09:00
return codegen . NewStruct ( comment ,
2018-11-04 00:56:09 +09:00
p . StructName ( ) ,
2018-10-09 05:19:10 +09:00
methods ,
funcs ,
kindMembers )
2018-09-26 06:21:07 +09:00
}
2018-10-10 07:32:37 +09:00
// multiTypeFuncs generates the methods for a functional property with more than
// one Kind of value.
2018-10-20 05:44:13 +09:00
func ( p * FunctionalPropertyGenerator ) multiTypeFuncs ( ) [ ] * codegen . Method {
var methods [ ] * codegen . Method
2018-10-09 05:19:10 +09:00
// HasAny Method
hasAnyMethodName := fmt . Sprintf ( "%sAny" , hasMethod )
isLine := make ( [ ] jen . Code , len ( p . Kinds ) )
for i := range p . Kinds {
or := jen . Empty ( )
if i != len ( p . Kinds ) - 1 {
or = jen . Op ( "||" )
}
2018-10-20 05:44:13 +09:00
isLine [ i ] = jen . Id ( codegen . This ( ) ) . Dot ( p . isMethodName ( i ) ) . Parens ( nil ) . Add ( or )
2018-10-09 05:19:10 +09:00
}
2018-10-17 05:00:18 +09:00
hasAnyComment := jen . Commentf (
"%s returns true if any of the different values is set." , hasAnyMethodName ,
)
if p . HasNaturalLanguageMap {
hasAnyComment = jen . Commentf (
"%s returns true if any of the values are set, except for the natural language map" ,
hasAnyMethodName ,
) . Line ( ) . Commentf ( "" ) . Line ( ) . Commentf (
"When true, the specific has, getter, and setter methods may be used to determine what kind of value there is to access and set this property." ,
) . Line ( ) . Commentf ( "" ) . Line ( ) . Commentf (
"To determine if the property was set as a natural language map, use the %s method instead." ,
isLanguageMapMethod ,
)
}
2018-10-20 05:44:13 +09:00
methods = append ( methods , codegen . NewCommentedPointerMethod (
2018-12-19 17:44:57 +09:00
p . PackageName ( ) ,
2018-10-09 05:19:10 +09:00
hasAnyMethodName ,
2018-11-04 00:56:09 +09:00
p . StructName ( ) ,
2018-10-09 05:19:10 +09:00
/*params=*/ nil ,
[ ] jen . Code { jen . Bool ( ) } ,
2018-10-10 07:32:37 +09:00
[ ] jen . Code { jen . Return ( join ( isLine ) ) } ,
2018-10-17 05:00:18 +09:00
hasAnyComment ,
2018-10-09 05:19:10 +09:00
) )
// Clear Method
2018-10-17 05:00:18 +09:00
clearComment := 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 ,
)
clearLine := p . multiTypeClearNonLanguageMapMembers ( )
if p . HasNaturalLanguageMap {
clearComment = jen . Commentf (
"%s ensures no value and no language map for this property is set." ,
p . clearMethodName ( ) ,
) . Line ( ) . Commentf ( "" ) . Line ( ) . Commentf (
"Calling %s or any of the 'Is' methods afterwards will return false." ,
hasAnyMethodName ,
)
2018-10-20 05:44:13 +09:00
clearLine = append ( clearLine , jen . Id ( codegen . This ( ) ) . Dot ( langMapMember ) . Op ( "=" ) . Nil ( ) )
2018-09-26 06:21:07 +09:00
}
2018-10-20 05:44:13 +09:00
methods = append ( methods , codegen . NewCommentedPointerMethod (
2018-12-19 17:44:57 +09:00
p . PackageName ( ) ,
2018-10-09 05:19:10 +09:00
p . clearMethodName ( ) ,
2018-11-04 00:56:09 +09:00
p . StructName ( ) ,
2018-10-09 05:19:10 +09:00
/*params=*/ nil ,
/*ret=*/ nil ,
clearLine ,
2018-10-17 05:00:18 +09:00
clearComment ,
2018-10-09 05:19:10 +09:00
) )
// Is Method
2018-09-26 06:21:07 +09:00
for i , kind := range p . Kinds {
2018-10-17 05:00:18 +09:00
isComment := jen . Commentf ( "%s returns true if this property has a type of value of %q." , p . isMethodName ( i ) , kind . ConcreteKind ) . Line ( ) . Commentf ( "" ) . Line ( ) . Commentf (
"When true, use the %s and %s methods to access and set this property." ,
p . getFnName ( i ) ,
p . setFnName ( i ) ,
)
if p . HasNaturalLanguageMap {
isComment = isComment . Line ( ) . Commentf ( "" ) . Line ( ) . Commentf (
"To determine if the property was set as a natural language map, use the %s method instead." ,
isLanguageMapMethod ,
)
}
2018-09-26 06:21:07 +09:00
if kind . Nilable {
2018-10-20 05:44:13 +09:00
methods = append ( methods , codegen . NewCommentedValueMethod (
2018-12-19 17:44:57 +09:00
p . PackageName ( ) ,
2018-09-26 06:21:07 +09:00
p . isMethodName ( i ) ,
2018-11-04 00:56:09 +09:00
p . StructName ( ) ,
2018-10-09 05:19:10 +09:00
/*params=*/ nil ,
[ ] jen . Code { jen . Bool ( ) } ,
2018-10-20 05:44:13 +09:00
[ ] jen . Code { jen . Return ( jen . Id ( codegen . This ( ) ) . Dot ( p . memberName ( i ) ) . Op ( "!=" ) . Nil ( ) ) } ,
2018-10-09 05:19:10 +09:00
isComment ,
2018-09-26 06:21:07 +09:00
) )
} else {
2018-10-20 05:44:13 +09:00
methods = append ( methods , codegen . NewCommentedValueMethod (
2018-12-19 17:44:57 +09:00
p . PackageName ( ) ,
2018-09-26 06:21:07 +09:00
p . isMethodName ( i ) ,
2018-11-04 00:56:09 +09:00
p . StructName ( ) ,
2018-10-09 05:19:10 +09:00
/*params=*/ nil ,
[ ] jen . Code { jen . Bool ( ) } ,
2018-10-20 05:44:13 +09:00
[ ] jen . Code { jen . Return ( jen . Id ( codegen . This ( ) ) . Dot ( p . hasMemberName ( i ) ) ) } ,
2018-10-09 05:19:10 +09:00
isComment ,
2018-09-26 06:21:07 +09:00
) )
2018-10-09 05:19:10 +09:00
}
}
// Set Method
for i , kind := range p . Kinds {
2018-10-17 05:00:18 +09:00
setComment := jen . Commentf ( "%s sets the value of this property. Calling %s afterwards returns true." , p . setFnName ( i ) , p . isMethodName ( i ) )
if p . HasNaturalLanguageMap {
setComment = jen . Commentf (
"%s sets the value of this property and clears the natural language map." ,
p . setFnName ( i ) ,
) . Line ( ) . Commentf ( "" ) . Line ( ) . Commentf (
"Calling %s afterwards will return true. Calling %s afterwards returns false." ,
p . isMethodName ( i ) ,
isLanguageMapMethod ,
)
}
2018-10-09 05:19:10 +09:00
if kind . Nilable {
2018-10-20 05:44:13 +09:00
methods = append ( methods , codegen . NewCommentedPointerMethod (
2018-12-19 17:44:57 +09:00
p . PackageName ( ) ,
2018-10-17 05:00:18 +09:00
p . setFnName ( i ) ,
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 . Id ( codegen . This ( ) ) . Dot ( p . clearMethodName ( ) ) . Call ( ) ,
jen . Id ( codegen . This ( ) ) . Dot ( p . memberName ( i ) ) . Op ( "=" ) . Id ( "v" ) ,
2018-10-09 05:19:10 +09:00
} ,
setComment ,
) )
} else {
2018-10-20 05:44:13 +09:00
methods = append ( methods , codegen . NewCommentedPointerMethod (
2018-12-19 17:44:57 +09:00
p . PackageName ( ) ,
2018-10-17 05:00:18 +09:00
p . setFnName ( i ) ,
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 . Id ( codegen . This ( ) ) . Dot ( p . clearMethodName ( ) ) . Call ( ) ,
jen . Id ( codegen . This ( ) ) . Dot ( p . memberName ( i ) ) . Op ( "=" ) . Id ( "v" ) ,
jen . Id ( codegen . This ( ) ) . Dot ( p . hasMemberName ( i ) ) . Op ( "=" ) . True ( ) ,
2018-10-09 05:19:10 +09:00
} ,
setComment ,
2018-09-26 06:21:07 +09:00
) )
}
2018-10-09 05:19:10 +09:00
}
// Get Method
for i , kind := range p . Kinds {
getComment := 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 ) )
2018-10-20 05:44:13 +09:00
methods = append ( methods , codegen . NewCommentedValueMethod (
2018-12-19 17:44:57 +09:00
p . PackageName ( ) ,
2018-09-26 06:21:07 +09:00
p . getFnName ( i ) ,
2018-11-04 00:56:09 +09:00
p . StructName ( ) ,
2018-10-09 05:19:10 +09:00
/*params=*/ nil ,
[ ] jen . Code { jen . Id ( kind . ConcreteKind ) } ,
2018-10-20 05:44:13 +09:00
[ ] jen . Code { jen . Return ( jen . Id ( codegen . This ( ) ) . Dot ( p . memberName ( i ) ) ) } ,
2018-10-09 05:19:10 +09:00
getComment ,
2018-09-26 06:21:07 +09:00
) )
}
2018-10-09 05:19:10 +09:00
return methods
2018-09-26 06:21:07 +09:00
}
2018-10-11 19:15:55 +09:00
// unknownMemberDef returns the definition of a struct member that handles
// a property whose type is unknown.
func ( p * FunctionalPropertyGenerator ) unknownMemberDef ( ) jen . Code {
return jen . Id ( unknownMemberName ) . Index ( ) . Byte ( )
}
2018-12-10 05:23:32 +09:00
// addUnknownDeserializeCode generates the "else if it's a []byte" code used for
2018-10-11 19:15:55 +09:00
// deserializing unknown values.
2018-12-10 05:23:32 +09:00
func ( p * FunctionalPropertyGenerator ) addUnknownDeserializeCode ( existing jen . Code ) jen . Code {
if len ( p . Kinds ) > 0 {
existing = jen . Add ( existing , jen . Else ( ) )
}
return jen . Add ( existing ,
jen . If (
jen . List (
jen . Id ( "v" ) ,
jen . Id ( "ok" ) ,
) . Op ( ":=" ) . Id ( "i" ) . Assert (
jen . Index ( ) . Byte ( ) ,
) ,
2018-10-11 19:15:55 +09:00
jen . Id ( "ok" ) ,
2018-12-10 05:23:32 +09:00
) . Block (
jen . Id ( codegen . This ( ) ) . Op ( ":=" ) . Op ( "&" ) . Id ( p . StructName ( ) ) . Values (
jen . Dict {
jen . Id ( unknownMemberName ) : jen . Id ( "v" ) ,
} ,
) ,
jen . Return (
jen . Id ( codegen . This ( ) ) ,
jen . Err ( ) ,
) ,
2018-10-11 19:15:55 +09:00
) ,
)
}