1use kotlin_codegen::{
19 merge_files, Check, KtClass, KtClassModifier, KtCode, KtCompanion, KtCtorParam, KtDecl,
20 KtEnumEntry, KtFile, KtFun, KtParam, KtProperty, KtType, KtVis, ValidationPolicy,
21};
22
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 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}
61
62fn broken_files() -> Vec<KtFile> {
64 vec![
65 broken_declarations(),
66 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 .decl(KtClass::class_("Session"))
75 .decl(KtClass::class_("Session"))
76 .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 .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 .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 .decl(
113 KtFun::new("asRaw")
114 .receiver(KtType::cls("io.example.Other"))
115 .body(KtCode::new()),
116 )
117 .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 .decl(KtProperty::val("bare"))
129 .decl(KtFun::new("nobody"))
132 .decl(
134 KtClass::class_with(KtClassModifier::Abstract, "Base")
135 .member(KtFun::new("unimplemented")),
136 )
137 .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 .decl(
147 KtClass::class_("Holder")
148 .ctor_param(KtCtorParam::new("id", KtType::long()).val())
149 .member(KtProperty::val("id").initializer("0")),
150 )
151 .decl(
153 KtClass::class_("Outer")
154 .member(KtClass::class_("Factory"))
155 .companion(KtCompanion::named("Factory")),
156 )
157 .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 .import("io.example.a.Codec")
169 .import("io.example.b.Codec")
170}
171
172fn merge_files_warning() -> (Vec<KtFile>, Vec<kotlin_codegen::Diagnostic>) {
176 kotlin_codegen::merge_files_with(broken_files(), &ValidationPolicy::warn_all())
177 .expect("warnings never stop generation")
178}
179
180fn refused_shapes() -> Vec<(&'static str, String)> {
183 vec![
184 (
185 "object Foo(x: Int) — an object has no primary constructor",
186 refused(|| {
187 KtClass::object_("Foo").ctor_param(KtCtorParam::new("x", KtType::int()));
188 }),
189 ),
190 (
191 "data class Foo(x: Int) — a data class parameter must be a property",
192 refused(|| {
193 KtClass::data("Foo", KtCtorParam::new("x", KtType::int()));
194 }),
195 ),
196 (
197 "value class Foo(var x: Long) — a value class wraps one read-only property",
198 refused(|| {
199 KtClass::value("Foo", KtCtorParam::new("x", KtType::long()).var());
200 }),
201 ),
202 (
203 "class A : B(x), C(y) — Kotlin constructs at most one superclass",
204 refused(|| {
205 KtClass::class_("A")
206 .extends(KtType::cls("B"), Some("x"))
207 .extends(KtType::cls("C"), Some("y"));
208 }),
209 ),
210 (
211 "external fun f() { … } — `external` is a body kind, not a modifier",
212 refused(|| {
213 KtFun::new("f").modifier("external");
214 }),
215 ),
216 (
217 "enum entry on a non-enum class",
218 refused(|| {
219 KtClass::class_("C").entry(KtEnumEntry::new("A"));
220 }),
221 ),
222 (
223 "companion object with an empty name",
224 refused(|| {
225 KtCompanion::named("");
226 }),
227 ),
228 ]
229}
230
231fn refused(f: impl FnOnce() + std::panic::UnwindSafe) -> String {
234 let hook = std::panic::take_hook();
237 std::panic::set_hook(Box::new(|_| {}));
238 let result = std::panic::catch_unwind(f);
239 std::panic::set_hook(hook);
240 match result {
241 Ok(()) => "built successfully (unexpected!)".to_string(),
242 Err(payload) => payload
243 .downcast_ref::<String>()
244 .cloned()
245 .or_else(|| payload.downcast_ref::<&str>().map(|s| s.to_string()))
246 .unwrap_or_else(|| "panicked".to_string()),
247 }
248}