All Downloads are FREE. Search and download functionalities are using the official Maven repository.

coursier.cache.MockCache.scala Maven / Gradle / Ivy

The newest version!
package coursier.cache

import java.io._
import java.net.URI
import java.nio.charset.StandardCharsets
import java.nio.file.{Files, Path}
import java.util.concurrent.ExecutorService

import coursier.cache.internal.MockCacheEscape
import coursier.paths.Util
import coursier.util.{Artifact, EitherT, Sync, WebPage}
import coursier.util.Monad.ops._
import dataclass._

import scala.concurrent.ExecutionContext
import scala.util.{Failure, Success, Try}

// format: off
@data class MockCache[F[_]](
  base: Path,
  extraData: Seq[Path],
  writeMissing: Boolean,
  pool: ExecutorService,
  S: Sync[F],
  dummyArtifact: Artifact => Boolean = _ => false,
  @since
    proxy: Option[java.net.Proxy] = None
) extends Cache[F] {
// format: on

  private implicit def S0 = S

  def fetch: Cache.Fetch[F] = { artifact =>

    val (artifact0, links) =
      if (artifact.url.endsWith("/.links"))
        (artifact.withUrl(artifact.url.stripSuffix(".links")), true)
      else
        (artifact, false)

    if (proxy.nonEmpty || artifact0.url.startsWith("http://localhost:"))
      EitherT(MockCache.readFully(
        ConnectionBuilder(artifact0.url)
          .withAuthentication(artifact0.authentication)
          .withProxy(proxy)
          .connection()
          .getInputStream,
        if (links) Some(artifact0.url) else None
      ))
    else
      file(artifact0)
        .leftMap(_.describe)
        .flatMap { f =>
          EitherT {
            MockCache.readFully(
              new FileInputStream(f),
              if (links) Some(artifact0.url) else None
            )
          }
        }
  }

  def file(artifact: Artifact): EitherT[F, ArtifactError, File] =
    if (artifact.url.startsWith("file:")) {
      val url =
        if (artifact.url.endsWith("/"))
          artifact.url + ".directory"
        else
          artifact.url
      val f = new File(new URI(url))
      EitherT.point(f)
    }
    else {

      assert(artifact.authentication.isEmpty)

      val path = base.resolve(MockCacheEscape.urlAsPath(artifact.url))

      val fromExtraData = extraData.foldLeft(S.point(Option.empty[Path])) {
        (acc, p) =>
          acc.flatMap {
            case Some(_) => acc
            case None =>
              val path = p.resolve(MockCacheEscape.urlAsPath(artifact.url))
              S.schedule(pool)(Files.exists(path)).map {
                case true  => Some(path)
                case false => None
              }
          }
      }

      val init0 = S.schedule(pool)(Files.exists(path)).flatMap {
        case true => S.point(Right(path)): F[Either[ArtifactError, Path]]
        case false =>
          val res: F[Either[ArtifactError, Path]] =
            if (writeMissing) {
              val f = S.schedule[Either[ArtifactError, Path]](pool) {
                Util.createDirectories(path.getParent)
                def is(): InputStream =
                  if (dummyArtifact(artifact))
                    new ByteArrayInputStream(Array.emptyByteArray)
                  else
                    ConnectionBuilder(artifact.url)
                      .withAuthentication(artifact.authentication)
                      .connection()
                      .getInputStream
                val b = MockCache.readFullySync(is())
                Files.write(path, b)
                Right(path)
              }

              S.handle(f) {
                case _: FileNotFoundException =>
                  Left(new ArtifactError.NotFound(artifact.url))
                case e: Exception =>
                  Left(new ArtifactError.DownloadError(e.toString, Some(e)))
              }
            }
            else
              S.point(Left(new ArtifactError.NotFound(path.toString)))
          res
      }

      val e = fromExtraData.flatMap {
        case None    => init0
        case Some(f) => S.point(Right(f)): F[Either[ArtifactError, Path]]
      }
      EitherT[F, ArtifactError, Path](e)
        .map(_.toFile)
    }

  lazy val ec = ExecutionContext.fromExecutorService(pool)

}

object MockCache {

  def create[F[_]: Sync](
    base: Path,
    pool: ExecutorService,
    extraData: Seq[Path] = Nil,
    writeMissing: Boolean = false
  ): MockCache[F] =
    MockCache(
      base,
      extraData,
      writeMissing,
      pool,
      Sync[F]
    )

  private def readFullySync(is: InputStream) = {
    val buffer = new ByteArrayOutputStream
    val data   = Array.ofDim[Byte](16384)

    var nRead = is.read(data, 0, data.length)
    while (nRead != -1) {
      buffer.write(data, 0, nRead)
      nRead = is.read(data, 0, data.length)
    }

    buffer.flush()
    buffer.toByteArray
  }

  private def readFully[F[_]: Sync](
    is: => InputStream,
    parseLinksUrl: Option[String]
  ): F[Either[String, String]] =
    Sync[F].delay {
      val t = Try {
        val is0 = is
        val b =
          try readFullySync(is0)
          finally is0.close()

        val s = new String(b, StandardCharsets.UTF_8)
        parseLinksUrl match {
          case None => s
          case Some(url) =>
            WebPage.listElements(url, s).mkString("\n")
        }
      }

      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("")(" (" + _ + ")")}")
      }
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy