aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorOskari Timperi <oskari.timperi@iki.fi>2018-04-08 23:01:09 +0300
committerOskari Timperi <oskari.timperi@iki.fi>2018-04-08 23:01:09 +0300
commit2684b0e6b03352e6bb079101a14f395ef56ef877 (patch)
tree8e1e3108b93a2c03eaead5b6fe17dce3eb990ab7
parent641e564c7d231b536e9e44c04c95307fef7cfe06 (diff)
downloadnimpb_protoc-2684b0e6b03352e6bb079101a14f395ef56ef877.tar.gz
nimpb_protoc-2684b0e6b03352e6bb079101a14f395ef56ef877.zip
Initial JSON parsing support
-rw-r--r--src/nimpb_buildpkg/plugin.nim75
1 files changed, 75 insertions, 0 deletions
diff --git a/src/nimpb_buildpkg/plugin.nim b/src/nimpb_buildpkg/plugin.nim
index abdad07..d375582 100644
--- a/src/nimpb_buildpkg/plugin.nim
+++ b/src/nimpb_buildpkg/plugin.nim
@@ -31,6 +31,7 @@ type
defaultValue: string
message: Message
jsonName: string
+ protoName: string
Message = ref object
names: Names
@@ -118,6 +119,21 @@ proc isNumeric(field: Field): bool =
result = true
else: discard
+proc isFloat(field: Field): bool =
+ case field.ftype
+ of google_protobuf_FieldDescriptorProtoType.TypeDouble: result = true
+ of google_protobuf_FieldDescriptorProtoType.TypeFloat: result = true
+ else: result = false
+
+proc isUnsigned(field: Field): bool =
+ case field.ftype
+ of google_protobuf_FieldDescriptorProtoType.TypeUInt64,
+ google_protobuf_FieldDescriptorProtoType.TypeFixed64,
+ google_protobuf_FieldDescriptorProtoType.TypeFixed32,
+ google_protobuf_FieldDescriptorProtoType.TypeUInt32:
+ result = true
+ else: result = false
+
proc isMapEntry(message: Message): bool =
result = message.mapEntry
@@ -145,6 +161,11 @@ proc nimTypeName(field: Field): string =
of google_protobuf_FieldDescriptorProtoType.TypeSInt32: result = "int32"
of google_protobuf_FieldDescriptorProtoType.TypeSInt64: result = "int64"
+proc mapKeyField(field: Field): Field =
+ for f in field.mapEntry.fields:
+ if f.name == "key":
+ return f
+
proc mapKeyType(field: Field): string =
for f in field.mapEntry.fields:
if f.name == "key":
@@ -301,6 +322,7 @@ proc sizeOfProc(field: Field): string =
proc newField(file: ProtoFile, message: Message, desc: google_protobuf_FieldDescriptorProto): Field =
new(result)
+ result.protoName = desc.name
result.name = desc.name
result.number = desc.number
result.label = desc.label
@@ -919,6 +941,56 @@ iterator genMessageToJsonProc(msg: Message): string =
yield ""
+iterator genMessageFromJsonProc(msg: Message): string =
+ yield &"proc parse{msg.names}FromJson*(obj: JsonNode): {msg.names} ="
+ yield indent(&"result = new{msg.names}()", 4)
+ yield indent(&"var node: JsonNode", 4)
+
+ proc fieldFromJson(field: Field, n: string): string =
+ if isMessage(field):
+ result = &"parse{field.typeName}FromJson({n})"
+ elif isEnum(field):
+ result = &"parseEnum[{field.typeName}]({n})"
+ elif isFloat(field):
+ result = &"parseFloat({n})"
+ elif field.ftype == google_protobuf_FieldDescriptorProto_Type.TypeBool:
+ result = &"parseBool({n})"
+ elif isNumeric(field):
+ result = &"parseInt[{field.nimTypeName}]({n})"
+ elif field.ftype == google_protobuf_FieldDescriptorProto_Type.TypeString:
+ result = &"parseString({n})"
+ elif field.ftype == google_protobuf_FieldDescriptorProto_Type.TypeBytes:
+ result = &"parseBytes({n})"
+
+ for field in msg.fields:
+ yield indent(&"node = getJsonField(obj, \"{field.protoName}\", \"{field.jsonName}\")", 4)
+ yield indent(&"if node != nil:", 4)
+ if isMapEntry(field):
+ yield indent("if node.kind != JObject:", 8)
+ yield indent("raise newException(ValueError, \"not an object\")", 12)
+ yield indent("for keyString, valueNode in node:", 8)
+ let keyField = mapKeyField(field)
+ if isUnsigned(keyField):
+ yield indent(&"let key = {keyField.nimTypeName}(parseBiggestUInt(keyString))", 12)
+ elif isNumeric(keyField):
+ yield indent(&"let key = {keyField.nimTypeName}(parseBiggestInt(keyString))", 12)
+ elif keyField.ftype == google_protobuf_FieldDescriptorProto_Type.TypeString:
+ yield indent("let key = keyString", 12)
+ let valueField = mapValueField(field)
+ let parser = fieldFromJson(valueField, "valueNode")
+ yield indent(&"result.{field.name}[key] = {parser}", 12)
+ elif isRepeated(field):
+ let parser = fieldFromJson(field, "value")
+ yield indent("if node.kind != JArray:", 8)
+ yield indent("raise newException(ValueError, \"not an array\")", 12)
+ yield indent("for value in node:", 8)
+ yield indent(&"add{field.name}(result, {parser})", 12)
+ else:
+ let parser = fieldFromJson(field, "node")
+ yield indent(&"set{field.name}(result, {parser})", 8)
+
+ yield ""
+
iterator genMessageProcForwards(msg: Message): string =
# TODO: can we be more intelligent and only forward declare the minimum set
# of procs?
@@ -930,6 +1002,7 @@ iterator genMessageProcForwards(msg: Message): string =
yield &"proc sizeOf{msg.names}*(message: {msg.names}): uint64"
if shouldGenerateJsonProcs($msg.names):
yield &"proc toJson*(message: {msg.names}): JsonNode"
+ yield &"proc parse{msg.names}FromJson*(obj: JsonNode): {msg.names}"
else:
let
key = mapKeyField(msg)
@@ -963,6 +1036,7 @@ iterator genProcs(msg: Message): string =
if shouldGenerateJsonProcs($msg.names):
for line in genMessageToJsonProc(msg): yield line
+ for line in genMessageFromJsonProc(msg): yield line
yield &"proc serialize*(message: {msg.names}): string ="
yield indent("let", 4)
@@ -1008,6 +1082,7 @@ proc processFile(fdesc: google_protobuf_FileDescriptorProto,
addLine(result.data, "import base64")
addLine(result.data, "import intsets")
addLine(result.data, "import json")
+ addLine(result.data, "import strutils")
if hasMaps:
addLine(result.data, "import tables")
addLine(result.data, "export tables")