Keep resolution error infos in ad hoc ADT

This commit is contained in:
Alexandre Archambault 2016-05-31 13:43:27 +02:00
parent 201ef286db
commit e688828f7a
No known key found for this signature in database
GPG Key ID: 14640A6839C263A9
3 changed files with 92 additions and 39 deletions

View File

@ -0,0 +1,74 @@
package coursier
sealed abstract class ResolutionError extends Product with Serializable {
def cause: Option[Throwable] = this match {
case ResolutionError.MaximumIterationsReached => None
case ResolutionError.UnknownException(ex) => Some(ex)
case ResolutionError.UnknownDownloadException(ex) => Some(ex)
case _: ResolutionError.Conflicts => None
case _: ResolutionError.MetadataDownloadErrors => None
case _: ResolutionError.DownloadErrors => None
}
def message: String = this match {
case ResolutionError.MaximumIterationsReached =>
"Maximum number of iteration of dependency resolution reached"
case ResolutionError.UnknownException(ex) =>
"Exception during resolution"
case ResolutionError.UnknownDownloadException(ex) =>
"Error while downloading / verifying artifacts"
case ResolutionError.Conflicts(description) =>
description
case ResolutionError.MetadataDownloadErrors(errors) =>
s"Encountered ${errors.length} error(s) in dependency resolution:\n" +
errors.map {
case (dep, errs) =>
s" ${dep.module}:${dep.version}:\n" +
errs
.map(" " + _.replace("\n", " \n"))
.mkString("\n")
}.mkString("\n")
case err: ResolutionError.DownloadErrors =>
err.description(verbose = true)
}
def exception(): ResolutionException =
new ResolutionException(this)
def throwException(): Nothing =
throw exception()
}
object ResolutionError {
case object MaximumIterationsReached extends ResolutionError
case class UnknownException(ex: Throwable) extends ResolutionError
case class UnknownDownloadException(ex: Throwable) extends ResolutionError
case class Conflicts(description: String) extends ResolutionError
case class MetadataDownloadErrors(errors: Seq[(Dependency, Seq[String])]) extends ResolutionError
case class DownloadErrors(errors: Seq[FileError]) extends ResolutionError {
def description(verbose: Boolean): String = {
val groupedArtifactErrors = errors
.groupBy(_.`type`)
.mapValues(_.map(_.message).sorted)
.toVector
.sortBy(_._1)
val b = new StringBuilder
for ((type0, errors) <- groupedArtifactErrors) {
b ++= s"${errors.size} $type0"
if (verbose)
for (err <- errors)
b ++= " " + err
}
b.result()
}
}
}

View File

@ -1,11 +1,10 @@
package coursier
final class ResolutionException(
val message: String,
val cause: Throwable = null
val error: ResolutionError
) extends Exception(
message,
cause,
error.message,
error.cause.orNull,
true,
false // don't keep stack trace around (improves readability from the SBT console)
)

View File

@ -506,35 +506,31 @@ object Tasks {
.process
.run(fetch, maxIterations)
.attemptRun
.leftMap(ex => throw new ResolutionException("Exception during resolution", ex))
.leftMap(ex =>
ResolutionError.UnknownException(ex)
.throwException()
)
.merge
resLogger.stop()
if (!res.isDone)
throw new ResolutionException("Maximum number of iteration of dependency resolution reached")
ResolutionError.MaximumIterationsReached
.throwException()
if (res.conflicts.nonEmpty) {
val projCache = res.projectCache.mapValues { case (_, p) => p }
throw new ResolutionException(
ResolutionError.Conflicts(
"Conflict(s) in dependency resolution:\n " +
Print.dependenciesUnknownConfigs(res.conflicts.toVector, projCache)
)
).throwException()
}
if (res.errors.nonEmpty)
throw new ResolutionException(
s"Encountered ${res.errors.length} error(s) in dependency resolution:\n" +
res.errors.map {
case (dep, errs) =>
s" ${dep.module}:${dep.version}:\n" +
errs
.map(" " + _.replace("\n", " \n"))
.mkString("\n")
}.mkString("\n")
)
ResolutionError.MetadataDownloadErrors(res.errors)
.throwException()
if (verbosityLevel >= 0)
log.info(s"Resolved ${projectDescription(currentProject)} dependencies")
@ -703,7 +699,8 @@ object Tasks {
val artifactFilesOrErrors = Task.gatherUnordered(artifactFileOrErrorTasks).attemptRun match {
case -\/(ex) =>
throw new ResolutionException("Error while downloading / verifying artifacts", ex)
ResolutionError.UnknownDownloadException(ex)
.throwException()
case \/-(l) =>
l.toMap
}
@ -727,29 +724,12 @@ object Tasks {
}
if (artifactErrors.nonEmpty) {
val groupedArtifactErrors = artifactErrors
.groupBy(_.`type`)
.mapValues(_.map(_.message).sorted)
.toVector
.sortBy(_._1)
val b = new StringBuilder
for ((type0, errors) <- groupedArtifactErrors) {
def msg = s"${errors.size} $type0"
b ++= msg
if (!ignoreArtifactErrors || verbosityLevel >= 1)
for (err <- errors)
b ++= " " + err
}
val error = ResolutionError.DownloadErrors(artifactErrors)
if (ignoreArtifactErrors)
log.warn(b.result())
log.warn(error.description(verbosityLevel >= 1))
else
throw new ResolutionException(
s"Encountered ${artifactErrors.length} errors:\n" + b.result()
)
error.throwException()
}
// can be non empty only if ignoreArtifactErrors is true