0
# Error Handling and Diagnostics
1
2
Comprehensive error handling system with diagnostic reporting, result wrapper types, and utility functions. Provides structured error reporting and result handling throughout the Kotlin scripting system.
3
4
## Capabilities
5
6
### ResultWithDiagnostics
7
8
Primary result wrapper type that combines operation results with diagnostic information.
9
10
```kotlin { .api }
11
/**
12
* Result wrapper that includes diagnostic information
13
*/
14
sealed class ResultWithDiagnostics<out R> {
15
/** List of diagnostic reports associated with this result */
16
abstract val reports: List<ScriptDiagnostic>
17
18
/**
19
* Successful result with optional diagnostic reports
20
*/
21
data class Success<out R>(
22
val value: R,
23
override val reports: List<ScriptDiagnostic> = emptyList()
24
) : ResultWithDiagnostics<R>()
25
26
/**
27
* Failed result with diagnostic reports explaining the failure
28
*/
29
data class Failure(
30
override val reports: List<ScriptDiagnostic>
31
) : ResultWithDiagnostics<Nothing>()
32
}
33
```
34
35
### ScriptDiagnostic
36
37
Detailed diagnostic information for script processing errors, warnings, and information.
38
39
```kotlin { .api }
40
/**
41
* Diagnostic report for script processing
42
*/
43
data class ScriptDiagnostic(
44
/** Diagnostic code identifier */
45
val code: Int,
46
/** Human-readable diagnostic message */
47
val message: String,
48
/** Severity level of the diagnostic */
49
val severity: Severity = Severity.ERROR,
50
/** Source file path where the diagnostic occurred */
51
val sourcePath: String? = null,
52
/** Location within the source where the diagnostic occurred */
53
val location: SourceCode.Location? = null,
54
/** Exception that caused this diagnostic, if any */
55
val exception: Throwable? = null
56
) {
57
/**
58
* Severity levels for diagnostics
59
*/
60
enum class Severity {
61
DEBUG,
62
INFO,
63
WARNING,
64
ERROR,
65
FATAL
66
}
67
68
/**
69
* Alternative constructor with LocationWithId
70
*/
71
constructor(
72
code: Int,
73
message: String,
74
severity: Severity = Severity.ERROR,
75
locationWithId: SourceCode.LocationWithId?,
76
exception: Throwable? = null
77
) : this(code, message, severity, locationWithId?.codeLocationId, locationWithId?.locationInText, exception)
78
}
79
```
80
81
### Result Extension Functions
82
83
Utility functions for working with ResultWithDiagnostics instances.
84
85
```kotlin { .api }
86
/**
87
* Execute action if result is successful
88
*/
89
fun <R, T> ResultWithDiagnostics<R>.onSuccess(
90
action: (R) -> T
91
): T?
92
93
/**
94
* Execute action if result is a failure
95
*/
96
fun <R> ResultWithDiagnostics<R>.onFailure(
97
action: (List<ScriptDiagnostic>) -> Unit
98
): ResultWithDiagnostics<R>
99
100
/**
101
* Get the value if successful, null if failed
102
*/
103
fun <R> ResultWithDiagnostics<R>.valueOrNull(): R?
104
105
/**
106
* Get the value if successful, or default value if failed
107
*/
108
fun <R> ResultWithDiagnostics<R>.valueOr(default: R): R
109
110
/**
111
* Get the value if successful, or throw exception if failed
112
*/
113
fun <R> ResultWithDiagnostics<R>.valueOrThrow(): R
114
115
/**
116
* Transform successful result value
117
*/
118
fun <R, T> ResultWithDiagnostics<R>.map(transform: (R) -> T): ResultWithDiagnostics<T>
119
120
/**
121
* Transform successful result and flatten nested results
122
*/
123
fun <R, T> ResultWithDiagnostics<R>.flatMap(
124
transform: (R) -> ResultWithDiagnostics<T>
125
): ResultWithDiagnostics<T>
126
```
127
128
### Diagnostic Extension Functions
129
130
Utility functions for working with ScriptDiagnostic instances.
131
132
```kotlin { .api }
133
/**
134
* Check if diagnostic represents an error
135
*/
136
fun ScriptDiagnostic.isError(): Boolean
137
138
/**
139
* Check if diagnostic represents a warning
140
*/
141
fun ScriptDiagnostic.isWarning(): Boolean
142
143
/**
144
* Check if diagnostic represents informational message
145
*/
146
fun ScriptDiagnostic.isInfo(): Boolean
147
```
148
149
### Collection Utilities
150
151
Utilities for working with collections of results and diagnostics.
152
153
```kotlin { .api }
154
/**
155
* Collector for iterating over multiple results
156
*/
157
class IterableResultsCollector<T> {
158
fun collect(iterable: Iterable<ResultWithDiagnostics<T>>): ResultWithDiagnostics<List<T>>
159
}
160
161
/**
162
* Transform collection if all results are successful
163
*/
164
fun <T, R> Iterable<ResultWithDiagnostics<T>>.mapSuccess(
165
transform: (T) -> R
166
): ResultWithDiagnostics<List<R>>
167
168
/**
169
* Transform collection and flatten nested results
170
*/
171
fun <T, R> Iterable<ResultWithDiagnostics<T>>.flatMapSuccess(
172
transform: (T) -> ResultWithDiagnostics<R>
173
): ResultWithDiagnostics<List<R>>
174
```
175
176
**Usage Examples:**
177
178
```kotlin
179
import kotlin.script.experimental.api.*
180
181
// Creating successful results
182
val successResult = "Hello World".asSuccess()
183
val successWithWarnings = "Hello World".asSuccess(listOf(
184
ScriptDiagnostic(
185
message = "Deprecated API used",
186
severity = ScriptDiagnostic.Severity.WARNING
187
)
188
))
189
190
// Creating failure results
191
val failureResult = listOf(
192
ScriptDiagnostic(
193
code = 1001,
194
message = "Compilation failed: unresolved reference",
195
severity = ScriptDiagnostic.Severity.ERROR,
196
location = SourceCode.Location(
197
start = SourceCode.Position(line = 5, col = 10)
198
)
199
)
200
).asFailure()
201
202
// Handling results
203
when (val result = compileScript()) {
204
is ResultWithDiagnostics.Success -> {
205
val compiledScript = result.value
206
println("Compilation successful: ${compiledScript.sourceLocationId}")
207
208
// Handle warnings
209
result.reports.filter { it.isWarning() }.forEach { warning ->
210
println("Warning: ${warning.message}")
211
}
212
}
213
is ResultWithDiagnostics.Failure -> {
214
result.reports.forEach { diagnostic ->
215
val location = diagnostic.location?.let { loc ->
216
" at line ${loc.start.line}, column ${loc.start.col}"
217
} ?: ""
218
println("${diagnostic.severity}: ${diagnostic.message}$location")
219
}
220
}
221
}
222
```
223
224
### Result Transformation
225
226
```kotlin
227
// Transform successful results
228
val originalResult: ResultWithDiagnostics<String> = "42".asSuccess()
229
val transformedResult: ResultWithDiagnostics<Int> = originalResult.map { it.toInt() }
230
231
// Chain operations with flatMap
232
val chainedResult = originalResult
233
.flatMap { value ->
234
if (value.isNotEmpty()) {
235
value.toIntOrNull()?.asSuccess() ?: "Invalid number".asFailure()
236
} else {
237
"Empty value".asFailure()
238
}
239
}
240
.map { number -> number * 2 }
241
242
// Using utility functions
243
val value = originalResult.valueOrNull() // "42" or null
244
val safeValue = originalResult.valueOr("default") // "42" or "default"
245
246
try {
247
val requiredValue = originalResult.valueOrThrow() // "42" or throws exception
248
} catch (e: Exception) {
249
println("Failed to get value: ${e.message}")
250
}
251
```
252
253
### Error Creation Utilities
254
255
```kotlin
256
// Create diagnostic reports
257
fun createCompilationError(message: String, location: SourceCode.Location? = null): ScriptDiagnostic {
258
return ScriptDiagnostic(
259
code = 2000,
260
message = message,
261
severity = ScriptDiagnostic.Severity.ERROR,
262
location = location
263
)
264
}
265
266
fun createWarning(message: String): ScriptDiagnostic {
267
return ScriptDiagnostic(
268
message = message,
269
severity = ScriptDiagnostic.Severity.WARNING
270
)
271
}
272
273
// Helper extensions for creating results
274
fun <T> T.asSuccess(reports: List<ScriptDiagnostic> = emptyList()): ResultWithDiagnostics<T> {
275
return ResultWithDiagnostics.Success(this, reports)
276
}
277
278
fun List<ScriptDiagnostic>.asFailure(): ResultWithDiagnostics<Nothing> {
279
return ResultWithDiagnostics.Failure(this)
280
}
281
282
fun String.asFailure(): ResultWithDiagnostics<Nothing> {
283
return listOf(ScriptDiagnostic(message = this)).asFailure()
284
}
285
286
// Usage
287
val result1 = "success".asSuccess()
288
val result2 = "Compilation failed".asFailure()
289
val result3 = listOf(
290
createCompilationError("Missing import"),
291
createWarning("Unused variable")
292
).asFailure()
293
```
294
295
### Advanced Error Handling
296
297
```kotlin
298
// Collect multiple results
299
fun processMultipleScripts(scripts: List<SourceCode>): ResultWithDiagnostics<List<CompiledScript>> {
300
val results = scripts.map { script ->
301
compileScript(script) // Returns ResultWithDiagnostics<CompiledScript>
302
}
303
304
// Collect all results - fails if any individual result fails
305
return IterableResultsCollector<CompiledScript>().collect(results)
306
}
307
308
// Transform with success mapping
309
fun getScriptNames(scripts: List<SourceCode>): ResultWithDiagnostics<List<String>> {
310
return scripts
311
.map { compileScript(it) }
312
.mapSuccess { compiledScript ->
313
compiledScript.sourceLocationId ?: "unknown"
314
}
315
}
316
317
// Chain operations with error accumulation
318
fun processScriptPipeline(source: SourceCode): ResultWithDiagnostics<String> {
319
return compileScript(source)
320
.flatMap { compiled -> evaluateScript(compiled) }
321
.flatMap { evaluated -> formatResult(evaluated) }
322
.map { formatted -> "Result: $formatted" }
323
}
324
325
// Custom error handling
326
class ScriptProcessingException(
327
message: String,
328
val diagnostics: List<ScriptDiagnostic>,
329
cause: Throwable? = null
330
) : Exception(message, cause)
331
332
fun handleScriptErrors(result: ResultWithDiagnostics<*>) {
333
result.onFailure { diagnostics ->
334
val errorMessages = diagnostics
335
.filter { it.severity >= ScriptDiagnostic.Severity.ERROR }
336
.map { it.message }
337
338
if (errorMessages.isNotEmpty()) {
339
throw ScriptProcessingException(
340
message = "Script processing failed: ${errorMessages.joinToString("; ")}",
341
diagnostics = diagnostics
342
)
343
}
344
}
345
}
346
```
347
348
### Diagnostic Filtering and Analysis
349
350
```kotlin
351
// Filter diagnostics by severity
352
fun filterErrors(diagnostics: List<ScriptDiagnostic>): List<ScriptDiagnostic> {
353
return diagnostics.filter { it.severity >= ScriptDiagnostic.Severity.ERROR }
354
}
355
356
fun filterWarnings(diagnostics: List<ScriptDiagnostic>): List<ScriptDiagnostic> {
357
return diagnostics.filter { it.severity == ScriptDiagnostic.Severity.WARNING }
358
}
359
360
// Diagnostic analysis
361
fun analyzeDiagnostics(diagnostics: List<ScriptDiagnostic>): DiagnosticSummary {
362
return DiagnosticSummary(
363
errorCount = diagnostics.count { it.isError() },
364
warningCount = diagnostics.count { it.isWarning() },
365
infoCount = diagnostics.count { it.isInfo() },
366
hasErrors = diagnostics.any { it.isError() },
367
mostSevere = diagnostics.maxByOrNull { it.severity.ordinal }?.severity
368
)
369
}
370
371
data class DiagnosticSummary(
372
val errorCount: Int,
373
val warningCount: Int,
374
val infoCount: Int,
375
val hasErrors: Boolean,
376
val mostSevere: ScriptDiagnostic.Severity?
377
)
378
```