fork of hey-api/openapi-ts because I need some additional things
1import type { SchemaVisitorContext, SchemaWithType, Walker } from '@hey-api/shared';
2import { childContext, deduplicateSchema } from '@hey-api/shared';
3
4import { $ } from '../../../../py-dsl';
5import type { ArrayResolverContext } from '../../resolvers';
6import type { PydanticFinal, PydanticResult, PydanticType } from '../../shared/types';
7import type { PydanticPlugin } from '../../types';
8import type { FieldConstraints } from '../constants';
9
10function baseNode(ctx: ArrayResolverContext): PydanticType {
11 const { applyModifiers, childResults, plugin } = ctx;
12 const any = plugin.external('typing.Any');
13
14 if (!childResults.length) {
15 return {
16 type: $('list').slice(any),
17 };
18 }
19
20 if (childResults.length === 1) {
21 const itemResult = applyModifiers(childResults[0]!);
22 return {
23 type: $('list').slice(itemResult.type ?? any),
24 };
25 }
26
27 if (childResults.length > 1) {
28 const union = plugin.external('typing.Union');
29 const itemTypes = childResults.map((r) => applyModifiers(r).type ?? any);
30 return {
31 type: $('list').slice($(union).slice(...itemTypes)),
32 };
33 }
34
35 return {
36 type: $('list').slice(any),
37 };
38}
39
40function minLengthNode(ctx: ArrayResolverContext): PydanticType | undefined {
41 const { schema } = ctx;
42 if (schema.minItems === undefined) return;
43 return {
44 fieldConstraints: { min_length: schema.minItems },
45 };
46}
47
48function maxLengthNode(ctx: ArrayResolverContext): PydanticType | undefined {
49 const { schema } = ctx;
50 if (schema.maxItems === undefined) return;
51 return {
52 fieldConstraints: { max_length: schema.maxItems },
53 };
54}
55
56function arrayResolver(ctx: ArrayResolverContext): PydanticType {
57 const baseResult = ctx.nodes.base(ctx);
58 const minLengthResult = ctx.nodes.minLength(ctx);
59 const maxLengthResult = ctx.nodes.maxLength(ctx);
60
61 const fieldConstraints: FieldConstraints = {
62 ...(baseResult.fieldConstraints ?? {}),
63 ...(minLengthResult?.fieldConstraints ?? {}),
64 ...(maxLengthResult?.fieldConstraints ?? {}),
65 };
66
67 if (ctx.schema.description !== undefined) {
68 fieldConstraints.description = ctx.schema.description;
69 }
70
71 return {
72 ...baseResult,
73 fieldConstraints: Object.keys(fieldConstraints).length ? fieldConstraints : undefined,
74 };
75}
76
77export interface ArrayToTypeResult extends PydanticType {
78 childResults: Array<PydanticResult>;
79}
80
81export function arrayToType(ctx: {
82 applyModifiers: (result: PydanticResult, options?: { optional?: boolean }) => PydanticFinal;
83 plugin: PydanticPlugin['Instance'];
84 schema: SchemaWithType<'array'>;
85 walk: Walker<PydanticResult, PydanticPlugin['Instance']>;
86 walkerCtx: SchemaVisitorContext<PydanticPlugin['Instance']>;
87}): ArrayToTypeResult {
88 const { applyModifiers, plugin, schema, walk, walkerCtx } = ctx;
89 const any = plugin.external('typing.Any');
90
91 const childResults: Array<PydanticResult> = [];
92
93 if (schema.items) {
94 const normalizedSchema = deduplicateSchema({ schema });
95 for (let i = 0; i < normalizedSchema.items!.length; i++) {
96 const item = normalizedSchema.items![i]!;
97 const result = walk(item, childContext(walkerCtx, 'items', i));
98 childResults.push(result);
99 }
100 }
101
102 const resolverCtx: ArrayResolverContext = {
103 $,
104 applyModifiers,
105 childResults,
106 nodes: {
107 base: baseNode,
108 maxLength: maxLengthNode,
109 minLength: minLengthNode,
110 },
111 plugin,
112 schema,
113 walk,
114 walkerCtx,
115 };
116
117 const resolver = plugin.config['~resolvers']?.array;
118 const resolved = resolver?.(resolverCtx) ?? arrayResolver(resolverCtx);
119
120 if (!resolved.type) {
121 resolved.type = $('list').slice(any);
122 }
123
124 return {
125 ...resolved,
126 childResults,
127 };
128}