nestjs-protobuf-es
Version:
Protobuf-ES integration for NestJS
99 lines (98 loc) • 3.46 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.isFieldRequired = isFieldRequired;
const protobuf_1 = require("@bufbuild/protobuf");
const wire_1 = require("@bufbuild/protobuf/wire");
const wkt_1 = require("@bufbuild/protobuf/wkt");
const PV_EXT_NUMBER = 1159;
const PV_FIELD_RULES_MAP = new Map([
[25, { name: 'required', type: protobuf_1.ScalarType.BOOL }],
[27, { name: 'ignore', type: protobuf_1.ScalarType.INT32 }],
]);
const PV_MESSAGE_RULES_MAP = new Map([[1, { name: 'disabled', type: protobuf_1.ScalarType.BOOL }]]);
const IS_FIELD_REQUIRED_CACHE = new WeakMap();
/**
* Returns true if the field is required by protovalidate or has the proto2 `required` label, or the Edition
* feature field_presence = LEGACY_REQUIRED.
*
* Results for each field are cached for performance reasons.
*/
function isFieldRequired(field) {
let required = IS_FIELD_REQUIRED_CACHE.get(field);
if (required == null) {
required = isProtoValidateRequired(field) || isLegacyRequired(field);
IS_FIELD_REQUIRED_CACHE.set(field, required);
}
return required;
}
/**
* Returns true if the field is required by protovalidate.
*
* Note that this function only applies to message fields (singular, repeated, map),
* and always returns false for other field types.
*/
function isProtoValidateRequired(field) {
if (!field.proto.options?.$unknown) {
return false;
}
let fieldRules = {};
for (const uf of field.proto.options.$unknown) {
if (uf.no !== PV_EXT_NUMBER)
continue;
fieldRules = { ...extractFields(uf.data, PV_FIELD_RULES_MAP) };
}
if (!fieldRules.required || fieldRules.ignore === 3) {
return false;
}
if (field.parent.proto.options?.$unknown) {
let messageRules = {};
for (const uf of field.parent.proto.options.$unknown) {
if (uf.no !== PV_EXT_NUMBER)
continue;
messageRules = { ...extractFields(uf.data, PV_MESSAGE_RULES_MAP) };
}
if (messageRules.disabled) {
return false;
}
}
return true;
}
/**
* Returns true if the field has the proto2 `required` label, or the Edition
* feature field_presence = LEGACY_REQUIRED.
*
* Note that this function only applies to singular message fields, and always
* returns false for other fields.
*/
function isLegacyRequired(field) {
return (field.fieldKind === 'message' && field.presence === wkt_1.FeatureSet_FieldPresence.LEGACY_REQUIRED);
}
function extractFields(bytes, fieldMap) {
const reader = new wire_1.BinaryReader(bytes);
const end = reader.pos + reader.uint32();
const results = {};
let resultSize = 0;
while (reader.pos < end) {
const [fieldNo, wireType] = reader.tag();
const fieldDesc = fieldMap.get(fieldNo);
if (!fieldDesc) {
reader.skip(wireType);
continue;
}
results[fieldDesc.name] = readValue(reader, fieldDesc.type);
// Check if all fields were extracted and leave early.
if (++resultSize === fieldMap.size)
break;
}
return results;
}
function readValue(reader, type) {
switch (type) {
case protobuf_1.ScalarType.INT32:
return reader.int32();
case protobuf_1.ScalarType.BOOL:
return reader.bool();
default:
throw new Error(`Unknown scalar type: ${type}`);
}
}