2018-01-24 08:00:59 +09:00
// Package gen contains the libraries and algorithms used to generate the
// code for the vocab package.
package gen
import (
"bytes"
"fmt"
"github.com/go-fed/activity/tools/defs"
"go/format"
2018-06-13 06:29:30 +09:00
"strings"
2018-01-24 08:00:59 +09:00
)
const (
objectName = "Object"
linkName = "Link"
ObjectTypeName = "ObjectType"
LinkTypeName = "LinkType"
typePropertyName = "type"
resolveLinkName = "resolveLink"
resolveObjectName = "resolveObject"
unknownValueDeserializeFnName = "unknownValueDeserialize"
unknownValueSerializeFnName = "unknownValueSerialize"
)
2018-06-14 04:53:01 +09:00
type File struct {
Name string
Content [ ] byte
}
func GenerateImplementations ( types [ ] * defs . Type , properties [ ] * defs . PropertyType , values [ ] * defs . ValueType ) ( f [ ] * File , err error ) {
2018-01-24 08:00:59 +09:00
// Validate inputs
2018-06-14 04:53:01 +09:00
err = validateDomains ( properties )
2018-01-24 08:00:59 +09:00
if err != nil {
2018-06-14 04:53:01 +09:00
return
2018-01-24 08:00:59 +09:00
}
err = validateProperties ( types )
if err != nil {
2018-06-14 04:53:01 +09:00
return
2018-01-24 08:00:59 +09:00
}
err = validateValues ( values , properties )
if err != nil {
2018-06-14 04:53:01 +09:00
return
2018-01-24 08:00:59 +09:00
}
p := generatePackageDefinition ( )
p . Defs = append ( p . Defs , generateUnknownType ( ) )
// Add ValueType serialize & deserialize functions
for _ , v := range values {
p . F = append ( p . F , v . DeserializeFn , v . SerializeFn )
}
p . F = append ( p . F , defs . IRIFuncs ( ) ... )
2018-06-13 06:29:30 +09:00
p . F = append ( p . F , generateHasTypeFuncs ( types ) ... )
p . I = append ( p . I , generateTyperInterface ( ) )
2018-01-24 08:00:59 +09:00
// Add functions to resolve string 'name' into concrete types
p . F = append ( p . F , generateResolveObjectFunction ( types ) )
p . F = append ( p . F , generateResolveLinkFunction ( types ) )
unknown := generateUnknownValueType ( )
p . F = append ( p . F , unknown . DeserializeFn , unknown . SerializeFn )
2018-06-14 04:53:01 +09:00
var b [ ] byte
b , err = format . Source ( [ ] byte ( p . Generate ( ) ) )
if err != nil {
return
}
f = append ( f , & File {
Name : "gen_vocab.go" ,
Content : b ,
} )
2018-06-18 01:51:30 +09:00
// ActivityStream Types
m := make ( map [ * defs . PropertyType ] * intermedDef )
2018-01-24 08:00:59 +09:00
for _ , t := range types {
2018-06-14 04:53:01 +09:00
p := & defs . PackageDef {
Name : "vocab" ,
}
2018-06-18 01:51:30 +09:00
funcs , defs , interfaces , imports := generateDefinitions ( t , m )
2018-06-14 04:53:01 +09:00
for i , _ := range imports {
p . Imports = append ( p . Imports , i )
}
2018-01-24 08:00:59 +09:00
p . F = append ( p . F , funcs ... )
p . Defs = append ( p . Defs , defs ... )
p . I = append ( p . I , interfaces ... )
2018-06-14 04:53:01 +09:00
var b [ ] byte
b , err = format . Source ( [ ] byte ( p . Generate ( ) ) )
if err != nil {
return
}
f = append ( f , & File {
Name : fmt . Sprintf ( "gen_%s.go" , strings . ToLower ( t . Name ) ) ,
Content : b ,
} )
2018-01-24 08:00:59 +09:00
}
2018-06-18 01:51:30 +09:00
// Intermediate definitions
p = & defs . PackageDef {
Name : "vocab" ,
Imports : [ ] string { "fmt" , "net/url" , "time" } ,
}
for _ , v := range m {
p . F = append ( p . F , v . F ... )
p . Defs = append ( p . Defs , v . S )
}
b , err = format . Source ( [ ] byte ( p . Generate ( ) ) )
if err != nil {
return
}
f = append ( f , & File {
Name : fmt . Sprintf ( "gen_intermediate.go" ) ,
Content : b ,
} )
2018-06-14 04:53:01 +09:00
return
2018-01-24 08:00:59 +09:00
}
2018-06-13 06:29:30 +09:00
func generateTyperInterface ( ) * defs . InterfaceDef {
return & defs . InterfaceDef {
Typename : "Typer" ,
Comment : "Typer supports common functions for determining an ActivityStream type" ,
F : [ ] * defs . FunctionDef {
{
Name : "TypeLen" ,
Return : [ ] * defs . FunctionVarDef { { Name : "l" , Type : "int" } } ,
} ,
{
Name : "GetType" ,
Args : [ ] * defs . FunctionVarDef { { Name : "index" , Type : "int" } } ,
Return : [ ] * defs . FunctionVarDef { { Name : "v" , Type : "interface{}" } } ,
} ,
} ,
}
}
func generateHasTypeFuncs ( types [ ] * defs . Type ) ( f [ ] * defs . FunctionDef ) {
var activityTypes [ ] string
for _ , t := range types {
t := t
if defs . IsActivity ( t ) {
activityTypes = append ( activityTypes , t . Name )
}
f = append ( f , & defs . FunctionDef {
Name : fmt . Sprintf ( "HasType%s" , t . Name ) ,
Comment : fmt . Sprintf ( "HasType%s returns true if the Typer has a type of %s." , t . Name , t . Name ) ,
Args : [ ] * defs . FunctionVarDef { { Name : "t" , Type : "Typer" } } ,
Return : [ ] * defs . FunctionVarDef { { Name : "b" , Type : "bool" } } ,
Body : func ( ) string {
var b bytes . Buffer
b . WriteString ( "for i := 0; i < t.TypeLen(); i++ {\n" )
b . WriteString ( "v := t.GetType(i)\n" )
b . WriteString ( "if s, ok := v.(string); ok {\n" )
b . WriteString ( fmt . Sprintf ( "if s == \"%s\" {\n" , t . Name ) )
b . WriteString ( "return true\n" )
b . WriteString ( "}\n" )
b . WriteString ( "}\n" )
b . WriteString ( "}\n" )
b . WriteString ( "return false\n" )
return b . String ( )
} ,
} )
}
f = append ( f , & defs . FunctionDef {
Name : "IsActivityType" ,
Comment : "Returns true if the provided Typer is an Activity." ,
Args : [ ] * defs . FunctionVarDef { { Name : "t" , Type : "Typer" } } ,
Return : [ ] * defs . FunctionVarDef { { Name : "b" , Type : "bool" } } ,
Body : func ( ) string {
var b bytes . Buffer
b . WriteString ( fmt . Sprintf ( "var activityTypes = []string{\"%s\"}\n" , strings . Join ( activityTypes , "\", \"" ) ) )
b . WriteString ( "hasType := make(map[string]bool, 1)\n" )
b . WriteString ( "for i := 0; i < t.TypeLen(); i++ {\n" )
b . WriteString ( "v := t.GetType(i)\n" )
b . WriteString ( "if s, ok := v.(string); ok {\n" )
b . WriteString ( "hasType[s] = true\n" )
b . WriteString ( "}\n" )
b . WriteString ( "}\n" )
b . WriteString ( "for _, t := range activityTypes {\n" )
b . WriteString ( "if hasType[t] {\n" )
b . WriteString ( "return true\n" )
b . WriteString ( "}\n" )
b . WriteString ( "}\n" )
b . WriteString ( "return false\n" )
return b . String ( )
} ,
} )
return
}
2018-01-24 08:00:59 +09:00
func generatePackageDefinition ( ) * defs . PackageDef {
return & defs . PackageDef {
Name : "vocab" ,
Comment : "Package vocab provides an implementation of serializing and deserializing activity streams into native golang structs without relying on reflection. This package is code-generated from the vocabulary specification available at https://www.w3.org/TR/activitystreams-vocabulary and by design forgoes full resolution of raw JSON-LD data. However, custom extensions of the vocabulary are supported by modifying the data definitions in the generation tool and rerunning it. Do not modify this package directly." ,
Imports : [ ] string { "fmt" , "time" , "net/url" , "regexp" , "strconv" , "math" } ,
I : [ ] * defs . InterfaceDef {
{
Typename : "Serializer" ,
Comment : "Serializer implementations can serialize themselves to a generic map form." ,
F : [ ] * defs . FunctionDef { {
Name : "Serialize" ,
Return : [ ] * defs . FunctionVarDef { { "m" , "map[string]interface{}" } , { "e" , "error" } } ,
} } ,
} ,
{
Typename : "Deserializer" ,
Comment : "Deserializer implementations can deserialize themselves from a generic map form." ,
F : [ ] * defs . FunctionDef { {
Name : "Deserialize" ,
Args : [ ] * defs . FunctionVarDef { { "m" , "map[string]interface{}" } } ,
Return : [ ] * defs . FunctionVarDef { { "e" , "error" } } ,
} } ,
} ,
} ,
}
}
func generateUnknownValueType ( ) * defs . ValueType {
return & defs . ValueType {
Name : "Unknown Value" ,
DefinitionType : "interface{}" ,
Zero : "nil" ,
DeserializeFn : & defs . FunctionDef {
Name : unknownValueDeserializeFnName ,
Comment : "unknownValueDeserialize transparently stores the object." ,
Args : [ ] * defs . FunctionVarDef { { "v" , "interface{}" } } ,
Return : [ ] * defs . FunctionVarDef { { "o" , "interface{}" } } ,
Body : func ( ) string {
var b bytes . Buffer
b . WriteString ( "o = v\n" )
b . WriteString ( "return\n" )
return b . String ( )
} ,
} ,
SerializeFn : & defs . FunctionDef {
Name : unknownValueSerializeFnName ,
Comment : "unknownValueSerialize transparently returns the object." ,
Args : [ ] * defs . FunctionVarDef { { "v" , "interface{}" } } ,
Return : [ ] * defs . FunctionVarDef { { "o" , "interface{}" } } ,
Body : func ( ) string {
var b bytes . Buffer
b . WriteString ( "o = v\n" )
b . WriteString ( "return\n" )
return b . String ( )
} ,
} ,
}
}
func generateUnknownType ( ) * defs . StructDef {
u := & defs . StructDef {
Typename : "Unknown" ,
Comment : "Unknown is an entry whose root type is unknown." ,
M : [ ] * defs . StructMember { {
Name : "u" ,
Type : "map[string]interface{}" ,
Comment : "Raw unknown, untyped values" ,
} } ,
}
u . F = append ( u . F , [ ] * defs . MemberFunctionDef {
{
Name : "Serialize" ,
Comment : "Serialize turns this object into a map[string]interface{}. Note that for the Unknown type, the \"type\" property is NOT populated with anything special during this process." ,
P : u ,
Return : [ ] * defs . FunctionVarDef { { "m" , "map[string]interface{}" } , { "err" , "error" } } ,
Body : func ( ) string {
var b bytes . Buffer
b . WriteString ( "m = t.u\n" )
b . WriteString ( "return\n" )
return b . String ( )
} ,
} ,
{
Name : "Deserialize" ,
Comment : "Deserialize populates this object from a map[string]interface{}" ,
P : u ,
Args : [ ] * defs . FunctionVarDef { { "m" , "map[string]interface{}" } } ,
Return : [ ] * defs . FunctionVarDef { { "err" , "error" } } ,
Body : func ( ) string {
var b bytes . Buffer
b . WriteString ( "t.u = m\n" )
b . WriteString ( "return\n" )
return b . String ( )
} ,
} ,
{
Name : "HasField" ,
Comment : "HasField determines whether the call to GetField is safe with the specified field" ,
P : u ,
Args : [ ] * defs . FunctionVarDef { { "f" , "string" } } ,
Return : [ ] * defs . FunctionVarDef { { "ok" , "bool" } } ,
Body : func ( ) string {
return "return t.u != nil && t.u[f] != nil\n"
} ,
} ,
{
Name : "GetField" ,
Comment : "GetField returns the unknown field value" ,
P : u ,
Args : [ ] * defs . FunctionVarDef { { "f" , "string" } } ,
Return : [ ] * defs . FunctionVarDef { { "v" , "interface{}" } } ,
Body : func ( ) string {
return "return t.u[f]"
} ,
} ,
{
Name : "SetField" ,
Comment : "SetField sets the unknown field value" ,
P : u ,
Args : [ ] * defs . FunctionVarDef { { "f" , "string" } , { "i" , "interface{}" } } ,
Return : [ ] * defs . FunctionVarDef { { "this" , "*" + u . Typename } } ,
Body : func ( ) string {
var b bytes . Buffer
b . WriteString ( "if t.u == nil {\n" )
b . WriteString ( "t.u = make(map[string]interface{})\n" )
b . WriteString ( "}\n" )
b . WriteString ( "t.u[f] = i\n" )
b . WriteString ( "return t\n" )
return b . String ( )
} ,
} ,
} ... )
return u
}
func generateResolveObjectFunction ( types [ ] * defs . Type ) * defs . FunctionDef {
return & defs . FunctionDef {
Name : resolveObjectName ,
Comment : fmt . Sprintf ( "%s turns a string type that extends Object into a concrete type." , resolveObjectName ) ,
Args : [ ] * defs . FunctionVarDef { { "s" , "string" } } ,
Return : [ ] * defs . FunctionVarDef { { "i" , "interface{}" } } ,
Body : func ( ) string {
var b bytes . Buffer
for _ , r := range types {
if isAnObjectType ( r ) {
b . WriteString ( fmt . Sprintf ( "if s == \"%s\" {\n" , r . Name ) )
b . WriteString ( fmt . Sprintf ( "return &%s{}\n" , r . Name ) )
b . WriteString ( "}\n" )
}
}
b . WriteString ( "return nil\n" )
return b . String ( )
} ,
}
}
func generateResolveLinkFunction ( types [ ] * defs . Type ) * defs . FunctionDef {
return & defs . FunctionDef {
Name : resolveLinkName ,
Comment : fmt . Sprintf ( "%s turns a string type that extends Link into a concrete type." , resolveLinkName ) ,
Args : [ ] * defs . FunctionVarDef { { "s" , "string" } } ,
Return : [ ] * defs . FunctionVarDef { { "i" , "interface{}" } } ,
Body : func ( ) string {
var b bytes . Buffer
for _ , r := range types {
if isALinkType ( r ) {
b . WriteString ( fmt . Sprintf ( "if s == \"%s\" {\n" , r . Name ) )
b . WriteString ( fmt . Sprintf ( "return &%s{}\n" , r . Name ) )
b . WriteString ( "}\n" )
}
}
b . WriteString ( "return nil\n" )
return b . String ( )
} ,
}
}
func isAnObjectType ( t * defs . Type ) bool {
obj , _ := getIsAType ( t )
return obj
}
func isALinkType ( t * defs . Type ) bool {
_ , link := getIsAType ( t )
return link
}
func getIsAType ( t * defs . Type ) ( obj , link bool ) {
if t . Name == objectName {
return true , false
} else if t . Name == linkName {
return false , true
}
for _ , e := range t . Extends {
if obj , link = getIsAType ( e ) ; obj || link {
return
}
}
panic ( "Unknown root is-a-type" )
}