Merge pull request #781 from coursier/topic/less-scalaz

Use less things from scalaz
This commit is contained in:
Alexandre Archambault 2018-02-23 11:14:19 +01:00 committed by GitHub
commit 834fbd598a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
42 changed files with 720 additions and 642 deletions

View File

@ -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
```

View File

@ -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")
)

View File

@ -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

View File

@ -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."
)

View File

@ -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)
}
}

View File

@ -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

View File

@ -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)
})
}
}

View File

@ -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)
}
}

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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[_]](

View File

@ -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 }

View File

@ -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

View File

@ -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
}
}

View File

@ -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")
}
}
}

View File

@ -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)

View File

@ -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))
}
}

View File

@ -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, _))

View File

@ -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
}
}

View File

@ -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))
}
}

View File

@ -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))
}

View File

@ -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))

View File

@ -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
```

View File

@ -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))
}
}

View File

@ -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)
}
}
)

View File

@ -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
)

View File

@ -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))
}
}

View File

@ -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")
}

View File

@ -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

View File

@ -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)

View File

@ -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
}

View File

@ -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))
}
}
}

View File

@ -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")
)

View File

@ -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)")

View File

@ -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 { _ =>

View File

@ -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

View File

@ -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 - {

View File

@ -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))
}
}

View File

@ -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

View File

@ -2,7 +2,6 @@ package coursier
package test
import utest._
import scalaz._
import coursier.maven.Pom
@ -21,7 +20,7 @@ object PomParsingTests extends TestSuite {
</dependency>
"""
val expected = \/-(
val expected = Right(
"" -> Dependency(
Module("comp", "lib"),
"2.1",
@ -40,7 +39,7 @@ object PomParsingTests extends TestSuite {
</profile>
"""
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 {
</profile>
"""
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 {
</profile>
"""
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 {
</activation>
</profile>
"""
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 {
</activation>
</profile>
"""
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 {
</profile>
"""
val expected = \/-(Profile(
val expected = Right(Profile(
"profile1",
None,
Profile.Activation(Nil),
@ -157,7 +156,7 @@ object PomParsingTests extends TestSuite {
</profile>
"""
val expected = \/-(Profile(
val expected = Right(Profile(
"profile1",
None,
Profile.Activation(Nil),
@ -181,7 +180,7 @@ object PomParsingTests extends TestSuite {
</profile>
"""
val expected = \/-(Profile(
val expected = Right(Profile(
"profile1",
None,
Profile.Activation(Nil),
@ -204,7 +203,7 @@ object PomParsingTests extends TestSuite {
</profile>
"""
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"))

View File

@ -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")
)
)
}

View File

@ -1 +1 @@
version in ThisBuild := "1.0.3-SNAPSHOT"
version in ThisBuild := "1.1.0-SNAPSHOT"