.gitignore added
[dotfiles/.git] / .config / coc / extensions / node_modules / coc-prettier / node_modules / table / node_modules / ajv / lib / compile / jtd / serialize.ts
diff --git a/.config/coc/extensions/node_modules/coc-prettier/node_modules/table/node_modules/ajv/lib/compile/jtd/serialize.ts b/.config/coc/extensions/node_modules/coc-prettier/node_modules/table/node_modules/ajv/lib/compile/jtd/serialize.ts
new file mode 100644 (file)
index 0000000..f967100
--- /dev/null
@@ -0,0 +1,260 @@
+import type Ajv from "../../core"
+import type {SchemaObject} from "../../types"
+import {jtdForms, JTDForm, SchemaObjectMap} from "./types"
+import {SchemaEnv, getCompilingSchema} from ".."
+import {_, str, and, getProperty, CodeGen, Code, Name} from "../codegen"
+import MissingRefError from "../ref_error"
+import N from "../names"
+import {isOwnProperty} from "../../vocabularies/code"
+import {hasRef} from "../../vocabularies/jtd/ref"
+import {useFunc} from "../util"
+import quote from "../../runtime/quote"
+
+const genSerialize: {[F in JTDForm]: (cxt: SerializeCxt) => void} = {
+  elements: serializeElements,
+  values: serializeValues,
+  discriminator: serializeDiscriminator,
+  properties: serializeProperties,
+  optionalProperties: serializeProperties,
+  enum: serializeString,
+  type: serializeType,
+  ref: serializeRef,
+}
+
+interface SerializeCxt {
+  readonly gen: CodeGen
+  readonly self: Ajv // current Ajv instance
+  readonly schemaEnv: SchemaEnv
+  readonly definitions: SchemaObjectMap
+  schema: SchemaObject
+  data: Code
+}
+
+export default function compileSerializer(
+  this: Ajv,
+  sch: SchemaEnv,
+  definitions: SchemaObjectMap
+): SchemaEnv {
+  const _sch = getCompilingSchema.call(this, sch)
+  if (_sch) return _sch
+  const {es5, lines} = this.opts.code
+  const {ownProperties} = this.opts
+  const gen = new CodeGen(this.scope, {es5, lines, ownProperties})
+  const serializeName = gen.scopeName("serialize")
+  const cxt: SerializeCxt = {
+    self: this,
+    gen,
+    schema: sch.schema as SchemaObject,
+    schemaEnv: sch,
+    definitions,
+    data: N.data,
+  }
+
+  let sourceCode: string | undefined
+  try {
+    this._compilations.add(sch)
+    sch.serializeName = serializeName
+    gen.func(serializeName, N.data, false, () => {
+      gen.let(N.json, str``)
+      serializeCode(cxt)
+      gen.return(N.json)
+    })
+    gen.optimize(this.opts.code.optimize)
+    const serializeFuncCode = gen.toString()
+    sourceCode = `${gen.scopeRefs(N.scope)}return ${serializeFuncCode}`
+    const makeSerialize = new Function(`${N.scope}`, sourceCode)
+    const serialize: (data: unknown) => string = makeSerialize(this.scope.get())
+    this.scope.value(serializeName, {ref: serialize})
+    sch.serialize = serialize
+  } catch (e) {
+    if (sourceCode) this.logger.error("Error compiling serializer, function code:", sourceCode)
+    delete sch.serialize
+    delete sch.serializeName
+    throw e
+  } finally {
+    this._compilations.delete(sch)
+  }
+  return sch
+}
+
+function serializeCode(cxt: SerializeCxt): void {
+  let form: JTDForm | undefined
+  for (const key of jtdForms) {
+    if (key in cxt.schema) {
+      form = key
+      break
+    }
+  }
+  serializeNullable(cxt, form ? genSerialize[form] : serializeEmpty)
+}
+
+function serializeNullable(cxt: SerializeCxt, serializeForm: (_cxt: SerializeCxt) => void): void {
+  const {gen, schema, data} = cxt
+  if (!schema.nullable) return serializeForm(cxt)
+  gen.if(
+    _`${data} === undefined || ${data} === null`,
+    () => gen.add(N.json, _`"null"`),
+    () => serializeForm(cxt)
+  )
+}
+
+function serializeElements(cxt: SerializeCxt): void {
+  const {gen, schema, data} = cxt
+  gen.add(N.json, str`[`)
+  const first = gen.let("first", true)
+  gen.forOf("el", data, (el) => {
+    addComma(cxt, first)
+    serializeCode({...cxt, schema: schema.elements, data: el})
+  })
+  gen.add(N.json, str`]`)
+}
+
+function serializeValues(cxt: SerializeCxt): void {
+  const {gen, schema, data} = cxt
+  gen.add(N.json, str`{`)
+  const first = gen.let("first", true)
+  gen.forIn("key", data, (key) => serializeKeyValue(cxt, key, schema.values, first))
+  gen.add(N.json, str`}`)
+}
+
+function serializeKeyValue(cxt: SerializeCxt, key: Name, schema: SchemaObject, first: Name): void {
+  const {gen, data} = cxt
+  addComma(cxt, first)
+  serializeString({...cxt, data: key})
+  gen.add(N.json, str`:`)
+  const value = gen.const("value", _`${data}${getProperty(key)}`)
+  serializeCode({...cxt, schema, data: value})
+}
+
+function serializeDiscriminator(cxt: SerializeCxt): void {
+  const {gen, schema, data} = cxt
+  const {discriminator} = schema
+  gen.add(N.json, str`{${JSON.stringify(discriminator)}:`)
+  const tag = gen.const("tag", _`${data}${getProperty(discriminator)}`)
+  serializeString({...cxt, data: tag})
+  gen.if(false)
+  for (const tagValue in schema.mapping) {
+    gen.elseIf(_`${tag} === ${tagValue}`)
+    const sch = schema.mapping[tagValue]
+    serializeSchemaProperties({...cxt, schema: sch}, discriminator)
+  }
+  gen.endIf()
+  gen.add(N.json, str`}`)
+}
+
+function serializeProperties(cxt: SerializeCxt): void {
+  const {gen} = cxt
+  gen.add(N.json, str`{`)
+  serializeSchemaProperties(cxt)
+  gen.add(N.json, str`}`)
+}
+
+function serializeSchemaProperties(cxt: SerializeCxt, discriminator?: string): void {
+  const {gen, schema, data} = cxt
+  const {properties, optionalProperties} = schema
+  const props = keys(properties)
+  const optProps = keys(optionalProperties)
+  const allProps = allProperties(props.concat(optProps))
+  let first = !discriminator
+  for (const key of props) {
+    serializeProperty(key, properties[key], keyValue(key))
+  }
+  for (const key of optProps) {
+    const value = keyValue(key)
+    gen.if(and(_`${value} !== undefined`, isOwnProperty(gen, data, key)), () =>
+      serializeProperty(key, optionalProperties[key], value)
+    )
+  }
+  if (schema.additionalProperties) {
+    gen.forIn("key", data, (key) =>
+      gen.if(isAdditional(key, allProps), () =>
+        serializeKeyValue(cxt, key, {}, gen.let("first", first))
+      )
+    )
+  }
+
+  function keys(ps?: SchemaObjectMap): string[] {
+    return ps ? Object.keys(ps) : []
+  }
+
+  function allProperties(ps: string[]): string[] {
+    if (discriminator) ps.push(discriminator)
+    if (new Set(ps).size !== ps.length) {
+      throw new Error("JTD: properties/optionalProperties/disciminator overlap")
+    }
+    return ps
+  }
+
+  function keyValue(key: string): Name {
+    return gen.const("value", _`${data}${getProperty(key)}`)
+  }
+
+  function serializeProperty(key: string, propSchema: SchemaObject, value: Name): void {
+    if (first) first = false
+    else gen.add(N.json, str`,`)
+    gen.add(N.json, str`${JSON.stringify(key)}:`)
+    serializeCode({...cxt, schema: propSchema, data: value})
+  }
+
+  function isAdditional(key: Name, ps: string[]): Code | true {
+    return ps.length ? and(...ps.map((p) => _`${key} !== ${p}`)) : true
+  }
+}
+
+function serializeType(cxt: SerializeCxt): void {
+  const {gen, schema, data} = cxt
+  switch (schema.type) {
+    case "boolean":
+      gen.add(N.json, _`${data} ? "true" : "false"`)
+      break
+    case "string":
+      serializeString(cxt)
+      break
+    case "timestamp":
+      gen.if(
+        _`${data} instanceof Date`,
+        () => gen.add(N.json, _`${data}.toISOString()`),
+        () => serializeString(cxt)
+      )
+      break
+    default:
+      serializeNumber(cxt)
+  }
+}
+
+function serializeString({gen, data}: SerializeCxt): void {
+  gen.add(N.json, _`${useFunc(gen, quote)}(${data})`)
+}
+
+function serializeNumber({gen, data}: SerializeCxt): void {
+  gen.add(N.json, _`"" + ${data}`)
+}
+
+function serializeRef(cxt: SerializeCxt): void {
+  const {gen, self, data, definitions, schema, schemaEnv} = cxt
+  const {ref} = schema
+  const refSchema = definitions[ref]
+  if (!refSchema) throw new MissingRefError("", ref, `No definition ${ref}`)
+  if (!hasRef(refSchema)) return serializeCode({...cxt, schema: refSchema})
+  const {root} = schemaEnv
+  const sch = compileSerializer.call(self, new SchemaEnv({schema: refSchema, root}), definitions)
+  gen.add(N.json, _`${getSerialize(gen, sch)}(${data})`)
+}
+
+function getSerialize(gen: CodeGen, sch: SchemaEnv): Code {
+  return sch.serialize
+    ? gen.scopeValue("serialize", {ref: sch.serialize})
+    : _`${gen.scopeValue("wrapper", {ref: sch})}.serialize`
+}
+
+function serializeEmpty({gen, data}: SerializeCxt): void {
+  gen.add(N.json, _`JSON.stringify(${data})`)
+}
+
+function addComma({gen}: SerializeCxt, first: Name): void {
+  gen.if(
+    first,
+    () => gen.assign(first, false),
+    () => gen.add(N.json, str`,`)
+  )
+}