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
|
---|