Core interfaces and data structures for Kotlin script compilation, evaluation, and IDE integration
—
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.
Primary result wrapper type that combines operation results with diagnostic information.
/**
* Result wrapper that includes diagnostic information
*/
sealed class ResultWithDiagnostics<out R> {
/** List of diagnostic reports associated with this result */
abstract val reports: List<ScriptDiagnostic>
/**
* Successful result with optional diagnostic reports
*/
data class Success<out R>(
val value: R,
override val reports: List<ScriptDiagnostic> = emptyList()
) : ResultWithDiagnostics<R>()
/**
* Failed result with diagnostic reports explaining the failure
*/
data class Failure(
override val reports: List<ScriptDiagnostic>
) : ResultWithDiagnostics<Nothing>()
}Detailed diagnostic information for script processing errors, warnings, and information.
/**
* Diagnostic report for script processing
*/
data class ScriptDiagnostic(
/** Diagnostic code identifier */
val code: Int,
/** Human-readable diagnostic message */
val message: String,
/** Severity level of the diagnostic */
val severity: Severity = Severity.ERROR,
/** Source file path where the diagnostic occurred */
val sourcePath: String? = null,
/** Location within the source where the diagnostic occurred */
val location: SourceCode.Location? = null,
/** Exception that caused this diagnostic, if any */
val exception: Throwable? = null
) {
/**
* Severity levels for diagnostics
*/
enum class Severity {
DEBUG,
INFO,
WARNING,
ERROR,
FATAL
}
/**
* Alternative constructor with LocationWithId
*/
constructor(
code: Int,
message: String,
severity: Severity = Severity.ERROR,
locationWithId: SourceCode.LocationWithId?,
exception: Throwable? = null
) : this(code, message, severity, locationWithId?.codeLocationId, locationWithId?.locationInText, exception)
}Utility functions for working with ResultWithDiagnostics instances.
/**
* Execute action if result is successful
*/
fun <R, T> ResultWithDiagnostics<R>.onSuccess(
action: (R) -> T
): T?
/**
* Execute action if result is a failure
*/
fun <R> ResultWithDiagnostics<R>.onFailure(
action: (List<ScriptDiagnostic>) -> Unit
): ResultWithDiagnostics<R>
/**
* Get the value if successful, null if failed
*/
fun <R> ResultWithDiagnostics<R>.valueOrNull(): R?
/**
* Get the value if successful, or default value if failed
*/
fun <R> ResultWithDiagnostics<R>.valueOr(default: R): R
/**
* Get the value if successful, or throw exception if failed
*/
fun <R> ResultWithDiagnostics<R>.valueOrThrow(): R
/**
* Transform successful result value
*/
fun <R, T> ResultWithDiagnostics<R>.map(transform: (R) -> T): ResultWithDiagnostics<T>
/**
* Transform successful result and flatten nested results
*/
fun <R, T> ResultWithDiagnostics<R>.flatMap(
transform: (R) -> ResultWithDiagnostics<T>
): ResultWithDiagnostics<T>Utility functions for working with ScriptDiagnostic instances.
/**
* Check if diagnostic represents an error
*/
fun ScriptDiagnostic.isError(): Boolean
/**
* Check if diagnostic represents a warning
*/
fun ScriptDiagnostic.isWarning(): Boolean
/**
* Check if diagnostic represents informational message
*/
fun ScriptDiagnostic.isInfo(): BooleanUtilities for working with collections of results and diagnostics.
/**
* Collector for iterating over multiple results
*/
class IterableResultsCollector<T> {
fun collect(iterable: Iterable<ResultWithDiagnostics<T>>): ResultWithDiagnostics<List<T>>
}
/**
* Transform collection if all results are successful
*/
fun <T, R> Iterable<ResultWithDiagnostics<T>>.mapSuccess(
transform: (T) -> R
): ResultWithDiagnostics<List<R>>
/**
* Transform collection and flatten nested results
*/
fun <T, R> Iterable<ResultWithDiagnostics<T>>.flatMapSuccess(
transform: (T) -> ResultWithDiagnostics<R>
): ResultWithDiagnostics<List<R>>Usage Examples:
import kotlin.script.experimental.api.*
// Creating successful results
val successResult = "Hello World".asSuccess()
val successWithWarnings = "Hello World".asSuccess(listOf(
ScriptDiagnostic(
message = "Deprecated API used",
severity = ScriptDiagnostic.Severity.WARNING
)
))
// Creating failure results
val failureResult = listOf(
ScriptDiagnostic(
code = 1001,
message = "Compilation failed: unresolved reference",
severity = ScriptDiagnostic.Severity.ERROR,
location = SourceCode.Location(
start = SourceCode.Position(line = 5, col = 10)
)
)
).asFailure()
// Handling results
when (val result = compileScript()) {
is ResultWithDiagnostics.Success -> {
val compiledScript = result.value
println("Compilation successful: ${compiledScript.sourceLocationId}")
// Handle warnings
result.reports.filter { it.isWarning() }.forEach { warning ->
println("Warning: ${warning.message}")
}
}
is ResultWithDiagnostics.Failure -> {
result.reports.forEach { diagnostic ->
val location = diagnostic.location?.let { loc ->
" at line ${loc.start.line}, column ${loc.start.col}"
} ?: ""
println("${diagnostic.severity}: ${diagnostic.message}$location")
}
}
}// Transform successful results
val originalResult: ResultWithDiagnostics<String> = "42".asSuccess()
val transformedResult: ResultWithDiagnostics<Int> = originalResult.map { it.toInt() }
// Chain operations with flatMap
val chainedResult = originalResult
.flatMap { value ->
if (value.isNotEmpty()) {
value.toIntOrNull()?.asSuccess() ?: "Invalid number".asFailure()
} else {
"Empty value".asFailure()
}
}
.map { number -> number * 2 }
// Using utility functions
val value = originalResult.valueOrNull() // "42" or null
val safeValue = originalResult.valueOr("default") // "42" or "default"
try {
val requiredValue = originalResult.valueOrThrow() // "42" or throws exception
} catch (e: Exception) {
println("Failed to get value: ${e.message}")
}// Create diagnostic reports
fun createCompilationError(message: String, location: SourceCode.Location? = null): ScriptDiagnostic {
return ScriptDiagnostic(
code = 2000,
message = message,
severity = ScriptDiagnostic.Severity.ERROR,
location = location
)
}
fun createWarning(message: String): ScriptDiagnostic {
return ScriptDiagnostic(
message = message,
severity = ScriptDiagnostic.Severity.WARNING
)
}
// Helper extensions for creating results
fun <T> T.asSuccess(reports: List<ScriptDiagnostic> = emptyList()): ResultWithDiagnostics<T> {
return ResultWithDiagnostics.Success(this, reports)
}
fun List<ScriptDiagnostic>.asFailure(): ResultWithDiagnostics<Nothing> {
return ResultWithDiagnostics.Failure(this)
}
fun String.asFailure(): ResultWithDiagnostics<Nothing> {
return listOf(ScriptDiagnostic(message = this)).asFailure()
}
// Usage
val result1 = "success".asSuccess()
val result2 = "Compilation failed".asFailure()
val result3 = listOf(
createCompilationError("Missing import"),
createWarning("Unused variable")
).asFailure()// Collect multiple results
fun processMultipleScripts(scripts: List<SourceCode>): ResultWithDiagnostics<List<CompiledScript>> {
val results = scripts.map { script ->
compileScript(script) // Returns ResultWithDiagnostics<CompiledScript>
}
// Collect all results - fails if any individual result fails
return IterableResultsCollector<CompiledScript>().collect(results)
}
// Transform with success mapping
fun getScriptNames(scripts: List<SourceCode>): ResultWithDiagnostics<List<String>> {
return scripts
.map { compileScript(it) }
.mapSuccess { compiledScript ->
compiledScript.sourceLocationId ?: "unknown"
}
}
// Chain operations with error accumulation
fun processScriptPipeline(source: SourceCode): ResultWithDiagnostics<String> {
return compileScript(source)
.flatMap { compiled -> evaluateScript(compiled) }
.flatMap { evaluated -> formatResult(evaluated) }
.map { formatted -> "Result: $formatted" }
}
// Custom error handling
class ScriptProcessingException(
message: String,
val diagnostics: List<ScriptDiagnostic>,
cause: Throwable? = null
) : Exception(message, cause)
fun handleScriptErrors(result: ResultWithDiagnostics<*>) {
result.onFailure { diagnostics ->
val errorMessages = diagnostics
.filter { it.severity >= ScriptDiagnostic.Severity.ERROR }
.map { it.message }
if (errorMessages.isNotEmpty()) {
throw ScriptProcessingException(
message = "Script processing failed: ${errorMessages.joinToString("; ")}",
diagnostics = diagnostics
)
}
}
}// Filter diagnostics by severity
fun filterErrors(diagnostics: List<ScriptDiagnostic>): List<ScriptDiagnostic> {
return diagnostics.filter { it.severity >= ScriptDiagnostic.Severity.ERROR }
}
fun filterWarnings(diagnostics: List<ScriptDiagnostic>): List<ScriptDiagnostic> {
return diagnostics.filter { it.severity == ScriptDiagnostic.Severity.WARNING }
}
// Diagnostic analysis
fun analyzeDiagnostics(diagnostics: List<ScriptDiagnostic>): DiagnosticSummary {
return DiagnosticSummary(
errorCount = diagnostics.count { it.isError() },
warningCount = diagnostics.count { it.isWarning() },
infoCount = diagnostics.count { it.isInfo() },
hasErrors = diagnostics.any { it.isError() },
mostSevere = diagnostics.maxByOrNull { it.severity.ordinal }?.severity
)
}
data class DiagnosticSummary(
val errorCount: Int,
val warningCount: Int,
val infoCount: Int,
val hasErrors: Boolean,
val mostSevere: ScriptDiagnostic.Severity?
)Install with Tessl CLI
npx tessl i tessl/maven-org-jetbrains-kotlin--kotlin-scripting-common