Skip to main content

KtFile

Struct KtFile 

Source
pub struct KtFile {
    pub package: String,
    pub decls: Vec<KtDecl>,
    pub extra_imports: Vec<String>,
    pub banner: Option<String>,
}
Expand description

One Kotlin source file fragment: a package plus top-level declarations. Fragments of the same package are merged by super::file::merge_files.

Fields§

§package: String§decls: Vec<KtDecl>§extra_imports: Vec<String>

FQNs referenced only inside raw text the model can’t see (e.g. body strings built with pre-shortened type names). Registered into the file’s import set before any declaration renders.

§banner: Option<String>

Override the banner line prepended to the rendered file. None uses the default banner constant from the renderer. Some("") suppresses it.

Implementations§

Source§

impl KtFile

Source

pub fn new(package: impl Into<String>) -> Self

Examples found in repository?
examples/invalid.rs (line 67)
63fn broken_files() -> Vec<KtFile> {
64    vec![
65        broken_declarations(),
66        // A package path that is not a dotted sequence of legal identifiers.
67        KtFile::new("io..example.object").decl(KtFun::new("stillChecked").body(KtCode::new())),
68    ]
69}
70
71fn broken_declarations() -> KtFile {
72    KtFile::new("io.example.broken")
73        // Two classes of the same name: a redeclaration in the type namespace.
74        .decl(KtClass::class_("Session"))
75        .decl(KtClass::class_("Session"))
76        // An interface and a type alias are both classifier declarations, so they collide.
77        .decl(
78            KtClass::interface_("Describable")
79                .member(KtFun::new("describe").returns(KtType::string())),
80        )
81        .decl(KtDecl::TypeAlias {
82            vis: KtVis::Public,
83            name: "Describable".to_string(),
84            target: KtType::string(),
85        })
86        // Same name AND same parameter types: not an overload.
87        .decl(
88            KtFun::new("send")
89                .param(KtParam::new("value", KtType::int()))
90                .returns(KtType::boolean())
91                .body(KtCode::new()),
92        )
93        .decl(
94            KtFun::new("send")
95                .param(KtParam::new("other", KtType::int()))
96                .returns(KtType::long())
97                .body(KtCode::new()),
98        )
99        // Extensions are keyed on their receiver, so these two collide while
100        // the same pair on different receivers would not.
101        .decl(
102            KtFun::new("asRaw")
103                .receiver(KtType::cls("io.example.Codec"))
104                .body(KtCode::new()),
105        )
106        .decl(
107            KtFun::new("asRaw")
108                .receiver(KtType::cls("io.example.Codec"))
109                .body(KtCode::new()),
110        )
111        // ...as here: same name, different receiver, no diagnostic.
112        .decl(
113            KtFun::new("asRaw")
114                .receiver(KtType::cls("io.example.Other"))
115                .body(KtCode::new()),
116        )
117        // Names that are not legal Kotlin identifiers.
118        .decl(
119            KtClass::class_("My-Class")
120                .ctor_param(KtCtorParam::new("2fast", KtType::int()))
121                .member(
122                    KtFun::new("object")
123                        .param(KtParam::new("in", KtType::int()))
124                        .body(KtCode::new()),
125                ),
126        )
127        // `val x` with no type, no value and no accessors.
128        .decl(KtProperty::val("bare"))
129        // A function with no body, at top level, where that cannot mean
130        // "abstract".
131        .decl(KtFun::new("nobody"))
132        // An abstract class member missing the `abstract` keyword.
133        .decl(
134            KtClass::class_with(KtClassModifier::Abstract, "Base")
135                .member(KtFun::new("unimplemented")),
136        )
137        // An enum whose entries do not call the constructor it declares.
138        .decl(
139            KtClass::enum_("Priority")
140                .ctor_param(KtCtorParam::new("code", KtType::int()).val())
141                .entry(KtEnumEntry::with_args("HIGH", "1"))
142                .entry(KtEnumEntry::new("LOW")),
143        )
144        // A constructor property and a member property of one name: both live
145        // in the value namespace.
146        .decl(
147            KtClass::class_("Holder")
148                .ctor_param(KtCtorParam::new("id", KtType::long()).val())
149                .member(KtProperty::val("id").initializer("0")),
150        )
151        // A named companion object colliding with a nested class.
152        .decl(
153            KtClass::class_("Outer")
154                .member(KtClass::class_("Factory"))
155                .companion(KtCompanion::named("Factory")),
156        )
157        // Two raw blocks claiming one identity with different bodies.
158        .decl(KtDecl::Raw {
159            name: "__loader".to_string(),
160            code: KtCode::new().line("internal val __loader = 1"),
161        })
162        .decl(KtDecl::Raw {
163            name: "__loader".to_string(),
164            code: KtCode::new().line("internal val __loader = 2"),
165        })
166        // Two different classes referenced from raw text under one short name:
167        // the text already says `Codec`, so it cannot mean both.
168        .import("io.example.a.Codec")
169        .import("io.example.b.Codec")
170}
More examples
Hide additional examples
examples/showcase.rs (line 155)
61fn types_fragment() -> KtFile {
62    // `enum class` with a primary constructor its entries call, plus a named
63    // companion object holding a factory.
64    let priority = KtClass::enum_("Priority")
65        .vis(KtVis::Public)
66        .kdoc("Delivery priority.\n\nMirrors the native `z_priority_t`.")
67        .ctor_param(
68            KtCtorParam::new("code", KtType::int())
69                .val()
70                .vis(KtVis::Public),
71        )
72        .entry(KtEnumEntry::with_args("REAL_TIME", "1"))
73        .entry(KtEnumEntry::with_args("INTERACTIVE", "4"))
74        .entry(KtEnumEntry::with_args("DATA", "5"))
75        .companion(
76            KtCompanion::named("Codes")
77                .vis(KtVis::Public)
78                .kdoc("Lookup helpers keyed by the native code.")
79                .member(
80                    KtFun::new("fromInt")
81                        .vis(KtVis::Public)
82                        .annotation("JvmStatic")
83                        .param(KtParam::new("value", KtType::int()))
84                        .returns(KtType::cls("Priority"))
85                        .body(
86                            KtCode::new()
87                                .blk("return when (value) {", |c| {
88                                    c.line("1 -> REAL_TIME")
89                                        .line("4 -> INTERACTIVE")
90                                        .line("5 -> DATA")
91                                        .line("else -> throw IllegalArgumentException(\"bad priority: $value\")")
92                                }),
93                        ),
94                ),
95        );
96
97    // `@JvmInline value class` — exactly one read-only property.
98    let zid = KtClass::value(
99        "ZenohId",
100        KtCtorParam::new("bytes", KtType::byte_array())
101            .val()
102            .vis(KtVis::Public),
103    )
104    .vis(KtVis::Public)
105    .kdoc("A 16-byte peer identifier, carried by value.");
106
107    // `data class` — every constructor parameter is a property.
108    let sample = KtClass::data(
109        "Sample",
110        KtCtorParam::new("keyExpr", KtType::string())
111            .val()
112            .vis(KtVis::Public),
113    )
114    .vis(KtVis::Public)
115    .ctor_param(
116        KtCtorParam::new("payload", KtType::byte_array())
117            .val()
118            .vis(KtVis::Public),
119    )
120    .ctor_param(
121        KtCtorParam::new("priority", KtType::cls("Priority"))
122            .val()
123            .vis(KtVis::Public)
124            .default("Priority.DATA"),
125    )
126    .ctor_param(
127        KtCtorParam::new("attachment", KtType::byte_array().nullable())
128            .var()
129            .vis(KtVis::Public)
130            .annotation("JvmField")
131            .default("null"),
132    );
133
134    // A `sealed interface` whose alternatives nest inside it: a `data class`
135    // with a payload and a `data object` without one.
136    let reply = KtClass::sealed_interface("Reply")
137        .vis(KtVis::Public)
138        .kdoc("Either a sample or the end of the stream.")
139        .member(
140            KtClass::data(
141                "Value",
142                KtCtorParam::new("sample", KtType::cls("Sample"))
143                    .val()
144                    .vis(KtVis::Public),
145            )
146            .vis(KtVis::Public)
147            .implements(KtType::cls("Reply")),
148        )
149        .member(
150            KtClass::data_object("Done")
151                .vis(KtVis::Public)
152                .implements(KtType::cls("Reply")),
153        );
154
155    KtFile::new("io.example.api")
156        .decl(priority)
157        .decl(zid)
158        .decl(sample)
159        .decl(reply)
160}
161
162/// The handle surface: an abstract base, a concrete subclass, an interface,
163/// a `fun interface`, and a type alias.
164fn session_fragment() -> KtFile {
165    // `abstract class` with an `abstract` member (bodiless, and it says so),
166    // a `@Volatile` property, and an interface it implements.
167    let base = KtClass::class_with(KtClassModifier::Abstract, "NativeHandle")
168        .vis(KtVis::Public)
169        .kdoc("Owns a raw pointer into Rust and frees it exactly once.")
170        .ctor_param(KtCtorParam::new("initialPtr", KtType::long()))
171        .implements(KtType::cls("AutoCloseable"))
172        .member(
173            KtProperty::var("ptr")
174                .ty(KtType::long())
175                .vis(KtVis::Internal)
176                .annotation("Volatile")
177                .kdoc("Zero once closed.")
178                .initializer("initialPtr"),
179        )
180        .member(
181            KtProperty::val("isClosed")
182                .ty(KtType::boolean())
183                .vis(KtVis::Public)
184                .accessors(KtCode::new().line("get() = ptr == 0L")),
185        )
186        .member(
187            KtFun::new("freePtr")
188                .vis(KtVis::Public)
189                .modifier("abstract")
190                .kdoc("Release the native allocation. Called once, under the lock.")
191                .param(KtParam::new("ptr", KtType::long())),
192        );
193
194    // `open class` extending the abstract base and constructing it.
195    let session = KtClass::class_with(KtClassModifier::Open, "Session")
196        .vis(KtVis::Public)
197        .annotation("Suppress(\"unused\")")
198        .ctor_param(KtCtorParam::new("initialPtr", KtType::long()))
199        .extends(KtType::cls("NativeHandle"), Some("initialPtr"))
200        .implements(KtType::cls("io.example.api.Describable"))
201        .member(
202            KtFun::new("freePtr")
203                .vis(KtVis::Public)
204                .modifier("override")
205                .param(KtParam::new("ptr", KtType::long()))
206                .body(KtCode::new().line("JNINative.sessionFree(ptr)")),
207        )
208        .member(
209            KtFun::new("describe")
210                .vis(KtVis::Public)
211                .modifier("override")
212                .returns(KtType::string())
213                .expr_body(KtCode::new().line("\"Session(0x${ptr.toString(16)})\"")),
214        )
215        // A generic method whose body uses every `KtCode` block form.
216        .member(
217            KtFun::new("get")
218                .vis(KtVis::Public)
219                .generic("R")
220                .kdoc("Run a query, folding each reply into an accumulator.")
221                .param(KtParam::new("selector", KtType::string()))
222                .param(KtParam::new(
223                    "onReply",
224                    KtType::lambda(
225                        [("reply".to_string(), KtType::cls("io.example.api.Reply"))],
226                        KtType::var_r(),
227                    ),
228                ))
229                .param(KtParam::new("timeoutMs", KtType::long()).default("10_000L"))
230                .returns(KtType::generic("List", [KtType::var_r()]))
231                .body(
232                    KtCode::new()
233                        .line("val acc = ArrayList<R>()")
234                        .line("val guard = Guard.acquire(this)")
235                        .import("io.example.internal.Guard")
236                        .try_finally(
237                            "",
238                            KtCode::new().blk("JNINative.sessionGet(ptr, selector, timeoutMs) { raw ->", |c| {
239                                c.line("acc.add(onReply(raw))")
240                            }),
241                            KtCode::new().line("guard.release()"),
242                        )
243                        .line("")
244                        // A long call that the renderer breaks by width at its
245                        // real nesting level.
246                        .wline("reportQueryOutcome(selector, timeoutMs, acc.size, acc.isNotEmpty(), \"query finished\", System.nanoTime())")
247                        .line("return acc"),
248                ),
249        )
250        // The interface's member extension, implemented.
251        .member(
252            KtFun::new("label")
253                .vis(KtVis::Public)
254                .modifier("override")
255                .receiver(KtType::cls("Sample"))
256                .returns(KtType::string())
257                .expr_body(KtCode::new().line("\"${keyExpr}@${describe()}\"")),
258        )
259        // A member extension of the class itself.
260        .member(
261            KtFun::new("toSample")
262                .vis(KtVis::Public)
263                .receiver(KtType::byte_array())
264                .returns(KtType::cls("Sample"))
265                .expr_body(KtCode::new().line("Sample(describe(), this)")),
266        )
267        // A nested class — its own scope, so its members may reuse names.
268        .member(
269            KtClass::class_("Config")
270                .vis(KtVis::Public)
271                .member(KtProperty::val("describe").initializer("\"config\"")),
272        )
273        .companion(
274            KtCompanion::new()
275                .vis(KtVis::Public)
276                .member(
277                    KtFun::new("open")
278                        .vis(KtVis::Public)
279                        .annotation("JvmStatic")
280                        .param(KtParam::new("config", KtType::string()).default("\"{}\""))
281                        .returns(KtType::cls("Session"))
282                        .expr_body(KtCode::new().line("Session(JNINative.sessionOpen(config))")),
283                ),
284        );
285
286    // A plain `interface`: bodiless members are abstract by position, and a
287    // `KtFunSig` is exactly that.
288    let describable = KtClass::interface_("Describable")
289        .vis(KtVis::Public)
290        .member(KtFunSig::new("describe").returns(KtType::string()))
291        // A member extension: abstract here, supplied by the implementor.
292        // `KtFunSig` carries a receiver too, so a signature does not quietly
293        // become a plain member.
294        .member(
295            KtFunSig::new("label")
296                .receiver(KtType::cls("Sample"))
297                .returns(KtType::string()),
298        );
299
300    // A `fun interface` (SAM) — its single method cannot carry a body.
301    let handler = KtFunInterface::new(
302        "ReplyHandler",
303        KtFunSig::new("onReply")
304            .param(KtParam::new("reply", KtType::cls("Reply")))
305            .returns(KtType::var_r()),
306    )
307    .vis(KtVis::Public)
308    .type_param("out R")
309    .kdoc("Invoked from the native thread for each reply.");
310
311    // Top-level extension functions. The receiver is a type, not part of the
312    // name, so it resolves through the import set and the name stays a plain
313    // identifier the validator can check as one.
314    let summary = KtFun::new("summary")
315        .vis(KtVis::Public)
316        .receiver(KtType::cls("Sample"))
317        .returns(KtType::string())
318        .expr_body(KtCode::new().line("\"$keyExpr (${payload.size} bytes)\""));
319
320    // Generics render before the receiver, which renders before the name.
321    let map_replies = KtFun::new("mapValues")
322        .vis(KtVis::Public)
323        .generic("R")
324        .receiver(KtType::generic("List", [KtType::cls("Reply")]))
325        .param(KtParam::new(
326            "transform",
327            KtType::lambda(
328                [("sample".to_string(), KtType::cls("Sample"))],
329                KtType::var_r(),
330            ),
331        ))
332        .returns(KtType::generic("List", [KtType::var_r()]))
333        .expr_body(
334            KtCode::new().line("filterIsInstance<Reply.Value>().map { transform(it.sample) }"),
335        );
336
337    // An extension on a *function type*. The receiver needs parentheses here
338    // or the `.` would bind to the return type instead.
339    let as_raw = KtFun::new("asRaw")
340        .vis(KtVis::Internal)
341        .receiver(KtType::lambda(
342            [("sample".to_string(), KtType::cls("Sample"))],
343            KtType::unit(),
344        ))
345        .returns(KtType::cls("io.example.api.internal.RawSink"))
346        // The proxy adapts a typed callback to the raw one the natives call,
347        // so it has to narrow `Reply` to the `Sample` the receiver takes.
348        .expr_body(
349            KtCode::new().line("RawSink { raw -> if (raw is Reply.Value) this(raw.sample) }"),
350        );
351
352    KtFile::new("io.example.api")
353        // FQNs named only from raw body text, which the model cannot see.
354        .imports(["io.example.api.internal.JNINative".to_string()])
355        .decl(base)
356        .decl(describable)
357        .decl(handler)
358        .decl(session)
359        .decl(summary)
360        .decl(map_replies)
361        .decl(as_raw)
362        .decl(KtDecl::TypeAlias {
363            vis: KtVis::Public,
364            name: "SampleList".to_string(),
365            target: KtType::generic("List", [KtType::cls("Sample")]),
366        })
367}
368
369/// The `external` surface — an object of natives, in its own subpackage.
370fn natives_fragment() -> KtFile {
371    let natives = KtClass::object_("JNINative")
372        .vis(KtVis::Internal)
373        .kdoc("One-to-one with the exported Rust symbols.")
374        .member(
375            KtFun::new("sessionOpen")
376                .vis(KtVis::Internal)
377                .param(KtParam::new("config", KtType::string()))
378                .returns(KtType::long())
379                .external(),
380        )
381        .member(
382            KtFun::new("sessionFree")
383                .vis(KtVis::Internal)
384                .param(KtParam::new("ptr", KtType::long()))
385                .external(),
386        )
387        .member(
388            KtFun::new("sessionGet")
389                .vis(KtVis::Internal)
390                .param(KtParam::new("ptr", KtType::long()))
391                .param(KtParam::new("selector", KtType::string()))
392                .param(KtParam::new("timeoutMs", KtType::long()))
393                .param(KtParam::new(
394                    "sink",
395                    KtType::lambda(
396                        [("reply".to_string(), KtType::cls("io.example.api.Reply"))],
397                        KtType::unit(),
398                    ),
399                ))
400                .external(),
401        );
402
403    // A pre-rendered block, for text the model does not describe. Its name is
404    // a merge identity, never emitted.
405    let loader = KtDecl::Raw {
406        name: "__loadNative".to_string(),
407        code: KtCode::raw_reindent(
408            "internal val __loaded: Boolean = run {\n\
409             System.loadLibrary(\"example_jni\")\n\
410             true\n\
411             }",
412        ),
413    };
414
415    let raw_sink = KtFunInterface::new(
416        "RawSink",
417        KtFunSig::new("accept").param(KtParam::new("reply", KtType::cls("io.example.api.Reply"))),
418    )
419    .vis(KtVis::Internal);
420
421    KtFile::new("io.example.api.internal")
422        .decl(raw_sink)
423        .decl(natives)
424        .decl(loader)
425        // An FQN referenced only from raw text the model cannot see.
426        .import("io.example.api.Reply")
427}
428
429/// A file in the default (root) package, exercising a banner override and
430/// top-level declarations of every value kind.
431fn root_fragment() -> KtFile {
432    KtFile::new("")
433        .banner("// Hand-tuned banner for the root package.")
434        .decl(
435            KtProperty::val("LIBRARY_VERSION")
436                .ty(KtType::string())
437                .vis(KtVis::Public)
438                .kdoc("Version this binding was generated against.")
439                .initializer("\"1.9.0\""),
440        )
441        // A keyword modifier rendered between visibility and `val`.
442        .decl(
443            KtProperty::val("PROTOCOL")
444                .ty(KtType::string())
445                .vis(KtVis::Public)
446                .modifier("const")
447                .initializer("\"tcp\""),
448        )
449        // A delegated property: `by <expr>` rather than `= <expr>`.
450        .decl(
451            KtProperty::val("defaultTimeout")
452                .vis(KtVis::Internal)
453                .ty(KtType::cls("java.time.Duration"))
454                .delegate("lazy { Duration.ofSeconds(10) }"),
455        )
456        .decl(
457            KtFun::new("describeAll")
458                .vis(KtVis::Public)
459                // Generic parameter lists are free-form text, so a bound is
460                // written as given — it is not shortened against the imports.
461                .generic("T : io.example.api.Describable")
462                .param(KtParam::new(
463                    "items",
464                    KtType::generic("List", [KtType::var_("T")]),
465                ))
466                .returns(KtType::string())
467                .expr_body(KtCode::new().line("items.joinToString { it.describe() }")),
468        )
469}
Source

pub fn banner(self, text: impl Into<String>) -> Self

Override the banner comment prepended to the rendered file. Pass "" to suppress the banner entirely.

Examples found in repository?
examples/showcase.rs (line 433)
431fn root_fragment() -> KtFile {
432    KtFile::new("")
433        .banner("// Hand-tuned banner for the root package.")
434        .decl(
435            KtProperty::val("LIBRARY_VERSION")
436                .ty(KtType::string())
437                .vis(KtVis::Public)
438                .kdoc("Version this binding was generated against.")
439                .initializer("\"1.9.0\""),
440        )
441        // A keyword modifier rendered between visibility and `val`.
442        .decl(
443            KtProperty::val("PROTOCOL")
444                .ty(KtType::string())
445                .vis(KtVis::Public)
446                .modifier("const")
447                .initializer("\"tcp\""),
448        )
449        // A delegated property: `by <expr>` rather than `= <expr>`.
450        .decl(
451            KtProperty::val("defaultTimeout")
452                .vis(KtVis::Internal)
453                .ty(KtType::cls("java.time.Duration"))
454                .delegate("lazy { Duration.ofSeconds(10) }"),
455        )
456        .decl(
457            KtFun::new("describeAll")
458                .vis(KtVis::Public)
459                // Generic parameter lists are free-form text, so a bound is
460                // written as given — it is not shortened against the imports.
461                .generic("T : io.example.api.Describable")
462                .param(KtParam::new(
463                    "items",
464                    KtType::generic("List", [KtType::var_("T")]),
465                ))
466                .returns(KtType::string())
467                .expr_body(KtCode::new().line("items.joinToString { it.describe() }")),
468        )
469}
Source

pub fn decl(self, d: impl Into<KtDecl>) -> Self

Examples found in repository?
examples/invalid.rs (line 67)
63fn broken_files() -> Vec<KtFile> {
64    vec![
65        broken_declarations(),
66        // A package path that is not a dotted sequence of legal identifiers.
67        KtFile::new("io..example.object").decl(KtFun::new("stillChecked").body(KtCode::new())),
68    ]
69}
70
71fn broken_declarations() -> KtFile {
72    KtFile::new("io.example.broken")
73        // Two classes of the same name: a redeclaration in the type namespace.
74        .decl(KtClass::class_("Session"))
75        .decl(KtClass::class_("Session"))
76        // An interface and a type alias are both classifier declarations, so they collide.
77        .decl(
78            KtClass::interface_("Describable")
79                .member(KtFun::new("describe").returns(KtType::string())),
80        )
81        .decl(KtDecl::TypeAlias {
82            vis: KtVis::Public,
83            name: "Describable".to_string(),
84            target: KtType::string(),
85        })
86        // Same name AND same parameter types: not an overload.
87        .decl(
88            KtFun::new("send")
89                .param(KtParam::new("value", KtType::int()))
90                .returns(KtType::boolean())
91                .body(KtCode::new()),
92        )
93        .decl(
94            KtFun::new("send")
95                .param(KtParam::new("other", KtType::int()))
96                .returns(KtType::long())
97                .body(KtCode::new()),
98        )
99        // Extensions are keyed on their receiver, so these two collide while
100        // the same pair on different receivers would not.
101        .decl(
102            KtFun::new("asRaw")
103                .receiver(KtType::cls("io.example.Codec"))
104                .body(KtCode::new()),
105        )
106        .decl(
107            KtFun::new("asRaw")
108                .receiver(KtType::cls("io.example.Codec"))
109                .body(KtCode::new()),
110        )
111        // ...as here: same name, different receiver, no diagnostic.
112        .decl(
113            KtFun::new("asRaw")
114                .receiver(KtType::cls("io.example.Other"))
115                .body(KtCode::new()),
116        )
117        // Names that are not legal Kotlin identifiers.
118        .decl(
119            KtClass::class_("My-Class")
120                .ctor_param(KtCtorParam::new("2fast", KtType::int()))
121                .member(
122                    KtFun::new("object")
123                        .param(KtParam::new("in", KtType::int()))
124                        .body(KtCode::new()),
125                ),
126        )
127        // `val x` with no type, no value and no accessors.
128        .decl(KtProperty::val("bare"))
129        // A function with no body, at top level, where that cannot mean
130        // "abstract".
131        .decl(KtFun::new("nobody"))
132        // An abstract class member missing the `abstract` keyword.
133        .decl(
134            KtClass::class_with(KtClassModifier::Abstract, "Base")
135                .member(KtFun::new("unimplemented")),
136        )
137        // An enum whose entries do not call the constructor it declares.
138        .decl(
139            KtClass::enum_("Priority")
140                .ctor_param(KtCtorParam::new("code", KtType::int()).val())
141                .entry(KtEnumEntry::with_args("HIGH", "1"))
142                .entry(KtEnumEntry::new("LOW")),
143        )
144        // A constructor property and a member property of one name: both live
145        // in the value namespace.
146        .decl(
147            KtClass::class_("Holder")
148                .ctor_param(KtCtorParam::new("id", KtType::long()).val())
149                .member(KtProperty::val("id").initializer("0")),
150        )
151        // A named companion object colliding with a nested class.
152        .decl(
153            KtClass::class_("Outer")
154                .member(KtClass::class_("Factory"))
155                .companion(KtCompanion::named("Factory")),
156        )
157        // Two raw blocks claiming one identity with different bodies.
158        .decl(KtDecl::Raw {
159            name: "__loader".to_string(),
160            code: KtCode::new().line("internal val __loader = 1"),
161        })
162        .decl(KtDecl::Raw {
163            name: "__loader".to_string(),
164            code: KtCode::new().line("internal val __loader = 2"),
165        })
166        // Two different classes referenced from raw text under one short name:
167        // the text already says `Codec`, so it cannot mean both.
168        .import("io.example.a.Codec")
169        .import("io.example.b.Codec")
170}
More examples
Hide additional examples
examples/showcase.rs (line 156)
61fn types_fragment() -> KtFile {
62    // `enum class` with a primary constructor its entries call, plus a named
63    // companion object holding a factory.
64    let priority = KtClass::enum_("Priority")
65        .vis(KtVis::Public)
66        .kdoc("Delivery priority.\n\nMirrors the native `z_priority_t`.")
67        .ctor_param(
68            KtCtorParam::new("code", KtType::int())
69                .val()
70                .vis(KtVis::Public),
71        )
72        .entry(KtEnumEntry::with_args("REAL_TIME", "1"))
73        .entry(KtEnumEntry::with_args("INTERACTIVE", "4"))
74        .entry(KtEnumEntry::with_args("DATA", "5"))
75        .companion(
76            KtCompanion::named("Codes")
77                .vis(KtVis::Public)
78                .kdoc("Lookup helpers keyed by the native code.")
79                .member(
80                    KtFun::new("fromInt")
81                        .vis(KtVis::Public)
82                        .annotation("JvmStatic")
83                        .param(KtParam::new("value", KtType::int()))
84                        .returns(KtType::cls("Priority"))
85                        .body(
86                            KtCode::new()
87                                .blk("return when (value) {", |c| {
88                                    c.line("1 -> REAL_TIME")
89                                        .line("4 -> INTERACTIVE")
90                                        .line("5 -> DATA")
91                                        .line("else -> throw IllegalArgumentException(\"bad priority: $value\")")
92                                }),
93                        ),
94                ),
95        );
96
97    // `@JvmInline value class` — exactly one read-only property.
98    let zid = KtClass::value(
99        "ZenohId",
100        KtCtorParam::new("bytes", KtType::byte_array())
101            .val()
102            .vis(KtVis::Public),
103    )
104    .vis(KtVis::Public)
105    .kdoc("A 16-byte peer identifier, carried by value.");
106
107    // `data class` — every constructor parameter is a property.
108    let sample = KtClass::data(
109        "Sample",
110        KtCtorParam::new("keyExpr", KtType::string())
111            .val()
112            .vis(KtVis::Public),
113    )
114    .vis(KtVis::Public)
115    .ctor_param(
116        KtCtorParam::new("payload", KtType::byte_array())
117            .val()
118            .vis(KtVis::Public),
119    )
120    .ctor_param(
121        KtCtorParam::new("priority", KtType::cls("Priority"))
122            .val()
123            .vis(KtVis::Public)
124            .default("Priority.DATA"),
125    )
126    .ctor_param(
127        KtCtorParam::new("attachment", KtType::byte_array().nullable())
128            .var()
129            .vis(KtVis::Public)
130            .annotation("JvmField")
131            .default("null"),
132    );
133
134    // A `sealed interface` whose alternatives nest inside it: a `data class`
135    // with a payload and a `data object` without one.
136    let reply = KtClass::sealed_interface("Reply")
137        .vis(KtVis::Public)
138        .kdoc("Either a sample or the end of the stream.")
139        .member(
140            KtClass::data(
141                "Value",
142                KtCtorParam::new("sample", KtType::cls("Sample"))
143                    .val()
144                    .vis(KtVis::Public),
145            )
146            .vis(KtVis::Public)
147            .implements(KtType::cls("Reply")),
148        )
149        .member(
150            KtClass::data_object("Done")
151                .vis(KtVis::Public)
152                .implements(KtType::cls("Reply")),
153        );
154
155    KtFile::new("io.example.api")
156        .decl(priority)
157        .decl(zid)
158        .decl(sample)
159        .decl(reply)
160}
161
162/// The handle surface: an abstract base, a concrete subclass, an interface,
163/// a `fun interface`, and a type alias.
164fn session_fragment() -> KtFile {
165    // `abstract class` with an `abstract` member (bodiless, and it says so),
166    // a `@Volatile` property, and an interface it implements.
167    let base = KtClass::class_with(KtClassModifier::Abstract, "NativeHandle")
168        .vis(KtVis::Public)
169        .kdoc("Owns a raw pointer into Rust and frees it exactly once.")
170        .ctor_param(KtCtorParam::new("initialPtr", KtType::long()))
171        .implements(KtType::cls("AutoCloseable"))
172        .member(
173            KtProperty::var("ptr")
174                .ty(KtType::long())
175                .vis(KtVis::Internal)
176                .annotation("Volatile")
177                .kdoc("Zero once closed.")
178                .initializer("initialPtr"),
179        )
180        .member(
181            KtProperty::val("isClosed")
182                .ty(KtType::boolean())
183                .vis(KtVis::Public)
184                .accessors(KtCode::new().line("get() = ptr == 0L")),
185        )
186        .member(
187            KtFun::new("freePtr")
188                .vis(KtVis::Public)
189                .modifier("abstract")
190                .kdoc("Release the native allocation. Called once, under the lock.")
191                .param(KtParam::new("ptr", KtType::long())),
192        );
193
194    // `open class` extending the abstract base and constructing it.
195    let session = KtClass::class_with(KtClassModifier::Open, "Session")
196        .vis(KtVis::Public)
197        .annotation("Suppress(\"unused\")")
198        .ctor_param(KtCtorParam::new("initialPtr", KtType::long()))
199        .extends(KtType::cls("NativeHandle"), Some("initialPtr"))
200        .implements(KtType::cls("io.example.api.Describable"))
201        .member(
202            KtFun::new("freePtr")
203                .vis(KtVis::Public)
204                .modifier("override")
205                .param(KtParam::new("ptr", KtType::long()))
206                .body(KtCode::new().line("JNINative.sessionFree(ptr)")),
207        )
208        .member(
209            KtFun::new("describe")
210                .vis(KtVis::Public)
211                .modifier("override")
212                .returns(KtType::string())
213                .expr_body(KtCode::new().line("\"Session(0x${ptr.toString(16)})\"")),
214        )
215        // A generic method whose body uses every `KtCode` block form.
216        .member(
217            KtFun::new("get")
218                .vis(KtVis::Public)
219                .generic("R")
220                .kdoc("Run a query, folding each reply into an accumulator.")
221                .param(KtParam::new("selector", KtType::string()))
222                .param(KtParam::new(
223                    "onReply",
224                    KtType::lambda(
225                        [("reply".to_string(), KtType::cls("io.example.api.Reply"))],
226                        KtType::var_r(),
227                    ),
228                ))
229                .param(KtParam::new("timeoutMs", KtType::long()).default("10_000L"))
230                .returns(KtType::generic("List", [KtType::var_r()]))
231                .body(
232                    KtCode::new()
233                        .line("val acc = ArrayList<R>()")
234                        .line("val guard = Guard.acquire(this)")
235                        .import("io.example.internal.Guard")
236                        .try_finally(
237                            "",
238                            KtCode::new().blk("JNINative.sessionGet(ptr, selector, timeoutMs) { raw ->", |c| {
239                                c.line("acc.add(onReply(raw))")
240                            }),
241                            KtCode::new().line("guard.release()"),
242                        )
243                        .line("")
244                        // A long call that the renderer breaks by width at its
245                        // real nesting level.
246                        .wline("reportQueryOutcome(selector, timeoutMs, acc.size, acc.isNotEmpty(), \"query finished\", System.nanoTime())")
247                        .line("return acc"),
248                ),
249        )
250        // The interface's member extension, implemented.
251        .member(
252            KtFun::new("label")
253                .vis(KtVis::Public)
254                .modifier("override")
255                .receiver(KtType::cls("Sample"))
256                .returns(KtType::string())
257                .expr_body(KtCode::new().line("\"${keyExpr}@${describe()}\"")),
258        )
259        // A member extension of the class itself.
260        .member(
261            KtFun::new("toSample")
262                .vis(KtVis::Public)
263                .receiver(KtType::byte_array())
264                .returns(KtType::cls("Sample"))
265                .expr_body(KtCode::new().line("Sample(describe(), this)")),
266        )
267        // A nested class — its own scope, so its members may reuse names.
268        .member(
269            KtClass::class_("Config")
270                .vis(KtVis::Public)
271                .member(KtProperty::val("describe").initializer("\"config\"")),
272        )
273        .companion(
274            KtCompanion::new()
275                .vis(KtVis::Public)
276                .member(
277                    KtFun::new("open")
278                        .vis(KtVis::Public)
279                        .annotation("JvmStatic")
280                        .param(KtParam::new("config", KtType::string()).default("\"{}\""))
281                        .returns(KtType::cls("Session"))
282                        .expr_body(KtCode::new().line("Session(JNINative.sessionOpen(config))")),
283                ),
284        );
285
286    // A plain `interface`: bodiless members are abstract by position, and a
287    // `KtFunSig` is exactly that.
288    let describable = KtClass::interface_("Describable")
289        .vis(KtVis::Public)
290        .member(KtFunSig::new("describe").returns(KtType::string()))
291        // A member extension: abstract here, supplied by the implementor.
292        // `KtFunSig` carries a receiver too, so a signature does not quietly
293        // become a plain member.
294        .member(
295            KtFunSig::new("label")
296                .receiver(KtType::cls("Sample"))
297                .returns(KtType::string()),
298        );
299
300    // A `fun interface` (SAM) — its single method cannot carry a body.
301    let handler = KtFunInterface::new(
302        "ReplyHandler",
303        KtFunSig::new("onReply")
304            .param(KtParam::new("reply", KtType::cls("Reply")))
305            .returns(KtType::var_r()),
306    )
307    .vis(KtVis::Public)
308    .type_param("out R")
309    .kdoc("Invoked from the native thread for each reply.");
310
311    // Top-level extension functions. The receiver is a type, not part of the
312    // name, so it resolves through the import set and the name stays a plain
313    // identifier the validator can check as one.
314    let summary = KtFun::new("summary")
315        .vis(KtVis::Public)
316        .receiver(KtType::cls("Sample"))
317        .returns(KtType::string())
318        .expr_body(KtCode::new().line("\"$keyExpr (${payload.size} bytes)\""));
319
320    // Generics render before the receiver, which renders before the name.
321    let map_replies = KtFun::new("mapValues")
322        .vis(KtVis::Public)
323        .generic("R")
324        .receiver(KtType::generic("List", [KtType::cls("Reply")]))
325        .param(KtParam::new(
326            "transform",
327            KtType::lambda(
328                [("sample".to_string(), KtType::cls("Sample"))],
329                KtType::var_r(),
330            ),
331        ))
332        .returns(KtType::generic("List", [KtType::var_r()]))
333        .expr_body(
334            KtCode::new().line("filterIsInstance<Reply.Value>().map { transform(it.sample) }"),
335        );
336
337    // An extension on a *function type*. The receiver needs parentheses here
338    // or the `.` would bind to the return type instead.
339    let as_raw = KtFun::new("asRaw")
340        .vis(KtVis::Internal)
341        .receiver(KtType::lambda(
342            [("sample".to_string(), KtType::cls("Sample"))],
343            KtType::unit(),
344        ))
345        .returns(KtType::cls("io.example.api.internal.RawSink"))
346        // The proxy adapts a typed callback to the raw one the natives call,
347        // so it has to narrow `Reply` to the `Sample` the receiver takes.
348        .expr_body(
349            KtCode::new().line("RawSink { raw -> if (raw is Reply.Value) this(raw.sample) }"),
350        );
351
352    KtFile::new("io.example.api")
353        // FQNs named only from raw body text, which the model cannot see.
354        .imports(["io.example.api.internal.JNINative".to_string()])
355        .decl(base)
356        .decl(describable)
357        .decl(handler)
358        .decl(session)
359        .decl(summary)
360        .decl(map_replies)
361        .decl(as_raw)
362        .decl(KtDecl::TypeAlias {
363            vis: KtVis::Public,
364            name: "SampleList".to_string(),
365            target: KtType::generic("List", [KtType::cls("Sample")]),
366        })
367}
368
369/// The `external` surface — an object of natives, in its own subpackage.
370fn natives_fragment() -> KtFile {
371    let natives = KtClass::object_("JNINative")
372        .vis(KtVis::Internal)
373        .kdoc("One-to-one with the exported Rust symbols.")
374        .member(
375            KtFun::new("sessionOpen")
376                .vis(KtVis::Internal)
377                .param(KtParam::new("config", KtType::string()))
378                .returns(KtType::long())
379                .external(),
380        )
381        .member(
382            KtFun::new("sessionFree")
383                .vis(KtVis::Internal)
384                .param(KtParam::new("ptr", KtType::long()))
385                .external(),
386        )
387        .member(
388            KtFun::new("sessionGet")
389                .vis(KtVis::Internal)
390                .param(KtParam::new("ptr", KtType::long()))
391                .param(KtParam::new("selector", KtType::string()))
392                .param(KtParam::new("timeoutMs", KtType::long()))
393                .param(KtParam::new(
394                    "sink",
395                    KtType::lambda(
396                        [("reply".to_string(), KtType::cls("io.example.api.Reply"))],
397                        KtType::unit(),
398                    ),
399                ))
400                .external(),
401        );
402
403    // A pre-rendered block, for text the model does not describe. Its name is
404    // a merge identity, never emitted.
405    let loader = KtDecl::Raw {
406        name: "__loadNative".to_string(),
407        code: KtCode::raw_reindent(
408            "internal val __loaded: Boolean = run {\n\
409             System.loadLibrary(\"example_jni\")\n\
410             true\n\
411             }",
412        ),
413    };
414
415    let raw_sink = KtFunInterface::new(
416        "RawSink",
417        KtFunSig::new("accept").param(KtParam::new("reply", KtType::cls("io.example.api.Reply"))),
418    )
419    .vis(KtVis::Internal);
420
421    KtFile::new("io.example.api.internal")
422        .decl(raw_sink)
423        .decl(natives)
424        .decl(loader)
425        // An FQN referenced only from raw text the model cannot see.
426        .import("io.example.api.Reply")
427}
428
429/// A file in the default (root) package, exercising a banner override and
430/// top-level declarations of every value kind.
431fn root_fragment() -> KtFile {
432    KtFile::new("")
433        .banner("// Hand-tuned banner for the root package.")
434        .decl(
435            KtProperty::val("LIBRARY_VERSION")
436                .ty(KtType::string())
437                .vis(KtVis::Public)
438                .kdoc("Version this binding was generated against.")
439                .initializer("\"1.9.0\""),
440        )
441        // A keyword modifier rendered between visibility and `val`.
442        .decl(
443            KtProperty::val("PROTOCOL")
444                .ty(KtType::string())
445                .vis(KtVis::Public)
446                .modifier("const")
447                .initializer("\"tcp\""),
448        )
449        // A delegated property: `by <expr>` rather than `= <expr>`.
450        .decl(
451            KtProperty::val("defaultTimeout")
452                .vis(KtVis::Internal)
453                .ty(KtType::cls("java.time.Duration"))
454                .delegate("lazy { Duration.ofSeconds(10) }"),
455        )
456        .decl(
457            KtFun::new("describeAll")
458                .vis(KtVis::Public)
459                // Generic parameter lists are free-form text, so a bound is
460                // written as given — it is not shortened against the imports.
461                .generic("T : io.example.api.Describable")
462                .param(KtParam::new(
463                    "items",
464                    KtType::generic("List", [KtType::var_("T")]),
465                ))
466                .returns(KtType::string())
467                .expr_body(KtCode::new().line("items.joinToString { it.describe() }")),
468        )
469}
Source

pub fn import(self, fqn: impl Into<String>) -> Self

Register an FQN referenced only inside raw text.

Examples found in repository?
examples/showcase.rs (line 426)
370fn natives_fragment() -> KtFile {
371    let natives = KtClass::object_("JNINative")
372        .vis(KtVis::Internal)
373        .kdoc("One-to-one with the exported Rust symbols.")
374        .member(
375            KtFun::new("sessionOpen")
376                .vis(KtVis::Internal)
377                .param(KtParam::new("config", KtType::string()))
378                .returns(KtType::long())
379                .external(),
380        )
381        .member(
382            KtFun::new("sessionFree")
383                .vis(KtVis::Internal)
384                .param(KtParam::new("ptr", KtType::long()))
385                .external(),
386        )
387        .member(
388            KtFun::new("sessionGet")
389                .vis(KtVis::Internal)
390                .param(KtParam::new("ptr", KtType::long()))
391                .param(KtParam::new("selector", KtType::string()))
392                .param(KtParam::new("timeoutMs", KtType::long()))
393                .param(KtParam::new(
394                    "sink",
395                    KtType::lambda(
396                        [("reply".to_string(), KtType::cls("io.example.api.Reply"))],
397                        KtType::unit(),
398                    ),
399                ))
400                .external(),
401        );
402
403    // A pre-rendered block, for text the model does not describe. Its name is
404    // a merge identity, never emitted.
405    let loader = KtDecl::Raw {
406        name: "__loadNative".to_string(),
407        code: KtCode::raw_reindent(
408            "internal val __loaded: Boolean = run {\n\
409             System.loadLibrary(\"example_jni\")\n\
410             true\n\
411             }",
412        ),
413    };
414
415    let raw_sink = KtFunInterface::new(
416        "RawSink",
417        KtFunSig::new("accept").param(KtParam::new("reply", KtType::cls("io.example.api.Reply"))),
418    )
419    .vis(KtVis::Internal);
420
421    KtFile::new("io.example.api.internal")
422        .decl(raw_sink)
423        .decl(natives)
424        .decl(loader)
425        // An FQN referenced only from raw text the model cannot see.
426        .import("io.example.api.Reply")
427}
More examples
Hide additional examples
examples/invalid.rs (line 168)
71fn broken_declarations() -> KtFile {
72    KtFile::new("io.example.broken")
73        // Two classes of the same name: a redeclaration in the type namespace.
74        .decl(KtClass::class_("Session"))
75        .decl(KtClass::class_("Session"))
76        // An interface and a type alias are both classifier declarations, so they collide.
77        .decl(
78            KtClass::interface_("Describable")
79                .member(KtFun::new("describe").returns(KtType::string())),
80        )
81        .decl(KtDecl::TypeAlias {
82            vis: KtVis::Public,
83            name: "Describable".to_string(),
84            target: KtType::string(),
85        })
86        // Same name AND same parameter types: not an overload.
87        .decl(
88            KtFun::new("send")
89                .param(KtParam::new("value", KtType::int()))
90                .returns(KtType::boolean())
91                .body(KtCode::new()),
92        )
93        .decl(
94            KtFun::new("send")
95                .param(KtParam::new("other", KtType::int()))
96                .returns(KtType::long())
97                .body(KtCode::new()),
98        )
99        // Extensions are keyed on their receiver, so these two collide while
100        // the same pair on different receivers would not.
101        .decl(
102            KtFun::new("asRaw")
103                .receiver(KtType::cls("io.example.Codec"))
104                .body(KtCode::new()),
105        )
106        .decl(
107            KtFun::new("asRaw")
108                .receiver(KtType::cls("io.example.Codec"))
109                .body(KtCode::new()),
110        )
111        // ...as here: same name, different receiver, no diagnostic.
112        .decl(
113            KtFun::new("asRaw")
114                .receiver(KtType::cls("io.example.Other"))
115                .body(KtCode::new()),
116        )
117        // Names that are not legal Kotlin identifiers.
118        .decl(
119            KtClass::class_("My-Class")
120                .ctor_param(KtCtorParam::new("2fast", KtType::int()))
121                .member(
122                    KtFun::new("object")
123                        .param(KtParam::new("in", KtType::int()))
124                        .body(KtCode::new()),
125                ),
126        )
127        // `val x` with no type, no value and no accessors.
128        .decl(KtProperty::val("bare"))
129        // A function with no body, at top level, where that cannot mean
130        // "abstract".
131        .decl(KtFun::new("nobody"))
132        // An abstract class member missing the `abstract` keyword.
133        .decl(
134            KtClass::class_with(KtClassModifier::Abstract, "Base")
135                .member(KtFun::new("unimplemented")),
136        )
137        // An enum whose entries do not call the constructor it declares.
138        .decl(
139            KtClass::enum_("Priority")
140                .ctor_param(KtCtorParam::new("code", KtType::int()).val())
141                .entry(KtEnumEntry::with_args("HIGH", "1"))
142                .entry(KtEnumEntry::new("LOW")),
143        )
144        // A constructor property and a member property of one name: both live
145        // in the value namespace.
146        .decl(
147            KtClass::class_("Holder")
148                .ctor_param(KtCtorParam::new("id", KtType::long()).val())
149                .member(KtProperty::val("id").initializer("0")),
150        )
151        // A named companion object colliding with a nested class.
152        .decl(
153            KtClass::class_("Outer")
154                .member(KtClass::class_("Factory"))
155                .companion(KtCompanion::named("Factory")),
156        )
157        // Two raw blocks claiming one identity with different bodies.
158        .decl(KtDecl::Raw {
159            name: "__loader".to_string(),
160            code: KtCode::new().line("internal val __loader = 1"),
161        })
162        .decl(KtDecl::Raw {
163            name: "__loader".to_string(),
164            code: KtCode::new().line("internal val __loader = 2"),
165        })
166        // Two different classes referenced from raw text under one short name:
167        // the text already says `Codec`, so it cannot mean both.
168        .import("io.example.a.Codec")
169        .import("io.example.b.Codec")
170}
Source

pub fn imports(self, fqns: impl IntoIterator<Item = String>) -> Self

Examples found in repository?
examples/showcase.rs (line 354)
164fn session_fragment() -> KtFile {
165    // `abstract class` with an `abstract` member (bodiless, and it says so),
166    // a `@Volatile` property, and an interface it implements.
167    let base = KtClass::class_with(KtClassModifier::Abstract, "NativeHandle")
168        .vis(KtVis::Public)
169        .kdoc("Owns a raw pointer into Rust and frees it exactly once.")
170        .ctor_param(KtCtorParam::new("initialPtr", KtType::long()))
171        .implements(KtType::cls("AutoCloseable"))
172        .member(
173            KtProperty::var("ptr")
174                .ty(KtType::long())
175                .vis(KtVis::Internal)
176                .annotation("Volatile")
177                .kdoc("Zero once closed.")
178                .initializer("initialPtr"),
179        )
180        .member(
181            KtProperty::val("isClosed")
182                .ty(KtType::boolean())
183                .vis(KtVis::Public)
184                .accessors(KtCode::new().line("get() = ptr == 0L")),
185        )
186        .member(
187            KtFun::new("freePtr")
188                .vis(KtVis::Public)
189                .modifier("abstract")
190                .kdoc("Release the native allocation. Called once, under the lock.")
191                .param(KtParam::new("ptr", KtType::long())),
192        );
193
194    // `open class` extending the abstract base and constructing it.
195    let session = KtClass::class_with(KtClassModifier::Open, "Session")
196        .vis(KtVis::Public)
197        .annotation("Suppress(\"unused\")")
198        .ctor_param(KtCtorParam::new("initialPtr", KtType::long()))
199        .extends(KtType::cls("NativeHandle"), Some("initialPtr"))
200        .implements(KtType::cls("io.example.api.Describable"))
201        .member(
202            KtFun::new("freePtr")
203                .vis(KtVis::Public)
204                .modifier("override")
205                .param(KtParam::new("ptr", KtType::long()))
206                .body(KtCode::new().line("JNINative.sessionFree(ptr)")),
207        )
208        .member(
209            KtFun::new("describe")
210                .vis(KtVis::Public)
211                .modifier("override")
212                .returns(KtType::string())
213                .expr_body(KtCode::new().line("\"Session(0x${ptr.toString(16)})\"")),
214        )
215        // A generic method whose body uses every `KtCode` block form.
216        .member(
217            KtFun::new("get")
218                .vis(KtVis::Public)
219                .generic("R")
220                .kdoc("Run a query, folding each reply into an accumulator.")
221                .param(KtParam::new("selector", KtType::string()))
222                .param(KtParam::new(
223                    "onReply",
224                    KtType::lambda(
225                        [("reply".to_string(), KtType::cls("io.example.api.Reply"))],
226                        KtType::var_r(),
227                    ),
228                ))
229                .param(KtParam::new("timeoutMs", KtType::long()).default("10_000L"))
230                .returns(KtType::generic("List", [KtType::var_r()]))
231                .body(
232                    KtCode::new()
233                        .line("val acc = ArrayList<R>()")
234                        .line("val guard = Guard.acquire(this)")
235                        .import("io.example.internal.Guard")
236                        .try_finally(
237                            "",
238                            KtCode::new().blk("JNINative.sessionGet(ptr, selector, timeoutMs) { raw ->", |c| {
239                                c.line("acc.add(onReply(raw))")
240                            }),
241                            KtCode::new().line("guard.release()"),
242                        )
243                        .line("")
244                        // A long call that the renderer breaks by width at its
245                        // real nesting level.
246                        .wline("reportQueryOutcome(selector, timeoutMs, acc.size, acc.isNotEmpty(), \"query finished\", System.nanoTime())")
247                        .line("return acc"),
248                ),
249        )
250        // The interface's member extension, implemented.
251        .member(
252            KtFun::new("label")
253                .vis(KtVis::Public)
254                .modifier("override")
255                .receiver(KtType::cls("Sample"))
256                .returns(KtType::string())
257                .expr_body(KtCode::new().line("\"${keyExpr}@${describe()}\"")),
258        )
259        // A member extension of the class itself.
260        .member(
261            KtFun::new("toSample")
262                .vis(KtVis::Public)
263                .receiver(KtType::byte_array())
264                .returns(KtType::cls("Sample"))
265                .expr_body(KtCode::new().line("Sample(describe(), this)")),
266        )
267        // A nested class — its own scope, so its members may reuse names.
268        .member(
269            KtClass::class_("Config")
270                .vis(KtVis::Public)
271                .member(KtProperty::val("describe").initializer("\"config\"")),
272        )
273        .companion(
274            KtCompanion::new()
275                .vis(KtVis::Public)
276                .member(
277                    KtFun::new("open")
278                        .vis(KtVis::Public)
279                        .annotation("JvmStatic")
280                        .param(KtParam::new("config", KtType::string()).default("\"{}\""))
281                        .returns(KtType::cls("Session"))
282                        .expr_body(KtCode::new().line("Session(JNINative.sessionOpen(config))")),
283                ),
284        );
285
286    // A plain `interface`: bodiless members are abstract by position, and a
287    // `KtFunSig` is exactly that.
288    let describable = KtClass::interface_("Describable")
289        .vis(KtVis::Public)
290        .member(KtFunSig::new("describe").returns(KtType::string()))
291        // A member extension: abstract here, supplied by the implementor.
292        // `KtFunSig` carries a receiver too, so a signature does not quietly
293        // become a plain member.
294        .member(
295            KtFunSig::new("label")
296                .receiver(KtType::cls("Sample"))
297                .returns(KtType::string()),
298        );
299
300    // A `fun interface` (SAM) — its single method cannot carry a body.
301    let handler = KtFunInterface::new(
302        "ReplyHandler",
303        KtFunSig::new("onReply")
304            .param(KtParam::new("reply", KtType::cls("Reply")))
305            .returns(KtType::var_r()),
306    )
307    .vis(KtVis::Public)
308    .type_param("out R")
309    .kdoc("Invoked from the native thread for each reply.");
310
311    // Top-level extension functions. The receiver is a type, not part of the
312    // name, so it resolves through the import set and the name stays a plain
313    // identifier the validator can check as one.
314    let summary = KtFun::new("summary")
315        .vis(KtVis::Public)
316        .receiver(KtType::cls("Sample"))
317        .returns(KtType::string())
318        .expr_body(KtCode::new().line("\"$keyExpr (${payload.size} bytes)\""));
319
320    // Generics render before the receiver, which renders before the name.
321    let map_replies = KtFun::new("mapValues")
322        .vis(KtVis::Public)
323        .generic("R")
324        .receiver(KtType::generic("List", [KtType::cls("Reply")]))
325        .param(KtParam::new(
326            "transform",
327            KtType::lambda(
328                [("sample".to_string(), KtType::cls("Sample"))],
329                KtType::var_r(),
330            ),
331        ))
332        .returns(KtType::generic("List", [KtType::var_r()]))
333        .expr_body(
334            KtCode::new().line("filterIsInstance<Reply.Value>().map { transform(it.sample) }"),
335        );
336
337    // An extension on a *function type*. The receiver needs parentheses here
338    // or the `.` would bind to the return type instead.
339    let as_raw = KtFun::new("asRaw")
340        .vis(KtVis::Internal)
341        .receiver(KtType::lambda(
342            [("sample".to_string(), KtType::cls("Sample"))],
343            KtType::unit(),
344        ))
345        .returns(KtType::cls("io.example.api.internal.RawSink"))
346        // The proxy adapts a typed callback to the raw one the natives call,
347        // so it has to narrow `Reply` to the `Sample` the receiver takes.
348        .expr_body(
349            KtCode::new().line("RawSink { raw -> if (raw is Reply.Value) this(raw.sample) }"),
350        );
351
352    KtFile::new("io.example.api")
353        // FQNs named only from raw body text, which the model cannot see.
354        .imports(["io.example.api.internal.JNINative".to_string()])
355        .decl(base)
356        .decl(describable)
357        .decl(handler)
358        .decl(session)
359        .decl(summary)
360        .decl(map_replies)
361        .decl(as_raw)
362        .decl(KtDecl::TypeAlias {
363            vis: KtVis::Public,
364            name: "SampleList".to_string(),
365            target: KtType::generic("List", [KtType::cls("Sample")]),
366        })
367}
Source§

impl KtFile

Source

pub fn render(&self) -> String

Render the complete file: banner, package, sorted imports, then declarations in insertion order separated by blank lines.

Examples found in repository?
examples/showcase.rs (line 36)
22fn main() {
23    let fragments = vec![
24        types_fragment(),
25        session_fragment(),
26        natives_fragment(),
27        root_fragment(),
28    ];
29
30    let files = merge_files(fragments).expect("the showcase model is valid");
31
32    println!("=== generated files ===");
33    for file in &files {
34        let path = merged_file_path(std::path::Path::new("kotlin"), file, "Generated");
35        println!("\n--- {} ---", path.display());
36        print!("{}", file.render());
37    }
38
39    println!("\n=== identifier helpers ===");
40    for raw in ["myValue", "my-field", "2fast", "object", "data", ""] {
41        let quoted = format!("{raw:?}");
42        let mangled = format!("{:?}", mangle_kotlin_ident(raw));
43        let escaped = format!("{:?}", escape_kotlin_ident(raw));
44        let valid = is_valid_kotlin_ident(raw);
45        let keyword = is_kotlin_hard_keyword(raw);
46        println!(
47            "{quoted:>10} -> valid={valid:<5} keyword={keyword:<5} \
48             mangled={mangled:<10} escaped={escaped}"
49        );
50    }
51    for raw in ["io.example.api", "fun.my-pkg", "io..jni."] {
52        let quoted = format!("{raw:?}");
53        let mangled = format!("{:?}", mangle_kotlin_package(raw));
54        let valid = is_valid_kotlin_package(raw);
55        println!("{quoted:>16} -> valid={valid:<5} mangled={mangled}");
56    }
57}
Source§

impl KtFile

Source

pub fn validate(&self) -> Vec<Diagnostic>

Every problem in this file, with each check at its default severity (error).

An empty result means the file is as good as the model can prove: names, redeclarations and declaration shapes are checked, and Check enumerates exactly which. Anything needing type resolution, or reading the raw text inside KtCode bodies, stays the Kotlin compiler’s job.

Examples found in repository?
examples/invalid.rs (line 26)
23fn main() {
24    println!("=== diagnostics (default: every check is an error) ===");
25    for file in broken_files() {
26        for d in file.validate() {
27            println!("{d}");
28        }
29    }
30
31    println!("\n=== merging refuses to produce anything ===");
32    match merge_files(broken_files()) {
33        Ok(_) => println!("unexpectedly merged"),
34        // The Display of the error is what a build script would surface.
35        Err(e) => print!("{e}"),
36    }
37
38    println!("\n=== the same model under `warn_all()` ===");
39    let (files, warnings) = merge_files_warning();
40    println!(
41        "merged {} file(s) with {} warning(s); generation continues",
42        files.len(),
43        warnings.len()
44    );
45
46    println!("\n=== one check downgraded, another switched off ===");
47    let policy = ValidationPolicy::new()
48        .warn(Check::DuplicateFunction)
49        .allow(Check::InvalidIdentifier);
50    for file in broken_files() {
51        for d in file.validate_with(&policy) {
52            println!("{d}");
53        }
54    }
55
56    println!("\n=== shapes the model will not build at all ===");
57    for (what, outcome) in refused_shapes() {
58        println!("{what}\n    -> {outcome}");
59    }
60}
Source

pub fn validate_with(&self, policy: &ValidationPolicy) -> Vec<Diagnostic>

KtFile::validate with per-check severities.

Examples found in repository?
examples/invalid.rs (line 51)
23fn main() {
24    println!("=== diagnostics (default: every check is an error) ===");
25    for file in broken_files() {
26        for d in file.validate() {
27            println!("{d}");
28        }
29    }
30
31    println!("\n=== merging refuses to produce anything ===");
32    match merge_files(broken_files()) {
33        Ok(_) => println!("unexpectedly merged"),
34        // The Display of the error is what a build script would surface.
35        Err(e) => print!("{e}"),
36    }
37
38    println!("\n=== the same model under `warn_all()` ===");
39    let (files, warnings) = merge_files_warning();
40    println!(
41        "merged {} file(s) with {} warning(s); generation continues",
42        files.len(),
43        warnings.len()
44    );
45
46    println!("\n=== one check downgraded, another switched off ===");
47    let policy = ValidationPolicy::new()
48        .warn(Check::DuplicateFunction)
49        .allow(Check::InvalidIdentifier);
50    for file in broken_files() {
51        for d in file.validate_with(&policy) {
52            println!("{d}");
53        }
54    }
55
56    println!("\n=== shapes the model will not build at all ===");
57    for (what, outcome) in refused_shapes() {
58        println!("{what}\n    -> {outcome}");
59    }
60}

Trait Implementations§

Source§

impl Clone for KtFile

Source§

fn clone(&self) -> KtFile

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for KtFile

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.