skir-kotlin-gen 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +469 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +671 -0
- package/dist/index.js.map +1 -0
- package/dist/naming.d.ts +20 -0
- package/dist/naming.d.ts.map +1 -0
- package/dist/naming.js +121 -0
- package/dist/naming.js.map +1 -0
- package/dist/type_speller.d.ts +30 -0
- package/dist/type_speller.d.ts.map +1 -0
- package/dist/type_speller.js +180 -0
- package/dist/type_speller.js.map +1 -0
- package/package.json +50 -0
- package/src/index.ts +1010 -0
- package/src/naming.ts +146 -0
- package/src/type_speller.ts +225 -0
package/README.md
ADDED
|
@@ -0,0 +1,469 @@
|
|
|
1
|
+
[](https://www.npmjs.com/package/skir-kotlin-gen)
|
|
2
|
+
[](https://github.com/gepheum/skir-kotlin-gen/actions)
|
|
3
|
+
|
|
4
|
+
# Soia's Kotlin code generator
|
|
5
|
+
|
|
6
|
+
Official plugin for generating Kotlin code from [.skir](https://github.com/gepheum/skir) files.
|
|
7
|
+
|
|
8
|
+
## Installation
|
|
9
|
+
|
|
10
|
+
From your project's root directory, run `npm i --save-dev skir-kotlin-gen`.
|
|
11
|
+
|
|
12
|
+
In your `skir.yml` file, add the following snippet under `generators`:
|
|
13
|
+
```yaml
|
|
14
|
+
- mod: skir-kotlin-gen
|
|
15
|
+
config: {}
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
The `npm run skirc` command will now generate .kt files within the `skirout` directory.
|
|
19
|
+
|
|
20
|
+
The generated Kotlin code has a runtime dependency on `build.skir:skir-client`. Add this line to your `build.gradle.kts` file in the `dependencies` section:
|
|
21
|
+
|
|
22
|
+
```kotlin
|
|
23
|
+
implementation("build.skir:skir-client:1.1.4") // Pick the latest version
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
For more information, see this Kotlin project [example](https://github.com/gepheum/skir-kotlin-example).
|
|
27
|
+
|
|
28
|
+
## Kotlin generated code guide
|
|
29
|
+
|
|
30
|
+
The examples below are for the code generated from [this](https://github.com/gepheum/skir-kotlin-example/blob/main/skir-src/user.skir) .skir file.
|
|
31
|
+
|
|
32
|
+
### Referring to generated symbols
|
|
33
|
+
|
|
34
|
+
```kotlin
|
|
35
|
+
// Import the given symbols from the Kotlin module generated from "user.skir"
|
|
36
|
+
import skirout.user.User
|
|
37
|
+
import skirout.user.UserRegistry
|
|
38
|
+
import skirout.user.SubscriptionStatus
|
|
39
|
+
import skirout.user.TARZAN
|
|
40
|
+
|
|
41
|
+
// Now you can use: TARZAN, User, UserRegistry, SubscriptionStatus, etc.
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### Frozen struct classes
|
|
45
|
+
|
|
46
|
+
For every struct S in the .skir file, skir generates a frozen (deeply immutable) class `S` and a mutable class `S.Mutable`.
|
|
47
|
+
|
|
48
|
+
```kotlin
|
|
49
|
+
// Construct a frozen User.
|
|
50
|
+
val john =
|
|
51
|
+
User(
|
|
52
|
+
userId = 42,
|
|
53
|
+
name = "John Doe",
|
|
54
|
+
quote = "Coffee is just a socially acceptable form of rage.",
|
|
55
|
+
pets =
|
|
56
|
+
listOf(
|
|
57
|
+
User.Pet(
|
|
58
|
+
name = "Dumbo",
|
|
59
|
+
heightInMeters = 1.0f,
|
|
60
|
+
picture = "🐘",
|
|
61
|
+
),
|
|
62
|
+
),
|
|
63
|
+
subscriptionStatus = SubscriptionStatus.FREE,
|
|
64
|
+
// foo = "bar",
|
|
65
|
+
// ^ Does not compile: 'foo' is not a field of User
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
assert(john.name == "John Doe")
|
|
69
|
+
|
|
70
|
+
// john.name = "John Smith";
|
|
71
|
+
// ^ Does not compile: all the properties are read-only
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
#### Partial construction
|
|
75
|
+
|
|
76
|
+
```kotlin
|
|
77
|
+
// With .partial(), you don't need to specify all the fields of the struct.
|
|
78
|
+
val jane =
|
|
79
|
+
User.partial(
|
|
80
|
+
userId = 43,
|
|
81
|
+
name = "Jane Doe",
|
|
82
|
+
pets =
|
|
83
|
+
listOf(
|
|
84
|
+
User.Pet.partial(
|
|
85
|
+
name = "Fido",
|
|
86
|
+
picture = "🐶",
|
|
87
|
+
),
|
|
88
|
+
),
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
// Missing fields are initialized to their default values.
|
|
92
|
+
assert(jane.quote == "")
|
|
93
|
+
|
|
94
|
+
// User.partial() with no arguments returns an instance of User with all
|
|
95
|
+
// fields set to their default values.
|
|
96
|
+
assert(User.partial().pets.isEmpty())
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
#### Creating modified copies
|
|
100
|
+
|
|
101
|
+
```kotlin
|
|
102
|
+
// User.copy() creates a shallow copy of the struct with the specified fields
|
|
103
|
+
// modified.
|
|
104
|
+
val evilJohn =
|
|
105
|
+
john.copy(
|
|
106
|
+
name = "Evil John",
|
|
107
|
+
quote = "I solemnly swear I am up to no good.",
|
|
108
|
+
)
|
|
109
|
+
assert(evilJohn.name == "Evil John")
|
|
110
|
+
assert(evilJohn.userId == 42)
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### Mutable struct classes
|
|
114
|
+
|
|
115
|
+
`User.Mutable` is a dataclass similar to User except it is mutable.
|
|
116
|
+
|
|
117
|
+
```kotlin
|
|
118
|
+
val lyla = User.Mutable()
|
|
119
|
+
lyla.userId = 44
|
|
120
|
+
lyla.name = "Lyla Doe"
|
|
121
|
+
|
|
122
|
+
val userHistory = UserHistory.Mutable()
|
|
123
|
+
userHistory.user = lyla
|
|
124
|
+
// ^ The right-hand side of the assignment can be either frozen or mutable.
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
#### Mutable accessors
|
|
128
|
+
|
|
129
|
+
```kotlin
|
|
130
|
+
// The 'mutableUser' getter provides access to a mutable version of 'user'.
|
|
131
|
+
// If 'user' is already mutable, it returns it directly.
|
|
132
|
+
// If 'user' is frozen, it creates a mutable shallow copy, assigns it to
|
|
133
|
+
// 'user', and returns it.
|
|
134
|
+
|
|
135
|
+
// The user is currently 'lyla', which is mutable.
|
|
136
|
+
assert(userHistory.mutableUser === lyla)
|
|
137
|
+
// Now assign a frozen User to 'user'.
|
|
138
|
+
userHistory.user = john
|
|
139
|
+
// Since 'john' is frozen, mutableUser makes a mutable shallow copy of it.
|
|
140
|
+
userHistory.mutableUser.name = "John the Second"
|
|
141
|
+
assert(userHistory.user.name == "John the Second")
|
|
142
|
+
assert(userHistory.user.userId == 42)
|
|
143
|
+
|
|
144
|
+
// Similarly, 'mutablePets' provides access to a mutable version of 'pets'.
|
|
145
|
+
// It returns the existing list if already mutable, or creates and returns a
|
|
146
|
+
// mutable shallow copy.
|
|
147
|
+
lyla.mutablePets.add(
|
|
148
|
+
User.Pet(
|
|
149
|
+
name = "Simba",
|
|
150
|
+
heightInMeters = 0.4f,
|
|
151
|
+
picture = "🦁",
|
|
152
|
+
),
|
|
153
|
+
)
|
|
154
|
+
lyla.mutablePets.add(User.Pet.Mutable(name = "Cupcake"))
|
|
155
|
+
|
|
156
|
+
// lyla.pets.add(User.Pet.Mutable(name = "Cupcake"));
|
|
157
|
+
// ^ Does not compile: 'User.pets' is read-only
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
### Converting between frozen and mutable structs
|
|
161
|
+
|
|
162
|
+
```kotlin
|
|
163
|
+
// toMutable() does a shallow copy of the frozen struct, so it's cheap. All
|
|
164
|
+
// the properties of the copy hold a frozen value.
|
|
165
|
+
val evilJaneBuilder = jane.toMutable()
|
|
166
|
+
evilJaneBuilder.name = "Evil Jane"
|
|
167
|
+
evilJaneBuilder.mutablePets.add(
|
|
168
|
+
User.Pet(
|
|
169
|
+
name = "Shadow",
|
|
170
|
+
heightInMeters = 0.5f,
|
|
171
|
+
picture = "🐺",
|
|
172
|
+
),
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
// toFrozen() recursively copies the mutable values held by properties of the
|
|
176
|
+
// object.
|
|
177
|
+
val evilJane = evilJaneBuilder.toFrozen()
|
|
178
|
+
|
|
179
|
+
assert(evilJane.name == "Evil Jane")
|
|
180
|
+
assert(evilJane.userId == 43)
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
#### Type aliases for frozen or mutable
|
|
184
|
+
|
|
185
|
+
```kotlin
|
|
186
|
+
// 'User_OrMutable' is a type alias for the sealed class that both 'User' and
|
|
187
|
+
// 'User.Mutable' implement.
|
|
188
|
+
val greet: (User_OrMutable) -> Unit = {
|
|
189
|
+
println("Hello, $it")
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
greet(jane)
|
|
193
|
+
// Hello, Jane Doe
|
|
194
|
+
greet(lyla)
|
|
195
|
+
// Hello, Lyla Doe
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
### Enum classes
|
|
199
|
+
|
|
200
|
+
Soia generates a deeply immutable Kotlin class for every enum in the .skir file. This class is *not* a Kotlin enum, although the syntax for referring to constants is similar.
|
|
201
|
+
|
|
202
|
+
```kotlin
|
|
203
|
+
val someStatuses =
|
|
204
|
+
listOf(
|
|
205
|
+
// The UNKNOWN constant is present in all Soia enums even if it is not
|
|
206
|
+
// declared in the .skir file.
|
|
207
|
+
SubscriptionStatus.UNKNOWN,
|
|
208
|
+
SubscriptionStatus.FREE,
|
|
209
|
+
SubscriptionStatus.PREMIUM,
|
|
210
|
+
// Soia generates one subclass {VariantName}Wrapper for every wrapper
|
|
211
|
+
// variant. The constructor of this subclass expects the value to
|
|
212
|
+
// wrap.
|
|
213
|
+
SubscriptionStatus.TrialWrapper(
|
|
214
|
+
SubscriptionStatus.Trial(
|
|
215
|
+
startTime = Instant.now(),
|
|
216
|
+
),
|
|
217
|
+
),
|
|
218
|
+
// Same as above (^), with a more concise syntax.
|
|
219
|
+
// Available when the wrapped value is a struct.
|
|
220
|
+
SubscriptionStatus.createTrial(
|
|
221
|
+
startTime = Instant.now(),
|
|
222
|
+
),
|
|
223
|
+
)
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
### Conditions on enums
|
|
227
|
+
|
|
228
|
+
```kotlin
|
|
229
|
+
assert(john.subscriptionStatus == SubscriptionStatus.FREE)
|
|
230
|
+
|
|
231
|
+
// UNKNOWN is the default value for enums.
|
|
232
|
+
assert(jane.subscriptionStatus == SubscriptionStatus.UNKNOWN)
|
|
233
|
+
|
|
234
|
+
val now = Instant.now()
|
|
235
|
+
val trialStatus: SubscriptionStatus =
|
|
236
|
+
SubscriptionStatus.TrialWrapper(
|
|
237
|
+
Trial(startTime = now),
|
|
238
|
+
)
|
|
239
|
+
|
|
240
|
+
assert(
|
|
241
|
+
trialStatus is SubscriptionStatus.TrialWrapper &&
|
|
242
|
+
trialStatus.value.startTime == now,
|
|
243
|
+
)
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
#### Branching on enum variants
|
|
247
|
+
|
|
248
|
+
```kotlin
|
|
249
|
+
val getInfoText: (SubscriptionStatus) -> String = {
|
|
250
|
+
when (it) {
|
|
251
|
+
SubscriptionStatus.FREE -> "Free user"
|
|
252
|
+
SubscriptionStatus.PREMIUM -> "Premium user"
|
|
253
|
+
is SubscriptionStatus.TrialWrapper -> "On trial since ${it.value.startTime}"
|
|
254
|
+
is SubscriptionStatus.Unknown -> "Unknown subscription status"
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
println(getInfoText(john.subscriptionStatus))
|
|
259
|
+
// "Free user"
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
### Serialization
|
|
263
|
+
|
|
264
|
+
Every frozen struct class and enum class has a static `serializer` property which can be used for serializing and deserializing instances of the class.
|
|
265
|
+
|
|
266
|
+
```kotlin
|
|
267
|
+
val serializer = User.serializer
|
|
268
|
+
|
|
269
|
+
// Serialize 'john' to dense JSON.
|
|
270
|
+
println(serializer.toJsonCode(john))
|
|
271
|
+
// [42,"John Doe","Coffee is just a socially acceptable form of rage.",[["Dumbo",1.0,"🐘"]],[1]]
|
|
272
|
+
|
|
273
|
+
// Serialize 'john' to readable JSON.
|
|
274
|
+
println(serializer.toJsonCode(john, JsonFlavor.READABLE))
|
|
275
|
+
// {
|
|
276
|
+
// "user_id": 42,
|
|
277
|
+
// "name": "John Doe",
|
|
278
|
+
// "quote": "Coffee is just a socially acceptable form of rage.",
|
|
279
|
+
// "pets": [
|
|
280
|
+
// {
|
|
281
|
+
// "name": "Dumbo",
|
|
282
|
+
// "height_in_meters": 1.0,
|
|
283
|
+
// "picture": "🐘"
|
|
284
|
+
// }
|
|
285
|
+
// ],
|
|
286
|
+
// "subscription_status": "FREE"
|
|
287
|
+
// }
|
|
288
|
+
|
|
289
|
+
// The dense JSON flavor is the flavor you should pick if you intend to
|
|
290
|
+
// deserialize the value in the future. Soia allows fields to be renamed,
|
|
291
|
+
// and because field names are not part of the dense JSON, renaming a field
|
|
292
|
+
// does not prevent you from deserializing the value.
|
|
293
|
+
// You should pick the readable flavor mostly for debugging purposes.
|
|
294
|
+
|
|
295
|
+
// Serialize 'john' to binary format.
|
|
296
|
+
val johnBytes = serializer.toBytes(john)
|
|
297
|
+
|
|
298
|
+
// The binary format is not human readable, but it is slightly more compact
|
|
299
|
+
// than JSON, and serialization/deserialization can be a bit faster in
|
|
300
|
+
// languages like C++. Only use it when this small performance gain is
|
|
301
|
+
// likely to matter, which should be rare.
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
### Deserialization
|
|
305
|
+
|
|
306
|
+
```kotlin
|
|
307
|
+
// Use fromJson(), fromJsonCode() and fromBytes() to deserialize.
|
|
308
|
+
val reserializedJohn: User =
|
|
309
|
+
serializer.fromJsonCode(serializer.toJsonCode(john))
|
|
310
|
+
assert(reserializedJohn.equals(john))
|
|
311
|
+
|
|
312
|
+
// fromJson/fromJsonCode can deserialize both dense and readable JSON
|
|
313
|
+
val reserializedEvilJohn: User =
|
|
314
|
+
serializer.fromJsonCode(
|
|
315
|
+
serializer.toJsonCode(john, JsonFlavor.READABLE),
|
|
316
|
+
)
|
|
317
|
+
assert(reserializedEvilJohn.equals(evilJohn))
|
|
318
|
+
|
|
319
|
+
val reserializedJane: User =
|
|
320
|
+
serializer.fromBytes(serializer.toBytes(jane))
|
|
321
|
+
assert(reserializedJane.equals(jane))
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
### Constants
|
|
325
|
+
|
|
326
|
+
```kotlin
|
|
327
|
+
println(TARZAN)
|
|
328
|
+
// User(
|
|
329
|
+
// userId = 123,
|
|
330
|
+
// name = "Tarzan",
|
|
331
|
+
// quote = "AAAAaAaAaAyAAAAaAaAaAyAAAAaAaAaA",
|
|
332
|
+
// pets = listOf(
|
|
333
|
+
// User.Pet(
|
|
334
|
+
// name = "Cheeta",
|
|
335
|
+
// heightInMeters = 1.67F,
|
|
336
|
+
// picture = "🐒",
|
|
337
|
+
// ),
|
|
338
|
+
// ),
|
|
339
|
+
// subscriptionStatus = SubscriptionStatus.TrialWrapper(
|
|
340
|
+
// SubscriptionStatus.Trial(
|
|
341
|
+
// startTime = Instant.ofEpochMillis(
|
|
342
|
+
// // 2025-04-02T11:13:29Z
|
|
343
|
+
// 1743592409000L
|
|
344
|
+
// ),
|
|
345
|
+
// )
|
|
346
|
+
// ),
|
|
347
|
+
// )
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
### Keyed lists
|
|
351
|
+
|
|
352
|
+
```kotlin
|
|
353
|
+
// In the .skir file:
|
|
354
|
+
// struct UserRegistry {
|
|
355
|
+
// users: [User|user_id];
|
|
356
|
+
// }
|
|
357
|
+
|
|
358
|
+
val userRegistry = UserRegistry(users = listOf(john, jane, evilJohn))
|
|
359
|
+
|
|
360
|
+
// find() returns the user with the given key (specified in the .skir file).
|
|
361
|
+
// In this example, the key is the user id.
|
|
362
|
+
// The first lookup runs in O(N) time, and the following lookups run in O(1)
|
|
363
|
+
// time.
|
|
364
|
+
assert(userRegistry.users.findByKey(43) === jane)
|
|
365
|
+
|
|
366
|
+
// If multiple elements have the same key, the last one is returned.
|
|
367
|
+
assert(userRegistry.users.findByKey(42) === evilJohn)
|
|
368
|
+
assert(userRegistry.users.findByKey(100) == null)
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
### Frozen lists and copies
|
|
372
|
+
|
|
373
|
+
```kotlin
|
|
374
|
+
// Since all Soia objects are deeply immutable, all lists contained in a
|
|
375
|
+
// Soia object are also deeply immutable.
|
|
376
|
+
// This section helps understand when lists are copied and when they are
|
|
377
|
+
// not.
|
|
378
|
+
val pets: MutableList<Pet> =
|
|
379
|
+
mutableListOf(
|
|
380
|
+
Pet.partial(name = "Fluffy", picture = "🐶"),
|
|
381
|
+
Pet.partial(name = "Fido", picture = "🐻"),
|
|
382
|
+
)
|
|
383
|
+
|
|
384
|
+
val jade =
|
|
385
|
+
User.partial(
|
|
386
|
+
name = "Jade",
|
|
387
|
+
pets = pets,
|
|
388
|
+
// ^ 'pets' is mutable, so Soia makes an immutable shallow copy of it
|
|
389
|
+
)
|
|
390
|
+
|
|
391
|
+
assert(pets == jade.pets)
|
|
392
|
+
assert(pets !== jade.pets)
|
|
393
|
+
|
|
394
|
+
val jack =
|
|
395
|
+
User.partial(
|
|
396
|
+
name = "Jack",
|
|
397
|
+
pets = jade.pets,
|
|
398
|
+
// ^ 'jade.pets' is already immutable, so Soia does not make a copy
|
|
399
|
+
)
|
|
400
|
+
|
|
401
|
+
assert(jack.pets === jade.pets)
|
|
402
|
+
```
|
|
403
|
+
|
|
404
|
+
### Soia services
|
|
405
|
+
|
|
406
|
+
#### Starting a skir service on an HTTP server
|
|
407
|
+
|
|
408
|
+
Full example [here](https://github.com/gepheum/skir-kotlin-example/blob/main/src/main/kotlin/examples/startservice/StartService.kt).
|
|
409
|
+
|
|
410
|
+
#### Sending RPCs to a skir service
|
|
411
|
+
|
|
412
|
+
Full example [here](https://github.com/gepheum/skir-kotlin-example/blob/main/src/main/kotlin/examples/callservice/CallService.kt).
|
|
413
|
+
|
|
414
|
+
### Reflection
|
|
415
|
+
|
|
416
|
+
Reflection allows you to inspect a skir type at runtime.
|
|
417
|
+
|
|
418
|
+
```kotlin
|
|
419
|
+
println(
|
|
420
|
+
User.typeDescriptor
|
|
421
|
+
.fields
|
|
422
|
+
.map { field -> field.name }
|
|
423
|
+
.toList(),
|
|
424
|
+
)
|
|
425
|
+
// [user_id, name, quote, pets, subscription_status]
|
|
426
|
+
|
|
427
|
+
// A type descriptor can be serialized to JSON and deserialized later.
|
|
428
|
+
val typeDescriptor =
|
|
429
|
+
TypeDescriptor.parseFromJsonCode(
|
|
430
|
+
User.serializer.typeDescriptor.asJsonCode(),
|
|
431
|
+
)
|
|
432
|
+
|
|
433
|
+
assert(typeDescriptor is StructDescriptor)
|
|
434
|
+
assert((typeDescriptor as StructDescriptor).fields.size == 5)
|
|
435
|
+
|
|
436
|
+
// The 'allStringsToUpperCase' function uses reflection to convert all the
|
|
437
|
+
// strings contained in a given Soia value to upper case.
|
|
438
|
+
// See the implementation at
|
|
439
|
+
// https://github.com/gepheum/skir-kotlin-example/blob/main/src/main/kotlin/AllStringsToUpperCase.kt
|
|
440
|
+
println(allStringsToUpperCase(TARZAN, User.typeDescriptor))
|
|
441
|
+
// User(
|
|
442
|
+
// userId = 123,
|
|
443
|
+
// name = "TARZAN",
|
|
444
|
+
// quote = "AAAAAAAAAAYAAAAAAAAAAYAAAAAAAAAA",
|
|
445
|
+
// pets = listOf(
|
|
446
|
+
// User.Pet(
|
|
447
|
+
// name = "CHEETA",
|
|
448
|
+
// heightInMeters = 1.67F,
|
|
449
|
+
// picture = "🐒",
|
|
450
|
+
// ),
|
|
451
|
+
// ),
|
|
452
|
+
// subscriptionStatus = SubscriptionStatus.TrialWrapper(
|
|
453
|
+
// SubscriptionStatus.Trial(
|
|
454
|
+
// startTime = Instant.ofEpochMillis(
|
|
455
|
+
// // 2025-04-02T11:13:29Z
|
|
456
|
+
// 1743592409000L
|
|
457
|
+
// ),
|
|
458
|
+
// )
|
|
459
|
+
// ),
|
|
460
|
+
// )
|
|
461
|
+
```
|
|
462
|
+
|
|
463
|
+
## Java codegen versus Kotlin codegen
|
|
464
|
+
|
|
465
|
+
While Java and Kotlin code can interoperate seamlessly, Soia provides separate code generators for each language to leverage their unique strengths and idioms. For instance, the Kotlin generator utilizes named parameters for struct construction, whereas the Java generator employs the builder pattern.
|
|
466
|
+
|
|
467
|
+
Although it's technically feasible to use Kotlin-generated code in a Java project (or vice versa), doing so results in an API that feels unnatural and cumbersome in the calling language. For the best developer experience, use the code generator that matches your project's primary language.
|
|
468
|
+
|
|
469
|
+
Note that both the Java and Kotlin generated code share the same runtime dependency: `build.skir:skir-client`.
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { type CodeGenerator } from "skir-internal";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
declare const Config: z.ZodObject<{
|
|
4
|
+
packagePrefix: z.ZodOptional<z.ZodString>;
|
|
5
|
+
}, z.core.$strip>;
|
|
6
|
+
type Config = z.infer<typeof Config>;
|
|
7
|
+
declare class KotlinCodeGenerator implements CodeGenerator<Config> {
|
|
8
|
+
readonly id = "kotlin";
|
|
9
|
+
readonly configType: z.ZodObject<{
|
|
10
|
+
packagePrefix: z.ZodOptional<z.ZodString>;
|
|
11
|
+
}, z.core.$strip>;
|
|
12
|
+
readonly version = "1.0.0";
|
|
13
|
+
generateCode(input: CodeGenerator.Input<Config>): CodeGenerator.Output;
|
|
14
|
+
}
|
|
15
|
+
export declare const GENERATOR: KotlinCodeGenerator;
|
|
16
|
+
export {};
|
|
17
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,KAAK,aAAa,EAWnB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxB,QAAA,MAAM,MAAM;;iBAKV,CAAC;AAEH,KAAK,MAAM,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,MAAM,CAAC,CAAC;AAErC,cAAM,mBAAoB,YAAW,aAAa,CAAC,MAAM,CAAC;IACxD,QAAQ,CAAC,EAAE,YAAY;IACvB,QAAQ,CAAC,UAAU;;sBAAU;IAC7B,QAAQ,CAAC,OAAO,WAAW;IAE3B,YAAY,CAAC,KAAK,EAAE,aAAa,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,aAAa,CAAC,MAAM;CAevE;AAi8BD,eAAO,MAAM,SAAS,qBAA4B,CAAC"}
|