diff --git a/README.md b/README.md index 58b84a627..5a181f670 100644 --- a/README.md +++ b/README.md @@ -131,6 +131,8 @@ libraryDependencies ++= Seq( ) ``` +Note that the examples below are validated against the current sources of coursier. You may want to read the [documentation of the latest release](https://github.com/coursier/coursier/blob/v1.0.2/README.md#api) of coursier instead. + Add an import for coursier, ```scala import coursier._ @@ -181,10 +183,9 @@ These would mean that the resolution wasn't able to get metadata about some depe Then fetch and get local copies of the artifacts themselves (the JARs) with ```scala import java.io.File -import scalaz.\/ import scalaz.concurrent.Task -val localArtifacts: Seq[FileError \/ File] = Task.gatherUnordered( +val localArtifacts: Seq[Either[FileError, File]] = Task.gatherUnordered( resolution.artifacts.map(Cache.file(_).run) ).unsafePerformSync ``` @@ -422,6 +423,8 @@ libraryDependencies ++= Seq( ) ``` +Note that the examples below are validated against the current sources of coursier. You may want to read the [documentation of the latest release](https://github.com/coursier/coursier/blob/v1.0.2/README.md#api-1) of coursier instead. + The first module, `"io.get-coursier" %% "coursier" % "1.0.1"`, mainly depends on `scalaz-core` (and only it, *not* `scalaz-concurrent` for example). It contains among others, definitions, @@ -509,7 +512,7 @@ res6: coursier.maven.MavenRepository = MavenRepository(https://nexus.corp.com/co Now that we have repositories, we're going to mix these with things from the `coursier-cache` module, for resolution to happen via the cache. We'll create a function -of type `Seq[(Module, String)] => F[Seq[((Module, String), Seq[String] \/ (Artifact.Source, Project))]]`. +of type `Seq[(Module, String)] => F[Seq[((Module, String), Either[Seq[String], (Artifact.Source, Project)])]]`. Given a sequence of dependencies, designated by their `Module` (organisation and name in most cases) and version (just a `String`), it gives either errors (`Seq[String]`) or metadata (`(Artifact.Source, Project)`), wrapping the whole in a monad `F`. @@ -564,10 +567,9 @@ which are dependencies whose versions could not be unified. Then, if all went well, we can fetch and get local copies of the artifacts themselves (the JARs) with ```scala import java.io.File -import scalaz.\/ import scalaz.concurrent.Task -val localArtifacts: Seq[FileError \/ File] = Task.gatherUnordered( +val localArtifacts: Seq[Either[FileError, File]] = Task.gatherUnordered( resolution.artifacts.map(Cache.file(_).run) ).unsafePerformSync ``` diff --git a/cache/src/main/scala/coursier/Cache.scala b/cache/src/main/scala/coursier/Cache.scala index 13859d102..ea7dd3227 100644 --- a/cache/src/main/scala/coursier/Cache.scala +++ b/cache/src/main/scala/coursier/Cache.scala @@ -1,10 +1,10 @@ package coursier import java.math.BigInteger -import java.net.{ HttpURLConnection, URL, URLConnection, URLStreamHandler, URLStreamHandlerFactory } -import java.nio.channels.{ OverlappingFileLockException, FileLock } +import java.net.{HttpURLConnection, URL, URLConnection, URLStreamHandler, URLStreamHandlerFactory} +import java.nio.channels.{FileLock, OverlappingFileLockException} import java.security.MessageDigest -import java.util.concurrent.{ Callable, ConcurrentHashMap, Executors, ExecutorService } +import java.util.concurrent.{Callable, ConcurrentHashMap, ExecutorService, Executors} import java.util.regex.Pattern import coursier.core.Authentication @@ -13,15 +13,14 @@ import coursier.internal.FileUtil import coursier.util.Base64.Encoder import scala.annotation.tailrec - -import scalaz._ -import scalaz.Scalaz.ToEitherOps -import scalaz.concurrent.{ Task, Strategy } - -import java.io.{ Serializable => _, _ } +import scalaz.Nondeterminism +import scalaz.concurrent.{Strategy, Task} +import java.io.{Serializable => _, _} import java.nio.charset.Charset -import scala.concurrent.duration.{ Duration, DurationInt } +import coursier.util.EitherT + +import scala.concurrent.duration.{Duration, DurationInt} import scala.util.Try import scala.util.control.NonFatal @@ -83,9 +82,9 @@ object Cache { cache: File, file: File )( - f: => FileError \/ T, - ifLocked: => Option[FileError \/ T] - ): FileError \/ T = { + f: => Either[FileError, T], + ifLocked: => Option[Either[FileError, T]] + ): Either[FileError, T] = { val lockFile = CachePath.lockFile(file) @@ -97,7 +96,7 @@ object Cache { } @tailrec - def loop(): FileError \/ T = { + def loop(): Either[FileError, T] = { val resOpt = { var lock: FileLock = null @@ -133,8 +132,8 @@ object Cache { finally if (out != null) out.close() } - def withLockFor[T](cache: File, file: File)(f: => FileError \/ T): FileError \/ T = - withLockOr(cache, file)(f, Some(-\/(FileError.Locked(file)))) + def withLockFor[T](cache: File, file: File)(f: => Either[FileError, T]): Either[FileError, T] = + withLockOr(cache, file)(f, Some(Left(FileError.Locked(file)))) private def defaultRetryCount = 3 @@ -152,11 +151,11 @@ object Cache { logger: Option[Logger], retry: Int = retryCount )( - f: => FileError \/ T - ): FileError \/ T = { + f: => Either[FileError, T] + ): Either[FileError, T] = { @tailrec - def helper(retry: Int): FileError \/ T = { + def helper(retry: Int): Either[FileError, T] = { val resOpt = try { @@ -164,20 +163,17 @@ object Cache { val prev = urlLocks.putIfAbsent(url, o) val res = - if (prev == null) { - val res = - try \/-(f) - catch { - case nfe: FileNotFoundException if nfe.getMessage != null => - -\/(-\/(FileError.NotFound(nfe.getMessage))) - } - finally { - urlLocks.remove(url) - } - - res.merge[FileError \/ T] - } else - -\/(FileError.ConcurrentDownload(url)) + if (prev == null) + try f + catch { + case nfe: FileNotFoundException if nfe.getMessage != null => + Left(FileError.NotFound(nfe.getMessage)) + } + finally { + urlLocks.remove(url) + } + else + Left(FileError.ConcurrentDownload(url)) Some(res) } @@ -186,7 +182,7 @@ object Cache { // TODO If Cache is made an (instantiated) class at some point, allow to log that exception. None case NonFatal(e) => - Some(-\/( + Some(Left( FileError.DownloadError( s"Caught $e${Option(e.getMessage).fold("")(" (" + _ + ")")} while downloading $url" ) @@ -325,7 +321,7 @@ object Cache { url: String, authentication: Option[Authentication], logger0: Option[Logger] - ): FileError \/ Option[Long] = { + ): Either[FileError, Option[Long]] = { val logger = logger0.map(Logger.Extended(_)) @@ -350,14 +346,14 @@ object Cache { success = true logger.foreach(_.gettingLengthResult(url, len)) - len.right + Right(len) } finally { if (!success) logger.foreach(_.gettingLengthResult(url, None)) } case other => - -\/(FileError.DownloadError(s"Cannot do HEAD request with connection $other ($url)")) + Left(FileError.DownloadError(s"Cannot do HEAD request with connection $other ($url)")) } } finally { if (conn != null) @@ -373,7 +369,7 @@ object Cache { pool: ExecutorService, logger0: Option[Logger] = None, ttl: Option[Duration] = defaultTtl - ): Task[Seq[((File, String), FileError \/ Unit)]] = { + ): Task[Seq[((File, String), Either[FileError, Unit])]] = { implicit val pool0 = pool @@ -391,13 +387,13 @@ object Cache { def fileLastModified(file: File): EitherT[Task, FileError, Option[Long]] = EitherT { Task { - \/- { + Right { val lastModified = file.lastModified() if (lastModified > 0L) Some(lastModified) else None - } : FileError \/ Option[Long] + } : Either[FileError, Option[Long]] } } @@ -433,14 +429,14 @@ object Cache { success = true logger.foreach(_.checkingUpdatesResult(url, currentLastModifiedOpt, res)) - res.right + Right(res) } finally { if (!success) logger.foreach(_.checkingUpdatesResult(url, currentLastModifiedOpt, None)) } case other => - -\/(FileError.DownloadError(s"Cannot do HEAD request with connection $other ($url)")) + Left(FileError.DownloadError(s"Cannot do HEAD request with connection $other ($url)")) } } finally { if (conn != null) @@ -510,17 +506,17 @@ object Cache { EitherT { fileExists(file).flatMap { case false => - Task.now(true.right) + Task.now(Right(true)) case true => checkNeeded.flatMap { case false => - Task.now(false.right) + Task.now(Right(false)) case true => check.run.flatMap { - case \/-(false) => + case Right(false) => Task { doTouchCheckFile(file) - \/-(false) + Right(false) } case other => Task.now(other) @@ -559,7 +555,7 @@ object Cache { var lenOpt = Option.empty[Option[Long]] - def doDownload(): FileError \/ Unit = + def doDownload(): Either[FileError, Unit] = downloading(url, file, logger) { val alreadyDownloaded = tmp.length() @@ -587,9 +583,9 @@ object Cache { } if (responseCode(conn) == Some(404)) - FileError.NotFound(url, permanent = Some(true)).left + Left(FileError.NotFound(url, permanent = Some(true))) else if (responseCode(conn) == Some(401)) - FileError.Unauthorized(url, realm = realm(conn)).left + Left(FileError.Unauthorized(url, realm = realm(conn))) else { // TODO Use the safer getContentLengthLong when switching back to Java >= 7 for (len0 <- Option(conn.getContentLength) if len0 >= 0L) { @@ -619,7 +615,7 @@ object Cache { doTouchCheckFile(file) - result.right + Right(result) } } finally { if (conn != null) @@ -627,11 +623,11 @@ object Cache { } } - def checkDownload(): Option[FileError \/ Unit] = { + def checkDownload(): Option[Either[FileError, Unit]] = { def progress(currentLen: Long): Unit = if (lenOpt.isEmpty) { - lenOpt = Some(contentLength(url, artifact.authentication, logger).toOption.flatten) + lenOpt = Some(contentLength(url, artifact.authentication, logger).right.toOption.flatten) for (o <- lenOpt; len <- o) logger.foreach(_.downloadLength(url, len, currentLen, watching = true)) } else @@ -639,7 +635,7 @@ object Cache { def done(): Unit = if (lenOpt.isEmpty) { - lenOpt = Some(contentLength(url, artifact.authentication, logger).toOption.flatten) + lenOpt = Some(contentLength(url, artifact.authentication, logger).right.toOption.flatten) for (o <- lenOpt; len <- o) logger.foreach(_.downloadLength(url, len, len, watching = true)) } else @@ -648,7 +644,7 @@ object Cache { if (file.exists()) { done() - Some(().right) + Some(Right(())) } else { // yes, Thread.sleep. 'tis our thread pool anyway. // (And the various resources make it not straightforward to switch to a more Task-based internal API here.) @@ -658,7 +654,7 @@ object Cache { if (currentLen == 0L && file.exists()) { // check again if file exists in case it was created in the mean time done() - Some(().right) + Some(Right(())) } else { progress(currentLen) None @@ -668,7 +664,7 @@ object Cache { logger.foreach(_.downloadingArtifact(url, file)) - var res: FileError \/ Unit = null + var res: Either[FileError, Unit] = null try { res = withLockOr(cache, file)( @@ -691,37 +687,37 @@ object Cache { def validErrFileExists = EitherT { - Task { - (referenceFileExists && errFile0.exists()).right[FileError] + Task[Either[FileError, Boolean]] { + Right(referenceFileExists && errFile0.exists()) } } def createErrFile = EitherT { - Task { + Task[Either[FileError, Unit]] { if (referenceFileExists) { if (!errFile0.exists()) FileUtil.write(errFile0, "".getBytes(UTF_8)) } - ().right[FileError] + Right(()) } } def deleteErrFile = EitherT { - Task { + Task[Either[FileError, Unit]] { if (errFile0.exists()) errFile0.delete() - ().right[FileError] + Right(()) } } def retainError = EitherT { remote(file, url).run.flatMap { - case err @ -\/(FileError.NotFound(_, Some(true))) => + case err @ Left(FileError.NotFound(_, Some(true))) => createErrFile.run.map(_ => err) case other => deleteErrFile.run.map(_ => other) @@ -732,7 +728,7 @@ object Cache { case CachePolicy.FetchMissing | CachePolicy.LocalOnly | CachePolicy.LocalUpdate | CachePolicy.LocalUpdateChanging => validErrFileExists.flatMap { exists => if (exists) - EitherT(Task.now(FileError.NotFound(url, Some(true)).left[Unit])) + EitherT(Task.now[Either[FileError, Unit]](Left(FileError.NotFound(url, Some(true))))) else retainError } @@ -748,13 +744,13 @@ object Cache { // memo-ized - lazy val res = + lazy val res: Either[FileError, Boolean] = if (file.exists()) - true.right[FileError] + Right(true) else if (referenceFileExists && errFile0.exists()) - FileError.NotFound(url, Some(true)).left[Boolean] + Left(FileError.NotFound(url, Some(true)): FileError) else - false.right[FileError] + Right(false) EitherT(Task(res)) } @@ -764,9 +760,9 @@ object Cache { Task { if (file.exists()) { logger.foreach(_.foundLocally(url, file)) - \/-(()) + Right(()) } else - -\/(FileError.NotFound(file.toString)) + Left(FileError.NotFound(file.toString)) } } @@ -788,19 +784,19 @@ object Cache { val requiredArtifactCheck = artifact.extra.get("required") match { case None => - EitherT(Task.now(().right[FileError])) + EitherT(Task.now[Either[FileError, Unit]](Right(()))) case Some(required) => cachePolicy0 match { case CachePolicy.LocalOnly | CachePolicy.LocalUpdateChanging | CachePolicy.LocalUpdate => val file = localFile(required.url, cache, artifact.authentication.map(_.user)) localInfo(file, required.url).flatMap { case true => - EitherT(Task.now(().right[FileError])) + EitherT(Task.now[Either[FileError, Unit]](Right(()))) case false => - EitherT(Task.now(FileError.NotFound(file.toString).left[Unit])) + EitherT(Task.now[Either[FileError, Unit]](Left(FileError.NotFound(file.toString)))) } case _ => - EitherT(Task.now(().right[FileError])) + EitherT(Task.now[Either[FileError, Unit]](Right(()))) } } @@ -823,7 +819,7 @@ object Cache { case true => remoteKeepErrors(file, url) case false => - EitherT(Task.now[FileError \/ Unit](().right)) + EitherT(Task.now[Either[FileError, Unit]](Right(()))) } cachePolicy0 match { @@ -912,7 +908,7 @@ object Cache { sumOpt match { case None => - FileError.ChecksumFormatError(sumType, sumFile.getPath).left + Left(FileError.ChecksumFormatError(sumType, sumFile.getPath)) case Some(sum) => val md = MessageDigest.getInstance(sumType) @@ -925,20 +921,20 @@ object Cache { val calculatedSum = new BigInteger(1, digest) if (sum == calculatedSum) - ().right + Right(()) else - FileError.WrongChecksum( + Left(FileError.WrongChecksum( sumType, calculatedSum.toString(16), sum.toString(16), localFile0.getPath, sumFile.getPath - ).left + )) } } case None => - Task.now(FileError.ChecksumNotFound(sumType, localFile0.getPath).left) + Task.now(Left(FileError.ChecksumNotFound(sumType, localFile0.getPath))) } } } @@ -978,20 +974,20 @@ object Cache { } val ((f, _), res) = results.head - res.flatMap { _ => + res.right.flatMap { _ => checksum match { case None => // FIXME All the checksums should be in the error, possibly with their URLs // from artifact.checksumUrls - -\/(FileError.ChecksumNotFound(checksums0.last.get, "")) - case Some(c) => \/-((f, c)) + Left(FileError.ChecksumNotFound(checksums0.last.get, "")) + case Some(c) => Right((f, c)) } } } } res.flatMap { - case (f, None) => EitherT(Task.now[FileError \/ File](\/-(f))) + case (f, None) => EitherT(Task.now[Either[FileError, File]](Right(f))) case (f, Some(c)) => validateChecksum(artifact, c, cache, pool).map(_ => f) } @@ -1068,7 +1064,7 @@ object Cache { } else notFound(f) - EitherT.fromEither(Task.now[Either[String, String]](res)) + EitherT(Task.now[Either[String, String]](res)) } } @@ -1102,7 +1098,7 @@ object Cache { withChecksums = false, withSignatures = false, dropInfoAttributes = true - ).getOrElse( + ).right.getOrElse( throw new Exception("Cannot happen") ) diff --git a/cache/src/main/scala/coursier/CacheParse.scala b/cache/src/main/scala/coursier/CacheParse.scala index a18759c33..335b76c83 100644 --- a/cache/src/main/scala/coursier/CacheParse.scala +++ b/cache/src/main/scala/coursier/CacheParse.scala @@ -6,7 +6,9 @@ import coursier.core.Authentication import coursier.ivy.IvyRepository import coursier.util.Parse -import scalaz._, Scalaz._ +import scalaz.{Validation, ValidationNel} +import scalaz.Scalaz.vectorInstance +import scalaz.Scalaz.{ToEitherOpsFromEither, ToNelOps, ToTraverseOps, ToValidationOps} object CacheParse { @@ -18,7 +20,7 @@ object CacheParse { else { val repo = Parse.repository(s) - val url = repo.map { + val url = repo.right.map { case m: MavenRepository => m.root case i: IvyRepository => @@ -32,13 +34,13 @@ object CacheParse { } val validatedUrl = try { - url.map(Cache.url) + url.right.map(Cache.url) } catch { case e: MalformedURLException => - ("Error parsing URL " + url + Option(e.getMessage).fold("")(" (" + _ + ")")).left + Left("Error parsing URL " + url + Option(e.getMessage).fold("")(" (" + _ + ")")) } - validatedUrl.flatMap { url => + validatedUrl.right.flatMap { url => Option(url.getUserInfo) match { case None => repo @@ -52,7 +54,7 @@ object CacheParse { url.getFile ).toString - repo.map { + repo.right.map { case m: MavenRepository => m.copy( root = baseUrl, @@ -73,7 +75,7 @@ object CacheParse { } case _ => - s"No password found in user info of URL $url".left + Left(s"No password found in user info of URL $url") } } }.validation diff --git a/cache/src/main/scala/coursier/CachePolicy.scala b/cache/src/main/scala/coursier/CachePolicy.scala index a9ec3445e..9c072b2d4 100644 --- a/cache/src/main/scala/coursier/CachePolicy.scala +++ b/cache/src/main/scala/coursier/CachePolicy.scala @@ -1,5 +1,7 @@ package coursier +import scalaz.{Failure, Success} + sealed abstract class CachePolicy extends Product with Serializable object CachePolicy { @@ -80,14 +82,14 @@ object CachePolicy { value.filter(_.nonEmpty).flatMap { str => CacheParse.cachePolicies(str) match { - case scalaz.Success(Seq()) => + case Success(Seq()) => Console.err.println( s"Warning: no mode found in $description, ignoring it." ) None - case scalaz.Success(policies) => + case Success(policies) => Some(policies) - case scalaz.Failure(errors) => + case Failure(errors) => Console.err.println( s"Warning: unrecognized mode in $description, ignoring it." ) diff --git a/cache/src/main/scala/coursier/Platform.scala b/cache/src/main/scala/coursier/Platform.scala index 7fcee2193..068df7bdd 100644 --- a/cache/src/main/scala/coursier/Platform.scala +++ b/cache/src/main/scala/coursier/Platform.scala @@ -3,9 +3,10 @@ package coursier import java.io._ import java.nio.charset.Charset -import scala.language.implicitConversions +import coursier.util.EitherT -import scalaz._ +import scala.language.implicitConversions +import scala.util.{Failure, Success, Try} import scalaz.concurrent.Task object Platform { @@ -26,27 +27,30 @@ object Platform { private lazy val UTF_8 = Charset.forName("UTF-8") - def readFully(is: => InputStream) = + def readFully(is: => InputStream): Task[Either[String, String]] = Task { - \/.fromTryCatchNonFatal { + val t = Try { val is0 = is val b = try readFullySync(is0) finally is0.close() new String(b, UTF_8) - } .leftMap{ - case e: java.io.FileNotFoundException if e.getMessage != null => - s"Not found: ${e.getMessage}" - case e => - s"$e${Option(e.getMessage).fold("")(" (" + _ + ")")}" + } + + t match { + case Success(r) => Right(r) + case Failure(e: java.io.FileNotFoundException) if e.getMessage != null => + Left(s"Not found: ${e.getMessage}") + case Failure(e) => + Left(s"$e${Option(e.getMessage).fold("")(" (" + _ + ")")}") } } val artifact: Fetch.Content[Task] = { artifact => EitherT { val conn = Cache.urlConnection(artifact.url, artifact.authentication) - readFully(conn.getInputStream()) + readFully(conn.getInputStream) } } diff --git a/cli/src/main/scala-2.12/coursier/cli/Helper.scala b/cli/src/main/scala-2.12/coursier/cli/Helper.scala index 4fc2dc97b..88c51d2a4 100644 --- a/cli/src/main/scala-2.12/coursier/cli/Helper.scala +++ b/cli/src/main/scala-2.12/coursier/cli/Helper.scala @@ -17,7 +17,7 @@ import coursier.util.{Parse, Print} import scala.annotation.tailrec import scala.concurrent.duration.Duration import scalaz.concurrent.{Strategy, Task} -import scalaz.{-\/, Failure, Nondeterminism, Success, \/-} +import scalaz.{Failure, Nondeterminism, Success} object Helper { @@ -182,14 +182,14 @@ class Helper( logger.foreach(_.stop()) - val errors = res.collect { case -\/(err) => err } + val errors = res.collect { case Left(err) => err } prematureExitIf(errors.nonEmpty) { s"Error getting scaladex infos:\n" + errors.map(" " + _).mkString("\n") } res - .collect { case \/-(l) => l } + .collect { case Right(l) => l } .flatten .map { case (mod, ver) => (Dependency(mod, ver), Map[String, String]()) } } @@ -644,7 +644,7 @@ class Helper( val (ignoredErrors, errors) = results .collect { - case (artifact, -\/(err)) => + case (artifact, Left(err)) => artifact -> err } .partition { @@ -657,7 +657,7 @@ class Helper( } val artifactToFile = results.collect { - case (artifact: Artifact, \/-(f)) => + case (artifact: Artifact, Right(f)) => (artifact.url, f) }.toMap diff --git a/cli/src/main/scala-2.12/coursier/cli/scaladex/Scaladex.scala b/cli/src/main/scala-2.12/coursier/cli/scaladex/Scaladex.scala index 677667021..1fa1030fe 100644 --- a/cli/src/main/scala-2.12/coursier/cli/scaladex/Scaladex.scala +++ b/cli/src/main/scala-2.12/coursier/cli/scaladex/Scaladex.scala @@ -5,15 +5,13 @@ import java.nio.charset.StandardCharsets import java.util.concurrent.ExecutorService import argonaut._, Argonaut._, ArgonautShapeless._ -import coursier.core.{ Artifact, Attributes } -import coursier.{ Fetch, Module } +import coursier.core.{Artifact, Attributes} +import coursier.util.EitherT +import coursier.{Fetch, Module} import scala.language.higherKinds -import scalaz.{ -\/, EitherT, Monad, Nondeterminism, \/, \/- } -import scalaz.Scalaz.ToEitherOps -import scalaz.Scalaz.ToEitherOpsFromEither +import scalaz.Nondeterminism import scalaz.concurrent.Task -import scalaz.std.list._ object Scaladex { @@ -37,7 +35,7 @@ object Scaladex { def apply(pool: ExecutorService): Scaladex[Task] = Scaladex({ url => - EitherT(Task({ + EitherT(Task[Either[String, String]]({ var conn: HttpURLConnection = null val b = try { @@ -48,7 +46,7 @@ object Scaladex { coursier.Cache.closeConn(conn) } - new String(b, StandardCharsets.UTF_8).right[String] + Right(new String(b, StandardCharsets.UTF_8)) })(pool)) }, Nondeterminism[Task]) @@ -78,7 +76,7 @@ case class Scaladex[F[_]](fetch: String => EitherT[F, String, String], F: Nondet s"https://index.scala-lang.org/api/search?q=$name&target=$target&scalaVersion=$scalaVersion" ) - s.flatMap(s => EitherT.fromDisjunction[F](s.decodeEither[List[Scaladex.SearchResult]].disjunction)) + s.flatMap(s => EitherT.fromEither(s.decodeEither[List[Scaladex.SearchResult]])) } /** @@ -95,7 +93,7 @@ case class Scaladex[F[_]](fetch: String => EitherT[F, String, String], F: Nondet s"https://index.scala-lang.org/api/project?organization=$organization&repository=$repository&artifact=$artifactName" ) - s.flatMap(s => EitherT.fromDisjunction[F](s.decodeEither[Scaladex.ArtifactInfos].disjunction)) + s.flatMap(s => EitherT.fromEither(s.decodeEither[Scaladex.ArtifactInfos])) } /** @@ -113,7 +111,7 @@ case class Scaladex[F[_]](fetch: String => EitherT[F, String, String], F: Nondet case class Result(artifacts: List[String]) - s.flatMap(s => EitherT.fromDisjunction[F](s.decodeEither[Result].disjunction.map(_.artifacts))) + s.flatMap(s => EitherT.fromEither(s.decodeEither[Result].map(_.artifacts))) } @@ -135,9 +133,9 @@ case class Scaladex[F[_]](fetch: String => EitherT[F, String, String], F: Nondet .flatMap { case Seq(first, _*) => logger(s"Using ${first.organization}/${first.repository} for $name") - EitherT.fromDisjunction[F]((first.organization, first.repository, first.artifacts).right): EitherT[F, String, (String, String, Seq[String])] + EitherT.fromEither[F](Right((first.organization, first.repository, first.artifacts)): Either[String, (String, String, Seq[String])]) case Seq() => - EitherT.fromDisjunction[F](s"No project found for $name".left): EitherT[F, String, (String, String, Seq[String])] + EitherT.fromEither[F](Left(s"No project found for $name"): Either[String, (String, String, Seq[String])]) } orgNameOrError.flatMap { @@ -145,10 +143,10 @@ case class Scaladex[F[_]](fetch: String => EitherT[F, String, String], F: Nondet val moduleVersions = F.map(F.gather(artifactNames.map { artifactName => F.map(artifactInfos(ghOrg, ghRepo, artifactName).run) { - case -\/(err) => + case Left(err) => logger(s"Cannot get infos about artifact $artifactName from $ghOrg/$ghRepo: $err, ignoring it") Nil - case \/-(infos) => + case Right(infos) => logger(s"Found module ${infos.groupId}:${infos.artifactId}:${infos.version}") Seq(Module(infos.groupId, infos.artifactId) -> infos.version) } @@ -156,9 +154,9 @@ case class Scaladex[F[_]](fetch: String => EitherT[F, String, String], F: Nondet EitherT(F.map(moduleVersions) { l => if (l.isEmpty) - s"No module found for $ghOrg/$ghRepo".left + Left(s"No module found for $ghOrg/$ghRepo") else - l.right + Right(l) }) } } diff --git a/cli/src/main/scala-2.12/coursier/cli/spark/Assembly.scala b/cli/src/main/scala-2.12/coursier/cli/spark/Assembly.scala index b547cdb57..0c0ed04bc 100644 --- a/cli/src/main/scala-2.12/coursier/cli/spark/Assembly.scala +++ b/cli/src/main/scala-2.12/coursier/cli/spark/Assembly.scala @@ -14,7 +14,6 @@ import coursier.cli.util.Zip import coursier.internal.FileUtil import scala.collection.mutable -import scalaz.\/- object Assembly { @@ -273,8 +272,8 @@ object Assembly { // FIXME Acquire lock on tmpDest Assembly.make(jars, tmpDest, assemblyRules) FileUtil.atomicMove(tmpDest, dest) - \/-((dest, jars)) - }.leftMap(_.describe).toEither + Right((dest, jars)) + }.left.map(_.describe) } } diff --git a/core/shared/src/main/scala/coursier/Fetch.scala b/core/shared/src/main/scala/coursier/Fetch.scala index 236bb183c..dcf9e520e 100644 --- a/core/shared/src/main/scala/coursier/Fetch.scala +++ b/core/shared/src/main/scala/coursier/Fetch.scala @@ -1,8 +1,9 @@ package coursier -import scala.language.higherKinds +import coursier.util.EitherT -import scalaz._ +import scala.language.higherKinds +import scalaz.{Monad, Nondeterminism} object Fetch { @@ -11,7 +12,7 @@ object Fetch { type MD = Seq[( (Module, String), - Seq[String] \/ (Artifact.Source, Project) + Either[Seq[String], (Artifact.Source, Project)] )] type Metadata[F[_]] = Seq[(Module, String)] => F[MD] @@ -39,17 +40,18 @@ object Fetch { val lookups = repositories .map(repo => repo -> repo.find(module, version, fetch).run) - val task = lookups.foldLeft[F[Seq[String] \/ (Artifact.Source, Project)]](F.point(-\/(Nil))) { - case (acc, (repo, eitherProjTask)) => + val task0 = lookups.foldLeft[F[Either[Seq[String], (Artifact.Source, Project)]]](F.point(Left(Nil))) { + case (acc, (_, eitherProjTask)) => F.bind(acc) { - case -\/(errors) => - F.map(eitherProjTask)(_.leftMap(error => error +: errors)) - case res @ \/-(_) => + case Left(errors) => + F.map(eitherProjTask)(_.left.map(error => error +: errors)) + case res @ Right(_) => F.point(res) } } - EitherT(F.map(task)(_.leftMap(_.reverse))) + val task = F.map(task0)(e => e.left.map(_.reverse): Either[Seq[String], (Artifact.Source, Project)]) + EitherT(task) } def from[F[_]]( @@ -62,14 +64,13 @@ object Fetch { modVers => F.map( - F.gatherUnordered( - modVers.map { case (module, version) => - def get(fetch: Content[F]) = - find(repositories, module, version, fetch) - F.map((get(fetch) /: extra)(_ orElse get(_)) - .run)((module, version) -> _) + F.gatherUnordered { + modVers.map { + case (module, version) => + def get(fetch: Content[F]) = find(repositories, module, version, fetch) + F.map((get(fetch) /: extra)(_ orElse get(_)).run)(d => (module, version) -> d) } - ) + } )(_.toSeq) } diff --git a/core/shared/src/main/scala/coursier/core/Activation.scala b/core/shared/src/main/scala/coursier/core/Activation.scala index 535e7cf2d..196eab4b9 100644 --- a/core/shared/src/main/scala/coursier/core/Activation.scala +++ b/core/shared/src/main/scala/coursier/core/Activation.scala @@ -1,12 +1,10 @@ package coursier.core -import scalaz.{-\/, \/, \/-} - // Maven-specific final case class Activation( properties: Seq[(String, Option[String])], os: Activation.Os, - jdk: Option[VersionInterval \/ Seq[Version]] + jdk: Option[Either[VersionInterval, Seq[Version]]] ) { def isEmpty: Boolean = properties.isEmpty && os.isEmpty && jdk.isEmpty @@ -35,9 +33,9 @@ final case class Activation( def fromOs = os.isActive(osInfo) def fromJdk = jdk.forall { - case -\/(itv) => + case Left(itv) => jdkVersion.exists(itv.contains) - case \/-(versions) => + case Right(versions) => jdkVersion.exists(versions.contains) } diff --git a/core/shared/src/main/scala/coursier/core/Repository.scala b/core/shared/src/main/scala/coursier/core/Repository.scala index 78ea9e050..8e88b403b 100644 --- a/core/shared/src/main/scala/coursier/core/Repository.scala +++ b/core/shared/src/main/scala/coursier/core/Repository.scala @@ -3,10 +3,9 @@ package coursier.core import coursier.Fetch import scala.language.higherKinds - -import scalaz._ - +import scalaz.Monad import coursier.core.compatibility.encodeURIComponent +import coursier.util.EitherT trait Repository extends Product with Serializable { def find[F[_]]( diff --git a/core/shared/src/main/scala/coursier/core/Resolution.scala b/core/shared/src/main/scala/coursier/core/Resolution.scala index 7431ca700..9728716f5 100644 --- a/core/shared/src/main/scala/coursier/core/Resolution.scala +++ b/core/shared/src/main/scala/coursier/core/Resolution.scala @@ -5,7 +5,6 @@ import java.util.regex.Pattern.quote import scala.annotation.tailrec import scala.collection.JavaConverters._ -import scalaz.{ \/-, -\/ } object Resolution { @@ -201,7 +200,7 @@ object Resolution { module -> { val (versionOpt, updatedDeps) = forceVersions.get(module) match { case None => - if (deps.lengthCompare(1) == 0) (Some(deps.head.version), \/-(deps)) + if (deps.lengthCompare(1) == 0) (Some(deps.head.version), Right(deps)) else { val versions = deps .map(_.version) @@ -210,14 +209,14 @@ object Resolution { (versionOpt, versionOpt match { case Some(version) => - \/-(deps.map(dep => dep.copy(version = version))) + Right(deps.map(dep => dep.copy(version = version))) case None => - -\/(deps) + Left(deps) }) } case Some(forcedVersion) => - (Some(forcedVersion), \/-(deps.map(dep => dep.copy(version = forcedVersion)))) + (Some(forcedVersion), Right(deps.map(dep => dep.copy(version = forcedVersion)))) } (updatedDeps, versionOpt) @@ -230,10 +229,10 @@ object Resolution { ( merged - .collect { case (-\/(dep), _) => dep } + .collect { case (Left(dep), _) => dep } .flatten, merged - .collect { case (\/-(dep), _) => dep } + .collect { case (Right(dep), _) => dep } .flatten, mergedByModVer .collect { case (mod, (_, Some(ver))) => mod -> ver } diff --git a/core/shared/src/main/scala/coursier/core/ResolutionProcess.scala b/core/shared/src/main/scala/coursier/core/ResolutionProcess.scala index 0f3f5ac75..8b458dd04 100644 --- a/core/shared/src/main/scala/coursier/core/ResolutionProcess.scala +++ b/core/shared/src/main/scala/coursier/core/ResolutionProcess.scala @@ -3,8 +3,8 @@ package core import scala.annotation.tailrec import scala.language.higherKinds -import scalaz.{-\/, Monad, \/, \/-} -import scalaz.Scalaz.{ToFunctorOps, ToBindOps, ToTraverseOps, vectorInstance} +import scalaz.Monad +import scalaz.Scalaz.{ToFunctorOps, ToBindOps} sealed abstract class ResolutionProcess { @@ -64,11 +64,11 @@ final case class Missing( def next(results: Fetch.MD): ResolutionProcess = { val errors = results.collect { - case (modVer, -\/(errs)) => + case (modVer, Left(errs)) => modVer -> errs } val successes = results.collect { - case (modVer, \/-(repoProj)) => + case (modVer, Right(repoProj)) => modVer -> repoProj } @@ -168,7 +168,7 @@ object ResolutionProcess { private[coursier] def fetchAll[F[_]]( modVers: Seq[(Module, String)], fetch: Fetch.Metadata[F] - )(implicit F: Monad[F]): F[Vector[((Module, String), Seq[String] \/ (Artifact.Source, Project))]] = { + )(implicit F: Monad[F]): F[Vector[((Module, String), Either[Seq[String], (Artifact.Source, Project)])]] = { def uniqueModules(modVers: Seq[(Module, String)]): Stream[Seq[(Module, String)]] = { @@ -193,7 +193,7 @@ object ResolutionProcess { uniqueModules(modVers) .toVector - .foldLeft(F.point(Vector.empty[((Module, String), Seq[String] \/ (Artifact.Source, Project))])) { + .foldLeft(F.point(Vector.empty[((Module, String), Either[Seq[String], (Artifact.Source, Project)])])) { (acc, l) => for (v <- acc; e <- fetch(l)) yield v ++ e diff --git a/core/shared/src/main/scala/coursier/core/Versions.scala b/core/shared/src/main/scala/coursier/core/Versions.scala index 3512b4ed7..0c3324aa7 100644 --- a/core/shared/src/main/scala/coursier/core/Versions.scala +++ b/core/shared/src/main/scala/coursier/core/Versions.scala @@ -1,8 +1,5 @@ package coursier.core -import scalaz.{ -\/, \/, \/- } -import scalaz.Scalaz.ToEitherOps - final case class VersionInterval( from: Option[Version], to: Option[Version], @@ -91,25 +88,25 @@ final case class VersionConstraint( interval: VersionInterval, preferred: Seq[Version] ) { - def blend: Option[VersionInterval \/ Version] = + def blend: Option[Either[VersionInterval, Version]] = if (interval.isValid) { val preferredInInterval = preferred.filter(interval.contains) if (preferredInInterval.isEmpty) - Some(interval.left) + Some(Left(interval)) else - Some(preferredInInterval.max.right) + Some(Right(preferredInInterval.max)) } else None def repr: Option[String] = blend.map { - case -\/(itv) => + case Left(itv) => if (itv == VersionInterval.zero) "" else itv.repr - case \/-(v) => v.repr + case Right(v) => v.repr } } diff --git a/core/shared/src/main/scala/coursier/ivy/IvyRepository.scala b/core/shared/src/main/scala/coursier/ivy/IvyRepository.scala index 591d7bf34..bcc1bf240 100644 --- a/core/shared/src/main/scala/coursier/ivy/IvyRepository.scala +++ b/core/shared/src/main/scala/coursier/ivy/IvyRepository.scala @@ -2,12 +2,10 @@ package coursier.ivy import coursier.Fetch import coursier.core._ -import coursier.util.WebPage +import coursier.util.{EitherT, WebPage} import scala.language.higherKinds - -import scalaz._ -import scalaz.Scalaz._ +import scalaz.Monad final case class IvyRepository( pattern: Pattern, @@ -115,7 +113,7 @@ final case class IvyRepository( p.name, p.ext, Some(p.classifier).filter(_.nonEmpty) - )).toList.map(p -> _) // FIXME Validation errors are ignored + )).right.toSeq.toList.map(p -> _) // FIXME Validation errors are ignored } retainedWithUrl.map { case (p, url) => @@ -160,22 +158,26 @@ final case class IvyRepository( case None => findNoInverval(module, version, fetch) case Some(itv) => - val listingUrl = revisionListingPattern.substituteVariables( - variables(module, None, "ivy", "ivy", "xml", None) - ).flatMap { s => - if (s.endsWith("/")) - s.right - else - s"Don't know how to list revisions of ${metadataPattern.string}".left - } + val listingUrl = revisionListingPattern + .substituteVariables(variables(module, None, "ivy", "ivy", "xml", None)) + .right + .flatMap { s => + if (s.endsWith("/")) + Right(s) + else + Left(s"Don't know how to list revisions of ${metadataPattern.string}") + } def fromWebPage(url: String, s: String) = { + val subDirs = WebPage.listDirectories(url, s) val versions = subDirs.map(Parse.version).collect { case Some(v) => v } val versionsInItv = versions.filter(itv.contains) if (versionsInItv.isEmpty) - EitherT(F.point(s"No version found for $version".left[(Artifact.Source, Project)])) + EitherT( + F.point[Either[String, (Artifact.Source, Project)]](Left(s"No version found for $version")) + ) else { val version0 = versionsInItv.max findNoInverval(module, version0.repr, fetch) @@ -209,11 +211,11 @@ final case class IvyRepository( F: Monad[F] ): EitherT[F, String, (Artifact.Source, Project)] = { - val eitherArtifact: String \/ Artifact = + val eitherArtifact: Either[String, Artifact] = for { url <- metadataPattern.substituteVariables( variables(module, Some(version), "ivy", "ivy", "xml", None) - ) + ).right } yield { var artifact = Artifact( url, @@ -235,13 +237,15 @@ final case class IvyRepository( for { artifact <- EitherT(F.point(eitherArtifact)) ivy <- fetch(artifact) - proj0 <- EitherT(F.point { - for { - xml <- \/.fromEither(compatibility.xmlParse(ivy)) - _ <- if (xml.label == "ivy-module") \/-(()) else -\/("Module definition not found") - proj <- IvyXml.project(xml) - } yield proj - }) + proj0 <- EitherT( + F.point { + for { + xml <- compatibility.xmlParse(ivy).right + _ <- (if (xml.label == "ivy-module") Right(()) else Left("Module definition not found")).right + proj <- IvyXml.project(xml).right + } yield proj + } + ) } yield { val proj = if (dropInfoAttributes) @@ -287,16 +291,18 @@ object IvyRepository { // hack for SBT putting infos in properties dropInfoAttributes: Boolean = false, authentication: Option[Authentication] = None - ): String \/ IvyRepository = + ): Either[String, IvyRepository] = for { - propertiesPattern <- PropertiesPattern.parse(pattern) + propertiesPattern <- PropertiesPattern.parse(pattern).right metadataPropertiesPatternOpt <- metadataPatternOpt - .fold(Option.empty[PropertiesPattern].right[String])(PropertiesPattern.parse(_) - .map(Some(_))) + .fold[Either[String, Option[PropertiesPattern]]](Right(None))(PropertiesPattern.parse(_).right.map(Some(_))) + .right - pattern <- propertiesPattern.substituteProperties(properties) - metadataPatternOpt <- metadataPropertiesPatternOpt.fold(Option.empty[Pattern].right[String])(_.substituteProperties(properties).map(Some(_))) + pattern <- propertiesPattern.substituteProperties(properties).right + metadataPatternOpt <- metadataPropertiesPatternOpt + .fold[Either[String, Option[Pattern]]](Right(None))(_.substituteProperties(properties).right.map(Some(_))) + .right } yield IvyRepository( @@ -359,8 +365,8 @@ object IvyRepository { dropInfoAttributes, authentication ) match { - case \/-(repo) => repo - case -\/(msg) => + case Right(repo) => repo + case Left(msg) => throw new IllegalArgumentException(s"Error while parsing Ivy patterns: $msg") } -} \ No newline at end of file +} diff --git a/core/shared/src/main/scala/coursier/ivy/IvyXml.scala b/core/shared/src/main/scala/coursier/ivy/IvyXml.scala index 19e941f4a..1b7e31083 100644 --- a/core/shared/src/main/scala/coursier/ivy/IvyXml.scala +++ b/core/shared/src/main/scala/coursier/ivy/IvyXml.scala @@ -3,29 +3,29 @@ package coursier.ivy import coursier.core._ import coursier.util.Xml._ -import scalaz.{ Node => _, _ }, Scalaz._ - object IvyXml { val attributesNamespace = "http://ant.apache.org/ivy/extra" - private def info(node: Node): String \/ (Module, String) = + private def info(node: Node): Either[String, (Module, String)] = for { - org <- node.attribute("organisation") - name <- node.attribute("module") - version <- node.attribute("revision") - attr = node.attributesFromNamespace(attributesNamespace) - } yield (Module(org, name, attr.toMap), version) + org <- node.attribute("organisation").right + name <- node.attribute("module").right + version <- node.attribute("revision").right + } yield { + val attr = node.attributesFromNamespace(attributesNamespace) + (Module(org, name, attr.toMap), version) + } // FIXME Errors are ignored here private def configurations(node: Node): Seq[(String, Seq[String])] = node.children .filter(_.label == "conf") .flatMap { node => - node.attribute("name").toOption.toSeq.map(_ -> node) + node.attribute("name").right.toOption.toSeq.map(_ -> node) } .map { case (name, node) => - name -> node.attribute("extends").toOption.toSeq.flatMap(_.split(',')) + name -> node.attribute("extends").right.toSeq.flatMap(_.split(',')) } // FIXME "default(compile)" likely not to be always the default @@ -52,9 +52,11 @@ object IvyXml { val excludes = node.children .filter(_.label == "exclude") .flatMap { node0 => - val org = node0.attribute("org").getOrElse("*") - val name = node0.attribute("module").orElse(node0.attribute("name")).getOrElse("*") - val confs = node0.attribute("conf").toOption.filter(_.nonEmpty).fold(Seq("*"))(_.split(',')) + val org = node0.attribute("org").right.getOrElse("*") + val name = node0.attribute("module").right.toOption + .orElse(node0.attribute("name").right.toOption) + .getOrElse("*") + val confs = node0.attribute("conf").right.toOption.filter(_.nonEmpty).fold(Seq("*"))(_.split(',')) confs.map(_ -> (org, name)) } .groupBy { case (conf, _) => conf } @@ -63,15 +65,15 @@ object IvyXml { val allConfsExcludes = excludes.getOrElse("*", Set.empty) for { - org <- node.attribute("org").toOption.toSeq - name <- node.attribute("name").toOption.toSeq - version <- node.attribute("rev").toOption.toSeq - rawConf <- node.attribute("conf").toOption.toSeq + org <- node.attribute("org").right.toOption.toSeq + name <- node.attribute("name").right.toOption.toSeq + version <- node.attribute("rev").right.toOption.toSeq + rawConf <- node.attribute("conf").right.toOption.toSeq (fromConf, toConf) <- mappings(rawConf) - attr = node.attributesFromNamespace(attributesNamespace) } yield { - val transitive = node.attribute("transitive").toOption match { - case Some("false") => false + val attr = node.attributesFromNamespace(attributesNamespace) + val transitive = node.attribute("transitive") match { + case Right("false") => false case _ => true } @@ -91,23 +93,24 @@ object IvyXml { node.children .filter(_.label == "artifact") .flatMap { node => - val name = node.attribute("name").getOrElse("") - val type0 = node.attribute("type").getOrElse("jar") - val ext = node.attribute("ext").getOrElse(type0) - val confs = node.attribute("conf").toOption.fold(Seq("*"))(_.split(',')) - val classifier = node.attribute("classifier").toOption.getOrElse("") + val name = node.attribute("name").right.getOrElse("") + val type0 = node.attribute("type").right.getOrElse("jar") + val ext = node.attribute("ext").right.getOrElse(type0) + val confs = node.attribute("conf").fold(_ => Seq("*"), _.split(',').toSeq) + val classifier = node.attribute("classifier").right.getOrElse("") confs.map(_ -> Publication(name, type0, ext, classifier)) } .groupBy { case (conf, _) => conf } .map { case (conf, l) => conf -> l.map { case (_, p) => p } } - def project(node: Node): String \/ Project = + def project(node: Node): Either[String, Project] = for { infoNode <- node.children .find(_.label == "info") - .toRightDisjunction("Info not found") + .toRight("Info not found") + .right - modVer <- info(infoNode) + modVer <- info(infoNode).right } yield { val (module, version) = modVer @@ -137,12 +140,13 @@ object IvyXml { val licenses = infoNode.children .filter(_.label == "license") .flatMap { n => - n.attribute("name").toOption.map { name => - (name, n.attribute("url").toOption) - }.toSeq + n.attribute("name").right.toSeq.map { name => + (name, n.attribute("url").right.toOption) + } } val publicationDate = infoNode.attribute("publication") + .right .toOption .flatMap(parseDateTime) diff --git a/core/shared/src/main/scala/coursier/ivy/Pattern.scala b/core/shared/src/main/scala/coursier/ivy/Pattern.scala index 3eed6e7bc..212c025a7 100644 --- a/core/shared/src/main/scala/coursier/ivy/Pattern.scala +++ b/core/shared/src/main/scala/coursier/ivy/Pattern.scala @@ -2,7 +2,8 @@ package coursier.ivy import scala.language.implicitConversions -import scalaz._, Scalaz._ +import scalaz.{Failure, Success, ValidationNel} +import scalaz.Scalaz.{ToEitherOpsFromEither, ToFoldableOps, ToTraverseOps, ToValidationOps, vectorInstance} import fastparse.all._ @@ -12,7 +13,7 @@ final case class PropertiesPattern(chunks: Seq[PropertiesPattern.ChunkOrProperty import PropertiesPattern.ChunkOrProperty - def substituteProperties(properties: Map[String, String]): String \/ Pattern = { + def substituteProperties(properties: Map[String, String]): Either[String, Pattern] = { val validation = chunks.toVector.traverseM[({ type L[X] = ValidationNel[String, X] })#L, Pattern.Chunk] { case ChunkOrProperty.Prop(name, alternativesOpt) => @@ -24,6 +25,7 @@ final case class PropertiesPattern(chunks: Seq[PropertiesPattern.ChunkOrProperty case Some(alt) => PropertiesPattern(alt) .substituteProperties(properties) + .right .map(_.chunks.toVector) .validation .toValidationNel @@ -35,6 +37,7 @@ final case class PropertiesPattern(chunks: Seq[PropertiesPattern.ChunkOrProperty case ChunkOrProperty.Opt(l @ _*) => PropertiesPattern(l) .substituteProperties(properties) + .right .map(l => Vector(Pattern.Chunk.Opt(l.chunks: _*))) .validation .toValidationNel @@ -47,7 +50,7 @@ final case class PropertiesPattern(chunks: Seq[PropertiesPattern.ChunkOrProperty }.map(Pattern(_)) - validation.disjunction.leftMap { notFoundProps => + validation.toEither.left.map { notFoundProps => s"Property(ies) not found: ${notFoundProps.toList.mkString(", ")}" } } @@ -62,7 +65,7 @@ final case class Pattern(chunks: Seq[Pattern.Chunk]) { def string: String = chunks.map(_.string).mkString - def substituteVariables(variables: Map[String, String]): String \/ String = { + def substituteVariables(variables: Map[String, String]): Either[String, String] = { def helper(chunks: Seq[Chunk]): ValidationNel[String, Seq[Chunk.Const]] = chunks.toVector.traverseU[ValidationNel[String, Seq[Chunk.Const]]] { @@ -87,11 +90,11 @@ final case class Pattern(chunks: Seq[Pattern.Chunk]) { validation match { case Failure(notFoundVariables) => - s"Variables not found: ${notFoundVariables.toList.mkString(", ")}".left + Left(s"Variables not found: ${notFoundVariables.toList.mkString(", ")}") case Success(constants) => val b = new StringBuilder constants.foreach(b ++= _.value) - b.result().right + Right(b.result()) } } } @@ -144,12 +147,12 @@ object PropertiesPattern { } - def parse(pattern: String): String \/ PropertiesPattern = + def parse(pattern: String): Either[String, PropertiesPattern] = parser.parse(pattern) match { case f: Parsed.Failure => - f.msg.left + Left(f.msg) case Parsed.Success(v, _) => - PropertiesPattern(v).right + Right(PropertiesPattern(v)) } } diff --git a/core/shared/src/main/scala/coursier/maven/MavenRepository.scala b/core/shared/src/main/scala/coursier/maven/MavenRepository.scala index ee76c724b..ee6ac4a92 100644 --- a/core/shared/src/main/scala/coursier/maven/MavenRepository.scala +++ b/core/shared/src/main/scala/coursier/maven/MavenRepository.scala @@ -3,10 +3,10 @@ package coursier.maven import coursier.Fetch import coursier.core._ import coursier.core.compatibility.encodeURIComponent -import coursier.util.WebPage +import coursier.util.{EitherT, WebPage} import scala.language.higherKinds -import scalaz._ +import scalaz.Monad object MavenRepository { val SnapshotTimestamp = "(.*-)?[0-9]{8}\\.[0-9]{6}-[0-9]+".r @@ -179,9 +179,9 @@ final case class MavenRepository( val res = if (files.contains("maven-metadata.xml")) - -\/("maven-metadata.xml found, not listing version from directory listing") + Left("maven-metadata.xml found, not listing version from directory listing") else if (rawVersions.isEmpty) - -\/(s"No versions found at $listingUrl") + Left(s"No versions found at $listingUrl") else { val parsedVersions = rawVersions.map(Version(_)) val nonPreVersions = parsedVersions.filter(_.items.forall { @@ -190,10 +190,10 @@ final case class MavenRepository( }) if (nonPreVersions.isEmpty) - -\/(s"Found only pre-versions at $listingUrl") + Left(s"Found only pre-versions at $listingUrl") else { val latest = nonPreVersions.max - \/-(Versions( + Right(Versions( latest.repr, latest.repr, nonPreVersions.map(_.repr).toList, @@ -214,16 +214,16 @@ final case class MavenRepository( ): EitherT[F, String, Versions] = EitherT( versionsArtifact(module) match { - case None => F.point(-\/("Not supported")) + case None => F.point(Left("Not supported")) case Some(artifact) => - F.map(fetch(artifact).run)(eitherStr => + F.map(fetch(artifact).run) { eitherStr => for { - str <- eitherStr - xml <- \/.fromEither(compatibility.xmlParse(str)) - _ <- if (xml.label == "metadata") \/-(()) else -\/("Metadata not found") - versions <- Pom.versions(xml) + str <- eitherStr.right + xml <- compatibility.xmlParse(str).right + _ <- (if (xml.label == "metadata") Right(()) else Left("Metadata not found")).right + versions <- Pom.versions(xml).right } yield versions - ) + } } ) @@ -237,16 +237,16 @@ final case class MavenRepository( EitherT( snapshotVersioningArtifact(module, version) match { - case None => F.point(-\/("Not supported")) + case None => F.point(Left("Not supported")) case Some(artifact) => - F.map(fetch(artifact).run)(eitherStr => + F.map(fetch(artifact).run) { eitherStr => for { - str <- eitherStr - xml <- \/.fromEither(compatibility.xmlParse(str)) - _ <- if (xml.label == "metadata") \/-(()) else -\/("Metadata not found") - snapshotVersioning <- Pom.snapshotVersioning(xml) + str <- eitherStr.right + xml <- compatibility.xmlParse(str).right + _ <- (if (xml.label == "metadata") Right(()) else Left("Metadata not found")).right + snapshotVersioning <- Pom.snapshotVersioning(xml).right } yield snapshotVersioning - ) + } } ) } @@ -269,7 +269,7 @@ final case class MavenRepository( versioningOption match { case None => EitherT[F, String, Project]( - F.point(-\/("No snapshot versioning value found")) + F.point(Left("No snapshot versioning value found")) ) case versioning @ Some(_) => findVersioning(module, version, versioning, fetch) @@ -290,7 +290,7 @@ final case class MavenRepository( } // keep exact version used to get metadata, in case the one inside the metadata is wrong - F.map(res)(_.map(proj => proj.copy(actualVersionOpt = Some(version)))) + F.map(res)(_.right.map(proj => proj.copy(actualVersionOpt = Some(version)))) } private def artifactFor(url: String, changing: Boolean) = @@ -314,9 +314,9 @@ final case class MavenRepository( def parseRawPom(str: String) = for { - xml <- \/.fromEither(compatibility.xmlParse(str)) - _ <- if (xml.label == "project") \/-(()) else -\/("Project definition not found") - proj <- Pom.project(xml, relocationAsDependency = true) + xml <- compatibility.xmlParse(str).right + _ <- (if (xml.label == "project") Right(()) else Left("Project definition not found")).right + proj <- Pom.project(xml, relocationAsDependency = true).right } yield proj def isArtifact(fileName: String, prefix: String): Option[(String, String)] = @@ -370,9 +370,9 @@ final case class MavenRepository( for { str <- fetch(requiringDirListingProjectArtifact) rawListFilesPageOpt <- EitherT(F.map(fetch(artifactFor(listFilesUrl, changing0)).run) { - e => \/-(e.toOption): String \/ Option[String] + e => Right(e.right.toOption): Either[String, Option[String]] }) - proj0 <- EitherT(F.point[String \/ Project](parseRawPom(str))) + proj0 <- EitherT(F.point[Either[String, Project]](parseRawPom(str))) } yield { val foundPublications = @@ -451,20 +451,20 @@ final case class MavenRepository( val eitherVersion = { val release = Version(versions0.release) - if (itv.contains(release)) \/-(versions0.release) + if (itv.contains(release)) Right(versions0.release) else { val inInterval = versions0.available .map(Version(_)) .filter(itv.contains) - if (inInterval.isEmpty) -\/(s"No version found for $version") - else \/-(inInterval.max.repr) + if (inInterval.isEmpty) Left(s"No version found for $version") + else Right(inInterval.max.repr) } } eitherVersion match { - case -\/(reason) => EitherT[F, String, (Artifact.Source, Project)](F.point(-\/(reason))) - case \/-(version0) => + case Left(reason) => EitherT[F, String, (Artifact.Source, Project)](F.point(Left(reason))) + case Right(version0) => findNoInterval(module, version0, fetch) .map(_.copy(versions = Some(versions0))) .map((source, _)) diff --git a/core/shared/src/main/scala/coursier/maven/Pom.scala b/core/shared/src/main/scala/coursier/maven/Pom.scala index 0f2472ec0..4a0ba003e 100644 --- a/core/shared/src/main/scala/coursier/maven/Pom.scala +++ b/core/shared/src/main/scala/coursier/maven/Pom.scala @@ -1,12 +1,14 @@ package coursier.maven import coursier.core._ - -import scalaz._ +import scalaz.Scalaz.{eitherMonad, listInstance, ToTraverseOps} object Pom { import coursier.util.Xml._ + private def point[T](t: T) = + Right(t).right + /** * Returns either a property's key-value pair or an error if the elem is not an element. * @@ -16,10 +18,10 @@ object Pom { * @return the key and the value of the property * @see [[https://issues.apache.org/jira/browse/MNG-5380]] */ - def property(elem: Node): String \/ (String, String) = { + def property(elem: Node): Either[String, (String, String)] = { // Not matching with Text, which fails on scala-js if the property value has xml comments - if (elem.isElement) \/-(elem.label -> elem.textContent.trim) - else -\/(s"Can't parse property $elem") + if (elem.isElement) Right(elem.label -> elem.textContent.trim) + else Left(s"Can't parse property $elem") } // TODO Allow no version in some contexts @@ -27,52 +29,53 @@ object Pom { node: Node, defaultGroupId: Option[String] = None, defaultArtifactId: Option[String] = None - ): String \/ Module = { + ): Either[String, Module] = { for { organization <- { val e = text(node, "groupId", "Organization") - defaultGroupId.fold(e)(g => e.orElse(\/-(g))) + defaultGroupId.fold(e)(g => Right(e.right.getOrElse(g))).right } name <- { val n = text(node, "artifactId", "Name") - defaultArtifactId.fold(n)(n0 => n.orElse(\/-(n0))) + defaultArtifactId.fold(n)(n0 => Right(n.right.getOrElse(n0))).right } } yield Module(organization, name, Map.empty).trim } private def readVersion(node: Node) = - text(node, "version", "Version").getOrElse("").trim + text(node, "version", "Version").right.getOrElse("").trim - def dependency(node: Node): String \/ (String, Dependency) = { - for { - mod <- module(node) - version0 = readVersion(node) - scopeOpt = text(node, "scope", "").toOption - typeOpt = text(node, "type", "").toOption - classifierOpt = text(node, "classifier", "").toOption - xmlExclusions = node.children + def dependency(node: Node): Either[String, (String, Dependency)] = + module(node).right.flatMap { mod => + + val version0 = readVersion(node) + val scopeOpt = text(node, "scope", "").right.toOption + val typeOpt = text(node, "type", "").right.toOption + val classifierOpt = text(node, "classifier", "").right.toOption + val xmlExclusions = node.children .find(_.label == "exclusions") .map(_.children.filter(_.label == "exclusion")) .getOrElse(Seq.empty) - exclusions <- { - import Scalaz._ - xmlExclusions.toList.traverseU(module(_, defaultArtifactId = Some("*"))) + + xmlExclusions.toList.traverseU(module(_, defaultArtifactId = Some("*"))).right.map { exclusions => + + val optional = text(node, "optional", "").right.toSeq.contains("true") + + scopeOpt.getOrElse("") -> Dependency( + mod, + version0, + "", + exclusions.map(mod => (mod.organization, mod.name)).toSet, + Attributes(typeOpt.getOrElse(""), classifierOpt.getOrElse("")), + optional, + transitive = true + ) } - optional = text(node, "optional", "").toOption.toSeq.contains("true") - } yield scopeOpt.getOrElse("") -> Dependency( - mod, - version0, - "", - exclusions.map(mod => (mod.organization, mod.name)).toSet, - Attributes(typeOpt.getOrElse(""), classifierOpt.getOrElse("")), - optional, - transitive = true - ) - } + } private def profileActivation(node: Node): (Option[Boolean], Activation) = { val byDefault = - text(node, "activeByDefault", "").toOption.flatMap{ + text(node, "activeByDefault", "").right.toOption.flatMap { case "true" => Some(true) case "false" => Some(false) case _ => None @@ -80,27 +83,27 @@ object Pom { val properties = node.children .filter(_.label == "property") - .flatMap{ p => + .flatMap { p => for{ - name <- text(p, "name", "").toOption - valueOpt = text(p, "value", "").toOption + name <- text(p, "name", "").right.toOption + valueOpt = text(p, "value", "").right.toOption } yield (name, valueOpt) } val osNodeOpt = node.children.collectFirst { case n if n.label == "os" => n } val os = Activation.Os( - osNodeOpt.flatMap(n => text(n, "arch", "").toOption), - osNodeOpt.flatMap(n => text(n, "family", "").toOption).toSet, - osNodeOpt.flatMap(n => text(n, "name", "").toOption), - osNodeOpt.flatMap(n => text(n, "version", "").toOption) + osNodeOpt.flatMap(n => text(n, "arch", "").right.toOption), + osNodeOpt.flatMap(n => text(n, "family", "").right.toOption).toSet, + osNodeOpt.flatMap(n => text(n, "name", "").right.toOption), + osNodeOpt.flatMap(n => text(n, "version", "").right.toOption) ) - val jdk = text(node, "jdk", "").toOption.flatMap { s => + val jdk = text(node, "jdk", "").right.toOption.flatMap { s => Parse.versionInterval(s) .orElse(Parse.multiVersionInterval(s)) - .map(-\/(_)) - .orElse(Parse.version(s).map(v => \/-(Seq(v)))) + .map(Left(_)) + .orElse(Parse.version(s).map(v => Right(Seq(v)))) } val activation = Activation(properties, os, jdk) @@ -108,10 +111,9 @@ object Pom { (byDefault, activation) } - def profile(node: Node): String \/ Profile = { - import Scalaz._ + def profile(node: Node): Either[String, Profile] = { - val id = text(node, "id", "Profile ID").getOrElse("") + val id = text(node, "id", "Profile ID").right.getOrElse("") val xmlActivationOpt = node.children .find(_.label == "activation") @@ -123,102 +125,117 @@ object Pom { .getOrElse(Seq.empty) for { - deps <- xmlDeps.toList.traverseU(dependency) + deps <- xmlDeps.toList.traverseU(dependency).right - xmlDepMgmts = node.children + depMgmts <- node + .children .find(_.label == "dependencyManagement") .flatMap(_.children.find(_.label == "dependencies")) .map(_.children.filter(_.label == "dependency")) .getOrElse(Seq.empty) - depMgmts <- xmlDepMgmts.toList.traverseU(dependency) + .toList + .traverseU(dependency) + .right - xmlProperties = node.children + properties <- node + .children .find(_.label == "properties") - .map(_.children.collect{case elem if elem.isElement => elem}) + .map(_.children.collect { case elem if elem.isElement => elem }) .getOrElse(Seq.empty) - - properties <- { - import Scalaz._ - xmlProperties.toList.traverseU(property) - } + .toList + .traverseU(property) + .right } yield Profile(id, activeByDefault, activation, deps, depMgmts, properties.toMap) } def packagingOpt(pom: Node): Option[String] = - text(pom, "packaging", "").toOption + text(pom, "packaging", "").right.toOption - def project(pom: Node): String \/ Project = + def project(pom: Node): Either[String, Project] = project(pom, relocationAsDependency = false) def project( pom: Node, relocationAsDependency: Boolean - ): String \/ Project = { - import Scalaz._ + ): Either[String, Project] = { for { - projModule <- module(pom, defaultGroupId = Some("")) - projVersion = readVersion(pom) + projModule <- module(pom, defaultGroupId = Some("")).right - parentOpt = pom.children - .find(_.label == "parent") + parentOpt <- point(pom.children.find(_.label == "parent")) parentModuleOpt <- parentOpt - .map(module(_).map(Some(_))) - .getOrElse(\/-(None)) - parentVersionOpt = parentOpt - .map(readVersion) + .map(module(_).right.map(Some(_))) + .getOrElse(Right(None)) + .right + parentVersionOpt <- point(parentOpt.map(readVersion)) - xmlDeps = pom.children - .find(_.label == "dependencies") - .map(_.children.filter(_.label == "dependency")) - .getOrElse(Seq.empty) - deps <- xmlDeps.toList.traverseU(dependency) + xmlDeps <- point( + pom.children + .find(_.label == "dependencies") + .map(_.children.filter(_.label == "dependency")) + .getOrElse(Seq.empty) + ) + deps <- xmlDeps.toList.traverseU(dependency).right - xmlDepMgmts = pom.children - .find(_.label == "dependencyManagement") - .flatMap(_.children.find(_.label == "dependencies")) - .map(_.children.filter(_.label == "dependency")) - .getOrElse(Seq.empty) - depMgmts <- xmlDepMgmts.toList.traverseU(dependency) + xmlDepMgmts <- point( + pom.children + .find(_.label == "dependencyManagement") + .flatMap(_.children.find(_.label == "dependencies")) + .map(_.children.filter(_.label == "dependency")) + .getOrElse(Seq.empty) + ) + depMgmts <- xmlDepMgmts.toList.traverseU(dependency).right groupId <- Some(projModule.organization).filter(_.nonEmpty) .orElse(parentModuleOpt.map(_.organization).filter(_.nonEmpty)) - .toRightDisjunction("No organization found") - version <- Some(projVersion).filter(_.nonEmpty) + .toRight("No organization found") + .right + version <- Some(readVersion(pom)).filter(_.nonEmpty) .orElse(parentVersionOpt.filter(_.nonEmpty)) - .toRightDisjunction("No version found") + .toRight("No version found") + .right _ <- parentVersionOpt - .map(v => if (v.isEmpty) -\/("Parent version missing") else \/-(())) - .getOrElse(\/-(())) + .map(v => if (v.isEmpty) Left("Parent version missing") else Right(())) + .getOrElse(Right(())) + .right _ <- parentModuleOpt - .map(mod => if (mod.organization.isEmpty) -\/("Parent organization missing") else \/-(())) - .getOrElse(\/-(())) + .map(mod => if (mod.organization.isEmpty) Left("Parent organization missing") else Right(())) + .getOrElse(Right(())) + .right - xmlProperties = pom.children - .find(_.label == "properties") - .map(_.children.collect{case elem if elem.isElement => elem}) - .getOrElse(Seq.empty) - properties <- xmlProperties.toList.traverseU(property) + xmlProperties <- point( + pom.children + .find(_.label == "properties") + .map(_.children.collect{case elem if elem.isElement => elem}) + .getOrElse(Seq.empty) + ) + properties <- xmlProperties.toList.traverseU(property).right - xmlProfiles = pom.children - .find(_.label == "profiles") - .map(_.children.filter(_.label == "profile")) - .getOrElse(Seq.empty) - profiles <- xmlProfiles.toList.traverseU(profile) + xmlProfiles <- point( + pom + .children + .find(_.label == "profiles") + .map(_.children.filter(_.label == "profile")) + .getOrElse(Seq.empty) + ) + profiles <- xmlProfiles.toList.traverseU(profile).right extraAttrs <- properties .collectFirst { case ("extraDependencyAttributes", s) => extraAttributes(s) } - .getOrElse(\/-(Map.empty)) - - extraAttrsMap = extraAttrs.map { - case (mod, ver) => - (mod.copy(attributes = Map.empty), ver) -> mod.attributes - }.toMap + .getOrElse(Right(Map.empty)) + .right } yield { + val extraAttrsMap = extraAttrs + .map { + case (mod, ver) => + (mod.copy(attributes = Map.empty), ver) -> mod.attributes + } + .toMap + val description = pom.children .find(_.label == "description") .map(_.textContent) @@ -235,8 +252,8 @@ object Pom { .flatMap(_.children) .filter(_.label == "license") .flatMap { n => - text(n, "name", "License name").toOption.map { name => - (name, text(n, "url", "License URL").toOption) + text(n, "name", "License name").right.toOption.map { name => + (name, text(n, "url", "License URL").right.toOption) }.toSeq } @@ -247,13 +264,13 @@ object Pom { .filter(_.label == "developer") .map { n => for { - id <- text(n, "id", "Developer ID") - name <- text(n, "name", "Developer name") - url <- text(n, "url", "Developer URL") + id <- text(n, "id", "Developer ID").right + name <- text(n, "name", "Developer name").right + url <- text(n, "url", "Developer URL").right } yield Info.Developer(id, name, url) } .collect { - case \/-(d) => d + case Right(d) => d } val finalProjModule = projModule.copy(organization = groupId) @@ -267,9 +284,9 @@ object Pom { // see https://maven.apache.org/guides/mini/guide-relocation.html - val relocatedGroupId = text(n, "groupId", "").getOrElse(finalProjModule.organization) - val relocatedArtifactId = text(n, "artifactId", "").getOrElse(finalProjModule.name) - val relocatedVersion = text(n, "version", "").getOrElse(version) + val relocatedGroupId = text(n, "groupId", "").right.getOrElse(finalProjModule.organization) + val relocatedArtifactId = text(n, "artifactId", "").right.getOrElse(finalProjModule.name) + val relocatedVersion = text(n, "version", "").right.getOrElse(version) "" -> Dependency( finalProjModule.copy( @@ -318,37 +335,44 @@ object Pom { } } - def versions(node: Node): String \/ Versions = { - import Scalaz._ + def versions(node: Node): Either[String, Versions] = { for { - organization <- text(node, "groupId", "Organization") // Ignored - name <- text(node, "artifactId", "Name") // Ignored + organization <- text(node, "groupId", "Organization").right // Ignored + name <- text(node, "artifactId", "Name").right // Ignored xmlVersioning <- node.children .find(_.label == "versioning") - .toRightDisjunction("Versioning info not found in metadata") + .toRight("Versioning info not found in metadata") + .right - latest = text(xmlVersioning, "latest", "Latest version") + } yield { + + val latest = text(xmlVersioning, "latest", "Latest version") + .right .getOrElse("") - release = text(xmlVersioning, "release", "Release version") + val release = text(xmlVersioning, "release", "Release version") + .right .getOrElse("") - versionsOpt = xmlVersioning.children + val versionsOpt = xmlVersioning.children .find(_.label == "versions") - .map(_.children.filter(_.label == "version").flatMap(_.children.collectFirst{case Text(t) => t})) + .map(_.children.filter(_.label == "version").flatMap(_.children.collectFirst { case Text(t) => t })) - lastUpdatedOpt = text(xmlVersioning, "lastUpdated", "Last update date and time") + val lastUpdatedOpt = text(xmlVersioning, "lastUpdated", "Last update date and time") + .right .toOption .flatMap(parseDateTime) - } yield Versions(latest, release, versionsOpt.map(_.toList).getOrElse(Nil), lastUpdatedOpt) + Versions(latest, release, versionsOpt.map(_.toList).getOrElse(Nil), lastUpdatedOpt) + } } - def snapshotVersion(node: Node): String \/ SnapshotVersion = { - def textOrEmpty(name: String, desc: String) = + def snapshotVersion(node: Node): Either[String, SnapshotVersion] = { + + def textOrEmpty(name: String, desc: String): String = text(node, name, desc) - .toOption + .right .getOrElse("") val classifier = textOrEmpty("classifier", "Classifier") @@ -356,10 +380,11 @@ object Pom { val value = textOrEmpty("value", "Value") val updatedOpt = text(node, "updated", "Updated") + .right .toOption .flatMap(parseDateTime) - \/-(SnapshotVersion( + Right(SnapshotVersion( classifier, ext, value, @@ -380,49 +405,72 @@ object Pom { SnapshotVersion("*", "*", value, None) } - def snapshotVersioning(node: Node): String \/ SnapshotVersioning = { - import Scalaz._ - + def snapshotVersioning(node: Node): Either[String, SnapshotVersioning] = // FIXME Quite similar to Versions above for { - organization <- text(node, "groupId", "Organization") - name <- text(node, "artifactId", "Name") - version = readVersion(node) + organization <- text(node, "groupId", "Organization").right + name <- text(node, "artifactId", "Name").right - xmlVersioning <- node.children + xmlVersioning <- node + .children .find(_.label == "versioning") - .toRightDisjunction("Versioning info not found in metadata") + .toRight("Versioning info not found in metadata") + .right - latest = text(xmlVersioning, "latest", "Latest version") + snapshotVersions <- { + + val xmlSnapshotVersions = xmlVersioning + .children + .find(_.label == "snapshotVersions") + .map(_.children.filter(_.label == "snapshotVersion")) + .getOrElse(Seq.empty) + + xmlSnapshotVersions + .toList + .traverseU(snapshotVersion) + .right + } + } yield { + + val version = readVersion(node) + + val latest = text(xmlVersioning, "latest", "Latest version") + .right .getOrElse("") - release = text(xmlVersioning, "release", "Release version") + val release = text(xmlVersioning, "release", "Release version") + .right .getOrElse("") - lastUpdatedOpt = text(xmlVersioning, "lastUpdated", "Last update date and time") + val lastUpdatedOpt = text(xmlVersioning, "lastUpdated", "Last update date and time") + .right .toOption .flatMap(parseDateTime) - xmlSnapshotOpt = xmlVersioning.children + val xmlSnapshotOpt = xmlVersioning + .children .find(_.label == "snapshot") - timestamp = xmlSnapshotOpt + val timestamp = xmlSnapshotOpt .flatMap( text(_, "timestamp", "Snapshot timestamp") + .right .toOption ) .getOrElse("") - buildNumber = xmlSnapshotOpt + val buildNumber = xmlSnapshotOpt .flatMap( text(_, "buildNumber", "Snapshot build number") + .right .toOption ) .filter(s => s.nonEmpty && s.forall(_.isDigit)) .map(_.toInt) - localCopy = xmlSnapshotOpt + val localCopy = xmlSnapshotOpt .flatMap( text(_, "localCopy", "Snapshot local copy") + .right .toOption ) .collect { @@ -430,28 +478,21 @@ object Pom { case "false" => false } - xmlSnapshotVersions = xmlVersioning.children - .find(_.label == "snapshotVersions") - .map(_.children.filter(_.label == "snapshotVersion")) - .getOrElse(Seq.empty) - snapshotVersions <- xmlSnapshotVersions - .toList - .traverseU(snapshotVersion) - } yield SnapshotVersioning( - Module(organization, name, Map.empty), - version, - latest, - release, - timestamp, - buildNumber, - localCopy, - lastUpdatedOpt, - if (!snapshotVersions.isEmpty) - snapshotVersions - else - buildNumber.map(bn => guessedSnapshotVersion(version, timestamp, bn)).toList - ) - } + SnapshotVersioning( + Module(organization, name, Map.empty), + version, + latest, + release, + timestamp, + buildNumber, + localCopy, + lastUpdatedOpt, + if (!snapshotVersions.isEmpty) + snapshotVersions + else + buildNumber.map(bn => guessedSnapshotVersion(version, timestamp, bn)).toList + ) + } val relocatedPackaging = s"$$relocated" @@ -471,7 +512,7 @@ object Pom { val extraAttributeDropPrefix = "e:" - def extraAttribute(s: String): String \/ (Module, String) = { + def extraAttribute(s: String): Either[String, (Module, String)] = { // vaguely does the same as: // https://github.com/apache/ant-ivy/blob/2.2.0/src/java/org/apache/ivy/core/module/id/ModuleRevisionId.java#L291 @@ -483,39 +524,46 @@ object Pom { if (rawParts.length % 2 == 0) { val malformed = rawParts.filter(!_.startsWith(extraAttributePrefix)) if (malformed.isEmpty) - \/-(rawParts.map(_.drop(extraAttributePrefix.length))) + Right(rawParts.map(_.drop(extraAttributePrefix.length))) else - -\/(s"Malformed attributes ${malformed.map("'"+_+"'").mkString(", ")} in extra attributes '$s'") + Left(s"Malformed attributes ${malformed.map("'"+_+"'").mkString(", ")} in extra attributes '$s'") } else - -\/(s"Malformed extra attributes '$s'") + Left(s"Malformed extra attributes '$s'") - def attrFrom(attrs: Map[String, String], name: String): String \/ String = - \/.fromEither( - attrs.get(name) - .toRight(s"$name not found in extra attributes '$s'") - ) + def attrFrom(attrs: Map[String, String], name: String): Either[String, String] = + attrs + .get(name) + .toRight(s"$name not found in extra attributes '$s'") for { - parts <- partsOrError - attrs = parts.grouped(2).collect { - case Seq(k, v) if v != "NULL" => - k.stripPrefix(extraAttributeDropPrefix) -> v - }.toMap - org <- attrFrom(attrs, extraAttributeOrg) - name <- attrFrom(attrs, extraAttributeName) - version <- attrFrom(attrs, extraAttributeVersion) - remainingAttrs = attrs.filterKeys(!extraAttributeBase(_)) - } yield (Module(org, name, remainingAttrs.toVector.toMap), version) + parts <- partsOrError.right + attrs <- point( + parts + .grouped(2) + .collect { + case Seq(k, v) if v != "NULL" => + k.stripPrefix(extraAttributeDropPrefix) -> v + } + .toMap + ) + org <- attrFrom(attrs, extraAttributeOrg).right + name <- attrFrom(attrs, extraAttributeName).right + version <- attrFrom(attrs, extraAttributeVersion).right + } yield { + val remainingAttrs = attrs.filterKeys(!extraAttributeBase(_)) + (Module(org, name, remainingAttrs.toVector.toMap), version) + } } - def extraAttributes(s: String): String \/ Seq[(Module, String)] = { + def extraAttributes(s: String): Either[String, Seq[(Module, String)]] = { + val lines = s.split('\n').toSeq.map(_.trim).filter(_.nonEmpty) - lines.foldLeft[String \/ Seq[(Module, String)]](\/-(Vector.empty)) { + lines.foldLeft[Either[String, Seq[(Module, String)]]](Right(Vector.empty)) { case (acc, line) => for { - modVers <- acc - modVer <- extraAttribute(line) + modVers <- acc.right + modVer <- extraAttribute(line).right } yield modVers :+ modVer } } diff --git a/core/shared/src/main/scala/coursier/util/EitherT.scala b/core/shared/src/main/scala/coursier/util/EitherT.scala new file mode 100644 index 000000000..c4324e9f1 --- /dev/null +++ b/core/shared/src/main/scala/coursier/util/EitherT.scala @@ -0,0 +1,58 @@ +package coursier.util + +import scala.language.higherKinds +import scalaz.Monad + +final case class EitherT[F[_], L, R](run: F[Either[L, R]]) { + + def map[S](f: R => S)(implicit M: Monad[F]): EitherT[F, L, S] = + EitherT( + M.map(run)(e => e.right.map(f)) + ) + + def flatMap[S](f: R => EitherT[F, L, S])(implicit M: Monad[F]): EitherT[F, L, S] = + EitherT( + M.bind(run) { + case Left(l) => + M.point(Left(l)) + case Right(r) => + f(r).run + } + ) + + def leftMap[M](f: L => M)(implicit M: Monad[F]): EitherT[F, M, R] = + EitherT( + M.map(run)(e => e.left.map(f)) + ) + + def orElse(other: => EitherT[F, L, R])(implicit M: Monad[F]): EitherT[F, L, R] = + EitherT( + M.bind(run) { + case Left(_) => + other.run + case Right(r) => + M.point(Right(r)) + } + ) + + def scalaz(implicit M: Monad[F]): _root_.scalaz.EitherT[F, L, R] = + _root_.scalaz.EitherT( + M.map(run)(_root_.scalaz.\/.fromEither) + ) + +} + +object EitherT { + + def point[F[_], L, R](r: R)(implicit M: Monad[F]): EitherT[F, L, R] = + EitherT(M.point(Right(r))) + + def fromEither[F[_]]: FromEither[F] = + new FromEither[F] + + final class FromEither[F[_]] { + def apply[L, R](either: Either[L, R])(implicit M: Monad[F]): EitherT[F, L, R] = + EitherT(M.point(either)) + } + +} diff --git a/core/shared/src/main/scala/coursier/util/Parse.scala b/core/shared/src/main/scala/coursier/util/Parse.scala index b372850b8..2613bc2db 100644 --- a/core/shared/src/main/scala/coursier/util/Parse.scala +++ b/core/shared/src/main/scala/coursier/util/Parse.scala @@ -6,8 +6,6 @@ import coursier.ivy.IvyRepository import coursier.maven.MavenRepository import scala.collection.mutable.ArrayBuffer -import scalaz.\/ -import scalaz.Scalaz.ToEitherOps object Parse { @@ -354,35 +352,35 @@ object Parse { defaultScalaVersion: String): (Seq[String], Seq[(Dependency, Map[String, String])]) = valuesAndErrors(moduleVersionConfig(_, req, transitive, defaultScalaVersion), l) - def repository(s: String): String \/ Repository = + def repository(s: String): Either[String, Repository] = if (s == "central") - MavenRepository("https://repo1.maven.org/maven2").right + Right(MavenRepository("https://repo1.maven.org/maven2")) else if (s.startsWith("sonatype:")) - MavenRepository(s"https://oss.sonatype.org/content/repositories/${s.stripPrefix("sonatype:")}").right + Right(MavenRepository(s"https://oss.sonatype.org/content/repositories/${s.stripPrefix("sonatype:")}")) else if (s.startsWith("bintray:")) - MavenRepository(s"https://dl.bintray.com/${s.stripPrefix("bintray:")}").right + Right(MavenRepository(s"https://dl.bintray.com/${s.stripPrefix("bintray:")}")) else if (s.startsWith("bintray-ivy:")) - IvyRepository.fromPattern( + Right(IvyRepository.fromPattern( s"https://dl.bintray.com/${s.stripPrefix("bintray-ivy:").stripSuffix("/")}/" +: coursier.ivy.Pattern.default - ).right + )) else if (s.startsWith("typesafe:ivy-")) - IvyRepository.fromPattern( + Right(IvyRepository.fromPattern( s"https://repo.typesafe.com/typesafe/ivy-${s.stripPrefix("typesafe:ivy-")}/" +: coursier.ivy.Pattern.default - ).right + )) else if (s.startsWith("typesafe:")) - MavenRepository(s"https://repo.typesafe.com/typesafe/${s.stripPrefix("typesafe:")}").right + Right(MavenRepository(s"https://repo.typesafe.com/typesafe/${s.stripPrefix("typesafe:")}")) else if (s.startsWith("sbt-plugin:")) - IvyRepository.fromPattern( + Right(IvyRepository.fromPattern( s"https://repo.scala-sbt.org/scalasbt/sbt-plugin-${s.stripPrefix("sbt-plugin:")}/" +: coursier.ivy.Pattern.default - ).right + )) else if (s.startsWith("ivy:")) IvyRepository.parse(s.stripPrefix("ivy:")) else if (s == "jitpack") - MavenRepository("https://jitpack.io").right + Right(MavenRepository("https://jitpack.io")) else - MavenRepository(s).right + Right(MavenRepository(s)) } diff --git a/core/shared/src/main/scala/coursier/util/Xml.scala b/core/shared/src/main/scala/coursier/util/Xml.scala index a7469d659..026ef15e3 100644 --- a/core/shared/src/main/scala/coursier/util/Xml.scala +++ b/core/shared/src/main/scala/coursier/util/Xml.scala @@ -2,8 +2,6 @@ package coursier.util import coursier.core.Versions -import scalaz.{\/-, -\/, \/, Scalaz} - object Xml { /** A representation of an XML node/document, with different implementations on JVM and JS */ @@ -23,10 +21,10 @@ object Xml { } lazy val attributesMap = attributes.map { case (_, k, v) => k -> v }.toMap - def attribute(name: String): String \/ String = + def attribute(name: String): Either[String, String] = attributesMap.get(name) match { - case None => -\/(s"Missing attribute $name") - case Some(value) => \/-(value) + case None => Left(s"Missing attribute $name") + case Some(value) => Right(value) } } @@ -48,14 +46,11 @@ object Xml { else None } - def text(elem: Node, label: String, description: String) = { - import Scalaz.ToOptionOpsFromOption - + def text(elem: Node, label: String, description: String): Either[String, String] = elem.children .find(_.label == label) .flatMap(_.children.collectFirst{case Text(t) => t}) - .toRightDisjunction(s"$description not found") - } + .toRight(s"$description not found") def parseDateTime(s: String): Option[Versions.DateTime] = if (s.length == 14 && s.forall(_.isDigit)) diff --git a/doc/readme/README.md b/doc/readme/README.md index f56413ea1..056b0deb0 100644 --- a/doc/readme/README.md +++ b/doc/readme/README.md @@ -131,6 +131,8 @@ libraryDependencies ++= Seq( ) ``` +Note that the examples below are validated against the current sources of coursier. You may want to read the [documentation of the latest release](https://github.com/coursier/coursier/blob/v1.0.2/README.md#api) of coursier instead. + Add an import for coursier, ```scala import coursier._ @@ -208,10 +210,9 @@ These would mean that the resolution wasn't able to get metadata about some depe Then fetch and get local copies of the artifacts themselves (the JARs) with ```tut:silent import java.io.File -import scalaz.\/ import scalaz.concurrent.Task -val localArtifacts: Seq[FileError \/ File] = Task.gatherUnordered( +val localArtifacts: Seq[Either[FileError, File]] = Task.gatherUnordered( resolution.artifacts.map(Cache.file(_).run) ).unsafePerformSync ``` @@ -451,6 +452,8 @@ libraryDependencies ++= Seq( ) ``` +Note that the examples below are validated against the current sources of coursier. You may want to read the [documentation of the latest release](https://github.com/coursier/coursier/blob/v1.0.2/README.md#api-1) of coursier instead. + The first module, `"io.get-coursier" %% "coursier" % "1.0.1"`, mainly depends on `scalaz-core` (and only it, *not* `scalaz-concurrent` for example). It contains among others, definitions, @@ -536,7 +539,7 @@ MavenRepository( Now that we have repositories, we're going to mix these with things from the `coursier-cache` module, for resolution to happen via the cache. We'll create a function -of type `Seq[(Module, String)] => F[Seq[((Module, String), Seq[String] \/ (Artifact.Source, Project))]]`. +of type `Seq[(Module, String)] => F[Seq[((Module, String), Either[Seq[String], (Artifact.Source, Project)])]]`. Given a sequence of dependencies, designated by their `Module` (organisation and name in most cases) and version (just a `String`), it gives either errors (`Seq[String]`) or metadata (`(Artifact.Source, Project)`), wrapping the whole in a monad `F`. @@ -591,10 +594,9 @@ which are dependencies whose versions could not be unified. Then, if all went well, we can fetch and get local copies of the artifacts themselves (the JARs) with ```tut:silent import java.io.File -import scalaz.\/ import scalaz.concurrent.Task -val localArtifacts: Seq[FileError \/ File] = Task.gatherUnordered( +val localArtifacts: Seq[Either[FileError, File]] = Task.gatherUnordered( resolution.artifacts.map(Cache.file(_).run) ).unsafePerformSync ``` diff --git a/extra/src/main/scala/coursier/FallbackDependenciesRepository.scala b/extra/src/main/scala/coursier/FallbackDependenciesRepository.scala index 9f7416282..1f984d00d 100644 --- a/extra/src/main/scala/coursier/FallbackDependenciesRepository.scala +++ b/extra/src/main/scala/coursier/FallbackDependenciesRepository.scala @@ -1,13 +1,12 @@ package coursier -import java.io.{ File, FileNotFoundException, IOException } -import java.net.{ HttpURLConnection, URL, URLConnection } +import java.io.{File, FileNotFoundException, IOException} +import java.net.{HttpURLConnection, URL, URLConnection} + +import coursier.util.EitherT import scala.language.higherKinds - -import scalaz.{ EitherT, Monad } -import scalaz.syntax.monad._ -import scalaz.Scalaz.ToEitherOps +import scalaz.Monad object FallbackDependenciesRepository { @@ -106,14 +105,14 @@ final case class FallbackDependenciesRepository( val res = fallbacks .get((module, version)) - .fold("No fallback URL found".left[(Artifact.Source, Project)]) { + .fold[Either[String, (Artifact.Source, Project)]](Left("No fallback URL found")) { case (url, _) => val urlStr = url.toExternalForm val idx = urlStr.lastIndexOf('/') if (idx < 0 || urlStr.endsWith("/")) - s"$url doesn't point to a file".left + Left(s"$url doesn't point to a file") else { val (dirUrlStr, fileName) = urlStr.splitAt(idx + 1) @@ -135,12 +134,12 @@ final case class FallbackDependenciesRepository( Info.empty ) - (source, proj).right + Right((source, proj)) } else - s"$fileName not found under $dirUrlStr".left + Left(s"$fileName not found under $dirUrlStr") } } - EitherT(res.point) + EitherT(F.point(res)) } } diff --git a/fetch-js/src/main/scala/coursier/Platform.scala b/fetch-js/src/main/scala/coursier/Platform.scala index 4e41f1c33..06c75a581 100644 --- a/fetch-js/src/main/scala/coursier/Platform.scala +++ b/fetch-js/src/main/scala/coursier/Platform.scala @@ -1,16 +1,13 @@ package coursier -import org.scalajs.dom.raw.{ Event, XMLHttpRequest } - -import scala.concurrent.{ ExecutionContext, Promise, Future } +import coursier.util.EitherT +import org.scalajs.dom.raw.{Event, XMLHttpRequest} +import scala.concurrent.{ExecutionContext, Future, Promise} import scala.language.implicitConversions - import scala.scalajs.js -import js.Dynamic.{ global => g } - +import js.Dynamic.{global => g} import scala.scalajs.js.timers._ -import scalaz.{ -\/, \/-, EitherT } object Platform { @@ -80,9 +77,9 @@ object Platform { EitherT( Task { implicit ec => get(artifact.url) - .map(\/-(_)) + .map(Right(_)) .recover { case e: Exception => - -\/(e.toString + Option(e.getMessage).fold("")(" (" + _ + ")")) + Left(e.toString + Option(e.getMessage).fold("")(" (" + _ + ")")) } } ) @@ -104,11 +101,11 @@ object Platform { Task { implicit ec => Future(logger.fetching(artifact.url)) .flatMap(_ => get(artifact.url)) - .map { s => logger.fetched(artifact.url); \/-(s) } + .map { s => logger.fetched(artifact.url); Right(s) } .recover { case e: Exception => val msg = e.toString + Option(e.getMessage).fold("")(" (" + _ + ")") logger.other(artifact.url, msg) - -\/(msg) + Left(msg) } } ) diff --git a/project/Mima.scala b/project/Mima.scala index 700d61e18..e2230a066 100644 --- a/project/Mima.scala +++ b/project/Mima.scala @@ -10,25 +10,6 @@ object Mima { // Important: the line with the "binary compatibility versions" comment below is matched during releases def binaryCompatibilityVersions = Set( - "1.0.0-RC1", - "1.0.0-RC2", - "1.0.0-RC3", - "1.0.0-RC4", - "1.0.0-RC5", - "1.0.0-RC6", - "1.0.0-RC7", - "1.0.0-RC8", - "1.0.0-RC9", - "1.0.0-RC10", - "1.0.0-RC11", - "1.0.0-RC12", - "1.0.0-RC12-1", - "1.0.0-RC13", - "1.0.0-RC14", - "1.0.0", - "1.0.1-M1", - "1.0.1", - "1.0.2", "" // binary compatibility versions ) diff --git a/sbt-coursier/src/main/scala/coursier/InterProjectRepository.scala b/sbt-coursier/src/main/scala/coursier/InterProjectRepository.scala index 9d1bd73a8..c6c58410e 100644 --- a/sbt-coursier/src/main/scala/coursier/InterProjectRepository.scala +++ b/sbt-coursier/src/main/scala/coursier/InterProjectRepository.scala @@ -1,10 +1,9 @@ package coursier -import scala.language.higherKinds +import coursier.util.EitherT -import scalaz.{EitherT, Monad} -import scalaz.syntax.monad._ -import scalaz.syntax.std.option._ +import scala.language.higherKinds +import scalaz.Monad final case class InterProjectRepository(projects: Seq[Project]) extends Repository { @@ -12,17 +11,19 @@ final case class InterProjectRepository(projects: Seq[Project]) extends Reposito .map(proj => proj.moduleVersion -> proj) .toMap - def find[F[_]: Monad]( + def find[F[_]]( module: Module, version: String, fetch: Fetch.Content[F] + )(implicit + F: Monad[F] ): EitherT[F, String, (Artifact.Source, Project)] = { val res = map .get((module, version)) - .toRightDisjunction("Not found") .map((Artifact.Source.empty, _)) + .toRight("Not found") - EitherT(res.point) + EitherT(F.point(res)) } } \ No newline at end of file diff --git a/sbt-coursier/src/main/scala/coursier/Keys.scala b/sbt-coursier/src/main/scala/coursier/Keys.scala index a76940abe..75c7441cd 100644 --- a/sbt-coursier/src/main/scala/coursier/Keys.scala +++ b/sbt-coursier/src/main/scala/coursier/Keys.scala @@ -8,7 +8,6 @@ import sbt.librarymanagement.GetClassifiersModule import sbt.{Resolver, SettingKey, TaskKey} import scala.concurrent.duration.Duration -import scalaz.\/ object Keys { val coursierParallelDownloads = SettingKey[Int]("coursier-parallel-downloads") @@ -65,8 +64,8 @@ object Keys { "Prints dependencies and transitive dependencies as an inverted tree (dependees as children)" ) - val coursierArtifacts = TaskKey[Map[Artifact, FileError \/ File]]("coursier-artifacts") - val coursierSignedArtifacts = TaskKey[Map[Artifact, FileError \/ File]]("coursier-signed-artifacts") - val coursierClassifiersArtifacts = TaskKey[Map[Artifact, FileError \/ File]]("coursier-classifiers-artifacts") - val coursierSbtClassifiersArtifacts = TaskKey[Map[Artifact, FileError \/ File]]("coursier-sbt-classifiers-artifacts") + val coursierArtifacts = TaskKey[Map[Artifact, Either[FileError, File]]]("coursier-artifacts") + val coursierSignedArtifacts = TaskKey[Map[Artifact, Either[FileError, File]]]("coursier-signed-artifacts") + val coursierClassifiersArtifacts = TaskKey[Map[Artifact, Either[FileError, File]]]("coursier-classifiers-artifacts") + val coursierSbtClassifiersArtifacts = TaskKey[Map[Artifact, Either[FileError, File]]]("coursier-sbt-classifiers-artifacts") } diff --git a/sbt-coursier/src/main/scala/coursier/Tasks.scala b/sbt-coursier/src/main/scala/coursier/Tasks.scala index 119db5a50..ad2649fc0 100644 --- a/sbt-coursier/src/main/scala/coursier/Tasks.scala +++ b/sbt-coursier/src/main/scala/coursier/Tasks.scala @@ -17,7 +17,6 @@ import sbt.Keys._ import scala.collection.mutable import scala.collection.mutable.ArrayBuffer import scala.util.Try -import scalaz.{-\/, \/-} import scalaz.concurrent.{Strategy, Task} object Tasks { @@ -450,16 +449,16 @@ object Tasks { { s => val p = PropertiesPattern.parse(s) match { - case -\/(err) => + case Left(err) => throw new Exception(s"Cannot parse pattern $s: $err") - case \/-(p) => + case Right(p) => p } p.substituteProperties(props ++ extraProps) match { - case -\/(err) => + case Left(err) => throw new Exception(err) - case \/-(p) => + case Right(p) => p } } @@ -754,7 +753,9 @@ object Tasks { .process .run(fetch, maxIterations) .unsafePerformSyncAttempt - .leftMap(ex => + .toEither + .left + .map(ex => ResolutionError.UnknownException(ex) .throwException() ) @@ -1042,11 +1043,11 @@ object Tasks { artifactsLogger.init(if (printOptionalMessage) log.info(artifactInitialMessage)) - Task.gatherUnordered(artifactFileOrErrorTasks).unsafePerformSyncAttempt match { - case -\/(ex) => + Task.gatherUnordered(artifactFileOrErrorTasks).unsafePerformSyncAttempt.toEither match { + case Left(ex) => ResolutionError.UnknownDownloadException(ex) .throwException() - case \/-(l) => + case Right(l) => l.toMap } } finally { @@ -1270,14 +1271,14 @@ object Tasks { } val artifactFiles = artifactFilesOrErrors0.collect { - case (artifact, \/-(file)) => + case (artifact, Right(file)) => artifact -> file } val artifactErrors = artifactFilesOrErrors0 .toVector .collect { - case (a, -\/(err)) if !a.isOptional || !err.notFound => + case (a, Left(err)) if !a.isOptional || !err.notFound => a -> err } @@ -1292,7 +1293,7 @@ object Tasks { // can be non empty only if ignoreArtifactErrors is true or some optional artifacts are not found val erroredArtifacts = artifactFilesOrErrors0.collect { - case (artifact, -\/(_)) => + case (artifact, Left(_)) => artifact }.toSet diff --git a/sbt-shading/src/main/scala/coursier/Shading.scala b/sbt-shading/src/main/scala/coursier/Shading.scala index 1158d0ae6..7350d4582 100644 --- a/sbt-shading/src/main/scala/coursier/Shading.scala +++ b/sbt-shading/src/main/scala/coursier/Shading.scala @@ -11,8 +11,6 @@ import com.tonicsystems.jarjar.transform.jar.DefaultJarProcessor import coursier.core.Orders import sbt.file -import scalaz.{\/, \/-} - object Shading { // FIXME Also vaguely in cli @@ -58,7 +56,7 @@ object Shading { currentProject: Project, res: Resolution, configs: Map[String, Set[String]], - artifactFilesOrErrors: Map[Artifact, FileError \/ File], + artifactFilesOrErrors: Map[Artifact, Either[FileError, File]], classpathTypes: Set[String], baseConfig: String, shadedConf: String, @@ -101,7 +99,7 @@ object Shading { val artifactFilesOrErrors0 = artifactFilesOrErrors .collect { - case (a, \/-(f)) => a.url -> f + case (a, Right(f)) => a.url -> f } val compileDeps = configDependencies(baseConfig) diff --git a/sbt-shared/src/main/scala/coursier/FromSbt.scala b/sbt-shared/src/main/scala/coursier/FromSbt.scala index 54c9d7eb2..204e87a3e 100644 --- a/sbt-shared/src/main/scala/coursier/FromSbt.scala +++ b/sbt-shared/src/main/scala/coursier/FromSbt.scala @@ -10,8 +10,6 @@ import sbt.librarymanagement._ import sbt.librarymanagement.Resolver import sbt.util.Logger -import scalaz.{-\/, \/-} - object FromSbt { def sbtModuleIdName( @@ -227,11 +225,11 @@ object FromSbt { dropInfoAttributes = true, authentication = authentication ) match { - case -\/(err) => + case Left(err) => sys.error( s"Cannot parse Ivy patterns ${r.patterns.artifactPatterns.head} and ${r.patterns.ivyPatterns.head}: $err" ) - case \/-(repo) => + case Right(repo) => repo } @@ -258,11 +256,11 @@ object FromSbt { dropInfoAttributes = true, authentication = authentication ) match { - case -\/(err) => + case Left(err) => sys.error( s"Cannot parse Ivy patterns ${r.patterns.artifactPatterns.head} and ${r.patterns.ivyPatterns.head}: $err" ) - case \/-(repo) => + case Right(repo) => repo } diff --git a/tests/js/src/test/scala/coursier/test/compatibility/package.scala b/tests/js/src/test/scala/coursier/test/compatibility/package.scala index ab3b31bce..0f2c9b472 100644 --- a/tests/js/src/test/scala/coursier/test/compatibility/package.scala +++ b/tests/js/src/test/scala/coursier/test/compatibility/package.scala @@ -1,12 +1,11 @@ package coursier.test -import coursier.util.TestEscape +import coursier.util.{EitherT, TestEscape} import coursier.{Fetch, Task} -import scala.concurrent.{Promise, ExecutionContext, Future} +import scala.concurrent.{ExecutionContext, Future, Promise} import scala.scalajs.js import js.Dynamic.{global => g} -import scalaz.{-\/, EitherT, \/-} package object compatibility { @@ -40,10 +39,10 @@ package object compatibility { Task { implicit ec => textResource0(path) - .map(\/-(_)) + .map(Right(_)) .recoverWith { case e: Exception => - Future.successful(-\/(e.getMessage)) + Future.successful(Left(e.getMessage)) } } } diff --git a/tests/jvm/src/test/scala/coursier/test/IvyTests.scala b/tests/jvm/src/test/scala/coursier/test/IvyTests.scala index 4f526e466..6632149b7 100644 --- a/tests/jvm/src/test/scala/coursier/test/IvyTests.scala +++ b/tests/jvm/src/test/scala/coursier/test/IvyTests.scala @@ -14,7 +14,7 @@ object IvyTests extends TestSuite { "[organisation]/[module]/(scala_[scalaVersion]/)(sbt_[sbtVersion]/)" + "[revision]/[type]s/[artifact](-[classifier]).[ext]", dropInfoAttributes = true - ).getOrElse( + ).right.getOrElse( throw new Exception("Cannot happen") ) diff --git a/tests/jvm/src/test/scala/coursier/test/ResolutionProcessTests.scala b/tests/jvm/src/test/scala/coursier/test/ResolutionProcessTests.scala index 6fa1039cc..1dd33e9be 100644 --- a/tests/jvm/src/test/scala/coursier/test/ResolutionProcessTests.scala +++ b/tests/jvm/src/test/scala/coursier/test/ResolutionProcessTests.scala @@ -8,7 +8,6 @@ import utest._ import scala.collection.JavaConverters._ import scala.concurrent.duration.DurationInt -import scalaz.{-\/, \/-} import scalaz.concurrent.Task object ResolutionProcessTests extends TestSuite { @@ -40,7 +39,7 @@ object ResolutionProcessTests extends TestSuite { case Seq(mv @ (`mod`, v)) => Task.async { cb => called.put(v, ()) - cb(\/-(Seq((mv, -\/(Seq("w/e")))))) + cb(scalaz.\/-(Seq((mv, Left(Seq("w/e")))))) } case _ => sys.error(s"Cannot happen ($modVers)") diff --git a/tests/jvm/src/test/scala/coursier/test/compatibility/package.scala b/tests/jvm/src/test/scala/coursier/test/compatibility/package.scala index f7179df15..8912d45ae 100644 --- a/tests/jvm/src/test/scala/coursier/test/compatibility/package.scala +++ b/tests/jvm/src/test/scala/coursier/test/compatibility/package.scala @@ -3,11 +3,10 @@ package coursier.test import java.nio.charset.StandardCharsets import java.nio.file.{Files, Paths} -import coursier.util.TestEscape +import coursier.util.{EitherT, TestEscape} import coursier.{Cache, Fetch, Platform} import scala.concurrent.{ExecutionContext, Future} -import scalaz.{-\/, EitherT, \/, \/-} import scalaz.concurrent.Task package object compatibility { @@ -49,20 +48,20 @@ package object compatibility { val init = EitherT[Task, String, Unit] { if (Files.exists(path)) - Task.now(\/-(())) + Task.now(Right(())) else if (fillChunks) - Task[String \/ Unit] { + Task[Either[String, Unit]] { Files.createDirectories(path.getParent) def is() = Cache.urlConnection(artifact.url, artifact.authentication).getInputStream val b = Platform.readFullySync(is()) Files.write(path, b) - \/-(()) + Right(()) }.handle { case e: Exception => - -\/(e.toString) + Left(e.toString) } else - Task.now(-\/(s"not found: $path")) + Task.now(Left(s"not found: $path")) } init.flatMap { _ => diff --git a/tests/shared/src/test/resources/resolutions/io.get-coursier/coursier_2.11/1.0.3-SNAPSHOT b/tests/shared/src/test/resources/resolutions/io.get-coursier/coursier_2.11/1.1.0-SNAPSHOT similarity index 72% rename from tests/shared/src/test/resources/resolutions/io.get-coursier/coursier_2.11/1.0.3-SNAPSHOT rename to tests/shared/src/test/resources/resolutions/io.get-coursier/coursier_2.11/1.1.0-SNAPSHOT index 11e1d67c5..1c067087c 100644 --- a/tests/shared/src/test/resources/resolutions/io.get-coursier/coursier_2.11/1.0.3-SNAPSHOT +++ b/tests/shared/src/test/resources/resolutions/io.get-coursier/coursier_2.11/1.1.0-SNAPSHOT @@ -1,4 +1,4 @@ -io.get-coursier:coursier_2.11:1.0.3-SNAPSHOT:compile +io.get-coursier:coursier_2.11:1.1.0-SNAPSHOT:compile org.scala-lang:scala-library:2.11.12:default org.scala-lang.modules:scala-xml_2.11:1.0.6:default org.scalaz:scalaz-core_2.11:7.2.16:default diff --git a/tests/shared/src/test/scala/coursier/test/ActivationTests.scala b/tests/shared/src/test/scala/coursier/test/ActivationTests.scala index 12a8117a6..d7d7f9a7c 100644 --- a/tests/shared/src/test/scala/coursier/test/ActivationTests.scala +++ b/tests/shared/src/test/scala/coursier/test/ActivationTests.scala @@ -4,8 +4,6 @@ import coursier.core.{Activation, Parse} import coursier.core.Activation.Os import utest._ -import scalaz.{-\/, \/-} - object ActivationTests extends TestSuite { def parseVersion(s: String) = Parse.version(s).getOrElse(???) @@ -210,7 +208,7 @@ object ActivationTests extends TestSuite { val activation = Activation( Nil, Os.empty, - Some(\/-(Seq(parseVersion("1.8.0_112")))) + Some(Right(Seq(parseVersion("1.8.0_112")))) ) val isActive = activation.isActive(Map(), Os.empty, Some(jdkVersion)) @@ -222,7 +220,7 @@ object ActivationTests extends TestSuite { val activation = Activation( Nil, Os.empty, - Some(\/-(Seq(parseVersion("1.8.0_102"), parseVersion("1.8.0_112")))) + Some(Right(Seq(parseVersion("1.8.0_102"), parseVersion("1.8.0_112")))) ) val isActive = activation.isActive(Map(), Os.empty, Some(jdkVersion)) @@ -235,7 +233,7 @@ object ActivationTests extends TestSuite { val activation = Activation( Nil, Os.empty, - Some(\/-(Seq(parseVersion("1.8.0_102")))) + Some(Right(Seq(parseVersion("1.8.0_102")))) ) val isActive = activation.isActive(Map(), Os.empty, Some(jdkVersion)) @@ -248,7 +246,7 @@ object ActivationTests extends TestSuite { val activation = Activation( Nil, Os.empty, - Some(\/-(Seq(parseVersion("1.8.0_92"), parseVersion("1.8.0_102")))) + Some(Right(Seq(parseVersion("1.8.0_92"), parseVersion("1.8.0_102")))) ) val isActive = activation.isActive(Map(), Os.empty, Some(jdkVersion)) @@ -260,7 +258,7 @@ object ActivationTests extends TestSuite { val activation = Activation( Nil, Os.empty, - Some(-\/(parseVersionInterval("[1.8,)"))) + Some(Left(parseVersionInterval("[1.8,)"))) ) val isActive = activation.isActive(Map(), Os.empty, Some(jdkVersion)) @@ -272,7 +270,7 @@ object ActivationTests extends TestSuite { val activation = Activation( Nil, Os.empty, - Some(-\/(parseVersionInterval("[1.7,1.8)"))) + Some(Left(parseVersionInterval("[1.7,1.8)"))) ) val isActive = activation.isActive(Map(), Os.empty, Some(jdkVersion)) @@ -290,7 +288,7 @@ object ActivationTests extends TestSuite { "requiredWithNegValue" -> Some("!bar") ), Os(None, Set("mac"), None, None), - Some(-\/(parseVersionInterval("[1.8,)"))) + Some(Left(parseVersionInterval("[1.8,)"))) ) 'match - { diff --git a/tests/shared/src/test/scala/coursier/test/IvyPatternParserTests.scala b/tests/shared/src/test/scala/coursier/test/IvyPatternParserTests.scala index 55d7742f1..4d40fe9e8 100644 --- a/tests/shared/src/test/scala/coursier/test/IvyPatternParserTests.scala +++ b/tests/shared/src/test/scala/coursier/test/IvyPatternParserTests.scala @@ -6,8 +6,6 @@ import coursier.ivy.PropertiesPattern.ChunkOrProperty._ import utest._ -import scalaz.Scalaz.ToEitherOps - object IvyPatternParserTests extends TestSuite { val tests = TestSuite { @@ -23,7 +21,7 @@ object IvyPatternParserTests extends TestSuite { "/resolved.xml.", Var("ext") ) - assert(PropertiesPattern.parse(strPattern).map(_.chunks) == expectedChunks.right) + assert(PropertiesPattern.parse(strPattern).right.map(_.chunks) == Right(expectedChunks)) } 'activatorLaunchLocal - { @@ -50,48 +48,48 @@ object IvyPatternParserTests extends TestSuite { ) val pattern0 = PropertiesPattern.parse(strPattern) - assert(pattern0.map(_.chunks) == expectedChunks.right) + assert(pattern0.right.map(_.chunks) == Right(expectedChunks)) - val pattern = pattern0.toOption.get + val pattern = pattern0.right.get * - { val varPattern = pattern.substituteProperties(Map( "activator.local.repository" -> "xyz" - )).map(_.string) + )).right.map(_.string) val expectedVarPattern = "file://xyz" + "/[organization]/[module]/(scala_[scalaVersion]/)(sbt_[sbtVersion]/)" + "[revision]/[type]s/[artifact](-[classifier]).[ext]" - assert(varPattern == expectedVarPattern.right) + assert(varPattern == Right(expectedVarPattern)) } * - { val varPattern = pattern.substituteProperties(Map( "activator.local.repository" -> "xyz", "activator.home" -> "aaaa" - )).map(_.string) + )).right.map(_.string) val expectedVarPattern = "file://xyz" + "/[organization]/[module]/(scala_[scalaVersion]/)(sbt_[sbtVersion]/)" + "[revision]/[type]s/[artifact](-[classifier]).[ext]" - assert(varPattern == expectedVarPattern.right) + assert(varPattern == Right(expectedVarPattern)) } * - { val varPattern = pattern.substituteProperties(Map( "activator.home" -> "aaaa" - )).map(_.string) + )).right.map(_.string) val expectedVarPattern = "file://aaaa/repository" + "/[organization]/[module]/(scala_[scalaVersion]/)(sbt_[sbtVersion]/)" + "[revision]/[type]s/[artifact](-[classifier]).[ext]" - assert(varPattern == expectedVarPattern.right) + assert(varPattern == Right(expectedVarPattern)) } * - { @@ -104,9 +102,9 @@ object IvyPatternParserTests extends TestSuite { "/[organization]/[module]/(scala_[scalaVersion]/)(sbt_[sbtVersion]/)" + "[revision]/[type]s/[artifact](-[classifier]).[ext]" - assert(varPattern0.map(_.string) == expectedVarPattern.right) + assert(varPattern0.right.map(_.string) == Right(expectedVarPattern)) - val varPattern = varPattern0.toOption.get + val varPattern = varPattern0.right.toOption.get * - { val res = varPattern.substituteVariables(Map( @@ -117,10 +115,10 @@ object IvyPatternParserTests extends TestSuite { "artifact" -> "art", "classifier" -> "docc", "ext" -> "jrr" - )).map(_.string) + )).right.map(_.string) val expectedRes = "file://homez/.activator/repository/org/mod/1.1.x/jarrs/art-docc.jrr" - assert(res == expectedRes.right) + assert(res == Right(expectedRes)) } } diff --git a/tests/shared/src/test/scala/coursier/test/ParseTests.scala b/tests/shared/src/test/scala/coursier/test/ParseTests.scala index f919419a5..f32c8c0db 100644 --- a/tests/shared/src/test/scala/coursier/test/ParseTests.scala +++ b/tests/shared/src/test/scala/coursier/test/ParseTests.scala @@ -25,30 +25,30 @@ object ParseTests extends TestSuite { val tests = TestSuite { "bintray-ivy:" - { val obtained = Parse.repository("bintray-ivy:scalameta/maven") - assert(obtained.exists(isIvyRepo)) + assert(obtained.right.exists(isIvyRepo)) } "bintray:" - { val obtained = Parse.repository("bintray:scalameta/maven") - assert(obtained.exists(isMavenRepo)) + assert(obtained.right.exists(isMavenRepo)) } "sbt-plugin:" - { val res = Parse.repository("sbt-plugin:releases") - assert(res.exists(isIvyRepo)) + assert(res.right.exists(isIvyRepo)) } "typesafe:ivy-" - { val res = Parse.repository("typesafe:ivy-releases") - assert(res.exists(isIvyRepo)) + assert(res.right.exists(isIvyRepo)) } "typesafe:" - { val res = Parse.repository("typesafe:releases") - assert(res.exists(isMavenRepo)) + assert(res.right.exists(isMavenRepo)) } "jitpack" - { val res = Parse.repository("jitpack") - assert(res.exists(isMavenRepo)) + assert(res.right.exists(isMavenRepo)) } // Module parsing tests diff --git a/tests/shared/src/test/scala/coursier/test/PomParsingTests.scala b/tests/shared/src/test/scala/coursier/test/PomParsingTests.scala index 0d2eadeca..f7006d7b6 100644 --- a/tests/shared/src/test/scala/coursier/test/PomParsingTests.scala +++ b/tests/shared/src/test/scala/coursier/test/PomParsingTests.scala @@ -2,7 +2,6 @@ package coursier package test import utest._ -import scalaz._ import coursier.maven.Pom @@ -21,7 +20,7 @@ object PomParsingTests extends TestSuite { """ - val expected = \/-( + val expected = Right( "" -> Dependency( Module("comp", "lib"), "2.1", @@ -40,7 +39,7 @@ object PomParsingTests extends TestSuite { """ - val expected = \/-(Profile("profile1", None, Profile.Activation(Nil), Nil, Nil, Map.empty)) + val expected = Right(Profile("profile1", None, Profile.Activation(Nil), Nil, Nil, Map.empty)) val result = Pom.profile(xmlParse(profileNode).right.get) @@ -55,7 +54,7 @@ object PomParsingTests extends TestSuite { """ - val expected = \/-(Profile("", Some(true), Profile.Activation(Nil), Nil, Nil, Map.empty)) + val expected = Right(Profile("", Some(true), Profile.Activation(Nil), Nil, Nil, Map.empty)) val result = Pom.profile(xmlParse(profileNode).right.get) @@ -71,7 +70,7 @@ object PomParsingTests extends TestSuite { """ - val expected = \/-(Profile("profile1", Some(true), Profile.Activation(Nil), Nil, Nil, Map.empty)) + val expected = Right(Profile("profile1", Some(true), Profile.Activation(Nil), Nil, Nil, Map.empty)) val result = Pom.profile(xmlParse(profileNode).right.get) @@ -88,7 +87,7 @@ object PomParsingTests extends TestSuite { """ - val expected = \/-(Profile("profile1", None, Profile.Activation(List("hadoop.profile" -> None)), Nil, Nil, Map.empty)) + val expected = Right(Profile("profile1", None, Profile.Activation(List("hadoop.profile" -> None)), Nil, Nil, Map.empty)) val result = Pom.profile(xmlParse(profileNode).right.get) assert(result == expected) @@ -106,7 +105,7 @@ object PomParsingTests extends TestSuite { """ - val expected = \/-(Profile("profile1", None, Profile.Activation(List("hadoop.profile" -> Some("yes"))), Nil, Nil, Map.empty)) + val expected = Right(Profile("profile1", None, Profile.Activation(List("hadoop.profile" -> Some("yes"))), Nil, Nil, Map.empty)) val result = Pom.profile(xmlParse(profileNode).right.get) assert(result == expected) @@ -126,7 +125,7 @@ object PomParsingTests extends TestSuite { """ - val expected = \/-(Profile( + val expected = Right(Profile( "profile1", None, Profile.Activation(Nil), @@ -157,7 +156,7 @@ object PomParsingTests extends TestSuite { """ - val expected = \/-(Profile( + val expected = Right(Profile( "profile1", None, Profile.Activation(Nil), @@ -181,7 +180,7 @@ object PomParsingTests extends TestSuite { """ - val expected = \/-(Profile( + val expected = Right(Profile( "profile1", None, Profile.Activation(Nil), @@ -204,7 +203,7 @@ object PomParsingTests extends TestSuite { """ - val expected = \/-(Profile( + val expected = Right(Profile( "profile1", None, Profile.Activation(Nil), @@ -218,7 +217,7 @@ object PomParsingTests extends TestSuite { assert(result == expected) } 'beFineWithCommentsInProperties{ - import scalaz._, Scalaz._ + import scalaz.Scalaz.{eitherMonad, listInstance, ToTraverseOps} val properties = """ @@ -258,12 +257,12 @@ object PomParsingTests extends TestSuite { val node = parsed.right.get assert(node.label == "properties") - val children = node.children.collect{case elem if elem.isElement => elem} + val children = node.children.collect { case elem if elem.isElement => elem } val props0 = children.toList.traverseU(Pom.property) assert(props0.isRight) - val props = props0.getOrElse(???).toMap + val props = props0.right.getOrElse(???).toMap assert(props.contains("commons.release.2.desc")) assert(props.contains("commons.osgi.export")) diff --git a/tests/shared/src/test/scala/coursier/test/TestRepository.scala b/tests/shared/src/test/scala/coursier/test/TestRepository.scala index 94e51dcb0..7e29cd4e1 100644 --- a/tests/shared/src/test/scala/coursier/test/TestRepository.scala +++ b/tests/shared/src/test/scala/coursier/test/TestRepository.scala @@ -2,11 +2,10 @@ package coursier package test import coursier.core._ +import coursier.util.EitherT import scala.language.higherKinds - -import scalaz.{ Monad, EitherT } -import scalaz.Scalaz._ +import scalaz.Monad final case class TestRepository(projects: Map[(Module, String), Project]) extends Repository { val source = new core.Artifact.Source { @@ -23,7 +22,9 @@ final case class TestRepository(projects: Map[(Module, String), Project]) extend )(implicit F: Monad[F] ) = - EitherT(F.point( - projects.get((module, version)).map((source, _)).toRightDisjunction("Not found") - )) + EitherT( + F.point( + projects.get((module, version)).map((source, _)).toRight("Not found") + ) + ) } diff --git a/version.sbt b/version.sbt index ba08e960d..bd727575e 100644 --- a/version.sbt +++ b/version.sbt @@ -1 +1 @@ -version in ThisBuild := "1.0.3-SNAPSHOT" +version in ThisBuild := "1.1.0-SNAPSHOT"