UNPKG

2.55 kBPlain TextView Raw
1import type {CodeKeywordDefinition, KeywordErrorDefinition} from "../../types"
2import type {KeywordCxt} from "../../compile/validate"
3import {_, nil, or, Code} from "../../compile/codegen"
4import validTimestamp from "../../runtime/timestamp"
5import {useFunc} from "../../compile/util"
6import {checkMetadata} from "./metadata"
7import {typeErrorMessage, typeErrorParams, _JTDTypeError} from "./error"
8
9export type JTDTypeError = _JTDTypeError<"type", JTDType, JTDType>
10
11export type IntType = "int8" | "uint8" | "int16" | "uint16" | "int32" | "uint32"
12
13export const intRange: {[T in IntType]: [number, number, number]} = {
14 int8: [-128, 127, 3],
15 uint8: [0, 255, 3],
16 int16: [-32768, 32767, 5],
17 uint16: [0, 65535, 5],
18 int32: [-2147483648, 2147483647, 10],
19 uint32: [0, 4294967295, 10],
20}
21
22export type JTDType = "boolean" | "string" | "timestamp" | "float32" | "float64" | IntType
23
24const error: KeywordErrorDefinition = {
25 message: (cxt) => typeErrorMessage(cxt, cxt.schema),
26 params: (cxt) => typeErrorParams(cxt, cxt.schema),
27}
28
29function timestampCode(cxt: KeywordCxt): Code {
30 const {gen, data, it} = cxt
31 const {timestamp, allowDate} = it.opts
32 if (timestamp === "date") return _`${data} instanceof Date `
33 const vts = useFunc(gen, validTimestamp)
34 const allowDateArg = allowDate ? _`, true` : nil
35 const validString = _`typeof ${data} == "string" && ${vts}(${data}${allowDateArg})`
36 return timestamp === "string" ? validString : or(_`${data} instanceof Date`, validString)
37}
38
39const def: CodeKeywordDefinition = {
40 keyword: "type",
41 schemaType: "string",
42 error,
43 code(cxt: KeywordCxt) {
44 checkMetadata(cxt)
45 const {data, schema, parentSchema, it} = cxt
46 let cond: Code
47 switch (schema) {
48 case "boolean":
49 case "string":
50 cond = _`typeof ${data} == ${schema}`
51 break
52 case "timestamp": {
53 cond = timestampCode(cxt)
54 break
55 }
56 case "float32":
57 case "float64":
58 cond = _`typeof ${data} == "number"`
59 break
60 default: {
61 const sch = schema as IntType
62 cond = _`typeof ${data} == "number" && isFinite(${data}) && !(${data} % 1)`
63 if (!it.opts.int32range && (sch === "int32" || sch === "uint32")) {
64 if (sch === "uint32") cond = _`${cond} && ${data} >= 0`
65 } else {
66 const [min, max] = intRange[sch]
67 cond = _`${cond} && ${data} >= ${min} && ${data} <= ${max}`
68 }
69 }
70 }
71 cxt.pass(parentSchema.nullable ? or(_`${data} === null`, cond) : cond)
72 },
73}
74
75export default def