1 // Copyright (c) 2020 Cisco and/or its affiliates.
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at:
7 // http://www.apache.org/licenses/LICENSE-2.0
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
24 "github.com/bennyscetbun/jsongo"
25 "github.com/sirupsen/logrus"
28 var Logger *logrus.Logger
31 if strings.Contains(os.Getenv("DEBUG_GOVPP"), "parser") {
32 Logger = logrus.StandardLogger()
36 func logf(f string, v ...interface{}) {
38 Logger.Debugf(f, v...)
44 objAPIVersion = "vl_api_version"
46 objMessages = "messages"
49 objServices = "services"
50 objAliases = "aliases"
51 objOptions = "options"
52 objImports = "imports"
55 messageFieldCrc = "crc"
58 aliasFieldLength = "length"
59 aliasFieldType = "type"
62 serviceFieldReply = "reply"
63 serviceFieldStream = "stream"
64 serviceFieldStreamMsg = "stream_msg"
65 serviceFieldEvents = "events"
70 fileOptionVersion = "version"
73 fieldOptionLimit = "limit"
74 fieldOptionDefault = "default"
77 serviceReplyNull = "null"
80 func parseJSON(data []byte) (module *File, err error) {
82 if e := recover(); e != nil {
83 err = fmt.Errorf("recovered panic: %v", e)
87 // parse JSON data into objects
88 jsonRoot := new(jsongo.Node)
89 if err := json.Unmarshal(data, jsonRoot); err != nil {
90 return nil, fmt.Errorf("unmarshalling JSON failed: %v", err)
93 logf("file contents:")
94 for _, key := range jsonRoot.GetKeys() {
95 if jsonRoot.At(key).Len() > 0 {
96 logf(" - %2d %s", jsonRoot.At(key).Len(), key)
103 if crc := jsonRoot.At(objAPIVersion); crc.GetType() == jsongo.TypeValue {
104 module.CRC = crc.Get().(string)
108 opt := jsonRoot.Map(objOptions)
109 if opt.GetType() == jsongo.TypeMap {
110 module.Options = make(map[string]string, 0)
111 for _, key := range opt.GetKeys() {
112 optionsNode := opt.At(key)
113 optionKey := key.(string)
114 optionValue := optionsNode.Get().(string)
115 module.Options[optionKey] = optionValue
120 imports := jsonRoot.Map(objImports)
121 module.Imports = make([]string, 0)
122 imported := make(map[string]struct{})
123 for i := 0; i < imports.Len(); i++ {
124 importNode := imports.At(i)
125 imp, err := parseImport(importNode)
129 if _, ok := imported[*imp]; ok {
130 logf("duplicate import found: %v", *imp)
133 imported[*imp] = struct{}{}
134 module.Imports = append(module.Imports, *imp)
137 // avoid duplicate objects
138 known := make(map[string]struct{})
139 exists := func(name string) bool {
140 if _, ok := known[name]; ok {
141 logf("duplicate object found: %v", name)
144 known[name] = struct{}{}
149 enumsNode := jsonRoot.Map(objEnums)
150 module.EnumTypes = make([]EnumType, 0)
151 for i := 0; i < enumsNode.Len(); i++ {
152 enumNode := enumsNode.At(i)
153 enum, err := parseEnum(enumNode)
157 if exists(enum.Name) {
160 module.EnumTypes = append(module.EnumTypes, *enum)
164 aliasesNode := jsonRoot.Map(objAliases)
165 if aliasesNode.GetType() == jsongo.TypeMap {
166 module.AliasTypes = make([]AliasType, 0)
167 for _, key := range aliasesNode.GetKeys() {
168 aliasNode := aliasesNode.At(key)
169 aliasName := key.(string)
170 alias, err := parseAlias(aliasName, aliasNode)
174 if exists(alias.Name) {
177 module.AliasTypes = append(module.AliasTypes, *alias)
181 // parse struct types
182 typesNode := jsonRoot.Map(objTypes)
183 module.StructTypes = make([]StructType, 0)
184 for i := 0; i < typesNode.Len(); i++ {
185 typNode := typesNode.At(i)
186 structyp, err := parseStruct(typNode)
190 if exists(structyp.Name) {
193 module.StructTypes = append(module.StructTypes, *structyp)
197 unionsNode := jsonRoot.Map(objUnions)
198 module.UnionTypes = make([]UnionType, 0)
199 for i := 0; i < unionsNode.Len(); i++ {
200 unionNode := unionsNode.At(i)
201 union, err := parseUnion(unionNode)
205 if exists(union.Name) {
208 module.UnionTypes = append(module.UnionTypes, *union)
212 messagesNode := jsonRoot.Map(objMessages)
213 if messagesNode.GetType() == jsongo.TypeArray {
214 module.Messages = make([]Message, messagesNode.Len())
215 for i := 0; i < messagesNode.Len(); i++ {
216 msgNode := messagesNode.At(i)
217 msg, err := parseMessage(msgNode)
221 module.Messages[i] = *msg
226 servicesNode := jsonRoot.Map(objServices)
227 if servicesNode.GetType() == jsongo.TypeMap {
228 module.Service = &Service{
229 RPCs: make([]RPC, servicesNode.Len()),
231 for i, key := range servicesNode.GetKeys() {
232 rpcNode := servicesNode.At(key)
233 rpcName := key.(string)
234 svc, err := parseServiceRPC(rpcName, rpcNode)
238 module.Service.RPCs[i] = *svc
245 // parseImport parses VPP binary API import from JSON node
246 func parseImport(importNode *jsongo.Node) (*string, error) {
247 if importNode.GetType() != jsongo.TypeValue {
248 return nil, errors.New("invalid JSON for import specified")
251 importName, ok := importNode.Get().(string)
253 return nil, fmt.Errorf("import name is %T, not a string", importNode.Get())
256 return &importName, nil
259 // parseEnum parses VPP binary API enum object from JSON node
260 func parseEnum(enumNode *jsongo.Node) (*EnumType, error) {
261 if enumNode.Len() == 0 || enumNode.At(0).GetType() != jsongo.TypeValue {
262 return nil, errors.New("invalid JSON for enum specified")
265 enumName, ok := enumNode.At(0).Get().(string)
267 return nil, fmt.Errorf("enum name is %T, not a string", enumNode.At(0).Get())
269 enumType, ok := enumNode.At(enumNode.Len() - 1).At("enumtype").Get().(string)
271 return nil, fmt.Errorf("enum type invalid or missing")
279 // loop through enum entries, skip first (name) and last (enumtype)
280 for j := 1; j < enumNode.Len()-1; j++ {
281 if enumNode.At(j).GetType() == jsongo.TypeArray {
282 entry := enumNode.At(j)
284 if entry.Len() < 2 || entry.At(0).GetType() != jsongo.TypeValue || entry.At(1).GetType() != jsongo.TypeValue {
285 return nil, errors.New("invalid JSON for enum entry specified")
288 entryName, ok := entry.At(0).Get().(string)
290 return nil, fmt.Errorf("enum entry name is %T, not a string", entry.At(0).Get())
292 entryVal := entry.At(1).Get().(float64)
294 enum.Entries = append(enum.Entries, EnumEntry{
296 Value: uint32(entryVal),
304 // parseUnion parses VPP binary API union object from JSON node
305 func parseUnion(unionNode *jsongo.Node) (*UnionType, error) {
306 if unionNode.Len() == 0 || unionNode.At(0).GetType() != jsongo.TypeValue {
307 return nil, errors.New("invalid JSON for union specified")
310 unionName, ok := unionNode.At(0).Get().(string)
312 return nil, fmt.Errorf("union name is %T, not a string", unionNode.At(0).Get())
319 // loop through union fields, skip first (name)
320 for j := 1; j < unionNode.Len(); j++ {
321 if unionNode.At(j).GetType() == jsongo.TypeArray {
322 fieldNode := unionNode.At(j)
324 field, err := parseField(fieldNode)
329 union.Fields = append(union.Fields, *field)
336 // parseStruct parses VPP binary API type object from JSON node
337 func parseStruct(typeNode *jsongo.Node) (*StructType, error) {
338 if typeNode.Len() == 0 || typeNode.At(0).GetType() != jsongo.TypeValue {
339 return nil, errors.New("invalid JSON for type specified")
342 typeName, ok := typeNode.At(0).Get().(string)
344 return nil, fmt.Errorf("type name is %T, not a string", typeNode.At(0).Get())
351 // loop through type fields, skip first (name)
352 for j := 1; j < typeNode.Len(); j++ {
353 if typeNode.At(j).GetType() == jsongo.TypeArray {
354 fieldNode := typeNode.At(j)
356 field, err := parseField(fieldNode)
361 typ.Fields = append(typ.Fields, *field)
368 // parseAlias parses VPP binary API alias object from JSON node
369 func parseAlias(aliasName string, aliasNode *jsongo.Node) (*AliasType, error) {
370 if aliasNode.Len() == 0 || aliasNode.At(aliasFieldType).GetType() != jsongo.TypeValue {
371 return nil, errors.New("invalid JSON for alias specified")
378 if typeNode := aliasNode.At(aliasFieldType); typeNode.GetType() == jsongo.TypeValue {
379 typ, ok := typeNode.Get().(string)
381 return nil, fmt.Errorf("alias type is %T, not a string", typeNode.Get())
388 if lengthNode := aliasNode.At(aliasFieldLength); lengthNode.GetType() == jsongo.TypeValue {
389 length, ok := lengthNode.Get().(float64)
391 return nil, fmt.Errorf("alias length is %T, not a float64", lengthNode.Get())
393 alias.Length = int(length)
399 // parseMessage parses VPP binary API message object from JSON node
400 func parseMessage(msgNode *jsongo.Node) (*Message, error) {
401 if msgNode.Len() == 0 || msgNode.At(0).GetType() != jsongo.TypeValue {
402 return nil, errors.New("invalid JSON for message specified")
405 msgName, ok := msgNode.At(0).Get().(string)
407 return nil, fmt.Errorf("message name is %T, not a string", msgNode.At(0).Get())
409 msgCRC, ok := msgNode.At(msgNode.Len() - 1).At(messageFieldCrc).Get().(string)
412 return nil, fmt.Errorf("message crc invalid or missing")
420 // loop through message fields, skip first (name) and last (crc)
421 for j := 1; j < msgNode.Len()-1; j++ {
422 if msgNode.At(j).GetType() == jsongo.TypeArray {
423 fieldNode := msgNode.At(j)
425 field, err := parseField(fieldNode)
430 msg.Fields = append(msg.Fields, *field)
437 // parseField parses VPP binary API object field from JSON node
438 func parseField(field *jsongo.Node) (*Field, error) {
439 if field.Len() < 2 || field.At(0).GetType() != jsongo.TypeValue || field.At(1).GetType() != jsongo.TypeValue {
440 return nil, errors.New("invalid JSON for field specified")
443 fieldType, ok := field.At(0).Get().(string)
445 return nil, fmt.Errorf("field type is %T, not a string", field.At(0).Get())
447 fieldName, ok := field.At(1).Get().(string)
449 return nil, fmt.Errorf("field name is %T, not a string", field.At(1).Get())
457 if field.Len() >= 3 {
458 switch field.At(2).GetType() {
459 case jsongo.TypeValue:
460 fieldLength, ok := field.At(2).Get().(float64)
462 return nil, fmt.Errorf("field length is %T, not float64", field.At(2).Get())
464 f.Length = int(fieldLength)
468 fieldMeta := field.At(2)
470 for _, key := range fieldMeta.GetKeys() {
471 metaNode := fieldMeta.At(key)
472 metaName := key.(string)
473 metaValue := metaNode.Get()
476 case fieldOptionLimit:
477 metaValue = int(metaNode.Get().(float64))
478 case fieldOptionDefault:
479 metaValue = metaNode.Get()
481 logrus.Warnf("unknown meta info (%s=%v) for field (%s)", metaName, metaValue, fieldName)
485 f.Meta = map[string]interface{}{}
487 f.Meta[metaName] = metaValue
490 return nil, errors.New("invalid JSON for field specified")
493 if field.Len() >= 4 {
494 fieldLengthFrom, ok := field.At(3).Get().(string)
496 return nil, fmt.Errorf("field length from is %T, not a string", field.At(3).Get())
498 f.SizeFrom = fieldLengthFrom
504 // parseServiceRPC parses VPP binary API service object from JSON node
505 func parseServiceRPC(rpcName string, rpcNode *jsongo.Node) (*RPC, error) {
506 if rpcNode.Len() == 0 || rpcNode.At(serviceFieldReply).GetType() != jsongo.TypeValue {
507 return nil, errors.New("invalid JSON for service RPC specified")
515 if replyNode := rpcNode.At(serviceFieldReply); replyNode.GetType() == jsongo.TypeValue {
516 reply, ok := replyNode.Get().(string)
518 return nil, fmt.Errorf("service RPC reply is %T, not a string", replyNode.Get())
520 if reply != serviceReplyNull {
526 if streamNode := rpcNode.At(serviceFieldStream); streamNode.GetType() == jsongo.TypeValue {
528 rpc.Stream, ok = streamNode.Get().(bool)
530 return nil, fmt.Errorf("service RPC stream is %T, not a boolean", streamNode.Get())
535 if streamMsgNode := rpcNode.At(serviceFieldStreamMsg); streamMsgNode.GetType() == jsongo.TypeValue {
537 rpc.StreamMsg, ok = streamMsgNode.Get().(string)
539 return nil, fmt.Errorf("service RPC stream msg is %T, not a string", streamMsgNode.Get())
543 // events service (event subscription)
544 if eventsNode := rpcNode.At(serviceFieldEvents); eventsNode.GetType() == jsongo.TypeArray {
545 for j := 0; j < eventsNode.Len(); j++ {
546 event := eventsNode.At(j).Get().(string)
547 rpc.Events = append(rpc.Events, event)