123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899 |
- import type Ajv from "ajv"
- import type {
- Plugin,
- CodeKeywordDefinition,
- KeywordErrorDefinition,
- Code,
- Name,
- ErrorObject,
- } from "ajv"
- import type {AddedFormat} from "ajv/dist/types"
- import type {Rule} from "ajv/dist/compile/rules"
- import {KeywordCxt} from "ajv"
- import {_, str, or, getProperty, operators} from "ajv/dist/compile/codegen"
- type Kwd = "formatMaximum" | "formatMinimum" | "formatExclusiveMaximum" | "formatExclusiveMinimum"
- type Comparison = "<=" | ">=" | "<" | ">"
- const ops = operators
- const KWDs: {[K in Kwd]: {okStr: Comparison; ok: Code; fail: Code}} = {
- formatMaximum: {okStr: "<=", ok: ops.LTE, fail: ops.GT},
- formatMinimum: {okStr: ">=", ok: ops.GTE, fail: ops.LT},
- formatExclusiveMaximum: {okStr: "<", ok: ops.LT, fail: ops.GTE},
- formatExclusiveMinimum: {okStr: ">", ok: ops.GT, fail: ops.LTE},
- }
- export type LimitFormatError = ErrorObject<Kwd, {limit: string; comparison: Comparison}>
- const error: KeywordErrorDefinition = {
- message: ({keyword, schemaCode}) => str`should be ${KWDs[keyword as Kwd].okStr} ${schemaCode}`,
- params: ({keyword, schemaCode}) =>
- _`{comparison: ${KWDs[keyword as Kwd].okStr}, limit: ${schemaCode}}`,
- }
- export const formatLimitDefinition: CodeKeywordDefinition = {
- keyword: Object.keys(KWDs),
- type: "string",
- schemaType: "string",
- $data: true,
- error,
- code(cxt) {
- const {gen, data, schemaCode, keyword, it} = cxt
- const {opts, self} = it
- if (!opts.validateFormats) return
- const fCxt = new KeywordCxt(it, (self.RULES.all.format as Rule).definition, "format")
- if (fCxt.$data) validate$DataFormat()
- else validateFormat()
- function validate$DataFormat(): void {
- const fmts = gen.scopeValue("formats", {
- ref: self.formats,
- code: opts.code.formats,
- })
- const fmt = gen.const("fmt", _`${fmts}[${fCxt.schemaCode}]`)
- cxt.fail$data(
- or(
- _`typeof ${fmt} != "object"`,
- _`${fmt} instanceof RegExp`,
- _`typeof ${fmt}.compare != "function"`,
- compareCode(fmt)
- )
- )
- }
- function validateFormat(): void {
- const format = fCxt.schema as string
- const fmtDef: AddedFormat | undefined = self.formats[format]
- if (!fmtDef || fmtDef === true) return
- if (
- typeof fmtDef != "object" ||
- fmtDef instanceof RegExp ||
- typeof fmtDef.compare != "function"
- ) {
- throw new Error(`"${keyword}": format "${format}" does not define "compare" function`)
- }
- const fmt = gen.scopeValue("formats", {
- key: format,
- ref: fmtDef,
- code: opts.code.formats ? _`${opts.code.formats}${getProperty(format)}` : undefined,
- })
- cxt.fail$data(compareCode(fmt))
- }
- function compareCode(fmt: Name): Code {
- return _`${fmt}.compare(${data}, ${schemaCode}) ${KWDs[keyword as Kwd].fail} 0`
- }
- },
- dependencies: ["format"],
- }
- const formatLimitPlugin: Plugin<undefined> = (ajv: Ajv): Ajv => {
- ajv.addKeyword(formatLimitDefinition)
- return ajv
- }
- export default formatLimitPlugin
|