2018-01-24 08:00:59 +09:00
package gen
import (
"bytes"
"fmt"
"github.com/go-fed/activity/tools/defs"
)
func generateDefinitions ( t * defs . Type ) ( fd [ ] * defs . FunctionDef , sd [ ] * defs . StructDef , x [ ] * defs . InterfaceDef ) {
this := & defs . StructDef {
Typename : t . Name ,
Comment : t . Notes ,
M : [ ] * defs . StructMember { { "unknown_" , "map[string]interface{}" , "An unknown value." } } ,
}
sd = append ( sd , this )
thisInterface := & defs . InterfaceDef {
Typename : InterfaceName ( t ) ,
Comment : fmt . Sprintf ( "%s is an interface for accepting types that extend from '%s'." , InterfaceName ( t ) , t . Name ) ,
O : [ ] string { "Serializer" , "Deserializer" } ,
}
x = append ( x , thisInterface )
var serializeFragments [ ] string
var deserializeFragments [ ] string
for _ , p := range t . GetProperties ( ) {
pf , pd , s , d := generatePropertyDefinitions ( p , this , thisInterface )
serializeFragments = append ( serializeFragments , s )
deserializeFragments = append ( deserializeFragments , d )
fd = append ( fd , pf ... )
sd = append ( sd , pd ... )
}
generateWithoutProperties ( t , this , thisInterface )
generateAddUnknownFunction ( t , this )
generateHasUnknownFunction ( t , this )
generateRemoveUnknownFunction ( t , this )
generateSerializeFunction ( t , this , serializeFragments )
generateDeserializeFunction ( t , this , deserializeFragments )
return
}
func Name ( r defs . RangeReference ) string {
if r . T != nil {
return r . T . Name
} else if r . V != nil {
return r . V . Name
} else if r . Any {
return "any"
} else {
panic ( "RangeReference must have exactly one member set" )
}
}
func Type ( r defs . RangeReference ) string {
if r . T != nil {
return InterfaceName ( r . T )
} else if r . V != nil {
return r . V . DefinitionType
} else if r . Any {
return "interface{}"
} else {
panic ( "RangeReference must have exactly one member set" )
}
}
func Deserialize ( r defs . RangeReference , mapName , field string , slice bool ) string {
if r . T != nil {
var parseCode bytes . Buffer
parseCode . WriteString ( fmt . Sprintf ( "tmp := &%s{}\n" , r . T . Name ) )
parseCode . WriteString ( "err = tmp.Deserialize(v)\n" )
parseCode . WriteString ( "if err != nil {\nreturn err\n}\n" )
var b bytes . Buffer
b . WriteString ( "// Begin generation by RangeReference.Deserialize for Type\n" )
deserializeCode ( & b , parseCode . String ( ) , mapName , "map[string]interface{}" , field , slice , false )
b . WriteString ( "// End generation by RangeReference.Deserialize for Type\n" )
return b . String ( )
} else if r . V != nil {
var b bytes . Buffer
b . WriteString ( "// Begin generation by RangeReference.Deserialize for Value\n" )
2018-05-30 04:57:39 +09:00
deserializeCode ( & b , fmt . Sprintf ( "tmp, err := %s(v)\n" , r . V . DeserializeFn . Name ) , mapName , "interface{}" , field , slice , false )
2018-01-24 08:00:59 +09:00
b . WriteString ( "// End generation by RangeReference.Deserialize for Value\n" )
return b . String ( )
} else {
panic ( "Bad call to Deserialize for Any" )
}
}
func Serialize ( r defs . RangeReference , mapName , field string , slice bool ) string {
if r . T != nil {
var sCode bytes . Buffer
sCode . WriteString ( "tmp, err := v.Serialize()\n" )
sCode . WriteString ( "if err != nil {\nreturn m, err\n}\n" )
var b bytes . Buffer
b . WriteString ( "// Begin generation by RangeReference.Serialize for Type\n" )
serializeCode ( & b , sCode . String ( ) , mapName , field , slice )
b . WriteString ( "// End generation by RangeReference.Serialize for Type\n" )
return b . String ( )
} else if r . V != nil {
deref := "*"
2018-05-30 04:57:39 +09:00
if slice || isIRIType ( Type ( r ) ) {
2018-01-24 08:00:59 +09:00
deref = ""
}
var b bytes . Buffer
b . WriteString ( "// Begin generation by RangeReference.Serialize for Value\n" )
serializeCode ( & b , fmt . Sprintf ( "tmp := %s(%sv)\n" , r . V . SerializeFn . Name , deref ) , mapName , field , slice )
b . WriteString ( "// End generation by RangeReference.Serialize for Value\n" )
return b . String ( )
} else {
panic ( "Bad call to Serialize for Any" )
}
}
func generateRemoveUnknownFunction ( t * defs . Type , this * defs . StructDef ) {
m := & defs . MemberFunctionDef {
Name : "RemoveUnknown" ,
Comment : "RemoveUnknown removes a raw extension from this object with the specified key" ,
P : this ,
Args : [ ] * defs . FunctionVarDef { { "k" , "string" } } ,
Return : [ ] * defs . FunctionVarDef { { "this" , "*" + t . Name } } ,
Body : func ( ) string {
var b bytes . Buffer
b . WriteString ( "delete(t.unknown_, k)\n" )
b . WriteString ( "return t\n" )
return b . String ( )
} ,
}
this . F = append ( this . F , m )
}
func generateHasUnknownFunction ( t * defs . Type , this * defs . StructDef ) {
m := & defs . MemberFunctionDef {
Name : "HasUnknown" ,
Comment : "HasUnknown returns true if there is an unknown object with the specified key" ,
P : this ,
Args : [ ] * defs . FunctionVarDef { { "k" , "string" } } ,
Return : [ ] * defs . FunctionVarDef { { "b" , "bool" } } ,
Body : func ( ) string {
var b bytes . Buffer
b . WriteString ( "if t.unknown_ == nil {\n" )
b . WriteString ( "return false" )
b . WriteString ( "}\n" )
b . WriteString ( "_, ok := t.unknown_[k]\n" )
b . WriteString ( "return ok\n" )
return b . String ( )
} ,
}
this . F = append ( this . F , m )
}
func generateAddUnknownFunction ( t * defs . Type , this * defs . StructDef ) {
m := & defs . MemberFunctionDef {
Name : "AddUnknown" ,
Comment : "AddUnknown adds a raw extension to this object with the specified key" ,
P : this ,
Args : [ ] * defs . FunctionVarDef { { "k" , "string" } , { "i" , "interface{}" } } ,
Return : [ ] * defs . FunctionVarDef { { "this" , "*" + t . Name } } ,
Body : func ( ) string {
var b bytes . Buffer
b . WriteString ( "if t.unknown_ == nil {\n" )
b . WriteString ( "t.unknown_ = make(map[string]interface{})\n" )
b . WriteString ( "}\n" )
b . WriteString ( "t.unknown_[k] = i\n" )
b . WriteString ( "return t\n" )
return b . String ( )
} ,
}
this . F = append ( this . F , m )
}
func generateDeserializeFunction ( t * defs . Type , this * defs . StructDef , fragments [ ] string ) {
d := & defs . MemberFunctionDef {
Name : "Deserialize" ,
Comment : "Deserialize populates this object from a map[string]interface{}" ,
P : this ,
Args : [ ] * defs . FunctionVarDef { { "m" , "map[string]interface{}" } } ,
Return : [ ] * defs . FunctionVarDef { { "err" , "error" } } ,
Body : func ( ) string {
var b bytes . Buffer
b . WriteString ( "for k, v := range m {\n" )
b . WriteString ( "handled := false\n" )
for _ , s := range fragments {
b . WriteString ( "if !handled {\n" )
b . WriteString ( s )
b . WriteString ( "}\n" )
}
b . WriteString ( "if !handled && k != \"@context\"{\n" )
b . WriteString ( "if t.unknown_ == nil {\n" )
b . WriteString ( "t.unknown_ = make(map[string]interface{})\n" )
b . WriteString ( "}\n" )
b . WriteString ( fmt . Sprintf ( "t.unknown_[k] = %s(v)\n" , unknownValueDeserializeFnName ) )
b . WriteString ( "}\n" )
b . WriteString ( "}\n" )
b . WriteString ( "return\n" )
return b . String ( )
} ,
}
this . F = append ( this . F , d )
}
func generateSerializeFunction ( t * defs . Type , this * defs . StructDef , fragments [ ] string ) {
d := & defs . MemberFunctionDef {
Name : "Serialize" ,
Comment : fmt . Sprintf ( "Serialize turns this object into a map[string]interface{}. Note that the \"type\" property will automatically be populated with \"%s\" if not manually set by the caller" , t . Name ) ,
P : this ,
Return : [ ] * defs . FunctionVarDef { { "m" , "map[string]interface{}" } , { "err" , "error" } } ,
Body : func ( ) string {
var b bytes . Buffer
b . WriteString ( "m = make(map[string]interface{})\n" )
b . WriteString ( "for k, v := range t.unknown_ {\n" )
b . WriteString ( fmt . Sprintf ( "m[k] = %s(v)\n" , unknownValueSerializeFnName ) )
b . WriteString ( "}\n" )
b . WriteString ( "var typeAlreadySet bool\n" )
b . WriteString ( fmt . Sprintf ( "for _, k := range t.%s {\n" , cleanName ( typePropertyName ) ) )
b . WriteString ( "if ks, ok := k.(string); ok {\n" )
b . WriteString ( fmt . Sprintf ( "if ks == \"%s\" {\n" , t . Name ) )
b . WriteString ( "typeAlreadySet = true\n" )
b . WriteString ( "break\n" )
b . WriteString ( "}\n" )
b . WriteString ( "}\n" )
b . WriteString ( "}\n" )
b . WriteString ( "if !typeAlreadySet {\n" )
b . WriteString ( fmt . Sprintf ( "t.%s = append(t.%s, \"%s\")\n" , cleanName ( typePropertyName ) , cleanName ( typePropertyName ) , t . Name ) )
b . WriteString ( "}\n" )
for _ , s := range fragments {
b . WriteString ( s )
}
b . WriteString ( "return\n" )
return b . String ( )
} ,
}
this . F = append ( this . F , d )
}
func generateWithoutProperties ( d * defs . Type , this * defs . StructDef , it * defs . InterfaceDef ) {
2018-01-26 08:19:21 +09:00
hasNamed := make ( map [ string ] bool , 0 )
for _ , p := range d . GetProperties ( ) {
hasNamed [ p . Name ] = true
}
2018-01-24 08:00:59 +09:00
for _ , t := range d . WithoutProperties {
2018-01-26 08:19:21 +09:00
if hasNamed [ t . Name ] {
// No need to stub -- already has a replacement.
continue
}
2018-01-24 08:00:59 +09:00
clonedThis := & defs . StructDef {
Typename : this . Typename ,
Comment : this . Comment ,
}
_ , _ , _ , _ = generatePropertyDefinitions ( t , clonedThis , it )
for _ , f := range clonedThis . F {
if len ( f . Return ) > 1 {
panic ( "generateWithoutProperties can only handle replacing functions with zero or one return types" )
}
f . P = this
f . Comment = fmt . Sprintf ( "%s is NOT a valid property for this type; calling its associated methods will always yield an empty-equivalent value such as false, nil, or empty string. This includes instances where it should return itself. This ugliness is a symptom of the fundamental design of the ActivityStream vocabulary as instead of 'W-is-a-X' relationships it contains the notion of 'W-is-a-X-except-for-Y'." , f . Name )
if len ( f . Return ) > 0 {
returnType := f . Return [ 0 ] . Type
f . Body = func ( ) string {
var b bytes . Buffer
if returnType == "url.URL" {
b . WriteString ( "return url.URL{}\n" )
} else if isSlice ( returnType ) || isPtrType ( returnType ) {
b . WriteString ( "return nil\n" )
} else if returnType == "bool" {
b . WriteString ( "return false\n" )
} else if returnType == "int" {
b . WriteString ( "return 0\n" )
} else if returnType == "interface{}" {
b . WriteString ( "return nil\n" )
} else {
found := false
for _ , v := range t . Range {
if v . V == nil {
continue
}
if returnType == deref ( Type ( v ) ) {
found = true
b . WriteString ( fmt . Sprintf ( "return %s\n" , v . V . Zero ) )
}
}
if ! found {
b . WriteString ( "return nil\n" )
}
}
return b . String ( )
}
this . F = append ( this . F , f )
} else {
f . Body = func ( ) string { return "\n" }
2018-01-26 08:19:21 +09:00
this . F = append ( this . F , f )
2018-01-24 08:00:59 +09:00
}
}
}
}
func InterfaceName ( t * defs . Type ) string {
return fmt . Sprintf ( "%sType" , t . Name )
}