import type {CodeKeywordDefinition, KeywordErrorDefinition} from "../../types" import type {KeywordCxt} from "../../compile/validate" import {_, nil, or, Code} from "../../compile/codegen" import validTimestamp from "../../runtime/timestamp" import {useFunc} from "../../compile/util" import {checkMetadata} from "./metadata" import {typeErrorMessage, typeErrorParams, _JTDTypeError} from "./error" export type JTDTypeError = _JTDTypeError<"type", JTDType, JTDType> export type IntType = "int8" | "uint8" | "int16" | "uint16" | "int32" | "uint32" export const intRange: {[T in IntType]: [number, number, number]} = { int8: [-128, 127, 3], uint8: [0, 255, 3], int16: [-32768, 32767, 5], uint16: [0, 65535, 5], int32: [-2147483648, 2147483647, 10], uint32: [0, 4294967295, 10], } export type JTDType = "boolean" | "string" | "timestamp" | "float32" | "float64" | IntType const error: KeywordErrorDefinition = { message: (cxt) => typeErrorMessage(cxt, cxt.schema), params: (cxt) => typeErrorParams(cxt, cxt.schema), } function timestampCode(cxt: KeywordCxt): Code { const {gen, data, it} = cxt const {timestamp, allowDate} = it.opts if (timestamp === "date") return _`${data} instanceof Date ` const vts = useFunc(gen, validTimestamp) const allowDateArg = allowDate ? _`, true` : nil const validString = _`typeof ${data} == "string" && ${vts}(${data}${allowDateArg})` return timestamp === "string" ? validString : or(_`${data} instanceof Date`, validString) } const def: CodeKeywordDefinition = { keyword: "type", schemaType: "string", error, code(cxt: KeywordCxt) { checkMetadata(cxt) const {data, schema, parentSchema, it} = cxt let cond: Code switch (schema) { case "boolean": case "string": cond = _`typeof ${data} == ${schema}` break case "timestamp": { cond = timestampCode(cxt) break } case "float32": case "float64": cond = _`typeof ${data} == "number"` break default: { const sch = schema as IntType cond = _`typeof ${data} == "number" && isFinite(${data}) && !(${data} % 1)` if (!it.opts.int32range && (sch === "int32" || sch === "uint32")) { if (sch === "uint32") cond = _`${cond} && ${data} >= 0` } else { const [min, max] = intRange[sch] cond = _`${cond} && ${data} >= ${min} && ${data} <= ${max}` } } } cxt.pass(parentSchema.nullable ? or(_`${data} === null`, cond) : cond) }, } export default def