No Description
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

proto.js 9.0KB

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