2 Copyright 2013 Daniel Wirtz <dcode@dcode.io>
\r
4 Licensed under the Apache License, Version 2.0 (the "License");
\r
5 you may not use this file except in compliance with the License.
\r
6 You may obtain a copy of the License at
\r
8 http://www.apache.org/licenses/LICENSE-2.0
\r
10 Unless required by applicable law or agreed to in writing, software
\r
11 distributed under the License is distributed on an "AS IS" BASIS,
\r
12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
\r
13 See the License for the specific language governing permissions and
\r
14 limitations under the License.
\r
16 var description = "Plain .proto descriptor";
\r
18 var ProtoBuf = require(__dirname+"/../../../index.js"),
\r
19 util = require("../util.js");
\r
22 * pbjs target: Plain .proto descriptor
\r
23 * @exports pbjs/targets/proto
\r
25 * @param {!ProtoBuf.Builder} builder Builder
\r
26 * @param {!Object.<string,*>=} options Options
\r
29 var proto = module.exports = function(builder, options) {
\r
30 options = options || {};
\r
31 builder.resolveAll();
\r
33 // Set the pointer to the lowest common namespace (with options)
\r
34 var ptr = builder.ns;
\r
35 while (ptr.children.length === 1 && Object.keys(ptr.options).length === 0 && ptr.children[0].className === "Namespace")
\r
36 ptr = ptr.children[0];
\r
41 out[out.length-1] = out[out.length-1].replace(/\n{2,}$/, "\n");
\r
44 // Builds a set of top level options
\r
45 function buildOptions(opt, indent) {
\r
47 if ((keys = Object.keys(opt)).length === 0)
\r
49 keys.forEach(function(key) {
\r
52 out.push("option ", key, options.min ? "=" : " = ", value(opt[key]), options.min ? ";" : ";\n");
\r
55 out[out.length-1] += "\n";
\r
58 // Builds everything within a namespace
\r
59 function buildNamespace(ns, indent) {
\r
60 ns.getChildren(ProtoBuf.Reflect.Enum).forEach(function(enm) {
\r
61 buildEnum(enm, indent);
\r
63 ns.getChildren(ProtoBuf.Reflect.Message).forEach(function(msg) {
\r
64 if (!msg.isGroup) // legacy groups are build within the respective field
\r
65 buildMessage(msg, indent);
\r
67 var exts = util.groupExtensions(ns);
\r
68 if (exts !== null) {
\r
69 Object.keys(exts).forEach(function(extFqn) {
\r
70 var extMsg = ns.resolve(extFqn),
\r
71 extFields = exts[extFqn];
\r
74 out.push("extend ", ns.qn(extMsg), options.min ? "{" : " {\n");
\r
75 extFields.forEach(function(extField) {
\r
76 buildMessageField(ns, extField, indent+" ", false);
\r
80 out.push(options.min ? "}" : "}\n\n");
\r
83 ns.getChildren(ProtoBuf.Reflect.Service).forEach(function(svc) {
\r
84 buildService(svc, indent);
\r
86 ns.getChildren(ProtoBuf.Reflect.Namespace).forEach(function(innerNs) {
\r
87 if (innerNs.className !== "Namespace")
\r
91 out.push("message ", innerNs.name, options.min ? "{" : " {\n");
\r
92 buildNamespace(innerNs, indent+" ");
\r
95 out.push(options.min ? "}" : "}\n");
\r
100 // Builds a message
\r
101 function buildMessage(msg, indent) {
\r
102 if (!msg.isGroup) {
\r
105 out.push("message ", msg.name);
\r
107 out.push(options.min ? "{" : " {\n");
\r
108 buildOptions(msg.options, indent+" ");
\r
109 var n = 0, oneofFields = [];
\r
110 msg.getChildren(ProtoBuf.Reflect.Message.OneOf).forEach(function(oneof) {
\r
112 out.push(indent, " ");
\r
113 out.push("oneof ", oneof.name, options.min ? "{" : " {\n");
\r
114 oneof.fields.forEach(function(fld) {
\r
115 buildMessageField(msg, fld, indent+" ", true);
\r
116 oneofFields.push(fld);
\r
119 out.push(indent, " ");
\r
120 out.push(options.min ? "}" : "}\n");
\r
122 msg.getChildren(ProtoBuf.Reflect.Message.Field).forEach(function(fld) {
\r
123 if (fld instanceof ProtoBuf.Reflect.Message.ExtensionField)
\r
125 if (oneofFields.indexOf(fld) >= 0)
\r
127 buildMessageField(msg, fld, indent+" ", false);
\r
130 if (n > 0 && !options.min)
\r
131 out[out.length-1] += "\n";
\r
132 if (msg.extensions) { // array of ranges
\r
134 out.push(indent, " ");
\r
135 out.push("extensions ");
\r
136 msg.extensions.forEach(function(range, index) {
\r
138 out.push(options.min ? "," : ", ");
\r
139 out.push(value(range[0]));
\r
140 if (range[1] !== range[0])
\r
141 out.push(" to ", range[1] === ProtoBuf.ID_MAX ? "max" : value(range[1]));
\r
143 out.push(options.min ? ";" : ";\n\n");
\r
145 buildNamespace(msg, indent+" ");
\r
148 out.push(options.min ? "}" : "}\n\n");
\r
151 // Builds a message field
\r
152 function buildMessageField(msg, fld, indent, isOneOf) {
\r
153 var isGroup = false;
\r
157 out.push(fld.required ? "required " : (fld.repeated ? "repeated " : "optional "));
\r
158 if (fld.resolvedType !== null) {
\r
159 if (fld.resolvedType instanceof ProtoBuf.Reflect.Message && fld.resolvedType.isGroup) {
\r
160 // inline legacy groups
\r
161 out.push("group ");
\r
164 out.push(msg.qn(fld.resolvedType));
\r
166 out.push(fld.type['name']);
\r
168 out.push(" ", fld instanceof ProtoBuf.Reflect.Message.ExtensionField ? fld.name.substring(fld.name.lastIndexOf(".")+1) : fld.name);
\r
169 out.push(options.min ? "=" : " = ", fld.id);
\r
170 if (isGroup) // inline
\r
171 buildMessage(fld.resolvedType, indent);
\r
173 var keys = Object.keys(fld.options);
\r
174 if (keys.length > 0) {
\r
175 out.push(options.min ? "[" : " [");
\r
177 keys.forEach(function (key) {
\r
179 out.push(options.min ? "," : ", ");
\r
180 out.push(key, options.min ? "=" : " = ",
\r
181 // BEWARE: Monkey patch for string enum defaults
\r
182 key === "default" && fld.type === ProtoBuf.TYPES["enum"] && typeof fld.options[key] === 'string' ? fld.options[key] : value(fld.options[key])
\r
188 out.push(options.min ? ";" : ";\n");
\r
193 function buildEnum(enm, indent) {
\r
196 out.push("enum ", enm.name, options.min ? "{" : " {\n");
\r
197 buildOptions(enm.options, indent+" ");
\r
198 enm.getChildren(ProtoBuf.Reflect.Enum.Value).forEach(function(val) {
\r
200 out.push(indent, " ");
\r
201 out.push(val.name, options.min ? "=" : " = ", val.id, options.min? ";" : ";\n");
\r
205 out.push(options.min ? "}" : "}\n\n");
\r
208 // Builds a service
\r
209 function buildService(svc, indent) {
\r
212 out.push("service ", svc.name, options.min ? "{" : " {\n");
\r
213 buildOptions(svc.options, indent+" ");
\r
214 svc.getChildren(ProtoBuf.Reflect.Service.RPCMethod).forEach(function(rpc) {
\r
216 out.push(indent+" ");
\r
217 out.push("rpc ", rpc.name, "(", svc.qn(rpc.resolvedRequestType), ") returns(", svc.qn(rpc.resolvedResponseType), ")");
\r
218 var keys = Object.keys(rpc.options);
\r
219 if (keys.length === 0) {
\r
220 out.push(options.min ? ";" : ";\n")
\r
222 out.push(options.min ? "{" : " {\n");
\r
223 buildOptions(rpc.options, indent+" ");
\r
226 out.push(indent+" ");
\r
227 out.push(options.min ? "}" : "}\n");
\r
230 out[out.length-1] += "\n";
\r
233 out.push(options.min ? "}" : "}\n");
\r
236 // Start by building the package namespace
\r
237 var pkg = ptr.fqn().substring(1);
\r
239 out.push("package ", pkg, options.min ? ";" : ";\n\n");
\r
240 buildOptions(ptr.options, "");
\r
241 buildNamespace(ptr, "");
\r
242 return out.join('');
\r
246 * Module description.
\r
249 proto.description = description;
\r
252 * Converts a JavaScript value to a .proto value.
\r
253 * @param {*} v Value
\r
254 * @returns {string} Dot proto value
\r
256 function value(v) {
\r
257 switch (typeof v) {
\r
259 return v ? 'true' : 'false';
\r
261 return v.toString();
\r
263 return '"'+v.replace(/"/g, '\\"')+'"';
\r
265 throw new Error("illegal value type: "+typeof(v));
\r