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

io.iteratee.files.SuspendableFileModule.scala Maven / Gradle / Ivy

The newest version!
package io.iteratee.files

import cats.MonadError
import io.iteratee.{ Enumerator, Iteratee, Module }
import io.iteratee.internal.Step
import java.io.{
  BufferedInputStream,
  BufferedOutputStream,
  BufferedReader,
  BufferedWriter,
  Closeable,
  File,
  FileInputStream,
  FileOutputStream,
  FileReader,
  FileWriter,
  InputStream,
  InputStreamReader,
  OutputStream,
  OutputStreamWriter,
  Reader,
  Writer
}
import java.util.zip.{ ZipEntry, ZipFile }
import scala.Predef.genericArrayOps
import scala.collection.JavaConverters._

/**
 * File operations for contexts that are suspendable and for which recursive
 * monadic binding is stack safe.
 */
trait SuspendableFileModule[F[_]] extends FileModule[F] {
    this: Module[F] { type M[f[_]] <: MonadError[f, Throwable] } =>
  protected def captureEffect[A](a: => A): F[A]
  private[this] def close(c: Closeable): F[Unit] = captureEffect(c.close())

  private[this] def newFileReader(file: File): F[FileReader] = captureEffect(new FileReader(file))
  private[this] def newFileInputStream(file: File): F[FileInputStream] = captureEffect(new FileInputStream(file))

  private[this] def newInputStreamReader(stream: InputStream): F[InputStreamReader] =
    captureEffect(new InputStreamReader(stream))

  private[this] def newBufferedReader(reader: Reader): F[BufferedReader] = captureEffect(new BufferedReader(reader))

  private[this] def newBufferedInputStream(stream: InputStream): F[BufferedInputStream] =
    captureEffect(new BufferedInputStream(stream))

  private[this] def newFileWriter(file: File): F[FileWriter] = captureEffect(new FileWriter(file))
  private[this] def newFileOutputStream(file: File): F[FileOutputStream] = captureEffect(new FileOutputStream(file))

  private[this] def newOutputStreamWriter(stream: OutputStream): F[OutputStreamWriter] =
    captureEffect(new OutputStreamWriter(stream))

  private[this] def newBufferedWriter(writer: Writer): F[BufferedWriter] = captureEffect(new BufferedWriter(writer))

  private[this] def newBufferedOutputStream(stream: OutputStream): F[BufferedOutputStream] =
    captureEffect(new BufferedOutputStream(stream))

  final def readLines(file: File): Enumerator[F, String] = Enumerator.liftM(
    bracket(newFileReader(file))(newBufferedReader)(F)
  )(F).flatMap(reader => new LineEnumerator(reader).ensure(close(reader))(F))(F)

  final def readLinesFromStream(stream: InputStream): Enumerator[F, String] = Enumerator.liftM(
    bracket(newInputStreamReader(stream))(newBufferedReader)(F)
  )(F).flatMap(reader => new LineEnumerator(reader).ensure(close(reader))(F))(F)

  final def readBytes(file: File): Enumerator[F, Array[Byte]] = Enumerator.liftM(
    bracket(newFileInputStream(file))(newBufferedInputStream)(F)
  )(F).flatMap(stream => new ByteEnumerator(stream).ensure(close(stream))(F))(F)

  final def readBytesFromStream(stream: InputStream): Enumerator[F, Array[Byte]] = Enumerator.liftM(
    newBufferedInputStream(stream)
  )(F).flatMap(stream => new ByteEnumerator(stream).ensure(close(stream))(F))(F)

  final def readZipStreams(file: File): Enumerator[F, (ZipEntry, InputStream)] =
    Enumerator.liftM(captureEffect(new ZipFile(file)))(F).flatMap { zipFile =>
      new ZipFileEnumerator(zipFile, zipFile.entries.asScala).ensure(close(zipFile))(F)
    }(F)

  final def listFiles(dir: File): Enumerator[F, File] =
    Enumerator.liftM(captureEffect(dir.listFiles))(F).flatMap {
      case null => Enumerator.empty[F, File](F)
      case files => Enumerator.enumVector(files.toVector)(F)
    }(F)

  final def listFilesRec(dir: File): Enumerator[F, File] = listFiles(dir).flatMap {
    case item if item.isDirectory => listFilesRec(item)
    case item => Enumerator.enumOne(item)(F)
  }(F)

  final def writeLines(file: File): Iteratee[F, String, Unit] = Iteratee.liftM(
    bracket(newFileWriter(file))(newBufferedWriter)(F)
  )(F).flatMap { writer =>
    Iteratee.foldM[F, String, Unit](())((_, line) =>
      captureEffect {
        writer.write(line)
        writer.newLine()
      }
    )(F).ensure(close(writer))(F)
  }(F)

  final def writeLinesToStream(stream: OutputStream): Iteratee[F, String, Unit] = Iteratee.liftM(
    bracket(newOutputStreamWriter(stream))(newBufferedWriter)(F)
  )(F).flatMap { writer =>
    Iteratee.foldM[F, String, Unit](())((_, line) =>
      captureEffect {
        writer.write(line)
        writer.newLine()
      }
    )(F).ensure(close(writer))(F)
  }(F)

  def writeBytes(file: File): Iteratee[F, Array[Byte], Unit] = Iteratee.liftM(
    bracket(newFileOutputStream(file))(newBufferedOutputStream)(F)
  )(F).flatMap { stream =>
    Iteratee.foldM[F, Array[Byte], Unit](())((_, bytes) =>
      captureEffect(stream.write(bytes))
    )(F).ensure(close(stream))(F)
  }(F)

  def writeBytesToStream(stream: OutputStream): Iteratee[F, Array[Byte], Unit] = Iteratee.liftM(
    newBufferedOutputStream(stream)
  )(F).flatMap { stream =>
    Iteratee.foldM[F, Array[Byte], Unit](())((_, bytes) =>
      captureEffect(stream.write(bytes))
    )(F).ensure(close(stream))(F)
  }(F)

  private[this] final class LineEnumerator(reader: BufferedReader) extends Enumerator[F, String] {
    final def apply[A](s: Step[F, String, A]): F[Step[F, String, A]] =
      if (s.isDone) F.pure(s) else F.flatMap(captureEffect(reader.readLine())) {
        case null => F.pure(s)
        case line => F.flatMap(s.feedEl(line))(apply)
      }
  }

  private[this] final class ByteEnumerator(stream: InputStream, bufferSize: Int = 8192)
      extends Enumerator[F, Array[Byte]] {
    final def apply[A](s: Step[F, Array[Byte], A]): F[Step[F, Array[Byte], A]] =
      if (s.isDone) F.pure(s) else F.flatten(
        captureEffect {
          val array = new Array[Byte](bufferSize)
          val bytesRead = stream.read(array, 0, bufferSize)
          val read = if (bytesRead == bufferSize) array else array.slice(0, bytesRead)

          if (bytesRead == -1) F.pure(s) else F.flatMap(s.feedEl(read))(apply(_))
        }
      )
  }

  private[this] final class ZipFileEnumerator(zipFile: ZipFile, iterator: Iterator[ZipEntry])
      extends Enumerator[F, (ZipEntry, InputStream)] {
    final def apply[A](s: Step[F, (ZipEntry, InputStream), A]): F[Step[F, (ZipEntry, InputStream), A]] =
      if (s.isDone) F.pure(s) else F.flatten(
        captureEffect(
          if (iterator.hasNext) {
            val entry = iterator.next

            F.flatMap(s.feedEl((entry, zipFile.getInputStream(entry))))(apply)
          } else F.pure(s)
        )
      )
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy