fork of hey-api/openapi-ts because I need some additional things
1import type { AnalysisContext } from '@hey-api/codegen-core';
2
3import { py } from '../../ts-python';
4import type { MaybePyDsl } from '../base';
5import { PyDsl } from '../base';
6import type { DoExpr } from '../mixins/do';
7import { BlockPyDsl } from './block';
8
9const Mixed = PyDsl<py.WithStatement>;
10
11export type WithItemInput =
12 | MaybePyDsl<py.Expression>
13 | { alias?: MaybePyDsl<py.Expression>; context: MaybePyDsl<py.Expression> };
14
15export class WithPyDsl extends Mixed {
16 readonly '~dsl' = 'WithPyDsl';
17
18 protected _body?: Array<DoExpr>;
19 protected _items: Array<WithItemInput> = [];
20 protected _modifier?: MaybePyDsl<py.Expression>;
21
22 constructor(...items: Array<WithItemInput>) {
23 super();
24 this._items = items;
25 }
26
27 override analyze(ctx: AnalysisContext): void {
28 super.analyze(ctx);
29
30 for (const item of this._items) {
31 if (typeof item === 'object' && 'context' in item) {
32 ctx.analyze(item.context);
33 ctx.analyze(item.alias);
34 } else {
35 ctx.analyze(item);
36 }
37 }
38 if (this._modifier) ctx.analyze(this._modifier);
39
40 if (this._body) {
41 ctx.pushScope();
42 try {
43 for (const stmt of this._body) ctx.analyze(stmt);
44 } finally {
45 ctx.popScope();
46 }
47 }
48 }
49
50 /** Returns true when all required builder calls are present. */
51 get isValid(): boolean {
52 return this.missingRequiredCalls().length === 0;
53 }
54
55 body(...items: Array<DoExpr>): this {
56 this._body = items;
57 return this;
58 }
59
60 item(item: WithItemInput): this {
61 this._items.push(item);
62 return this;
63 }
64
65 modifier(expr: MaybePyDsl<py.Expression>): this {
66 this._modifier = expr;
67 return this;
68 }
69
70 async(): this {
71 this._modifier = py.factory.createIdentifier('async');
72 return this;
73 }
74
75 override toAst(): py.WithStatement {
76 this.$validate();
77
78 const astItems = this._items.map((item) => {
79 if (typeof item === 'object' && 'context' in item) {
80 return py.factory.createWithItem(
81 this.$node(item.context) as py.Expression,
82 item.alias ? (this.$node(item.alias) as py.Expression) : undefined,
83 );
84 }
85 return py.factory.createWithItem(this.$node(item) as py.Expression, undefined);
86 });
87
88 const body = new BlockPyDsl(...this._body!).$do();
89
90 return py.factory.createWithStatement(
91 astItems,
92 [...body],
93 this._modifier ? [this.$node(this._modifier) as py.Expression] : undefined,
94 );
95 }
96
97 $validate(): asserts this is this & {
98 _body: Array<DoExpr>;
99 } {
100 const missing = this.missingRequiredCalls();
101 if (missing.length === 0) return;
102 throw new Error(`With statement missing ${missing.join(' and ')}`);
103 }
104
105 private missingRequiredCalls(): ReadonlyArray<string> {
106 const missing: Array<string> = [];
107 if (!this._items.length) missing.push('items');
108 if (!this._body || this._body.length === 0) missing.push('.body()');
109 return missing;
110 }
111}