263 行
8.0 KiB
Go
263 行
8.0 KiB
Go
// 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"
|
|
)
|
|
|
|
const (
|
|
objectName = "Object"
|
|
linkName = "Link"
|
|
ObjectTypeName = "ObjectType"
|
|
LinkTypeName = "LinkType"
|
|
typePropertyName = "type"
|
|
resolveLinkName = "resolveLink"
|
|
resolveObjectName = "resolveObject"
|
|
unknownValueDeserializeFnName = "unknownValueDeserialize"
|
|
unknownValueSerializeFnName = "unknownValueSerialize"
|
|
)
|
|
|
|
func GenerateImplementations(types []*defs.Type, properties []*defs.PropertyType, values []*defs.ValueType) ([]byte, error) {
|
|
// Validate inputs
|
|
err := validateDomains(properties)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
err = validateProperties(types)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
err = validateValues(values, properties)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
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()...)
|
|
|
|
// 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)
|
|
|
|
for _, t := range types {
|
|
funcs, defs, interfaces := generateDefinitions(t)
|
|
p.F = append(p.F, funcs...)
|
|
p.Defs = append(p.Defs, defs...)
|
|
p.I = append(p.I, interfaces...)
|
|
}
|
|
return format.Source([]byte(p.Generate()))
|
|
}
|
|
|
|
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")
|
|
}
|