UNPKG

@samchon/openapi

Version:

OpenAPI definitions and converters for 'typia' and 'nestia'.

183 lines (172 loc) 4.56 kB
import { OpenApi } from "../../OpenApi"; export namespace LlmDescriptionInverter { export const numeric = ( description: string | undefined, ): Pick< OpenApi.IJsonSchema.INumber, | "minimum" | "maximum" | "exclusiveMinimum" | "exclusiveMaximum" | "multipleOf" | "description" > => { if (description === undefined) return {}; const lines: string[] = description.split("\n"); const exclusiveMinimum: number | undefined = find({ type: "number", name: "exclusiveMinimum", lines, }); const exclusiveMaximum: number | undefined = find({ type: "number", name: "exclusiveMaximum", lines, }); return { minimum: exclusiveMinimum ?? find({ type: "number", name: "minimum", lines, }), maximum: exclusiveMaximum ?? find({ type: "number", name: "maximum", lines, }), exclusiveMinimum: exclusiveMinimum !== undefined ? true : undefined, exclusiveMaximum: exclusiveMaximum !== undefined ? true : undefined, multipleOf: find({ type: "number", name: "multipleOf", lines, }), description: describe(lines, [ "minimum", "maximum", "exclusiveMinimum", "exclusiveMaximum", "multipleOf", ]), }; }; export const string = ( description: string | undefined, ): Pick< OpenApi.IJsonSchema.IString, | "format" | "pattern" | "contentMediaType" | "minLength" | "maxLength" | "description" > => { if (description === undefined) return {}; const lines: string[] = description.split("\n"); return { format: find({ type: "string", name: "format", lines, }), pattern: find({ type: "string", name: "pattern", lines, }), contentMediaType: find({ type: "string", name: "contentMediaType", lines, }), minLength: find({ type: "number", name: "minLength", lines, }), maxLength: find({ type: "number", name: "maxLength", lines, }), description: describe(lines, [ "format", "pattern", "contentMediaType", "minLength", "maxLength", ]), }; }; export const array = ( description: string | undefined, ): Pick< OpenApi.IJsonSchema.IArray, "minItems" | "maxItems" | "uniqueItems" | "description" > => { if (description === undefined) return {}; const lines: string[] = description.split("\n"); return { minItems: find({ type: "number", name: "minItems", lines, }), maxItems: find({ type: "number", name: "maxItems", lines, }), uniqueItems: find({ type: "boolean", name: "uniqueItems", lines, }), description: describe(lines, ["minItems", "maxItems", "uniqueItems"]), }; }; const find = <Type extends "boolean" | "number" | "string">(props: { type: Type; name: string; lines: string[]; }): | (Type extends "boolean" ? true : Type extends "number" ? number : string) | undefined => { if (props.type === "boolean") return props.lines.some((line) => line.startsWith(`@${props.name}`)) ? true : (undefined as any); for (const line of props.lines) { if (line.startsWith(`@${props.name} `) === false) continue; const value: string = line.replace(`@${props.name} `, "").trim(); if (props.type === "number") return (isNaN(Number(value)) ? undefined : Number(value)) satisfies | number | undefined as any; return value as any; } return undefined as any; }; const describe = (lines: string[], tags: string[]): string | undefined => { const ret: string = trimArray( lines .map((str) => str.trim()) .filter((str) => tags.every((tag) => str.startsWith(`@${tag}`) === false), ), ).join("\n"); return ret.length === 0 ? undefined : ret; }; const trimArray = (array: string[]): string[] => { let first: number = 0; let last: number = array.length - 1; for (; first < array.length; ++first) if (array[first]!.trim().length !== 0) break; for (; last >= 0; --last) if (array[last]!.trim().length !== 0) break; return array.slice(first, last + 1); }; }