1// Package gen contains the libraries and algorithms used to generate the 2// code for the vocab package. 3package gen 4 5import ( 6 "bytes" 7 "fmt" 8 "github.com/writeas/activity/tools/defs" 9 "go/format" 10 "strings" 11) 12 13const ( 14 objectName = "Object" 15 linkName = "Link" 16 ObjectTypeName = "ObjectType" 17 LinkTypeName = "LinkType" 18 typePropertyName = "type" 19 resolveLinkName = "resolveLink" 20 resolveObjectName = "resolveObject" 21 unknownValueDeserializeFnName = "unknownValueDeserialize" 22 unknownValueSerializeFnName = "unknownValueSerialize" 23) 24 25type File struct { 26 Name string 27 Content []byte 28} 29 30func GenerateImplementations(types []*defs.Type, properties []*defs.PropertyType, values []*defs.ValueType) (f []*File, err error) { 31 // Validate inputs 32 err = validateDomains(properties) 33 if err != nil { 34 return 35 } 36 err = validateProperties(types) 37 if err != nil { 38 return 39 } 40 err = validateValues(values, properties) 41 if err != nil { 42 return 43 } 44 45 p := generatePackageDefinition() 46 p.Defs = append(p.Defs, generateUnknownType()) 47 48 // Add ValueType serialize & deserialize functions 49 for _, v := range values { 50 p.F = append(p.F, v.DeserializeFn, v.SerializeFn) 51 } 52 p.F = append(p.F, defs.IRIFuncs()...) 53 p.F = append(p.F, generateHasTypeFuncs(types)...) 54 p.I = append(p.I, generateTyperInterface()) 55 56 // Add functions to resolve string 'name' into concrete types 57 p.F = append(p.F, generateResolveObjectFunction(types)) 58 p.F = append(p.F, generateResolveLinkFunction(types)) 59 unknown := generateUnknownValueType() 60 p.F = append(p.F, unknown.DeserializeFn, unknown.SerializeFn) 61 62 var b []byte 63 b, err = format.Source([]byte(p.Generate())) 64 if err != nil { 65 return 66 } 67 f = append(f, &File{ 68 Name: "gen_vocab.go", 69 Content: b, 70 }) 71 72 // ActivityStream Types 73 m := make(map[*defs.PropertyType]*intermedDef) 74 for _, t := range types { 75 p := &defs.PackageDef{ 76 Name: "vocab", 77 } 78 funcs, defs, interfaces, imports := generateDefinitions(t, m) 79 for i, _ := range imports { 80 p.Imports = append(p.Imports, i) 81 } 82 p.F = append(p.F, funcs...) 83 p.Defs = append(p.Defs, defs...) 84 p.I = append(p.I, interfaces...) 85 86 var b []byte 87 b, err = format.Source([]byte(p.Generate())) 88 if err != nil { 89 return 90 } 91 f = append(f, &File{ 92 Name: fmt.Sprintf("gen_%s.go", strings.ToLower(t.Name)), 93 Content: b, 94 }) 95 } 96 97 // Intermediate definitions 98 p = &defs.PackageDef{ 99 Name: "vocab", 100 Imports: []string{"fmt", "net/url", "time"}, 101 } 102 for _, v := range m { 103 p.F = append(p.F, v.F...) 104 p.Defs = append(p.Defs, v.S) 105 } 106 b, err = format.Source([]byte(p.Generate())) 107 if err != nil { 108 return 109 } 110 f = append(f, &File{ 111 Name: fmt.Sprintf("gen_intermediate.go"), 112 Content: b, 113 }) 114 return 115} 116 117func generateTyperInterface() *defs.InterfaceDef { 118 return &defs.InterfaceDef{ 119 Typename: "Typer", 120 Comment: "Typer supports common functions for determining an ActivityStream type", 121 F: []*defs.FunctionDef{ 122 { 123 Name: "TypeLen", 124 Return: []*defs.FunctionVarDef{{Name: "l", Type: "int"}}, 125 }, 126 { 127 Name: "GetType", 128 Args: []*defs.FunctionVarDef{{Name: "index", Type: "int"}}, 129 Return: []*defs.FunctionVarDef{{Name: "v", Type: "interface{}"}}, 130 }, 131 }, 132 } 133} 134 135func generateHasTypeFuncs(types []*defs.Type) (f []*defs.FunctionDef) { 136 var activityTypes []string 137 for _, t := range types { 138 t := t 139 if defs.IsActivity(t) { 140 activityTypes = append(activityTypes, t.Name) 141 } 142 f = append(f, &defs.FunctionDef{ 143 Name: fmt.Sprintf("HasType%s", t.Name), 144 Comment: fmt.Sprintf("HasType%s returns true if the Typer has a type of %s.", t.Name, t.Name), 145 Args: []*defs.FunctionVarDef{{Name: "t", Type: "Typer"}}, 146 Return: []*defs.FunctionVarDef{{Name: "b", Type: "bool"}}, 147 Body: func() string { 148 var b bytes.Buffer 149 b.WriteString("for i := 0; i < t.TypeLen(); i++ {\n") 150 b.WriteString("v := t.GetType(i)\n") 151 b.WriteString("if s, ok := v.(string); ok {\n") 152 b.WriteString(fmt.Sprintf("if s == \"%s\" {\n", t.Name)) 153 b.WriteString("return true\n") 154 b.WriteString("}\n") 155 b.WriteString("}\n") 156 b.WriteString("}\n") 157 b.WriteString("return false\n") 158 return b.String() 159 }, 160 }) 161 } 162 f = append(f, &defs.FunctionDef{ 163 Name: "IsActivityType", 164 Comment: "Returns true if the provided Typer is an Activity.", 165 Args: []*defs.FunctionVarDef{{Name: "t", Type: "Typer"}}, 166 Return: []*defs.FunctionVarDef{{Name: "b", Type: "bool"}}, 167 Body: func() string { 168 var b bytes.Buffer 169 b.WriteString(fmt.Sprintf("var activityTypes = []string{\"%s\"}\n", strings.Join(activityTypes, "\", \""))) 170 b.WriteString("hasType := make(map[string]bool, 1)\n") 171 b.WriteString("for i := 0; i < t.TypeLen(); i++ {\n") 172 b.WriteString("v := t.GetType(i)\n") 173 b.WriteString("if s, ok := v.(string); ok {\n") 174 b.WriteString("hasType[s] = true\n") 175 b.WriteString("}\n") 176 b.WriteString("}\n") 177 b.WriteString("for _, t := range activityTypes {\n") 178 b.WriteString("if hasType[t] {\n") 179 b.WriteString("return true\n") 180 b.WriteString("}\n") 181 b.WriteString("}\n") 182 b.WriteString("return false\n") 183 return b.String() 184 }, 185 }) 186 return 187} 188 189func generatePackageDefinition() *defs.PackageDef { 190 return &defs.PackageDef{ 191 Name: "vocab", 192 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.", 193 Imports: []string{"fmt", "time", "net/url", "regexp", "strconv", "math"}, 194 I: []*defs.InterfaceDef{ 195 { 196 Typename: "Serializer", 197 Comment: "Serializer implementations can serialize themselves to a generic map form.", 198 F: []*defs.FunctionDef{{ 199 Name: "Serialize", 200 Return: []*defs.FunctionVarDef{{"m", "map[string]interface{}"}, {"e", "error"}}, 201 }}, 202 }, 203 { 204 Typename: "Deserializer", 205 Comment: "Deserializer implementations can deserialize themselves from a generic map form.", 206 F: []*defs.FunctionDef{{ 207 Name: "Deserialize", 208 Args: []*defs.FunctionVarDef{{"m", "map[string]interface{}"}}, 209 Return: []*defs.FunctionVarDef{{"e", "error"}}, 210 }}, 211 }, 212 }, 213 } 214} 215 216func generateUnknownValueType() *defs.ValueType { 217 return &defs.ValueType{ 218 Name: "Unknown Value", 219 DefinitionType: "interface{}", 220 Zero: "nil", 221 DeserializeFn: &defs.FunctionDef{ 222 Name: unknownValueDeserializeFnName, 223 Comment: "unknownValueDeserialize transparently stores the object.", 224 Args: []*defs.FunctionVarDef{{"v", "interface{}"}}, 225 Return: []*defs.FunctionVarDef{{"o", "interface{}"}}, 226 Body: func() string { 227 var b bytes.Buffer 228 b.WriteString("o = v\n") 229 b.WriteString("return\n") 230 return b.String() 231 }, 232 }, 233 SerializeFn: &defs.FunctionDef{ 234 Name: unknownValueSerializeFnName, 235 Comment: "unknownValueSerialize transparently returns the object.", 236 Args: []*defs.FunctionVarDef{{"v", "interface{}"}}, 237 Return: []*defs.FunctionVarDef{{"o", "interface{}"}}, 238 Body: func() string { 239 var b bytes.Buffer 240 b.WriteString("o = v\n") 241 b.WriteString("return\n") 242 return b.String() 243 }, 244 }, 245 } 246} 247 248func generateUnknownType() *defs.StructDef { 249 u := &defs.StructDef{ 250 Typename: "Unknown", 251 Comment: "Unknown is an entry whose root type is unknown.", 252 M: []*defs.StructMember{{ 253 Name: "u", 254 Type: "map[string]interface{}", 255 Comment: "Raw unknown, untyped values", 256 }}, 257 } 258 u.F = append(u.F, []*defs.MemberFunctionDef{ 259 { 260 Name: "Serialize", 261 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.", 262 P: u, 263 Return: []*defs.FunctionVarDef{{"m", "map[string]interface{}"}, {"err", "error"}}, 264 Body: func() string { 265 var b bytes.Buffer 266 b.WriteString("m = t.u\n") 267 b.WriteString("return\n") 268 return b.String() 269 }, 270 }, 271 { 272 Name: "Deserialize", 273 Comment: "Deserialize populates this object from a map[string]interface{}", 274 P: u, 275 Args: []*defs.FunctionVarDef{{"m", "map[string]interface{}"}}, 276 Return: []*defs.FunctionVarDef{{"err", "error"}}, 277 Body: func() string { 278 var b bytes.Buffer 279 b.WriteString("t.u = m\n") 280 b.WriteString("return\n") 281 return b.String() 282 }, 283 }, 284 { 285 Name: "HasField", 286 Comment: "HasField determines whether the call to GetField is safe with the specified field", 287 P: u, 288 Args: []*defs.FunctionVarDef{{"f", "string"}}, 289 Return: []*defs.FunctionVarDef{{"ok", "bool"}}, 290 Body: func() string { 291 return "return t.u != nil && t.u[f] != nil\n" 292 }, 293 }, 294 { 295 Name: "GetField", 296 Comment: "GetField returns the unknown field value", 297 P: u, 298 Args: []*defs.FunctionVarDef{{"f", "string"}}, 299 Return: []*defs.FunctionVarDef{{"v", "interface{}"}}, 300 Body: func() string { 301 return "return t.u[f]" 302 }, 303 }, 304 { 305 Name: "SetField", 306 Comment: "SetField sets the unknown field value", 307 P: u, 308 Args: []*defs.FunctionVarDef{{"f", "string"}, {"i", "interface{}"}}, 309 Return: []*defs.FunctionVarDef{{"this", "*" + u.Typename}}, 310 Body: func() string { 311 var b bytes.Buffer 312 b.WriteString("if t.u == nil {\n") 313 b.WriteString("t.u = make(map[string]interface{})\n") 314 b.WriteString("}\n") 315 b.WriteString("t.u[f] = i\n") 316 b.WriteString("return t\n") 317 return b.String() 318 }, 319 }, 320 }...) 321 return u 322} 323 324func generateResolveObjectFunction(types []*defs.Type) *defs.FunctionDef { 325 return &defs.FunctionDef{ 326 Name: resolveObjectName, 327 Comment: fmt.Sprintf("%s turns a string type that extends Object into a concrete type.", resolveObjectName), 328 Args: []*defs.FunctionVarDef{{"s", "string"}}, 329 Return: []*defs.FunctionVarDef{{"i", "interface{}"}}, 330 Body: func() string { 331 var b bytes.Buffer 332 for _, r := range types { 333 if isAnObjectType(r) { 334 b.WriteString(fmt.Sprintf("if s == \"%s\" {\n", r.Name)) 335 b.WriteString(fmt.Sprintf("return &%s{}\n", r.Name)) 336 b.WriteString("}\n") 337 } 338 } 339 b.WriteString("return nil\n") 340 return b.String() 341 }, 342 } 343} 344 345func generateResolveLinkFunction(types []*defs.Type) *defs.FunctionDef { 346 return &defs.FunctionDef{ 347 Name: resolveLinkName, 348 Comment: fmt.Sprintf("%s turns a string type that extends Link into a concrete type.", resolveLinkName), 349 Args: []*defs.FunctionVarDef{{"s", "string"}}, 350 Return: []*defs.FunctionVarDef{{"i", "interface{}"}}, 351 Body: func() string { 352 var b bytes.Buffer 353 for _, r := range types { 354 if isALinkType(r) { 355 b.WriteString(fmt.Sprintf("if s == \"%s\" {\n", r.Name)) 356 b.WriteString(fmt.Sprintf("return &%s{}\n", r.Name)) 357 b.WriteString("}\n") 358 } 359 } 360 b.WriteString("return nil\n") 361 return b.String() 362 }, 363 } 364} 365 366func isAnObjectType(t *defs.Type) bool { 367 obj, _ := getIsAType(t) 368 return obj 369} 370 371func isALinkType(t *defs.Type) bool { 372 _, link := getIsAType(t) 373 return link 374} 375 376func getIsAType(t *defs.Type) (obj, link bool) { 377 if t.Name == objectName { 378 return true, false 379 } else if t.Name == linkName { 380 return false, true 381 } 382 for _, e := range t.Extends { 383 if obj, link = getIsAType(e); obj || link { 384 return 385 } 386 } 387 panic("Unknown root is-a-type") 388} 389