Built motion from commit 6a09e18b.|2.6.11
[motion2.git] / legacy-libs / protobufjs / cli / targets / proto.js
1 "use strict";
2 module.exports = proto_target;
3
4 proto_target.private = true;
5
6 var protobuf = require("../..");
7
8 var Namespace  = protobuf.Namespace,
9     Enum       = protobuf.Enum,
10     Type       = protobuf.Type,
11     Field      = protobuf.Field,
12     OneOf      = protobuf.OneOf,
13     Service    = protobuf.Service,
14     Method     = protobuf.Method,
15     types      = protobuf.types,
16     util       = protobuf.util;
17
18 function underScore(str) {
19     return str.substring(0,1)
20          + str.substring(1)
21                .replace(/([A-Z])(?=[a-z]|$)/g, function($0, $1) { return "_" + $1.toLowerCase(); });
22 }
23
24 var out = [];
25 var indent = 0;
26 var first = false;
27 var syntax = 3;
28
29 function proto_target(root, options, callback) {
30     if (options) {
31         switch (options.syntax) {
32             case undefined:
33             case "proto3":
34             case "3":
35                 syntax = 3;
36                 break;
37             case "proto2":
38             case "2":
39                 syntax = 2;
40                 break;
41             default:
42                 return callback(Error("invalid syntax: " + options.syntax));
43         }
44     }
45     indent = 0;
46     first = false;
47     try {
48         buildRoot(root);
49         return callback(null, out.join("\n"));
50     } catch (err) {
51         return callback(err);
52     } finally {
53         out = [];
54         syntax = 3;
55     }
56 }
57
58 function push(line) {
59     if (line === "")
60         out.push("");
61     else {
62         var ind = "";
63         for (var i = 0; i < indent; ++i)
64             ind += "    ";
65         out.push(ind + line);
66     }
67 }
68
69 function escape(str) {
70     return str.replace(/[\\"']/g, "\\$&")
71               .replace(/\r/g, "\\r")
72               .replace(/\n/g, "\\n")
73               .replace(/\u0000/g, "\\0"); // eslint-disable-line no-control-regex
74 }
75
76 function value(v) {
77     switch (typeof v) {
78         case "boolean":
79             return v ? "true" : "false";
80         case "number":
81             return v.toString();
82         default:
83             return "\"" + escape(String(v)) + "\"";
84     }
85 }
86
87 function buildRoot(root) {
88     root.resolveAll();
89     var pkg = [];
90     var ptr = root;
91     var repeat = true;
92     do {
93         var nested = ptr.nestedArray;
94         if (nested.length === 1 && nested[0] instanceof Namespace && !(nested[0] instanceof Type || nested[0] instanceof Service)) {
95             ptr = nested[0];
96             if (ptr !== root)
97                 pkg.push(ptr.name);
98         } else
99             repeat = false;
100     } while (repeat);
101     out.push("syntax = \"proto" + syntax + "\";");
102     if (pkg.length)
103         out.push("", "package " + pkg.join(".") + ";");
104
105     buildOptions(ptr);
106     ptr.nestedArray.forEach(build);
107 }
108
109 function build(object) {
110     if (object instanceof Enum)
111         buildEnum(object);
112     else if (object instanceof Type)
113         buildType(object);
114     else if (object instanceof Field)
115         buildField(object);
116     else if (object instanceof OneOf)
117         buildOneOf(object);
118     else if (object instanceof Service)
119         buildService(object);
120     else if (object instanceof Method)
121         buildMethod(object);
122     else
123         buildNamespace(object);
124 }
125
126 function buildNamespace(namespace) { // just a namespace, not a type etc.
127     push("");
128     push("message " + namespace.name + " {");
129     ++indent;
130     buildOptions(namespace);
131     consolidateExtends(namespace.nestedArray).remaining.forEach(build);
132     --indent;
133     push("}");
134 }
135
136 function buildEnum(enm) {
137     push("");
138     push("enum " + enm.name + " {");
139     buildOptions(enm);
140     ++indent; first = true;
141     Object.keys(enm.values).forEach(function(name) {
142         var val = enm.values[name];
143         if (first) {
144             push("");
145             first = false;
146         }
147         push(name + " = " + val + ";");
148     });
149     --indent; first = false;
150     push("}");
151 }
152
153 function buildRanges(keyword, ranges) {
154     if (ranges && ranges.length) {
155         var parts = [];
156         ranges.forEach(function(range) {
157             if (typeof range === "string")
158                 parts.push("\"" + escape(range) + "\"");
159             else if (range[0] === range[1])
160                 parts.push(range[0]);
161             else
162                 parts.push(range[0] + " to " + (range[1] === 0x1FFFFFFF ? "max" : range[1]));
163         });
164         push("");
165         push(keyword + " " + parts.join(", ") + ";");
166     }
167 }
168
169 function buildType(type) {
170     if (type.group)
171         return; // built with the sister-field
172     push("");
173     push("message " + type.name + " {");
174     ++indent;
175     buildOptions(type);
176     type.oneofsArray.forEach(build);
177     first = true;
178     type.fieldsArray.forEach(build);
179     consolidateExtends(type.nestedArray).remaining.forEach(build);
180     buildRanges("extensions", type.extensions);
181     buildRanges("reserved", type.reserved);
182     --indent;
183     push("}");
184 }
185
186 function buildField(field, passExtend) {
187     if (field.partOf || field.declaringField || field.extend !== undefined && !passExtend)
188         return;
189     if (first) {
190         first = false;
191         push("");
192     }
193     if (field.resolvedType && field.resolvedType.group) {
194         buildGroup(field);
195         return;
196     }
197     var sb = [];
198     if (field.map)
199         sb.push("map<" + field.keyType + ", " + field.type + ">");
200     else if (field.repeated)
201         sb.push("repeated", field.type);
202     else if (syntax === 2 || field.parent.group)
203         sb.push(field.required ? "required" : "optional", field.type);
204     else
205         sb.push(field.type);
206     sb.push(underScore(field.name), "=", field.id);
207     var opts = buildFieldOptions(field);
208     if (opts)
209         sb.push(opts);
210     push(sb.join(" ") + ";");
211 }
212
213 function buildGroup(field) {
214     push(field.rule + " group " + field.resolvedType.name + " = " + field.id + " {");
215     ++indent;
216     buildOptions(field.resolvedType);
217     first = true;
218     field.resolvedType.fieldsArray.forEach(function(field) {
219         buildField(field);
220     });
221     --indent;
222     push("}");
223 }
224
225 function buildFieldOptions(field) {
226     var keys;
227     if (!field.options || !(keys = Object.keys(field.options)).length)
228         return null;
229     var sb = [];
230     keys.forEach(function(key) {
231         var val = field.options[key];
232         var wireType = types.packed[field.resolvedType instanceof Enum ? "int32" : field.type];
233         switch (key) {
234             case "packed":
235                 val = Boolean(val);
236                 // skip when not packable or syntax default
237                 if (wireType === undefined || syntax === 3 === val)
238                     return;
239                 break;
240             case "default":
241                 if (syntax === 3)
242                     return;
243                 // skip default (resolved) default values
244                 if (field.long && !util.longNeq(field.defaultValue, types.defaults[field.type]) || !field.long && field.defaultValue === types.defaults[field.type])
245                     return;
246                 // enum defaults specified as strings are type references and not enclosed in quotes
247                 if (field.resolvedType instanceof Enum)
248                     break;
249                 // otherwise fallthrough
250             default:
251                 val = value(val);
252                 break;
253         }
254         sb.push(key + "=" + val);
255     });
256     return sb.length
257         ? "[" + sb.join(", ") + "]"
258         : null;
259 }
260
261 function consolidateExtends(nested) {
262     var ext = {};
263     nested = nested.filter(function(obj) {
264         if (!(obj instanceof Field) || obj.extend === undefined)
265             return true;
266         (ext[obj.extend] || (ext[obj.extend] = [])).push(obj);
267         return false;
268     });
269     Object.keys(ext).forEach(function(extend) {
270         push("");
271         push("extend " + extend + " {");
272         ++indent; first = true;
273         ext[extend].forEach(function(field) {
274             buildField(field, true);
275         });
276         --indent;
277         push("}");
278     });
279     return {
280         remaining: nested
281     };
282 }
283
284 function buildOneOf(oneof) {
285     push("");
286     push("oneof " + underScore(oneof.name) + " {");
287     ++indent; first = true;
288     oneof.oneof.forEach(function(fieldName) {
289         var field = oneof.parent.get(fieldName);
290         if (first) {
291             first = false;
292             push("");
293         }
294         var opts = buildFieldOptions(field);
295         push(field.type + " " + underScore(field.name) + " = " + field.id + (opts ? " " + opts : "") + ";");
296     });
297     --indent;
298     push("}");
299 }
300
301 function buildService(service) {
302     push("service " + service.name + " {");
303     ++indent;
304     service.methodsArray.forEach(build);
305     consolidateExtends(service.nestedArray).remaining.forEach(build);
306     --indent;
307     push("}");
308 }
309
310 function buildMethod(method) {
311     push(method.type + " " + method.name + " (" + (method.requestStream ? "stream " : "") + method.requestType + ") returns (" + (method.responseStream ? "stream " : "") + method.responseType + ");");
312 }
313
314 function buildOptions(object) {
315     if (!object.options)
316         return;
317     first = true;
318     Object.keys(object.options).forEach(function(key) {
319         if (first) {
320             first = false;
321             push("");
322         }
323         var val = object.options[key];
324         push("option " + key + " = " + JSON.stringify(val) + ";");
325     });
326 }