import { BDS_PYTHON_API_CONTRACT_V1 } from './pythonApiContractV1'; const PYTHON_RESERVED_KEYWORDS = new Set([ 'false', 'none', 'true', 'and', 'as', 'assert', 'async', 'await', 'break', 'class', 'continue', 'def', 'del', 'elif', 'else', 'except', 'finally', 'for', 'from', 'global', 'if', 'import', 'in', 'is', 'lambda', 'nonlocal', 'not', 'or', 'pass', 'raise', 'return', 'try', 'while', 'with', 'yield', 'match', 'case', ]); function toSnakeCase(value: string): string { return value .replace(/([a-z0-9])([A-Z])/g, '$1_$2') .replace(/[^a-zA-Z0-9]+/g, '_') .replace(/^_+|_+$/g, '') .toLowerCase(); } function quotePython(value: string): string { return value.replace(/\\/g, '\\\\').replace(/"/g, '\\"'); } function toPythonIdentifier(value: string): string { let identifier = toSnakeCase(value); if (!identifier) { identifier = '_'; } if (/^[0-9]/.test(identifier)) { identifier = `_${identifier}`; } if (PYTHON_RESERVED_KEYWORDS.has(identifier)) { identifier = `${identifier}_`; } return identifier; } function buildPythonMethod(method: { method: string; description: string; params: Array<{ name: string; required: boolean }>; }): string { const [namespace, member] = method.method.split('.'); if (!namespace || !member) { return ''; } const pythonMethodName = toPythonIdentifier(member); const pythonParams = method.params.map((param) => ({ sourceName: param.name, pythonName: toPythonIdentifier(param.name), required: param.required, })); const signature = pythonParams.length > 0 ? `, ${pythonParams.map((param) => (param.required ? param.pythonName : `${param.pythonName}=None`)).join(', ')}` : ''; const argsDict = method.params.length > 0 ? `{ ${method.params.map((param, index) => `"${param.name}": ${pythonParams[index]?.pythonName}`).join(', ')} }` : '{}'; return [ ` async def ${pythonMethodName}(self${signature}):`, ` \"\"\"${quotePython(method.description)}\"\"\"`, ` return await self._transport.call("${method.method}", ${argsDict})`, '', ].join('\n'); } function buildPythonNamespaceClass( namespace: string, methods: Array<{ method: string; description: string; params: Array<{ name: string; required: boolean }> }> ): string { const className = `${namespace[0].toUpperCase()}${namespace.slice(1)}Api`; const methodBlocks = methods.map((method) => buildPythonMethod(method)).join(''); return [ `class ${className}:`, ' def __init__(self, transport):', ' self._transport = transport', '', methodBlocks.trimEnd(), '', ].join('\n'); } export function generatePythonApiModuleV1(): string { const namespaceMap = new Map }>>(); for (const method of BDS_PYTHON_API_CONTRACT_V1.methods) { const [namespace] = method.method.split('.'); if (!namespace) { continue; } const entries = namespaceMap.get(namespace) ?? []; entries.push({ method: method.method, description: method.description, params: method.params.map((param) => ({ name: param.name, required: param.required, })), }); namespaceMap.set(namespace, entries); } const namespaceBlocks = Array.from(namespaceMap.entries()) .sort(([left], [right]) => left.localeCompare(right)) .map(([namespace, methods]) => buildPythonNamespaceClass(namespace, methods)) .join('\n'); const namespaceAssignments = Array.from(namespaceMap.keys()) .sort((left, right) => left.localeCompare(right)) .map((namespace) => ` self.${toPythonIdentifier(namespace)} = ${namespace[0].toUpperCase()}${namespace.slice(1)}Api(transport)`) .join('\n'); return [ '# Auto-generated by generatePythonApiModuleV1.ts', `# Contract version: ${BDS_PYTHON_API_CONTRACT_V1.version}`, '', 'import json', '', 'class BdsApiError(Exception):', ' pass', '', namespaceBlocks.trimEnd(), '', 'class BdsApi:', ' def __init__(self, transport):', ' self._transport = transport', namespaceAssignments, '', 'class _Transport:', ' def __init__(self, call_impl):', ' self._call_impl = call_impl', '', ' async def call(self, method, args):', ' raw_result = await self._call_impl(method, json.dumps(args))', ' if raw_result is None or raw_result == "":', ' return None', ' return json.loads(raw_result)', '', 'def install_bds_api(call_impl):', ' _transport = _Transport(call_impl)', ' bds = BdsApi(_transport)', ' return bds', '', ].join('\n'); }