Skip to content

Commit a9b92c0

Browse files
author
nicosammito
committed
feat: implement getTypesFromFunction to parse function signatures and return types
1 parent fd2544a commit a9b92c0

7 files changed

Lines changed: 211 additions & 2 deletions

File tree

src/extraction/getTypeVariant.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ export const getTypeVariant = (
2626
const val: TargetType = {} as any;
2727
`;
2828

29+
console.log(sourceCode)
30+
2931
const fileName = `index.ts`;
3032
const host = createCompilerHost(fileName, sourceCode);
3133
const sourceFile = host.getSourceFile(fileName)!;
Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
import {FunctionDefinition} from "@code0-tech/sagittarius-graphql-types";
2+
3+
export interface FunctionTypes {
4+
parameters: string[];
5+
returnType: string;
6+
}
7+
8+
/**
9+
* Resolves the types of the parameters and the return type of a NodeFunction.
10+
*/
11+
export const getTypesFromFunction = (
12+
functionDefinition: FunctionDefinition
13+
): FunctionTypes => {
14+
const signature = functionDefinition.signature;
15+
16+
if (!signature) {
17+
return {parameters: [], returnType: "any"};
18+
}
19+
20+
let searchStart = 0;
21+
// Skip generics
22+
if (signature.trim().startsWith('<')) {
23+
let depth = 0;
24+
for (let i = 0; i < signature.length; i++) {
25+
const char = signature[i];
26+
if (char === '<') depth++;
27+
else if (char === '>') {
28+
depth--;
29+
if (depth === 0) {
30+
searchStart = i + 1;
31+
break;
32+
}
33+
}
34+
}
35+
}
36+
37+
const paramStart = signature.indexOf('(', searchStart);
38+
if (paramStart === -1) {
39+
return {parameters: [], returnType: "any"};
40+
}
41+
42+
// Find matching closing paren
43+
let paramEnd = -1;
44+
let depth = 0;
45+
for (let i = paramStart; i < signature.length; i++) {
46+
const char = signature[i];
47+
if (char === '(') depth++;
48+
else if (char === ')') {
49+
depth--;
50+
if (depth === 0) {
51+
paramEnd = i;
52+
break;
53+
}
54+
}
55+
}
56+
57+
if (paramEnd === -1) {
58+
return {parameters: [], returnType: "any"};
59+
}
60+
61+
const paramsString = signature.substring(paramStart + 1, paramEnd);
62+
let returnTypeString = signature.substring(paramEnd + 1).trim();
63+
64+
// Parse return type
65+
if (returnTypeString.startsWith(':')) {
66+
returnTypeString = returnTypeString.substring(1).trim();
67+
}
68+
const returnType = returnTypeString || "void";
69+
70+
// Parse parameters
71+
const parameters: string[] = [];
72+
if (paramsString.trim()) {
73+
let currentParam = "";
74+
let pDepthParen = 0;
75+
let pDepthAngle = 0;
76+
let pDepthBrace = 0;
77+
let pDepthBracket = 0;
78+
79+
const pushParam = (p: string) => {
80+
// Extract type from "name: Type"
81+
// Scan for first colon at top level
82+
let colonIndex = -1;
83+
84+
let cDepthBrace = 0;
85+
let cDepthBracket = 0;
86+
let cDepthParen = 0;
87+
let cDepthAngle = 0;
88+
89+
for (let i = 0; i < p.length; i++) {
90+
const c = p[i];
91+
if (c === '{') cDepthBrace++;
92+
else if (c === '}') cDepthBrace--;
93+
else if (c === '[') cDepthBracket++;
94+
else if (c === ']') cDepthBracket--;
95+
else if (c === '(') cDepthParen++;
96+
else if (c === ')') cDepthParen--;
97+
else if (c === '<') cDepthAngle++;
98+
else if (c === '>') cDepthAngle--;
99+
else if (c === ':' && cDepthBrace === 0 && cDepthBracket === 0 && cDepthParen === 0 && cDepthAngle === 0) {
100+
colonIndex = i;
101+
break;
102+
}
103+
}
104+
105+
if (colonIndex !== -1) {
106+
parameters.push(p.substring(colonIndex + 1).trim());
107+
} else {
108+
parameters.push("any");
109+
}
110+
};
111+
112+
for (const char of paramsString) {
113+
if (char === '(') pDepthParen++;
114+
else if (char === ')') pDepthParen--;
115+
else if (char === '<') pDepthAngle++;
116+
else if (char === '>') pDepthAngle--;
117+
else if (char === '{') pDepthBrace++;
118+
else if (char === '}') pDepthBrace--;
119+
else if (char === '[') pDepthBracket++;
120+
else if (char === ']') pDepthBracket--;
121+
else if (char === ',' && pDepthParen === 0 && pDepthAngle === 0 && pDepthBrace === 0 && pDepthBracket === 0) {
122+
pushParam(currentParam.trim());
123+
currentParam = "";
124+
continue;
125+
}
126+
currentParam += char;
127+
}
128+
if (currentParam.trim()) {
129+
pushParam(currentParam.trim());
130+
}
131+
}
132+
133+
return {
134+
parameters,
135+
returnType
136+
};
137+
};

src/extraction/getValueFromType.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ export const getValueFromType = (
7575
const typeRef = t as ts.TypeReference;
7676
const elementType = typeRef.typeArguments?.[0] || checker.getAnyType();
7777
const sample = generateSample(elementType, node, visited);
78-
return sample === "null" ? [generateSample(elementType, node, visited)] : [];
78+
return sample !== null ? [sample] : [];
7979
}
8080

8181
// 4. Handle Objects / Interfaces

src/utils.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ export const DEFAULT_COMPILER_OPTIONS: ts.CompilerOptions = {
8080
*/
8181
export function getSharedTypeDeclarations(dataTypes?: DataType[]): string {
8282
const genericDeclarations = Array.from(new Set(dataTypes?.flatMap(dt => dt.genericKeys || [])))
83-
.map(g => `type ${g} = any;`)
83+
.map(g => `type ${g} = unknown;`)
8484
.join("\n");
8585

8686
const typeAliasDeclarations = dataTypes?.map(dt =>

test/getTypeVariant.test.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ describe('getTypeVariant', () => {
1818
it('sollte OBJECT für Interfaces oder Objekte mit Properties zurückgeben', () => {
1919
expect(getTypeVariant("{ name: string }", DATA_TYPES)).toBe(DataTypeVariant.OBJECT);
2020
expect(getTypeVariant("{}", DATA_TYPES)).toBe(DataTypeVariant.OBJECT);
21+
expect(getTypeVariant("OBJECT<T>", DATA_TYPES)).toBe(DataTypeVariant.OBJECT);
2122
});
2223

2324
it('sollte TYPE für einfache Type-Aliase oder void zurückgeben', () => {
@@ -29,6 +30,7 @@ describe('getTypeVariant', () => {
2930
// In data.ts ist LIST als T[] definiert
3031
expect(getTypeVariant("LIST<NUMBER>", DATA_TYPES)).toBe(DataTypeVariant.ARRAY);
3132
expect(getTypeVariant("LIST<unknown>", DATA_TYPES)).toBe(DataTypeVariant.ARRAY);
33+
expect(getTypeVariant("LIST<T>", DATA_TYPES)).toBe(DataTypeVariant.ARRAY);
3234
});
3335

3436
it('sollte NODE für Funktionstypen wie CONSUMER zurückgeben', () => {

test/getTypesFromFunction.test.ts

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import {describe, expect, it} from "vitest";
2+
import {getTypesFromFunction} from "../src/extraction/getTypesFromFunction";
3+
import {FUNCTION_SIGNATURES} from "./data";
4+
5+
describe("getTypesFromFunction", () => {
6+
7+
it("should correctly parse std::list::filter", () => {
8+
const func = FUNCTION_SIGNATURES.find(f => f.identifier === "std::list::filter");
9+
expect(func).toBeDefined();
10+
if (func) {
11+
const result = getTypesFromFunction(func);
12+
expect(result.parameters).toEqual(["LIST<T>", "PREDICATE<T>"]);
13+
expect(result.returnType).toBe("LIST<T>");
14+
}
15+
});
16+
17+
it("should correctly parse std::list::first", () => {
18+
const func = FUNCTION_SIGNATURES.find(f => f.identifier === "std::list::first");
19+
expect(func).toBeDefined();
20+
if (func) {
21+
const result = getTypesFromFunction(func);
22+
expect(result.parameters).toEqual(["LIST<T>"]);
23+
expect(result.returnType).toBe("T");
24+
}
25+
});
26+
27+
it("should correctly parse std::list::for_each", () => {
28+
const func = FUNCTION_SIGNATURES.find(f => f.identifier === "std::list::for_each");
29+
expect(func).toBeDefined();
30+
if (func) {
31+
const result = getTypesFromFunction(func);
32+
expect(result.parameters).toEqual(["LIST<T>", "CONSUMER<T>"]);
33+
expect(result.returnType).toBe("void");
34+
}
35+
});
36+
37+
it("should correctly parse std::list::map", () => {
38+
const func = FUNCTION_SIGNATURES.find(f => f.identifier === "std::list::map");
39+
expect(func).toBeDefined();
40+
if (func) {
41+
const result = getTypesFromFunction(func);
42+
expect(result.parameters).toEqual(["LIST<T>", "TRANSFORM<T, R>"]);
43+
expect(result.returnType).toBe("LIST<R>");
44+
}
45+
});
46+
47+
it("should correctly parse std::list::at", () => {
48+
const func = FUNCTION_SIGNATURES.find(f => f.identifier === "std::list::at");
49+
expect(func).toBeDefined();
50+
if (func) {
51+
const result = getTypesFromFunction(func);
52+
expect(result.parameters).toEqual(["LIST<T>", "NUMBER"]);
53+
expect(result.returnType).toBe("T");
54+
}
55+
});
56+
57+
it("should correctly parse std::list::sort_reverse", () => {
58+
const func = FUNCTION_SIGNATURES.find(f => f.identifier === "std::list::sort_reverse");
59+
expect(func).toBeDefined();
60+
if (func) {
61+
const result = getTypesFromFunction(func);
62+
expect(result.parameters).toEqual(["LIST<T>", "COMPARATOR<T>"]);
63+
expect(result.returnType).toBe("LIST<T>");
64+
}
65+
});
66+
});

test/valueFromType.test.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,8 @@ describe('getValueFromType', () => {
7373
const type = 'LIST<{ uid: TEXT, tags: LIST<NUMBER> }>';
7474
const result = getValueFromType(type, DATA_TYPES);
7575

76+
console.log(result)
77+
7678
expect(result.value).toEqual([
7779
{
7880
uid: '',

0 commit comments

Comments
 (0)