1 | import type {CodeKeywordDefinition, KeywordErrorDefinition} from "../../types"
|
2 | import type {KeywordCxt} from "../../compile/validate"
|
3 | import {_, nil, or, Code} from "../../compile/codegen"
|
4 | import validTimestamp from "../../runtime/timestamp"
|
5 | import {useFunc} from "../../compile/util"
|
6 | import {checkMetadata} from "./metadata"
|
7 | import {typeErrorMessage, typeErrorParams, _JTDTypeError} from "./error"
|
8 |
|
9 | export type JTDTypeError = _JTDTypeError<"type", JTDType, JTDType>
|
10 |
|
11 | export type IntType = "int8" | "uint8" | "int16" | "uint16" | "int32" | "uint32"
|
12 |
|
13 | export 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 |
|
22 | export type JTDType = "boolean" | "string" | "timestamp" | "float32" | "float64" | IntType
|
23 |
|
24 | const error: KeywordErrorDefinition = {
|
25 | message: (cxt) => typeErrorMessage(cxt, cxt.schema),
|
26 | params: (cxt) => typeErrorParams(cxt, cxt.schema),
|
27 | }
|
28 |
|
29 | function 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 |
|
39 | const 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 |
|
75 | export default def
|