pub struct KtCode { /* private fields */ }Expand description
A sequence of raw lines and nested blocks plus the imports its text references. Rendered with 4-space indentation per nesting level.
Implementations§
Source§impl KtCode
impl KtCode
Sourcepub fn new() -> Self
pub fn new() -> Self
Examples found in repository?
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
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}pub fn is_empty(&self) -> bool
Sourcepub fn line(self, s: impl Into<String>) -> Self
pub fn line(self, s: impl Into<String>) -> Self
Append one raw line.
Examples found in repository?
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}More examples
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}Sourcepub fn lines(self, text: &str) -> Self
pub fn lines(self, text: &str) -> Self
Append a multi-line string: each line lands at the current level.
Sourcepub fn wline(self, s: impl Into<String>) -> Self
pub fn wline(self, s: impl Into<String>) -> Self
Append one line that is width-broken at render time (one call
argument / lambda parameter per line) if it exceeds the budget at its
actual nesting level. Use for composed statements of unbounded width
(call expressions, guards); plain Self::line for short fixed text.
Examples found in repository?
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}Sourcepub fn try_finally(
self,
opener_prefix: impl Into<String>,
body: KtCode,
fin: KtCode,
) -> Self
pub fn try_finally( self, opener_prefix: impl Into<String>, body: KtCode, fin: KtCode, ) -> Self
Append try { body } finally { fin }. opener_prefix glues a binding
onto the try (e.g. "val __ret = ").
Examples found in repository?
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}Sourcepub fn blk(
self,
opener: impl Into<String>,
f: impl FnOnce(KtCode) -> KtCode,
) -> Self
pub fn blk( self, opener: impl Into<String>, f: impl FnOnce(KtCode) -> KtCode, ) -> Self
Append a nested block: opener at the current level, children one
level deeper, then }.
Examples found in repository?
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}Sourcepub fn blk_with(
self,
opener: impl Into<String>,
closer: impl Into<String>,
f: impl FnOnce(KtCode) -> KtCode,
) -> Self
pub fn blk_with( self, opener: impl Into<String>, closer: impl Into<String>, f: impl FnOnce(KtCode) -> KtCode, ) -> Self
Self::blk with an explicit closer (}), },, …).
Sourcepub fn push(self, other: KtCode) -> Self
pub fn push(self, other: KtCode) -> Self
Append another KtCode’s items (same level) and imports.
Sourcepub fn import(self, fqn: impl Into<String>) -> Self
pub fn import(self, fqn: impl Into<String>) -> Self
Register an FQN referenced only inside this raw text.
Examples found in repository?
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}Sourcepub fn raw_reindent(text: &str) -> Self
pub fn raw_reindent(text: &str) -> Self
Build from a flat multi-line blob by recomputing nesting from brace
balance — for migrating bodies composed as unindented strings. Tracks
Kotlin string literals (incl. escapes) and // comments so braces
inside them don’t count. A line’s level drops first by its leading
closers (}, }) …), then the rest of its delta applies to the
following lines.
Examples found in repository?
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}Sourcepub fn raw_reindent_wrapped(text: &str) -> Self
pub fn raw_reindent_wrapped(text: &str) -> Self
Like Self::raw_reindent, but additionally breaks any line that would
exceed the renderer’s maximum signature width across multiple lines,
one call argument (or lambda parameter) per line — the body-side analogue
of the width-aware signature layout in the renderer. The brace
delta that drives nesting is still computed from the original (un-broken)
line, so the wrapping never disturbs the surrounding block structure.