Spaces:
Sleeping
Sleeping
| 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 | |