2019-01-12 23:28:21 +09:00
package gen
2018-12-31 00:09:14 +09:00
import (
"fmt"
"github.com/cjslep/activity/tools/exp/codegen"
"github.com/dave/jennifer/jen"
"sync"
)
const (
2019-01-12 20:52:48 +09:00
managerName = "Manager"
2018-12-31 00:09:14 +09:00
managerInitVarName = "mgr"
)
2019-01-06 00:22:37 +09:00
// managerInitName returns the package variable name for the manager.
2018-12-31 00:09:14 +09:00
func managerInitName ( ) string {
return managerInitVarName
}
2019-01-06 00:22:37 +09:00
// Generates the ActivityStreamManager that handles the creation of
// ActivityStream Core, Extended, and any extension types.
//
// This is implicitly used by Application code, but Application code usually
// won't need to manually use this Manager.
2018-12-31 00:09:14 +09:00
//
// This also provides interfaces to break the recursive/cyclic dependencies
// between properties and types. The previous version of this tool did not
// attempt to solve this problem, and instead just created one big and bloated
// library in order to avoid having to break the dependence. This version of
// the tool instead will generate interfaces for all of the required types.
//
// This means that developers will only ever need to interact with these
// interfaces, and could switch out using this implementation for another one of
// their own choosing.
//
// Also note that the manager links against all the implementations to generate
// a comprehensive registry. So while individual properties and types are able
// to be compiled separately, this generated output will link against all of
// these libraries.
//
// TODO: Improve the code generation to examine specific Golang code to
// determine which types to actually generate, and prune the unneeded types.
// This would cut down on the bloat on a per-program basis.
type ManagerGenerator struct {
2019-01-06 00:22:37 +09:00
pkg Package
2018-12-31 00:09:14 +09:00
tg [ ] * TypeGenerator
fp [ ] * FunctionalPropertyGenerator
nfp [ ] * NonFunctionalPropertyGenerator
// Constructed at creation time. These rely on pointer stability,
// which should happen as none of these generators are treated as
// values.
tgManagedMethods map [ * TypeGenerator ] * managedMethods
fpManagedMethods map [ * FunctionalPropertyGenerator ] * managedMethods
nfpManagedMethods map [ * NonFunctionalPropertyGenerator ] * managedMethods
// Cached during manager code generation.
cacheOnce sync . Once
cachedStruct * codegen . Struct
}
// managedMethods caches the specific methods and interfaces mapped to specific
// properties and types.
type managedMethods struct {
2019-01-01 02:42:39 +09:00
deserializor * codegen . Method
2018-12-31 00:09:14 +09:00
}
// NewManagerGenerator creates a new manager system.
//
// This generator should be constructed in the third pass, after types and
// property generators are all constructed.
2019-01-06 00:22:37 +09:00
func NewManagerGenerator ( pkg Package ,
2018-12-31 00:09:14 +09:00
tg [ ] * TypeGenerator ,
fp [ ] * FunctionalPropertyGenerator ,
nfp [ ] * NonFunctionalPropertyGenerator ) ( * ManagerGenerator , error ) {
mg := & ManagerGenerator {
2019-01-06 00:22:37 +09:00
pkg : pkg ,
2018-12-31 00:09:14 +09:00
tg : tg ,
fp : fp ,
nfp : nfp ,
tgManagedMethods : make ( map [ * TypeGenerator ] * managedMethods , len ( tg ) ) ,
fpManagedMethods : make ( map [ * FunctionalPropertyGenerator ] * managedMethods , len ( fp ) ) ,
nfpManagedMethods : make ( map [ * NonFunctionalPropertyGenerator ] * managedMethods , len ( nfp ) ) ,
}
// Pass 1: Get all deserializor-like methods created. Further passes may
// rely on already having this data available in the manager.
for _ , t := range tg {
mg . tgManagedMethods [ t ] = & managedMethods {
2019-01-06 00:22:37 +09:00
deserializor : mg . createDeserializationMethodForType ( t ) ,
2018-12-31 00:09:14 +09:00
}
}
for _ , p := range fp {
mg . fpManagedMethods [ p ] = & managedMethods {
2019-01-06 00:22:37 +09:00
deserializor : mg . createDeserializationMethodForFuncProperty ( p ) ,
2018-12-31 00:09:14 +09:00
}
}
for _ , p := range nfp {
mg . nfpManagedMethods [ p ] = & managedMethods {
2019-01-06 00:22:37 +09:00
deserializor : mg . createDeserializationMethodForNonFuncProperty ( p ) ,
2018-12-31 00:09:14 +09:00
}
}
// Pass 2: Inform the type of this ManagerGenerator so that it can keep
// all of its bookkeeping straight.
for _ , t := range tg {
if e := t . apply ( mg ) ; e != nil {
return nil , e
}
}
return mg , nil
}
2019-01-06 00:22:37 +09:00
// getDeserializationMethodForType obtains the deserialization method for a
// type.
func ( m * ManagerGenerator ) getDeserializationMethodForType ( t * TypeGenerator ) * codegen . Method {
2018-12-31 00:09:14 +09:00
return m . tgManagedMethods [ t ] . deserializor
}
2019-01-06 00:22:37 +09:00
// getDeserializationMethodForProperty obtains the deserialization method for a
// property regardless whether it is functional or non-functional.
func ( m * ManagerGenerator ) getDeserializationMethodForProperty ( p Property ) * codegen . Method {
2018-12-31 00:09:14 +09:00
switch v := p . ( type ) {
case * FunctionalPropertyGenerator :
return m . fpManagedMethods [ v ] . deserializor
case * NonFunctionalPropertyGenerator :
return m . nfpManagedMethods [ v ] . deserializor
default :
panic ( "unknown property type" )
}
}
2019-01-06 00:22:37 +09:00
// Definition creates a manager implementation that works with the interface
2018-12-31 00:09:14 +09:00
// types required by the other PropertyGenerators and TypeGenerators for
// serializing and deserializing.
//
2019-01-06 00:22:37 +09:00
// Applications will implicitly use this manager and be isolated from the
// underlying specific go-fed implementation. If another alternative to go-fed
// were to be created, it could target those interfaces and be a drop-in
// replacement for an application.
2018-12-31 00:09:14 +09:00
//
2019-01-06 00:22:37 +09:00
// It is necessary to have this to acheive isolation without cyclic
// dependencies: types and properties can each belong in their own package (if
// desired) to minimize binary bloat.
func ( m * ManagerGenerator ) Definition ( ) * codegen . Struct {
2018-12-31 00:09:14 +09:00
var methods [ ] * codegen . Method
for _ , tg := range m . tgManagedMethods {
methods = append ( methods , tg . deserializor )
}
for _ , fp := range m . fpManagedMethods {
methods = append ( methods , fp . deserializor )
}
for _ , nfp := range m . nfpManagedMethods {
methods = append ( methods , nfp . deserializor )
}
s := codegen . NewStruct (
2019-01-08 06:06:32 +09:00
fmt . Sprintf ( "%s manages interface types and deserializations for use by generated code. Application code implicitly uses this manager at run-time to create concrete implementations of the interfaces." , managerName ) ,
2018-12-31 00:09:14 +09:00
managerName ,
methods ,
2019-01-01 00:49:25 +09:00
/*functions=*/ nil ,
/*members=*/ nil )
2018-12-31 00:09:14 +09:00
return s
}
2019-01-06 00:22:37 +09:00
// createDeserializationMethodForType creates a new deserialization method for
// a type.
func ( m * ManagerGenerator ) createDeserializationMethodForType ( tg * TypeGenerator ) * codegen . Method {
return m . createDeserializationMethod (
tg . deserializationFnName ( ) ,
tg . PublicPackage ( ) ,
tg . PrivatePackage ( ) ,
tg . InterfaceName ( ) ,
tg . VocabName ( ) )
2018-12-31 00:09:14 +09:00
}
2019-01-06 00:22:37 +09:00
// createDeserializationMethodForFuncProperty creates a new deserialization
// method for a functional property.
func ( m * ManagerGenerator ) createDeserializationMethodForFuncProperty ( fp * FunctionalPropertyGenerator ) * codegen . Method {
return m . createDeserializationMethod (
fp . DeserializeFnName ( ) ,
fp . GetPublicPackage ( ) ,
fp . GetPrivatePackage ( ) ,
fp . InterfaceName ( ) ,
fp . VocabName ( ) )
2018-12-31 00:09:14 +09:00
}
2019-01-06 00:22:37 +09:00
// createDeserializationMethodForNonFuncProperty creates a new deserialization
// method for a non-functional property.
func ( m * ManagerGenerator ) createDeserializationMethodForNonFuncProperty ( nfp * NonFunctionalPropertyGenerator ) * codegen . Method {
return m . createDeserializationMethod (
nfp . DeserializeFnName ( ) ,
nfp . GetPublicPackage ( ) ,
nfp . GetPrivatePackage ( ) ,
nfp . InterfaceName ( ) ,
nfp . VocabName ( ) )
}
// createDeserializationMethod returns a function
func ( m * ManagerGenerator ) createDeserializationMethod ( deserName string , pubPkg , privPkg Package , interfaceName , vocabName string ) * codegen . Method {
name := fmt . Sprintf ( "%s%s" , deserName , vocabName )
2018-12-31 00:09:14 +09:00
return codegen . NewCommentedValueMethod (
2019-01-06 00:22:37 +09:00
m . pkg . Path ( ) ,
2018-12-31 00:09:14 +09:00
name ,
managerName ,
/*param=*/ nil ,
[ ] jen . Code {
jen . Func ( ) . Params (
2019-01-07 03:44:24 +09:00
jen . Map ( jen . String ( ) ) . Interface ( ) ,
2018-12-31 00:09:14 +09:00
) . Params (
2019-01-06 00:22:37 +09:00
jen . Qual ( pubPkg . Path ( ) , interfaceName ) ,
2018-12-31 00:09:14 +09:00
jen . Error ( ) ,
) ,
} ,
[ ] jen . Code {
jen . Return (
2019-01-06 00:22:37 +09:00
jen . Func ( ) . Params (
2019-01-07 03:44:24 +09:00
jen . Id ( "m" ) . Map ( jen . String ( ) ) . Interface ( ) ,
2019-01-06 00:22:37 +09:00
) . Params (
jen . Qual ( pubPkg . Path ( ) , interfaceName ) ,
jen . Error ( ) ,
) . Block (
jen . List (
2019-01-09 04:37:04 +09:00
// Note: this "i" must be the same as the "i" in the deserialization definition.
2019-01-06 00:22:37 +09:00
jen . Id ( "i" ) ,
jen . Err ( ) ,
2019-01-07 03:44:24 +09:00
) . Op ( ":=" ) . Qual ( privPkg . Path ( ) , deserName ) . Call ( jen . Id ( "m" ) ) ,
2019-01-06 00:22:37 +09:00
jen . Return ( jen . List (
jen . Id ( "i" ) ,
jen . Err ( ) ,
) ) ,
) ,
2018-12-31 00:09:14 +09:00
) ,
} ,
2019-01-08 06:06:32 +09:00
fmt . Sprintf ( "%s returns the deserialization method for the %q non-functional property in the vocabulary %q" , name , interfaceName , vocabName ) )
2018-12-31 00:09:14 +09:00
}