aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorOskari Timperi <oskari.timperi@iki.fi>2018-04-08 16:07:44 +0300
committerOskari Timperi <oskari.timperi@iki.fi>2018-04-08 16:07:44 +0300
commit641e564c7d231b536e9e44c04c95307fef7cfe06 (patch)
treefb854dcbb0bda3af1013b2de489f331671d77171
parent2d9ca1dabaabf85f3978242659eca0bed08b3371 (diff)
downloadnimpb_protoc-641e564c7d231b536e9e44c04c95307fef7cfe06.tar.gz
nimpb_protoc-641e564c7d231b536e9e44c04c95307fef7cfe06.zip
Add initial message to JSON serialization support
-rw-r--r--src/nimpb_buildpkg/plugin.nim80
1 files changed, 80 insertions, 0 deletions
diff --git a/src/nimpb_buildpkg/plugin.nim b/src/nimpb_buildpkg/plugin.nim
index 7354fa8..abdad07 100644
--- a/src/nimpb_buildpkg/plugin.nim
+++ b/src/nimpb_buildpkg/plugin.nim
@@ -30,6 +30,7 @@ type
mapEntry: Message
defaultValue: string
message: Message
+ jsonName: string
Message = ref object
names: Names
@@ -149,6 +150,11 @@ proc mapKeyType(field: Field): string =
if f.name == "key":
return f.nimTypeName
+proc mapValueField(field: Field): Field =
+ for f in field.mapEntry.fields:
+ if f.name == "value":
+ return f
+
proc mapValueType(field: Field): string =
for f in field.mapEntry.fields:
if f.name == "value":
@@ -343,6 +349,9 @@ proc newField(file: ProtoFile, message: Message, desc: google_protobuf_FieldDesc
if hasDefaultValue(desc):
result.defaultValue = desc.default_value
+ if hasJsonName(desc):
+ result.jsonName = desc.jsonName
+
log(&"newField {result.name} {$result.ftype} {result.typeName} PACKED={result.packed} SYNTAX={file.syntax}")
proc newOneof(name: string): Oneof =
@@ -847,6 +856,69 @@ iterator genSizeOfMessageProc(msg: Message): string =
yield ""
+proc shouldGenerateJsonProcs(typeName: string): bool =
+ const wktsHavingCustomProcs = [
+ "google_protobuf_Any",
+ "google_protobuf_BoolValue",
+ "google_protobuf_BytesValue",
+ "google_protobuf_DoubleValue",
+ "google_protobuf_Duration",
+ "google_protobuf_FieldMask",
+ "google_protobuf_FloatValue",
+ "google_protobuf_Int32Value",
+ "google_protobuf_Int64Value",
+ "google_protobuf_ListValue",
+ "google_protobuf_NullValue",
+ "google_protobuf_StringValue",
+ "google_protobuf_Struct",
+ "google_protobuf_Timestamp",
+ "google_protobuf_UInt32Value",
+ "google_protobuf_UInt64Value",
+ "google_protobuf_Value",
+ ]
+
+ return typeName notin wktsHavingCustomProcs
+
+iterator genMessageToJsonProc(msg: Message): string =
+ yield &"proc toJson*(message: {msg.names}): JsonNode ="
+ yield indent("result = newJObject()", 4)
+
+ proc fieldToJson(field: Field, v: string): string =
+ case field.ftype
+ of google_protobuf_FieldDescriptorProto_Type.TypeMessage,
+ google_protobuf_FieldDescriptorProto_Type.TypeInt64,
+ google_protobuf_FieldDescriptorProto_Type.TypeUInt64,
+ google_protobuf_FieldDescriptorProto_Type.TypeSFixed64,
+ google_protobuf_FieldDescriptorProto_Type.TypeFixed64,
+ google_protobuf_FieldDescriptorProto_Type.TypeDouble,
+ google_protobuf_FieldDescriptorProto_Type.TypeFloat:
+ result = &"toJson({v})"
+ of google_protobuf_FieldDescriptorProto_Type.TypeEnum:
+ result = &"%(${v})"
+ else:
+ result = &"%{v}"
+
+ for field in msg.fields:
+ yield indent(&"if has{field.name}(message):", 4)
+ if isMapEntry(field):
+ yield indent("let obj = newJObject()", 8)
+ yield indent(&"for key, value in message.{field.name}:", 8)
+ let f = mapValueField(field)
+ let j = fieldToJson(f, "value")
+ yield indent(&"obj[$key] = {j}", 12)
+ yield indent(&"result[\"{field.jsonName}\"] = obj", 8)
+ elif isRepeated(field):
+ yield indent(&"let arr = newJArray()", 8)
+ yield indent(&"for value in message.{field.name}:", 8)
+ let v = fieldToJson(field, "value")
+ yield indent(&"add(arr, {v})", 12)
+ yield indent(&"result[\"{field.jsonName}\"] = arr", 8)
+ else:
+ let v = fieldToJson(field, &"message.{field.name}")
+ yield indent(&"result[\"{field.jsonName}\"] = {v}", 8)
+
+ yield ""
+
iterator genMessageProcForwards(msg: Message): string =
# TODO: can we be more intelligent and only forward declare the minimum set
# of procs?
@@ -856,6 +928,8 @@ iterator genMessageProcForwards(msg: Message): string =
yield &"proc write{msg.names}*(stream: ProtobufStream, message: {msg.names})"
yield &"proc read{msg.names}*(stream: ProtobufStream): {msg.names}"
yield &"proc sizeOf{msg.names}*(message: {msg.names}): uint64"
+ if shouldGenerateJsonProcs($msg.names):
+ yield &"proc toJson*(message: {msg.names}): JsonNode"
else:
let
key = mapKeyField(msg)
@@ -887,6 +961,9 @@ iterator genProcs(msg: Message): string =
for line in genWriteMessageProc(msg): yield line
for line in genReadMessageProc(msg): yield line
+ if shouldGenerateJsonProcs($msg.names):
+ for line in genMessageToJsonProc(msg): yield line
+
yield &"proc serialize*(message: {msg.names}): string ="
yield indent("let", 4)
yield indent("ss = newStringStream()", 8)
@@ -928,12 +1005,15 @@ proc processFile(fdesc: google_protobuf_FileDescriptorProto,
addLine(result.data, "# Generated by protoc_gen_nim. Do not edit!")
addLine(result.data, "")
+ addLine(result.data, "import base64")
addLine(result.data, "import intsets")
+ addLine(result.data, "import json")
if hasMaps:
addLine(result.data, "import tables")
addLine(result.data, "export tables")
addLine(result.data, "")
addLine(result.data, "import nimpb/nimpb")
+ addLine(result.data, "import nimpb/json as nimpb_json")
addLine(result.data, "")
for dep in fdesc.dependency: