activity/tools/vocab/gen/generate_definitions.go

296 行
10 KiB
Go

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")
deserializeCode(&b, fmt.Sprintf("tmp, err := %s(v)\n", r.V.DeserializeFn.Name), mapName, "interface{}", field, slice, false)
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 := "*"
if slice || isIRIType(Type(r)) {
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) {
hasNamed := make(map[string]bool, 0)
for _, p := range d.GetProperties() {
hasNamed[p.Name] = true
}
for _, t := range d.WithoutProperties {
if hasNamed[t.Name] {
// No need to stub -- already has a replacement.
continue
}
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" }
this.F = append(this.F, f)
}
}
}
}
func InterfaceName(t *defs.Type) string {
return fmt.Sprintf("%sType", t.Name)
}