--- /dev/null
+/*\r
+ Copyright 2013 Daniel Wirtz <dcode@dcode.io>\r
+\r
+ Licensed under the Apache License, Version 2.0 (the "License");\r
+ you may not use this file except in compliance with the License.\r
+ You may obtain a copy of the License at\r
+\r
+ http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+ Unless required by applicable law or agreed to in writing, software\r
+ distributed under the License is distributed on an "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ See the License for the specific language governing permissions and\r
+ limitations under the License.\r
+ */\r
+var description = "Plain .proto descriptor";\r
+\r
+var ProtoBuf = require(__dirname+"/../../../index.js"),\r
+ util = require("../util.js");\r
+\r
+/**\r
+ * pbjs target: Plain .proto descriptor\r
+ * @exports pbjs/targets/proto\r
+ * @function\r
+ * @param {!ProtoBuf.Builder} builder Builder\r
+ * @param {!Object.<string,*>=} options Options\r
+ * @returns {string}\r
+ */\r
+var proto = module.exports = function(builder, options) {\r
+ options = options || {};\r
+ builder.resolveAll();\r
+\r
+ // Set the pointer to the lowest common namespace (with options)\r
+ var ptr = builder.ns;\r
+ while (ptr.children.length === 1 && Object.keys(ptr.options).length === 0 && ptr.children[0].className === "Namespace")\r
+ ptr = ptr.children[0];\r
+\r
+ var out = [];\r
+\r
+ function trim() {\r
+ out[out.length-1] = out[out.length-1].replace(/\n{2,}$/, "\n");\r
+ }\r
+\r
+ // Builds a set of top level options\r
+ function buildOptions(opt, indent) {\r
+ var keys;\r
+ if ((keys = Object.keys(opt)).length === 0)\r
+ return;\r
+ keys.forEach(function(key) {\r
+ if (!options.min)\r
+ out.push(indent);\r
+ out.push("option ", key, options.min ? "=" : " = ", value(opt[key]), options.min ? ";" : ";\n");\r
+ });\r
+ if (!options.min)\r
+ out[out.length-1] += "\n";\r
+ }\r
+\r
+ // Builds everything within a namespace\r
+ function buildNamespace(ns, indent) {\r
+ ns.getChildren(ProtoBuf.Reflect.Enum).forEach(function(enm) {\r
+ buildEnum(enm, indent);\r
+ });\r
+ ns.getChildren(ProtoBuf.Reflect.Message).forEach(function(msg) {\r
+ if (!msg.isGroup) // legacy groups are build within the respective field\r
+ buildMessage(msg, indent);\r
+ });\r
+ var exts = util.groupExtensions(ns);\r
+ if (exts !== null) {\r
+ Object.keys(exts).forEach(function(extFqn) {\r
+ var extMsg = ns.resolve(extFqn),\r
+ extFields = exts[extFqn];\r
+ if (!options.min)\r
+ out.push(indent);\r
+ out.push("extend ", ns.qn(extMsg), options.min ? "{" : " {\n");\r
+ extFields.forEach(function(extField) {\r
+ buildMessageField(ns, extField, indent+" ", false);\r
+ });\r
+ if (!options.min)\r
+ out.push(indent);\r
+ out.push(options.min ? "}" : "}\n\n");\r
+ });\r
+ }\r
+ ns.getChildren(ProtoBuf.Reflect.Service).forEach(function(svc) {\r
+ buildService(svc, indent);\r
+ });\r
+ ns.getChildren(ProtoBuf.Reflect.Namespace).forEach(function(innerNs) {\r
+ if (innerNs.className !== "Namespace")\r
+ return;\r
+ if (!options.min)\r
+ out.push(indent);\r
+ out.push("message ", innerNs.name, options.min ? "{" : " {\n");\r
+ buildNamespace(innerNs, indent+" ");\r
+ if (!options.min)\r
+ out.push(indent);\r
+ out.push(options.min ? "}" : "}\n");\r
+ });\r
+ trim();\r
+ }\r
+\r
+ // Builds a message\r
+ function buildMessage(msg, indent) {\r
+ if (!msg.isGroup) {\r
+ if (!options.min)\r
+ out.push(indent);\r
+ out.push("message ", msg.name);\r
+ }\r
+ out.push(options.min ? "{" : " {\n");\r
+ buildOptions(msg.options, indent+" ");\r
+ var n = 0, oneofFields = [];\r
+ msg.getChildren(ProtoBuf.Reflect.Message.OneOf).forEach(function(oneof) {\r
+ if (!options.min)\r
+ out.push(indent, " ");\r
+ out.push("oneof ", oneof.name, options.min ? "{" : " {\n");\r
+ oneof.fields.forEach(function(fld) {\r
+ buildMessageField(msg, fld, indent+" ", true);\r
+ oneofFields.push(fld);\r
+ });\r
+ if (!options.min)\r
+ out.push(indent, " ");\r
+ out.push(options.min ? "}" : "}\n");\r
+ });\r
+ msg.getChildren(ProtoBuf.Reflect.Message.Field).forEach(function(fld) {\r
+ if (fld instanceof ProtoBuf.Reflect.Message.ExtensionField)\r
+ return;\r
+ if (oneofFields.indexOf(fld) >= 0)\r
+ return;\r
+ buildMessageField(msg, fld, indent+" ", false);\r
+ n++;\r
+ });\r
+ if (n > 0 && !options.min)\r
+ out[out.length-1] += "\n";\r
+ if (msg.extensions) { // array of ranges\r
+ if (!options.min)\r
+ out.push(indent, " ");\r
+ out.push("extensions ");\r
+ msg.extensions.forEach(function(range, index) {\r
+ if (index > 0)\r
+ out.push(options.min ? "," : ", ");\r
+ out.push(value(range[0]));\r
+ if (range[1] !== range[0])\r
+ out.push(" to ", range[1] === ProtoBuf.ID_MAX ? "max" : value(range[1]));\r
+ });\r
+ out.push(options.min ? ";" : ";\n\n");\r
+ }\r
+ buildNamespace(msg, indent+" ");\r
+ if (!options.min)\r
+ out.push(indent);\r
+ out.push(options.min ? "}" : "}\n\n");\r
+ }\r
+\r
+ // Builds a message field\r
+ function buildMessageField(msg, fld, indent, isOneOf) {\r
+ var isGroup = false;\r
+ if (!options.min)\r
+ out.push(indent);\r
+ if (!isOneOf)\r
+ out.push(fld.required ? "required " : (fld.repeated ? "repeated " : "optional "));\r
+ if (fld.resolvedType !== null) {\r
+ if (fld.resolvedType instanceof ProtoBuf.Reflect.Message && fld.resolvedType.isGroup) {\r
+ // inline legacy groups\r
+ out.push("group ");\r
+ isGroup = true;\r
+ }\r
+ out.push(msg.qn(fld.resolvedType));\r
+ } else\r
+ out.push(fld.type['name']);\r
+ if (!isGroup)\r
+ out.push(" ", fld instanceof ProtoBuf.Reflect.Message.ExtensionField ? fld.name.substring(fld.name.lastIndexOf(".")+1) : fld.name);\r
+ out.push(options.min ? "=" : " = ", fld.id);\r
+ if (isGroup) // inline\r
+ buildMessage(fld.resolvedType, indent);\r
+ else {\r
+ var keys = Object.keys(fld.options);\r
+ if (keys.length > 0) {\r
+ out.push(options.min ? "[" : " [");\r
+ var n = 0;\r
+ keys.forEach(function (key) {\r
+ if (n > 0)\r
+ out.push(options.min ? "," : ", ");\r
+ out.push(key, options.min ? "=" : " = ",\r
+ // BEWARE: Monkey patch for string enum defaults\r
+ key === "default" && fld.type === ProtoBuf.TYPES["enum"] && typeof fld.options[key] === 'string' ? fld.options[key] : value(fld.options[key])\r
+ );\r
+ n++;\r
+ });\r
+ out.push("]");\r
+ }\r
+ out.push(options.min ? ";" : ";\n");\r
+ }\r
+ }\r
+\r
+ // Builds an enum\r
+ function buildEnum(enm, indent) {\r
+ if (!options.min)\r
+ out.push(indent);\r
+ out.push("enum ", enm.name, options.min ? "{" : " {\n");\r
+ buildOptions(enm.options, indent+" ");\r
+ enm.getChildren(ProtoBuf.Reflect.Enum.Value).forEach(function(val) {\r
+ if (!options.min)\r
+ out.push(indent, " ");\r
+ out.push(val.name, options.min ? "=" : " = ", val.id, options.min? ";" : ";\n");\r
+ });\r
+ if (!options.min)\r
+ out.push(indent);\r
+ out.push(options.min ? "}" : "}\n\n");\r
+ }\r
+\r
+ // Builds a service\r
+ function buildService(svc, indent) {\r
+ if (!options.min)\r
+ out.push(indent);\r
+ out.push("service ", svc.name, options.min ? "{" : " {\n");\r
+ buildOptions(svc.options, indent+" ");\r
+ svc.getChildren(ProtoBuf.Reflect.Service.RPCMethod).forEach(function(rpc) {\r
+ if (!options.min)\r
+ out.push(indent+" ");\r
+ out.push("rpc ", rpc.name, "(", svc.qn(rpc.resolvedRequestType), ") returns(", svc.qn(rpc.resolvedResponseType), ")");\r
+ var keys = Object.keys(rpc.options);\r
+ if (keys.length === 0) {\r
+ out.push(options.min ? ";" : ";\n")\r
+ } else {\r
+ out.push(options.min ? "{" : " {\n");\r
+ buildOptions(rpc.options, indent+" ");\r
+ trim();\r
+ if (!options.min)\r
+ out.push(indent+" ");\r
+ out.push(options.min ? "}" : "}\n");\r
+ }\r
+ if (!options.min)\r
+ out[out.length-1] += "\n";\r
+ });\r
+ trim();\r
+ out.push(options.min ? "}" : "}\n");\r
+ }\r
+\r
+ // Start by building the package namespace\r
+ var pkg = ptr.fqn().substring(1);\r
+ if (pkg !== "")\r
+ out.push("package ", pkg, options.min ? ";" : ";\n\n");\r
+ buildOptions(ptr.options, "");\r
+ buildNamespace(ptr, "");\r
+ return out.join('');\r
+};\r
+\r
+/**\r
+ * Module description.\r
+ * @type {string}\r
+ */\r
+proto.description = description;\r
+\r
+/**\r
+ * Converts a JavaScript value to a .proto value.\r
+ * @param {*} v Value\r
+ * @returns {string} Dot proto value\r
+ */\r
+function value(v) {\r
+ switch (typeof v) {\r
+ case 'boolean':\r
+ return v ? 'true' : 'false';\r
+ case 'number':\r
+ return v.toString();\r
+ case 'string':\r
+ return '"'+v.replace(/"/g, '\\"')+'"';\r
+ default:\r
+ throw new Error("illegal value type: "+typeof(v));\r
+ }\r
+}\r