1
0
mirror of https://github.com/musix-org/musix-oss synced 2024-12-23 22:33:17 +00:00
musix-oss/node_modules/protobufjs/cli/targets/proto.js
2020-03-03 22:30:50 +02:00

327 lines
9.0 KiB
JavaScript

"use strict";
module.exports = proto_target;
proto_target.private = true;
var protobuf = require("../..");
var Namespace = protobuf.Namespace,
Enum = protobuf.Enum,
Type = protobuf.Type,
Field = protobuf.Field,
OneOf = protobuf.OneOf,
Service = protobuf.Service,
Method = protobuf.Method,
types = protobuf.types,
util = protobuf.util;
function underScore(str) {
return str.substring(0,1)
+ str.substring(1)
.replace(/([A-Z])(?=[a-z]|$)/g, function($0, $1) { return "_" + $1.toLowerCase(); });
}
var out = [];
var indent = 0;
var first = false;
var syntax = 3;
function proto_target(root, options, callback) {
if (options) {
switch (options.syntax) {
case undefined:
case "proto3":
case "3":
syntax = 3;
break;
case "proto2":
case "2":
syntax = 2;
break;
default:
return callback(Error("invalid syntax: " + options.syntax));
}
}
indent = 0;
first = false;
try {
buildRoot(root);
return callback(null, out.join("\n"));
} catch (err) {
return callback(err);
} finally {
out = [];
syntax = 3;
}
}
function push(line) {
if (line === "")
out.push("");
else {
var ind = "";
for (var i = 0; i < indent; ++i)
ind += " ";
out.push(ind + line);
}
}
function escape(str) {
return str.replace(/[\\"']/g, "\\$&")
.replace(/\r/g, "\\r")
.replace(/\n/g, "\\n")
.replace(/\u0000/g, "\\0"); // eslint-disable-line no-control-regex
}
function value(v) {
switch (typeof v) {
case "boolean":
return v ? "true" : "false";
case "number":
return v.toString();
default:
return "\"" + escape(String(v)) + "\"";
}
}
function buildRoot(root) {
root.resolveAll();
var pkg = [];
var ptr = root;
var repeat = true;
do {
var nested = ptr.nestedArray;
if (nested.length === 1 && nested[0] instanceof Namespace && !(nested[0] instanceof Type || nested[0] instanceof Service)) {
ptr = nested[0];
if (ptr !== root)
pkg.push(ptr.name);
} else
repeat = false;
} while (repeat);
out.push("syntax = \"proto" + syntax + "\";");
if (pkg.length)
out.push("", "package " + pkg.join(".") + ";");
buildOptions(ptr);
ptr.nestedArray.forEach(build);
}
function build(object) {
if (object instanceof Enum)
buildEnum(object);
else if (object instanceof Type)
buildType(object);
else if (object instanceof Field)
buildField(object);
else if (object instanceof OneOf)
buildOneOf(object);
else if (object instanceof Service)
buildService(object);
else if (object instanceof Method)
buildMethod(object);
else
buildNamespace(object);
}
function buildNamespace(namespace) { // just a namespace, not a type etc.
push("");
push("message " + namespace.name + " {");
++indent;
buildOptions(namespace);
consolidateExtends(namespace.nestedArray).remaining.forEach(build);
--indent;
push("}");
}
function buildEnum(enm) {
push("");
push("enum " + enm.name + " {");
buildOptions(enm);
++indent; first = true;
Object.keys(enm.values).forEach(function(name) {
var val = enm.values[name];
if (first) {
push("");
first = false;
}
push(name + " = " + val + ";");
});
--indent; first = false;
push("}");
}
function buildRanges(keyword, ranges) {
if (ranges && ranges.length) {
var parts = [];
ranges.forEach(function(range) {
if (typeof range === "string")
parts.push("\"" + escape(range) + "\"");
else if (range[0] === range[1])
parts.push(range[0]);
else
parts.push(range[0] + " to " + (range[1] === 0x1FFFFFFF ? "max" : range[1]));
});
push("");
push(keyword + " " + parts.join(", ") + ";");
}
}
function buildType(type) {
if (type.group)
return; // built with the sister-field
push("");
push("message " + type.name + " {");
++indent;
buildOptions(type);
type.oneofsArray.forEach(build);
first = true;
type.fieldsArray.forEach(build);
consolidateExtends(type.nestedArray).remaining.forEach(build);
buildRanges("extensions", type.extensions);
buildRanges("reserved", type.reserved);
--indent;
push("}");
}
function buildField(field, passExtend) {
if (field.partOf || field.declaringField || field.extend !== undefined && !passExtend)
return;
if (first) {
first = false;
push("");
}
if (field.resolvedType && field.resolvedType.group) {
buildGroup(field);
return;
}
var sb = [];
if (field.map)
sb.push("map<" + field.keyType + ", " + field.type + ">");
else if (field.repeated)
sb.push("repeated", field.type);
else if (syntax === 2 || field.parent.group)
sb.push(field.required ? "required" : "optional", field.type);
else
sb.push(field.type);
sb.push(underScore(field.name), "=", field.id);
var opts = buildFieldOptions(field);
if (opts)
sb.push(opts);
push(sb.join(" ") + ";");
}
function buildGroup(field) {
push(field.rule + " group " + field.resolvedType.name + " = " + field.id + " {");
++indent;
buildOptions(field.resolvedType);
first = true;
field.resolvedType.fieldsArray.forEach(function(field) {
buildField(field);
});
--indent;
push("}");
}
function buildFieldOptions(field) {
var keys;
if (!field.options || !(keys = Object.keys(field.options)).length)
return null;
var sb = [];
keys.forEach(function(key) {
var val = field.options[key];
var wireType = types.packed[field.resolvedType instanceof Enum ? "int32" : field.type];
switch (key) {
case "packed":
val = Boolean(val);
// skip when not packable or syntax default
if (wireType === undefined || syntax === 3 === val)
return;
break;
case "default":
if (syntax === 3)
return;
// skip default (resolved) default values
if (field.long && !util.longNeq(field.defaultValue, types.defaults[field.type]) || !field.long && field.defaultValue === types.defaults[field.type])
return;
// enum defaults specified as strings are type references and not enclosed in quotes
if (field.resolvedType instanceof Enum)
break;
// otherwise fallthrough
default:
val = value(val);
break;
}
sb.push(key + "=" + val);
});
return sb.length
? "[" + sb.join(", ") + "]"
: null;
}
function consolidateExtends(nested) {
var ext = {};
nested = nested.filter(function(obj) {
if (!(obj instanceof Field) || obj.extend === undefined)
return true;
(ext[obj.extend] || (ext[obj.extend] = [])).push(obj);
return false;
});
Object.keys(ext).forEach(function(extend) {
push("");
push("extend " + extend + " {");
++indent; first = true;
ext[extend].forEach(function(field) {
buildField(field, true);
});
--indent;
push("}");
});
return {
remaining: nested
};
}
function buildOneOf(oneof) {
push("");
push("oneof " + underScore(oneof.name) + " {");
++indent; first = true;
oneof.oneof.forEach(function(fieldName) {
var field = oneof.parent.get(fieldName);
if (first) {
first = false;
push("");
}
var opts = buildFieldOptions(field);
push(field.type + " " + underScore(field.name) + " = " + field.id + (opts ? " " + opts : "") + ";");
});
--indent;
push("}");
}
function buildService(service) {
push("service " + service.name + " {");
++indent;
service.methodsArray.forEach(build);
consolidateExtends(service.nestedArray).remaining.forEach(build);
--indent;
push("}");
}
function buildMethod(method) {
push(method.type + " " + method.name + " (" + (method.requestStream ? "stream " : "") + method.requestType + ") returns (" + (method.responseStream ? "stream " : "") + method.responseType + ");");
}
function buildOptions(object) {
if (!object.options)
return;
first = true;
Object.keys(object.options).forEach(function(key) {
if (first) {
first = false;
push("");
}
var val = object.options[key];
push("option " + key + " = " + JSON.stringify(val) + ";");
});
}