typia
Version:
Superfast runtime validators with only one line
126 lines (110 loc) • 4.11 kB
text/typescript
import { IMetadataTypeTag } from "../../schemas/metadata/IMetadataTypeTag";
import { Metadata } from "../../schemas/metadata/Metadata";
import { MetadataObject } from "../../schemas/metadata/MetadataObject";
import { ProtobufAtomic } from "../../typings/ProtobufAtomic";
export namespace ProtobufUtil {
export const isStaticObject = (obj: MetadataObject): boolean =>
obj.properties.length >= 1 &&
obj.properties.every((p) => p.key.isSoleLiteral());
export const size = (meta: Metadata): number =>
getAtomics(meta).length +
meta.arrays.length +
meta.tuples.length +
meta.natives.length +
meta.objects.length +
meta.maps.length;
export const isUnion = (meta: Metadata): boolean => size(meta) > 1;
export const getAtomics = (meta: Metadata): ProtobufAtomic[] => {
const set: Set<ProtobufAtomic> = new Set();
if (meta.templates.length) set.add("string");
for (const c of meta.constants)
if (c.type === "boolean") set.add("bool");
else if (c.type === "bigint") set.add("uint64");
else if (c.type === "number")
set.add(deduce_numeric_type(c.values.map((v) => v.value) as number[]));
else if (c.type === "string") set.add("string");
for (const atomic of meta.atomics)
if (atomic.type === "boolean") set.add("bool");
else if (atomic.type === "bigint")
decode_bigint(atomic.tags).forEach((t) => set.add(t));
else if (atomic.type === "number")
decode_number(atomic.tags).forEach((t) => set.add(t));
else if (atomic.type === "string") set.add("string");
return [...set].sort(compare);
};
export const getNumbers = (meta: Metadata) => {
const set: Set<ProtobufAtomic.Numeric> = new Set();
for (const c of meta.constants)
if (c.type === "number")
set.add(deduce_numeric_type(c.values.map((v) => v.value) as number[]));
for (const atomic of meta.atomics)
if (atomic.type === "number")
decode_number(atomic.tags).forEach((t) => set.add(t));
return [...set].sort(compare);
};
export const getBigints = (meta: Metadata) => {
const set: Set<ProtobufAtomic.BigNumeric> = new Set();
for (const c of meta.constants) if (c.type === "bigint") set.add("uint64");
for (const atomic of meta.atomics)
if (atomic.type === "bigint")
decode_bigint(atomic.tags).forEach((t) => set.add(t));
return [...set].sort(compare);
};
const compare = (x: ProtobufAtomic, y: ProtobufAtomic): number =>
ATOMIC_ORDER.get(x)! - ATOMIC_ORDER.get(y)!;
}
const ATOMIC_ORDER = new Map<ProtobufAtomic, number>(
(
[
"bool",
"int32",
"uint32",
"int64",
"uint64",
"float",
"double",
"string",
] as const
).map((str, i) => [str, i]),
);
const deduce_numeric_type = (values: number[]): ProtobufAtomic.Numeric =>
values.every((v) => Math.floor(v) === v)
? values.every((v) => -2147483648 <= v && v <= 2147483647)
? "int32"
: "int64"
: "double";
const decode_bigint = (
typeTags: IMetadataTypeTag[][],
): ProtobufAtomic.BigNumeric[] => {
if (typeTags.length === 0) return ["int64"];
const types: Set<ProtobufAtomic.BigNumeric> = new Set();
for (const row of typeTags) {
const value: ProtobufAtomic.BigNumeric | undefined = row.find(
(tag) =>
tag.kind === "type" &&
(tag.value === "int64" || tag.value === "uint64"),
)?.value;
types.add(value ?? "int64");
}
return [...types];
};
const decode_number = (
typeTags: IMetadataTypeTag[][],
): ProtobufAtomic.Numeric[] => {
if (typeTags.length === 0) return ["double"];
const types: Set<ProtobufAtomic.Numeric> = new Set();
for (const row of typeTags) {
const value: ProtobufAtomic.Numeric | undefined = row.find(
(tag) =>
tag.kind === "type" &&
(tag.value === "int32" ||
tag.value === "uint32" ||
tag.value === "int64" ||
tag.value === "uint64" ||
tag.value === "float" ||
tag.value === "double"),
)?.value;
types.add(value ?? "double");
}
return [...types];
};