Skip to content

Commit 8b1e304

Browse files
lpoorthuispi-mono
andcommitted
feat(kotlin): support inheritance and discriminators
AI-Usage: Agent AI-Harness: pi-mono Co-authored-by: pi-mono <261679550+pi-mono@users.noreply.github.com>
1 parent 60995e1 commit 8b1e304

16 files changed

Lines changed: 972 additions & 57 deletions

File tree

examples/kotlin-generate-jackson/__snapshots__/index.spec.ts.snap

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ enum class OrderStatus(@get:JsonValue val value: String) {
1818
@JsonCreator
1919
fun forValue(value: String): OrderStatus {
2020
return values().firstOrNull { it.value == value }
21-
?: throw IllegalArgumentException(\\"Unexpected value '$value'\\")
21+
?: throw IllegalArgumentException(\\"Unexpected value '$value' for enum 'OrderStatus'\\")
2222
}
2323
}
2424
}",

src/generators/kotlin/KotlinConstrainer.ts

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
1-
import { ConstrainedEnumValueModel } from '../../models';
1+
import {
2+
ConstrainedEnumValueModel,
3+
ConstrainedObjectModel,
4+
ConstrainedReferenceModel,
5+
ConstrainedUnionModel
6+
} from '../../models';
27
import {
38
defaultEnumKeyConstraints,
49
defaultEnumValueConstraints
@@ -72,11 +77,28 @@ function interpretUnionValueType(types: string[]): string {
7277
return 'Any';
7378
}
7479

80+
export function kotlinUnionIncludesBuiltInTypes(
81+
model: ConstrainedUnionModel
82+
): boolean {
83+
return !model.union.every(
84+
(union) =>
85+
union instanceof ConstrainedObjectModel ||
86+
(union instanceof ConstrainedReferenceModel &&
87+
union.ref instanceof ConstrainedObjectModel)
88+
);
89+
}
90+
7591
export const KotlinDefaultTypeMapping: KotlinTypeMapping = {
7692
Object({ constrainedModel }): string {
7793
return constrainedModel.name;
7894
},
7995
Reference({ constrainedModel }): string {
96+
if (
97+
constrainedModel.ref instanceof ConstrainedUnionModel &&
98+
kotlinUnionIncludesBuiltInTypes(constrainedModel.ref)
99+
) {
100+
return 'Any';
101+
}
80102
return constrainedModel.name;
81103
},
82104
Any(): string {
@@ -137,9 +159,10 @@ export const KotlinDefaultTypeMapping: KotlinTypeMapping = {
137159
? interpretUnionValueType(uniqueTypes)
138160
: uniqueTypes[0];
139161
},
140-
Union(): string {
141-
// No Unions in Kotlin, use Any for now.
142-
return 'Any';
162+
Union({ constrainedModel }): string {
163+
return kotlinUnionIncludesBuiltInTypes(constrainedModel)
164+
? 'Any'
165+
: constrainedModel.name;
143166
},
144167
Dictionary({ constrainedModel }): string {
145168
return `Map<${constrainedModel.key.type}, ${constrainedModel.value.type}>`;

src/generators/kotlin/KotlinGenerator.ts

Lines changed: 55 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,16 @@ import {
1919
ConstrainedUnionModel,
2020
InputMetaModel,
2121
MetaModel,
22-
RenderOutput
22+
ObjectModel,
23+
ReferenceModel,
24+
RenderOutput,
25+
UnionModel
2326
} from '../../models';
2427
import { IndentationTypes, split, TypeMapping } from '../../helpers';
2528
import { KotlinPreset, KOTLIN_DEFAULT_PRESET } from './KotlinPreset';
2629
import { ClassRenderer } from './renderers/ClassRenderer';
2730
import { EnumRenderer } from './renderers/EnumRenderer';
31+
import { UnionRenderer } from './renderers/UnionRenderer';
2832
import { isReservedKotlinKeyword } from './Constants';
2933
import { Logger } from '../..';
3034
import {
@@ -38,7 +42,8 @@ import {
3842
} from '../../helpers/ConstrainHelpers';
3943
import {
4044
KotlinDefaultConstraints,
41-
KotlinDefaultTypeMapping
45+
KotlinDefaultTypeMapping,
46+
kotlinUnionIncludesBuiltInTypes
4247
} from './KotlinConstrainer';
4348
import { DeepPartial, mergePartialAndDefault } from '../../utils/Partials';
4449
import { KotlinDependencyManager } from './KotlinDependencyManager';
@@ -130,9 +135,19 @@ export class KotlinGenerator extends AbstractGenerator<
130135
splitMetaModel(model: MetaModel): MetaModel[] {
131136
const metaModelsToSplit = {
132137
splitEnum: true,
133-
splitObject: true
138+
splitObject: true,
139+
splitUnion: true
134140
};
135-
return split(model, metaModelsToSplit);
141+
return split(model, metaModelsToSplit).filter(
142+
(splitModel) =>
143+
!(splitModel instanceof UnionModel) ||
144+
splitModel.union.every(
145+
(unionModel) =>
146+
unionModel instanceof ObjectModel ||
147+
(unionModel instanceof ReferenceModel &&
148+
unionModel.ref instanceof ObjectModel)
149+
)
150+
);
136151
}
137152

138153
constrainToMetaModel(
@@ -182,6 +197,15 @@ export class KotlinGenerator extends AbstractGenerator<
182197
args.inputModel,
183198
optionsToUse
184199
);
200+
} else if (
201+
args.constrainedModel instanceof ConstrainedUnionModel &&
202+
!kotlinUnionIncludesBuiltInTypes(args.constrainedModel)
203+
) {
204+
return this.renderUnion(
205+
args.constrainedModel,
206+
args.inputModel,
207+
optionsToUse
208+
);
185209
}
186210
Logger.warn(
187211
`Kotlin generator, cannot generate this type of model, ${args.constrainedModel.name}`
@@ -270,6 +294,33 @@ ${outputModel.result}`;
270294
});
271295
}
272296

297+
async renderUnion(
298+
model: ConstrainedUnionModel,
299+
inputModel: InputMetaModel,
300+
options?: DeepPartial<KotlinOptions>
301+
): Promise<RenderOutput> {
302+
const optionsToUse = KotlinGenerator.getKotlinOptions({
303+
...this.options,
304+
...options
305+
});
306+
const dependencyManagerToUse = this.getDependencyManager(optionsToUse);
307+
const presets = this.getPresets('union');
308+
const renderer = new UnionRenderer(
309+
this.options,
310+
this,
311+
presets,
312+
model,
313+
inputModel,
314+
dependencyManagerToUse
315+
);
316+
const result = await renderer.runSelfPreset();
317+
return RenderOutput.toRenderOutput({
318+
result,
319+
renderedName: model.name,
320+
dependencies: dependencyManagerToUse.dependencies
321+
});
322+
}
323+
273324
async renderEnum(
274325
model: ConstrainedEnumModel,
275326
inputModel: InputMetaModel,

src/generators/kotlin/KotlinPreset.ts

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@ import {
44
ClassPreset,
55
EnumPreset,
66
PresetArgs,
7-
ConstrainedEnumModel
7+
ConstrainedEnumModel,
8+
ConstrainedUnionModel,
9+
CommonPreset
810
} from '../../models';
911
import { KotlinOptions } from './KotlinGenerator';
1012
import {
@@ -15,6 +17,10 @@ import {
1517
EnumRenderer,
1618
KOTLIN_DEFAULT_ENUM_PRESET
1719
} from './renderers/EnumRenderer';
20+
import {
21+
UnionRenderer,
22+
KOTLIN_DEFAULT_UNION_PRESET
23+
} from './renderers/UnionRenderer';
1824

1925
export type ClassPresetType<O> = ClassPreset<ClassRenderer, O>;
2026
export interface EnumPresetType<O> extends EnumPreset<EnumRenderer, O> {
@@ -26,12 +32,20 @@ export interface EnumPresetType<O> extends EnumPreset<EnumRenderer, O> {
2632
) => Promise<string> | string;
2733
}
2834

35+
export type UnionPresetType<O> = CommonPreset<
36+
UnionRenderer,
37+
O,
38+
ConstrainedUnionModel
39+
>;
40+
2941
export type KotlinPreset<O = any> = Preset<{
3042
class: ClassPresetType<O>;
3143
enum: EnumPresetType<O>;
44+
union: UnionPresetType<O>;
3245
}>;
3346

3447
export const KOTLIN_DEFAULT_PRESET: KotlinPreset<KotlinOptions> = {
3548
class: KOTLIN_DEFAULT_CLASS_PRESET,
36-
enum: KOTLIN_DEFAULT_ENUM_PRESET
49+
enum: KOTLIN_DEFAULT_ENUM_PRESET,
50+
union: KOTLIN_DEFAULT_UNION_PRESET
3751
};

0 commit comments

Comments
 (0)