fork of hey-api/openapi-ts because I need some additional things
1import type { BindingKind } from '@hey-api/codegen-core';
2// import type { BindingKind, NodeScope, Symbol } from '@hey-api/codegen-core';
3// import { isSymbol } from '@hey-api/codegen-core';
4import type { MaybeFunc } from '@hey-api/types';
5
6import type { DollarPyDsl } from '../../py-dsl';
7import type { py } from '../../ts-python';
8// import { $, PythonRenderer } from '../../py-dsl';
9import type { PyDsl } from '../base';
10import type { CallArgs } from '../expr/call';
11
12export type NodeChain = ReadonlyArray<PyDsl>;
13
14export interface AccessOptions {
15 /** The access context. */
16 context?: 'example';
17 /** Enable debug mode. */
18 debug?: boolean;
19 /** Transform function for each node in the access chain. */
20 transform?: (node: PyDsl, index: number, chain: NodeChain) => PyDsl;
21}
22
23// export type AccessResult = ReturnType<typeof $.expr | typeof $.attr | typeof $.call | typeof $.new>;
24
25export interface ExampleOptions {
26 /** Import kind for the root node. */
27 importKind?: BindingKind;
28 /** Import name for the root node. */
29 importName?: string;
30 /** Setup to run before calling the example. */
31 importSetup?: MaybeFunc<
32 (
33 ctx: DollarPyDsl & {
34 /** The imported expression. */
35 node: PyDsl<py.Expression>;
36 },
37 ) => PyDsl<py.Expression>
38 >;
39 /** Module to import from. */
40 moduleName?: string;
41 /** Example request payload. */
42 payload?: MaybeFunc<(ctx: DollarPyDsl) => CallArgs | CallArgs[number]>;
43 /** Variable name for setup node. */
44 setupName?: string;
45}
46
47// function accessChainToNode<T = AccessResult>(accessChain: NodeChain): T {
48// let result!: AccessResult;
49// accessChain.forEach((node, index) => {
50// if (index === 0) {
51// // assume correct node
52// result = node as typeof result;
53// } else {
54// result = result.attr(node.name);
55// }
56// });
57// return result as T;
58// }
59
60// function getAccessChainForNode(node: PyDsl): NodeChain {
61// const structuralChain = [...getStructuralChainForNode(node, new Set())];
62// const accessChain = structuralToAccessChain(structuralChain);
63// if (accessChain.length === 0) {
64// // I _think_ this should not happen, but it does and this fix works for now.
65// // I assume this will cause issues with imports in some cases, investigate
66// // when it actually happens.
67// return [node.clone()];
68// }
69// return accessChain.map((node) => node.clone());
70// }
71
72// function getScope(node: PyDsl): NodeScope {
73// return node.scope ?? 'value';
74// }
75
76// function getStructuralChainForNode(node: PyDsl, visited: Set<PyDsl>): NodeChain {
77// if (visited.has(node)) return [];
78// visited.add(node);
79
80// if (isStopNode(node)) return [];
81
82// if (node.structuralParents) {
83// for (const [parent] of node.structuralParents) {
84// if (getScope(parent) !== getScope(node)) continue;
85
86// const chain = getStructuralChainForNode(parent, visited);
87// if (chain.length > 0) return [...chain, node];
88// }
89// }
90
91// if (!node.root) return [];
92
93// return [node];
94// }
95
96// function isAccessorNode(node: PyDsl): boolean {
97// return (
98// node['~dsl'] === 'FieldPyDsl' ||
99// node['~dsl'] === 'GetterPyDsl' ||
100// node['~dsl'] === 'MethodPyDsl'
101// );
102// }
103
104// function isStopNode(node: PyDsl): boolean {
105// return node['~dsl'] === 'FuncPyDsl' || node['~dsl'] === 'TemplatePyDsl';
106// }
107
108/**
109 * Fold a structural chain to an access chain by removing
110 * non-accessor nodes.
111 */
112// function structuralToAccessChain(structuralChain: NodeChain): NodeChain {
113// const accessChain: Array<PyDsl> = [];
114// structuralChain.forEach((node, index) => {
115// // assume first node is always included
116// if (index === 0) {
117// accessChain.push(node);
118// } else if (isAccessorNode(node)) {
119// accessChain.push(node);
120// }
121// });
122// return accessChain;
123// }
124
125// function transformAccessChain(accessChain: NodeChain, options: AccessOptions = {}): NodeChain {
126// return accessChain.map((node, index) => {
127// const transformedNode = options.transform?.(node, index, accessChain);
128// if (transformedNode) return transformedNode;
129// const accessNode = node.toAccessNode?.(node, options, {
130// chain: accessChain,
131// index,
132// isLeaf: index === accessChain.length - 1,
133// isRoot: index === 0,
134// length: accessChain.length,
135// });
136// if (accessNode) return accessNode;
137// if (index === 0) {
138// if (node['~dsl'] === 'ClassPyDsl') {
139// const nextNode = accessChain[index + 1];
140// if (nextNode && isAccessorNode(nextNode)) {
141// if ((nextNode as ReturnType<typeof $.field>).hasModifier('static')) {
142// return $(node.name);
143// }
144// }
145// return $.new(node.name).args();
146// }
147// return $(node.name);
148// }
149// return node;
150// });
151// }
152
153export class PyDslContext {
154 /**
155 * Build an expression for accessing the node.
156 *
157 * @param node - The node or symbol to build access for
158 * @param options - Access options
159 * @returns Expression for accessing the node
160 *
161 * @example
162 * ```ts
163 * ctx.access(node); // → Expression for accessing the node
164 * ```
165 */
166 // access<T = AccessResult>(node: PyDsl | Symbol<PyDsl>, options?: AccessOptions): T {
167 // const n = isSymbol(node) ? node.node : node;
168 // if (!n) {
169 // throw new Error(`Symbol ${node.name} is not resolved to a node.`);
170 // }
171 // const accessChain = getAccessChainForNode(n);
172 // const finalChain = transformAccessChain(accessChain, options);
173 // return accessChainToNode<T>(finalChain);
174 // }
175 /**
176 * Build an example.
177 *
178 * @param node - The node to generate an example for
179 * @param options - Example options
180 * @returns Full example string
181 *
182 * @example
183 * ```ts
184 * ctx.example(node, { moduleName: 'my-sdk' }); // → Full example string
185 * ```
186 */
187 // example(
188 // node: PyDsl,
189 // options?: ExampleOptions,
190 // astOptions?: Parameters<typeof PythonRenderer.astToString>[0],
191 // ): string {
192 // if (astOptions) {
193 // return PythonRenderer.astToString(astOptions);
194 // }
195 // options ||= {};
196 // const accessChain = getAccessChainForNode(node);
197 // if (options.importName) {
198 // accessChain[0]!.name.set(options.importName);
199 // }
200 // const importNode = $(accessChain[0]!.name.toString()); // must store name before transform
201 // const finalChain = transformAccessChain(accessChain, {
202 // context: 'example',
203 // });
204 // const setupNode = options.importSetup
205 // ? typeof options.importSetup === 'function'
206 // ? options.importSetup({ $, node: importNode })
207 // : options.importSetup
208 // : (finalChain[0]! as PyDsl<py.Expression>);
209 // const setupName = options.setupName;
210 // let payload = typeof options.payload === 'function' ? options.payload({ $ }) : options.payload;
211 // payload = payload instanceof Array ? payload : payload ? [payload] : [];
212 // let nodes: Array<PyDsl> = [];
213 // if (setupName) {
214 // nodes = [
215 // $.const(setupName).assign(setupNode),
216 // $.await(accessChainToNode([$(setupName), ...finalChain.slice(1)]).call(...payload)),
217 // ];
218 // } else {
219 // nodes = [$.await(accessChainToNode([setupNode, ...finalChain.slice(1)]).call(...payload))];
220 // }
221 // const localName = importNode.name.toString();
222 // return PythonRenderer.astToString({
223 // imports: [
224 // [
225 // {
226 // imports:
227 // !options.importKind || options.importKind === 'named'
228 // ? [
229 // {
230 // isTypeOnly: false,
231 // localName,
232 // sourceName: localName,
233 // },
234 // ]
235 // : [],
236 // isTypeOnly: false,
237 // kind: options.importKind ?? 'named',
238 // localName: options.importKind !== 'named' ? localName : undefined,
239 // modulePath: options.moduleName ?? 'your-package',
240 // },
241 // ],
242 // ],
243 // nodes,
244 // trailingNewline: false,
245 // });
246 // }
247}
248
249export const ctx = new PyDslContext();