[6a3a178] | 1 | import type Ajv from "ajv"
|
---|
| 2 | import type {
|
---|
| 3 | Plugin,
|
---|
| 4 | CodeKeywordDefinition,
|
---|
| 5 | KeywordErrorDefinition,
|
---|
| 6 | Code,
|
---|
| 7 | Name,
|
---|
| 8 | ErrorObject,
|
---|
| 9 | } from "ajv"
|
---|
| 10 | import type {AddedFormat} from "ajv/dist/types"
|
---|
| 11 | import type {Rule} from "ajv/dist/compile/rules"
|
---|
| 12 | import {KeywordCxt} from "ajv"
|
---|
| 13 | import {_, str, or, getProperty, operators} from "ajv/dist/compile/codegen"
|
---|
| 14 |
|
---|
| 15 | type Kwd = "formatMaximum" | "formatMinimum" | "formatExclusiveMaximum" | "formatExclusiveMinimum"
|
---|
| 16 |
|
---|
| 17 | type Comparison = "<=" | ">=" | "<" | ">"
|
---|
| 18 |
|
---|
| 19 | const ops = operators
|
---|
| 20 |
|
---|
| 21 | const KWDs: {[K in Kwd]: {okStr: Comparison; ok: Code; fail: Code}} = {
|
---|
| 22 | formatMaximum: {okStr: "<=", ok: ops.LTE, fail: ops.GT},
|
---|
| 23 | formatMinimum: {okStr: ">=", ok: ops.GTE, fail: ops.LT},
|
---|
| 24 | formatExclusiveMaximum: {okStr: "<", ok: ops.LT, fail: ops.GTE},
|
---|
| 25 | formatExclusiveMinimum: {okStr: ">", ok: ops.GT, fail: ops.LTE},
|
---|
| 26 | }
|
---|
| 27 |
|
---|
| 28 | export type LimitFormatError = ErrorObject<Kwd, {limit: string; comparison: Comparison}>
|
---|
| 29 |
|
---|
| 30 | const error: KeywordErrorDefinition = {
|
---|
| 31 | message: ({keyword, schemaCode}) => str`should be ${KWDs[keyword as Kwd].okStr} ${schemaCode}`,
|
---|
| 32 | params: ({keyword, schemaCode}) =>
|
---|
| 33 | _`{comparison: ${KWDs[keyword as Kwd].okStr}, limit: ${schemaCode}}`,
|
---|
| 34 | }
|
---|
| 35 |
|
---|
| 36 | export const formatLimitDefinition: CodeKeywordDefinition = {
|
---|
| 37 | keyword: Object.keys(KWDs),
|
---|
| 38 | type: "string",
|
---|
| 39 | schemaType: "string",
|
---|
| 40 | $data: true,
|
---|
| 41 | error,
|
---|
| 42 | code(cxt) {
|
---|
| 43 | const {gen, data, schemaCode, keyword, it} = cxt
|
---|
| 44 | const {opts, self} = it
|
---|
| 45 | if (!opts.validateFormats) return
|
---|
| 46 |
|
---|
| 47 | const fCxt = new KeywordCxt(it, (self.RULES.all.format as Rule).definition, "format")
|
---|
| 48 | if (fCxt.$data) validate$DataFormat()
|
---|
| 49 | else validateFormat()
|
---|
| 50 |
|
---|
| 51 | function validate$DataFormat(): void {
|
---|
| 52 | const fmts = gen.scopeValue("formats", {
|
---|
| 53 | ref: self.formats,
|
---|
| 54 | code: opts.code.formats,
|
---|
| 55 | })
|
---|
| 56 | const fmt = gen.const("fmt", _`${fmts}[${fCxt.schemaCode}]`)
|
---|
| 57 | cxt.fail$data(
|
---|
| 58 | or(
|
---|
| 59 | _`typeof ${fmt} != "object"`,
|
---|
| 60 | _`${fmt} instanceof RegExp`,
|
---|
| 61 | _`typeof ${fmt}.compare != "function"`,
|
---|
| 62 | compareCode(fmt)
|
---|
| 63 | )
|
---|
| 64 | )
|
---|
| 65 | }
|
---|
| 66 |
|
---|
| 67 | function validateFormat(): void {
|
---|
| 68 | const format = fCxt.schema as string
|
---|
| 69 | const fmtDef: AddedFormat | undefined = self.formats[format]
|
---|
| 70 | if (!fmtDef || fmtDef === true) return
|
---|
| 71 | if (
|
---|
| 72 | typeof fmtDef != "object" ||
|
---|
| 73 | fmtDef instanceof RegExp ||
|
---|
| 74 | typeof fmtDef.compare != "function"
|
---|
| 75 | ) {
|
---|
| 76 | throw new Error(`"${keyword}": format "${format}" does not define "compare" function`)
|
---|
| 77 | }
|
---|
| 78 | const fmt = gen.scopeValue("formats", {
|
---|
| 79 | key: format,
|
---|
| 80 | ref: fmtDef,
|
---|
| 81 | code: opts.code.formats ? _`${opts.code.formats}${getProperty(format)}` : undefined,
|
---|
| 82 | })
|
---|
| 83 |
|
---|
| 84 | cxt.fail$data(compareCode(fmt))
|
---|
| 85 | }
|
---|
| 86 |
|
---|
| 87 | function compareCode(fmt: Name): Code {
|
---|
| 88 | return _`${fmt}.compare(${data}, ${schemaCode}) ${KWDs[keyword as Kwd].fail} 0`
|
---|
| 89 | }
|
---|
| 90 | },
|
---|
| 91 | dependencies: ["format"],
|
---|
| 92 | }
|
---|
| 93 |
|
---|
| 94 | const formatLimitPlugin: Plugin<undefined> = (ajv: Ajv): Ajv => {
|
---|
| 95 | ajv.addKeyword(formatLimitDefinition)
|
---|
| 96 | return ajv
|
---|
| 97 | }
|
---|
| 98 |
|
---|
| 99 | export default formatLimitPlugin
|
---|