Add API to get locations of collected script annotations
#KT-38404 fixed also: - Add wrapper class for the location combined with the location id - Add source code location parameters to external dependency resolvers - Add tests for locations in annotations - Add tests for order of annotation resolution for dependencies resolvers
This commit is contained in:
committed by
Ilya Chernikov
parent
1539128c3f
commit
83087291df
@@ -33,6 +33,14 @@ data class ScriptDiagnostic(
|
||||
*/
|
||||
enum class Severity { FATAL, ERROR, WARNING, INFO, DEBUG }
|
||||
|
||||
constructor(
|
||||
code: Int,
|
||||
message: String,
|
||||
severity: Severity = Severity.ERROR,
|
||||
locationWithId: SourceCode.LocationWithId?,
|
||||
exception: Throwable? = null
|
||||
) : this(code, message, severity, locationWithId?.codeLocationId, locationWithId?.locationInText, exception)
|
||||
|
||||
override fun toString(): String = render()
|
||||
|
||||
/**
|
||||
@@ -211,6 +219,12 @@ fun makeFailureResult(vararg reports: ScriptDiagnostic): ResultWithDiagnostics.F
|
||||
fun makeFailureResult(message: String, path: String? = null, location: SourceCode.Location? = null): ResultWithDiagnostics.Failure =
|
||||
ResultWithDiagnostics.Failure(message.asErrorDiagnostics(ScriptDiagnostic.unspecifiedError, path, location))
|
||||
|
||||
/**
|
||||
* Makes Failure result with diagnostic [message] with optional [locationWithId]
|
||||
*/
|
||||
fun makeFailureResult(message: String, locationWithId: SourceCode.LocationWithId?): ResultWithDiagnostics.Failure =
|
||||
ResultWithDiagnostics.Failure(message.asErrorDiagnostics(ScriptDiagnostic.unspecifiedError, locationWithId))
|
||||
|
||||
/**
|
||||
* Converts the receiver Throwable to the Failure results wrapper with optional [customMessage], [path] and [location]
|
||||
*/
|
||||
@@ -223,6 +237,17 @@ fun Throwable.asDiagnostics(
|
||||
): ScriptDiagnostic =
|
||||
ScriptDiagnostic(code, customMessage ?: message ?: "$this", severity, path, location, this)
|
||||
|
||||
/**
|
||||
* Converts the receiver Throwable to the Failure results wrapper with optional [customMessage], [locationWithId]
|
||||
*/
|
||||
fun Throwable.asDiagnostics(
|
||||
code: Int = ScriptDiagnostic.unspecifiedException,
|
||||
customMessage: String? = null,
|
||||
locationWithId: SourceCode.LocationWithId?,
|
||||
severity: ScriptDiagnostic.Severity = ScriptDiagnostic.Severity.ERROR
|
||||
): ScriptDiagnostic =
|
||||
ScriptDiagnostic(code, customMessage ?: message ?: "$this", severity, locationWithId, this)
|
||||
|
||||
/**
|
||||
* Converts the receiver String to error diagnostic report with optional [path] and [location]
|
||||
*/
|
||||
@@ -233,6 +258,15 @@ fun String.asErrorDiagnostics(
|
||||
): ScriptDiagnostic =
|
||||
ScriptDiagnostic(code, this, ScriptDiagnostic.Severity.ERROR, path, location)
|
||||
|
||||
/**
|
||||
* Converts the receiver String to error diagnostic report with optional [locationWithId]
|
||||
*/
|
||||
fun String.asErrorDiagnostics(
|
||||
code: Int = ScriptDiagnostic.unspecifiedError,
|
||||
locationWithId: SourceCode.LocationWithId?
|
||||
): ScriptDiagnostic =
|
||||
ScriptDiagnostic(code, this, ScriptDiagnostic.Severity.ERROR, locationWithId)
|
||||
|
||||
/**
|
||||
* Extracts the result value from the receiver wrapper or null if receiver represents a Failure
|
||||
*/
|
||||
|
||||
@@ -51,8 +51,30 @@ interface SourceCode {
|
||||
* @param end optional range location end position (after the last char)
|
||||
*/
|
||||
data class Location(val start: Position, val end: Position? = null) : Serializable
|
||||
|
||||
/**
|
||||
* The source code location including the path to the file
|
||||
* @param codeLocationId the file path or other script location identifier (see [SourceCode.locationId])
|
||||
* @param locationInText concrete location of the source code in file
|
||||
*/
|
||||
data class LocationWithId(val codeLocationId: String, val locationInText: Location) : Serializable
|
||||
}
|
||||
|
||||
/**
|
||||
* Annotation found during script source parsing along with its location
|
||||
*/
|
||||
data class ScriptSourceAnnotation<out A : Annotation>(
|
||||
/**
|
||||
* Annotation found during script source parsing
|
||||
*/
|
||||
val annotation: A,
|
||||
|
||||
/**
|
||||
* Location of annotation is script
|
||||
*/
|
||||
val location: SourceCode.LocationWithId?
|
||||
)
|
||||
|
||||
/**
|
||||
* The interface for the source code located externally
|
||||
*/
|
||||
@@ -91,6 +113,13 @@ class ScriptCollectedData(properties: Map<PropertiesCollection.Key<*>, Any>) : P
|
||||
*/
|
||||
val ScriptCollectedDataKeys.foundAnnotations by PropertiesCollection.key<List<Annotation>>()
|
||||
|
||||
/**
|
||||
* The script file-level annotations and their locations found during script source parsing
|
||||
*/
|
||||
val ScriptCollectedDataKeys.collectedAnnotations by PropertiesCollection.key<List<ScriptSourceAnnotation<*>>>(getDefaultValue = {
|
||||
get(ScriptCollectedData.foundAnnotations)?.map { ScriptSourceAnnotation(it, null) }
|
||||
})
|
||||
|
||||
/**
|
||||
* The facade to the script data for compilation configuration refinement callbacks
|
||||
*/
|
||||
@@ -150,4 +179,4 @@ data class ScriptEvaluationConfigurationRefinementContext(
|
||||
val compiledScript: CompiledScript,
|
||||
val evaluationConfiguration: ScriptEvaluationConfiguration,
|
||||
val contextData: ScriptEvaluationContextData? = null
|
||||
)
|
||||
)
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package kotlin.script.experimental.util
|
||||
|
||||
import kotlin.script.experimental.api.*
|
||||
|
||||
inline fun <reified A : Annotation> Iterable<ScriptSourceAnnotation<*>>.filterByAnnotationType(
|
||||
): List<ScriptSourceAnnotation<A>> = filter { it.annotation is A }
|
||||
.map {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
it as ScriptSourceAnnotation<A>
|
||||
}
|
||||
@@ -23,6 +23,7 @@ dependencies {
|
||||
testCompile(projectTests(":kotlin-scripting-dependencies"))
|
||||
testCompile(commonDep("junit"))
|
||||
testRuntimeOnly("org.slf4j:slf4j-nop:1.7.30")
|
||||
testImplementation(kotlin("reflect"))
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
|
||||
+14
-5
@@ -13,6 +13,8 @@ import org.eclipse.aether.util.repository.AuthenticationBuilder
|
||||
import java.io.File
|
||||
import java.util.*
|
||||
import kotlin.script.experimental.api.ResultWithDiagnostics
|
||||
import kotlin.script.experimental.api.SourceCode
|
||||
import kotlin.script.experimental.api.asSuccess
|
||||
import kotlin.script.experimental.dependencies.ExternalDependenciesResolver
|
||||
import kotlin.script.experimental.dependencies.RepositoryCoordinates
|
||||
import kotlin.script.experimental.dependencies.impl.makeResolveFailureResult
|
||||
@@ -51,7 +53,10 @@ class MavenDependenciesResolver : ExternalDependenciesResolver {
|
||||
if (this.isNotBlank() && this.count { it == ':' } >= 2) DefaultArtifact(this)
|
||||
else null
|
||||
|
||||
override suspend fun resolve(artifactCoordinates: String): ResultWithDiagnostics<List<File>> {
|
||||
override suspend fun resolve(
|
||||
artifactCoordinates: String,
|
||||
sourceCodeLocation: SourceCode.LocationWithId?
|
||||
): ResultWithDiagnostics<List<File>> {
|
||||
|
||||
val artifactId = artifactCoordinates.toMavenArtifact()!!
|
||||
|
||||
@@ -60,18 +65,21 @@ class MavenDependenciesResolver : ExternalDependenciesResolver {
|
||||
if (deps != null)
|
||||
return ResultWithDiagnostics.Success(deps.map { it.file })
|
||||
} catch (e: DependencyResolutionException) {
|
||||
return makeResolveFailureResult(e.message ?: "unknown error")
|
||||
return makeResolveFailureResult(e.message ?: "unknown error", sourceCodeLocation)
|
||||
}
|
||||
return makeResolveFailureResult(allRepositories().map { "$it: $artifactId not found" })
|
||||
return makeResolveFailureResult(allRepositories().map { "$it: $artifactId not found" }, sourceCodeLocation)
|
||||
}
|
||||
|
||||
private fun tryResolveEnvironmentVariable(str: String) =
|
||||
if (str.startsWith("$")) System.getenv(str.substring(1)) ?: str
|
||||
else str
|
||||
|
||||
override fun addRepository(repositoryCoordinates: RepositoryCoordinates) {
|
||||
override fun addRepository(
|
||||
repositoryCoordinates: RepositoryCoordinates,
|
||||
sourceCodeLocation: SourceCode.LocationWithId?
|
||||
): ResultWithDiagnostics<Boolean> {
|
||||
val url = repositoryCoordinates.toRepositoryUrlOrNull()
|
||||
?: throw IllegalArgumentException("Invalid Maven repository URL: ${repositoryCoordinates}")
|
||||
?: return false.asSuccess()
|
||||
val repo = RemoteRepository.Builder(
|
||||
repositoryCoordinates.string,
|
||||
"default",
|
||||
@@ -91,5 +99,6 @@ class MavenDependenciesResolver : ExternalDependenciesResolver {
|
||||
}
|
||||
}
|
||||
repos.add(repo.build())
|
||||
return true.asSuccess()
|
||||
}
|
||||
}
|
||||
+53
-1
@@ -7,16 +7,21 @@ package kotlin.script.experimental.test
|
||||
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.junit.Assert
|
||||
import org.junit.Ignore
|
||||
import java.io.File
|
||||
import kotlin.contracts.ExperimentalContracts
|
||||
import kotlin.reflect.full.primaryConstructor
|
||||
import kotlin.script.experimental.dependencies.maven.MavenDependenciesResolver
|
||||
import kotlin.script.experimental.api.ResultWithDiagnostics
|
||||
import kotlin.script.experimental.api.valueOrThrow
|
||||
import kotlin.script.experimental.dependencies.DependsOn
|
||||
import kotlin.script.experimental.dependencies.Repository
|
||||
import kotlin.script.experimental.dependencies.resolveFromAnnotations
|
||||
|
||||
@ExperimentalContracts
|
||||
class MavenResolverTest : ResolversTestBase() {
|
||||
|
||||
fun resolveAndCheck(coordinates: String, checkBody: (Iterable<File>) -> Boolean = { true } ) {
|
||||
fun resolveAndCheck(coordinates: String, checkBody: (Iterable<File>) -> Boolean = { true }) {
|
||||
val resolver = MavenDependenciesResolver()
|
||||
val result = runBlocking { resolver.resolve(coordinates) }
|
||||
if (result is ResultWithDiagnostics.Failure) {
|
||||
@@ -44,4 +49,51 @@ class MavenResolverTest : ResolversTestBase() {
|
||||
files.any { it.extension == "pom" }
|
||||
}
|
||||
}
|
||||
|
||||
// Ignored - tests with custom repos often break the CI due to the caching issues
|
||||
// TODO: find a way to enable iut back
|
||||
@Ignore
|
||||
fun ignore_testResolveFromAnnotationsWillResolveTheSameRegardlessOfAnnotationOrder() {
|
||||
val dependsOnConstructor = DependsOn::class.primaryConstructor!!
|
||||
val repositoryConstructor = Repository::class.primaryConstructor!!
|
||||
|
||||
// @DepensOn("eu.jrie.jetbrains:kotlin-shell-core:0.2")
|
||||
val dependsOn = dependsOnConstructor.callBy(
|
||||
mapOf(
|
||||
dependsOnConstructor.parameters.first() to arrayOf("eu.jrie.jetbrains:kotlin-shell-core:0.2")
|
||||
)
|
||||
)
|
||||
|
||||
// @Repository("https://dl.bintray.com/kotlin/kotlin-eap", "https://dl.bintray.com/jakubriegel/kotlin-shell")
|
||||
val repositories = repositoryConstructor.callBy(
|
||||
mapOf(
|
||||
repositoryConstructor.parameters.first() to arrayOf(
|
||||
"https://dl.bintray.com/kotlin/kotlin-eap",
|
||||
"https://dl.bintray.com/jakubriegel/kotlin-shell"
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
val annotationsWithReposFirst = listOf(repositories, dependsOn)
|
||||
val annotationsWithDependsOnFirst = listOf(dependsOn, repositories)
|
||||
|
||||
val filesWithReposFirst = runBlocking {
|
||||
MavenDependenciesResolver().resolveFromAnnotations(annotationsWithReposFirst)
|
||||
}.valueOrThrow()
|
||||
|
||||
val filesWithDependsOnFirst = runBlocking {
|
||||
MavenDependenciesResolver().resolveFromAnnotations(annotationsWithDependsOnFirst)
|
||||
}.valueOrThrow()
|
||||
|
||||
// Tests that the jar was resolved
|
||||
assert(
|
||||
filesWithReposFirst.any { it.name.startsWith("kotlin-shell-core-") && it.extension == "jar" }
|
||||
)
|
||||
assert(
|
||||
filesWithDependsOnFirst.any { it.name.startsWith("kotlin-shell-core-") && it.extension == "jar" }
|
||||
)
|
||||
|
||||
// Test that the the same files are resolved regardless of annotation order
|
||||
assertEquals(filesWithReposFirst.map { it.name }.sorted(), filesWithDependsOnFirst.map { it.name }.sorted())
|
||||
}
|
||||
}
|
||||
|
||||
+37
-7
@@ -8,6 +8,8 @@ package kotlin.script.experimental.dependencies
|
||||
import java.io.File
|
||||
import kotlin.script.experimental.api.ResultWithDiagnostics
|
||||
import kotlin.script.experimental.api.ScriptDiagnostic
|
||||
import kotlin.script.experimental.api.SourceCode
|
||||
import kotlin.script.experimental.api.asSuccess
|
||||
import kotlin.script.experimental.dependencies.impl.makeResolveFailureResult
|
||||
|
||||
class CompoundDependenciesResolver(private val resolvers: List<ExternalDependenciesResolver>) : ExternalDependenciesResolver {
|
||||
@@ -22,25 +24,53 @@ class CompoundDependenciesResolver(private val resolvers: List<ExternalDependenc
|
||||
return resolvers.any { it.acceptsRepository(repositoryCoordinates) }
|
||||
}
|
||||
|
||||
override fun addRepository(repositoryCoordinates: RepositoryCoordinates) {
|
||||
if (resolvers.count { it.tryAddRepository(repositoryCoordinates) } == 0)
|
||||
throw Exception("Failed to detect repository type: $repositoryCoordinates")
|
||||
override fun addRepository(
|
||||
repositoryCoordinates: RepositoryCoordinates,
|
||||
sourceCodeLocation: SourceCode.LocationWithId?
|
||||
): ResultWithDiagnostics<Boolean> {
|
||||
var success = false
|
||||
var repositoryAdded = false
|
||||
val reports = mutableListOf<ScriptDiagnostic>()
|
||||
|
||||
for (resolver in resolvers) {
|
||||
if (resolver.acceptsRepository(repositoryCoordinates)) {
|
||||
when (val resolveResult = resolver.addRepository(repositoryCoordinates, sourceCodeLocation)) {
|
||||
is ResultWithDiagnostics.Success -> {
|
||||
success = true
|
||||
repositoryAdded = repositoryAdded || resolveResult.value
|
||||
reports.addAll(resolveResult.reports)
|
||||
}
|
||||
is ResultWithDiagnostics.Failure -> reports.addAll(resolveResult.reports)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return when {
|
||||
success -> repositoryAdded.asSuccess(reports)
|
||||
reports.isEmpty() -> makeResolveFailureResult(
|
||||
"No dependency resolver found that recognizes the repository coordinates '$repositoryCoordinates'",
|
||||
sourceCodeLocation
|
||||
)
|
||||
else -> ResultWithDiagnostics.Failure(reports)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun resolve(artifactCoordinates: String): ResultWithDiagnostics<List<File>> {
|
||||
|
||||
override suspend fun resolve(
|
||||
artifactCoordinates: String,
|
||||
sourceCodeLocation: SourceCode.LocationWithId?
|
||||
): ResultWithDiagnostics<List<File>> {
|
||||
val reports = mutableListOf<ScriptDiagnostic>()
|
||||
|
||||
for (resolver in resolvers) {
|
||||
if (resolver.acceptsArtifact(artifactCoordinates)) {
|
||||
when (val resolveResult = resolver.resolve(artifactCoordinates)) {
|
||||
when (val resolveResult = resolver.resolve(artifactCoordinates, sourceCodeLocation)) {
|
||||
is ResultWithDiagnostics.Failure -> reports.addAll(resolveResult.reports)
|
||||
else -> return resolveResult
|
||||
}
|
||||
}
|
||||
}
|
||||
return if (reports.count() == 0) {
|
||||
makeResolveFailureResult("No suitable dependency resolver found for artifact '$artifactCoordinates'")
|
||||
makeResolveFailureResult("No suitable dependency resolver found for artifact '$artifactCoordinates'", sourceCodeLocation)
|
||||
} else {
|
||||
ResultWithDiagnostics.Failure(reports)
|
||||
}
|
||||
|
||||
+14
-15
@@ -7,6 +7,7 @@ package kotlin.script.experimental.dependencies
|
||||
|
||||
import java.io.File
|
||||
import kotlin.script.experimental.api.ResultWithDiagnostics
|
||||
import kotlin.script.experimental.api.SourceCode
|
||||
import kotlin.script.experimental.api.valueOrNull
|
||||
|
||||
open class RepositoryCoordinates(val string: String)
|
||||
@@ -16,23 +17,21 @@ interface ExternalDependenciesResolver {
|
||||
fun acceptsRepository(repositoryCoordinates: RepositoryCoordinates): Boolean
|
||||
fun acceptsArtifact(artifactCoordinates: String): Boolean
|
||||
|
||||
suspend fun resolve(artifactCoordinates: String): ResultWithDiagnostics<List<File>>
|
||||
fun addRepository(repositoryCoordinates: RepositoryCoordinates)
|
||||
suspend fun resolve(
|
||||
artifactCoordinates: String,
|
||||
sourceCodeLocation: SourceCode.LocationWithId? = null
|
||||
): ResultWithDiagnostics<List<File>>
|
||||
|
||||
fun addRepository(
|
||||
repositoryCoordinates: RepositoryCoordinates,
|
||||
sourceCodeLocation: SourceCode.LocationWithId? = null
|
||||
): ResultWithDiagnostics<Boolean>
|
||||
}
|
||||
|
||||
fun ExternalDependenciesResolver.acceptsRepository(repositoryCoordinates: String): Boolean =
|
||||
acceptsRepository(RepositoryCoordinates(repositoryCoordinates))
|
||||
|
||||
fun ExternalDependenciesResolver.addRepository(repositoryCoordinates: String) = addRepository(RepositoryCoordinates(repositoryCoordinates))
|
||||
|
||||
suspend fun ExternalDependenciesResolver.tryResolve(artifactCoordinates: String): List<File>? =
|
||||
if (acceptsArtifact(artifactCoordinates)) resolve(artifactCoordinates).valueOrNull() else null
|
||||
|
||||
fun ExternalDependenciesResolver.tryAddRepository(repositoryCoordinates: String) =
|
||||
tryAddRepository(RepositoryCoordinates(repositoryCoordinates))
|
||||
|
||||
fun ExternalDependenciesResolver.tryAddRepository(repositoryCoordinates: RepositoryCoordinates) =
|
||||
if (acceptsRepository(repositoryCoordinates)) {
|
||||
addRepository(repositoryCoordinates)
|
||||
true
|
||||
} else false
|
||||
fun ExternalDependenciesResolver.addRepository(
|
||||
repositoryCoordinates: String,
|
||||
sourceCodeLocation: SourceCode.LocationWithId? = null
|
||||
) = addRepository(RepositoryCoordinates(repositoryCoordinates), sourceCodeLocation)
|
||||
+17
-4
@@ -7,6 +7,8 @@ package kotlin.script.experimental.dependencies
|
||||
|
||||
import java.io.File
|
||||
import kotlin.script.experimental.api.ResultWithDiagnostics
|
||||
import kotlin.script.experimental.api.SourceCode
|
||||
import kotlin.script.experimental.api.asSuccess
|
||||
import kotlin.script.experimental.dependencies.impl.makeResolveFailureResult
|
||||
import kotlin.script.experimental.dependencies.impl.toRepositoryUrlOrNull
|
||||
|
||||
@@ -18,13 +20,24 @@ class FileSystemDependenciesResolver(vararg paths: File) : ExternalDependenciesR
|
||||
private fun RepositoryCoordinates.toFilePath() =
|
||||
(this.toRepositoryUrlOrNull()?.takeIf { it.protocol == "file" }?.path ?: string).toRepositoryFileOrNull()
|
||||
|
||||
override fun addRepository(repositoryCoordinates: RepositoryCoordinates) {
|
||||
override fun addRepository(
|
||||
repositoryCoordinates: RepositoryCoordinates,
|
||||
sourceCodeLocation: SourceCode.LocationWithId?
|
||||
): ResultWithDiagnostics<Boolean> {
|
||||
if (!acceptsRepository(repositoryCoordinates)) return false.asSuccess()
|
||||
|
||||
val repoDir = repositoryCoordinates.toFilePath()
|
||||
?: throw IllegalArgumentException("Invalid repository location: '${repositoryCoordinates}'")
|
||||
?: return makeResolveFailureResult("Invalid repository location: '${repositoryCoordinates}'", sourceCodeLocation)
|
||||
|
||||
localRepos.add(repoDir)
|
||||
|
||||
return true.asSuccess()
|
||||
}
|
||||
|
||||
override suspend fun resolve(artifactCoordinates: String): ResultWithDiagnostics<List<File>> {
|
||||
override suspend fun resolve(
|
||||
artifactCoordinates: String,
|
||||
sourceCodeLocation: SourceCode.LocationWithId?
|
||||
): ResultWithDiagnostics<List<File>> {
|
||||
if (!acceptsArtifact(artifactCoordinates)) throw IllegalArgumentException("Path is invalid")
|
||||
|
||||
val messages = mutableListOf<String>()
|
||||
@@ -38,7 +51,7 @@ class FileSystemDependenciesResolver(vararg paths: File) : ExternalDependenciesR
|
||||
else -> return ResultWithDiagnostics.Success(listOf(file))
|
||||
}
|
||||
}
|
||||
return makeResolveFailureResult(messages)
|
||||
return makeResolveFailureResult(messages, sourceCodeLocation)
|
||||
}
|
||||
|
||||
override fun acceptsArtifact(artifactCoordinates: String) =
|
||||
|
||||
+33
-12
@@ -6,9 +6,8 @@
|
||||
package kotlin.script.experimental.dependencies
|
||||
|
||||
import java.io.File
|
||||
import kotlin.script.experimental.api.ResultWithDiagnostics
|
||||
import kotlin.script.experimental.api.flatMapSuccess
|
||||
import kotlin.script.experimental.api.makeFailureResult
|
||||
import kotlin.script.experimental.api.*
|
||||
import kotlin.script.experimental.util.filterByAnnotationType
|
||||
|
||||
/**
|
||||
* A common annotation that could be used in a script to denote a dependency
|
||||
@@ -33,22 +32,44 @@ annotation class Repository(vararg val repositoriesCoordinates: String)
|
||||
/**
|
||||
* An extension function that configures repositories and resolves artifacts denoted by the [Repository] and [DependsOn] annotations
|
||||
*/
|
||||
suspend fun ExternalDependenciesResolver.resolveFromAnnotations(annotations: Iterable<Annotation>): ResultWithDiagnostics<List<File>> {
|
||||
annotations.forEach { annotation ->
|
||||
suspend fun ExternalDependenciesResolver.resolveFromScriptSourceAnnotations(
|
||||
annotations: Iterable<ScriptSourceAnnotation<*>>
|
||||
): ResultWithDiagnostics<List<File>> {
|
||||
val reports = mutableListOf<ScriptDiagnostic>()
|
||||
annotations.forEach { (annotation, locationWithId) ->
|
||||
when (annotation) {
|
||||
is Repository -> {
|
||||
for (coordinates in annotation.repositoriesCoordinates) {
|
||||
if (!tryAddRepository(coordinates))
|
||||
return makeFailureResult("Unrecognized repository coordinates: $coordinates")
|
||||
val added = addRepository(coordinates, locationWithId)
|
||||
.also { reports.addAll(it.reports) }
|
||||
.valueOr { return it }
|
||||
|
||||
if (!added)
|
||||
return reports + makeFailureResult(
|
||||
"Unrecognized repository coordinates: $coordinates",
|
||||
locationWithId = locationWithId
|
||||
)
|
||||
}
|
||||
}
|
||||
is DependsOn -> {}
|
||||
else -> return makeFailureResult("Unknown annotation ${annotation.javaClass}")
|
||||
else -> return reports + makeFailureResult("Unknown annotation ${annotation.javaClass}", locationWithId = locationWithId)
|
||||
}
|
||||
}
|
||||
return annotations.filterIsInstance(DependsOn::class.java)
|
||||
.flatMap { it.artifactsCoordinates.asIterable() }
|
||||
.flatMapSuccess { artifactCoordinates ->
|
||||
resolve(artifactCoordinates)
|
||||
|
||||
return reports + annotations.filterByAnnotationType<DependsOn>()
|
||||
.flatMapSuccess { (annotation, locationWithId) ->
|
||||
annotation.artifactsCoordinates.asIterable().flatMapSuccess { artifactCoordinates ->
|
||||
resolve(artifactCoordinates, locationWithId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An extension function that configures repositories and resolves artifacts denoted by the [Repository] and [DependsOn] annotations
|
||||
*/
|
||||
suspend fun ExternalDependenciesResolver.resolveFromAnnotations(
|
||||
annotations: Iterable<Annotation>
|
||||
): ResultWithDiagnostics<List<File>> {
|
||||
val scriptSourceAnnotations = annotations.map { ScriptSourceAnnotation(it, null) }
|
||||
return resolveFromScriptSourceAnnotations(scriptSourceAnnotations)
|
||||
}
|
||||
+8
-3
@@ -10,11 +10,16 @@ import java.net.URL
|
||||
import kotlin.script.experimental.dependencies.RepositoryCoordinates
|
||||
import kotlin.script.experimental.api.ResultWithDiagnostics
|
||||
import kotlin.script.experimental.api.ScriptDiagnostic
|
||||
import kotlin.script.experimental.api.SourceCode
|
||||
|
||||
fun makeResolveFailureResult(message: String) = makeResolveFailureResult(listOf(message))
|
||||
fun makeResolveFailureResult(message: String, location: SourceCode.LocationWithId? = null) = makeResolveFailureResult(listOf(message), location)
|
||||
|
||||
fun makeResolveFailureResult(messages: Iterable<String>) =
|
||||
ResultWithDiagnostics.Failure(messages.map { ScriptDiagnostic(ScriptDiagnostic.unspecifiedError, it, ScriptDiagnostic.Severity.WARNING) })
|
||||
fun makeResolveFailureResult(messages: Iterable<String>, location: SourceCode.LocationWithId? = null) =
|
||||
ResultWithDiagnostics.Failure(
|
||||
messages.map {
|
||||
ScriptDiagnostic(ScriptDiagnostic.unspecifiedError, it, ScriptDiagnostic.Severity.WARNING, location)
|
||||
}
|
||||
)
|
||||
|
||||
fun RepositoryCoordinates.toRepositoryUrlOrNull(): URL? =
|
||||
try {
|
||||
|
||||
+14
-3
@@ -9,6 +9,8 @@ import java.io.File
|
||||
import kotlin.contracts.ExperimentalContracts
|
||||
import kotlin.script.experimental.dependencies.*
|
||||
import kotlin.script.experimental.api.ResultWithDiagnostics
|
||||
import kotlin.script.experimental.api.SourceCode
|
||||
import kotlin.script.experimental.api.asSuccess
|
||||
import kotlin.script.experimental.dependencies.impl.makeResolveFailureResult
|
||||
|
||||
@ExperimentalContracts
|
||||
@@ -83,14 +85,23 @@ class ResolversTest : ResolversTestBase() {
|
||||
|
||||
override fun acceptsArtifact(artifactCoordinates: String): Boolean = acceptsArt(artifactCoordinates)
|
||||
|
||||
override suspend fun resolve(artifactCoordinates: String): ResultWithDiagnostics<List<File>> {
|
||||
override suspend fun resolve(
|
||||
artifactCoordinates: String,
|
||||
sourceCodeLocation: SourceCode.LocationWithId?
|
||||
): ResultWithDiagnostics<List<File>> {
|
||||
if (!acceptsArtifact(artifactCoordinates)) throw Exception("Path is invalid")
|
||||
val file = doResolve(artifactCoordinates) ?: return makeResolveFailureResult("Failed to resolve '$artifactCoordinates'")
|
||||
val file = doResolve(artifactCoordinates)
|
||||
?: return makeResolveFailureResult("Failed to resolve '$artifactCoordinates'", sourceCodeLocation)
|
||||
return ResultWithDiagnostics.Success(listOf(file))
|
||||
}
|
||||
|
||||
override fun addRepository(repositoryCoordinates: RepositoryCoordinates) {
|
||||
override fun addRepository(
|
||||
repositoryCoordinates: RepositoryCoordinates,
|
||||
sourceCodeLocation: SourceCode.LocationWithId?
|
||||
): ResultWithDiagnostics<Boolean> {
|
||||
if (!acceptsRepository(repositoryCoordinates)) return false.asSuccess()
|
||||
addRepo(repositoryCoordinates.string)
|
||||
return true.asSuccess()
|
||||
}
|
||||
|
||||
override fun acceptsRepository(repositoryCoordinates: RepositoryCoordinates): Boolean {
|
||||
|
||||
Reference in New Issue
Block a user