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

spice.streamer.Streamer.scala Maven / Gradle / Ivy

There is a newer version: 0.7.2
Show newest version
package spice.streamer

import cats.effect.IO

import java.io.{File, FileOutputStream, IOException}
import scala.annotation.tailrec
import scala.concurrent.duration.{DurationInt, FiniteDuration}

object Streamer {
  final def apply(reader: Reader,
                 writer: Writer,
                 monitor: Monitor = Monitor.Ignore,
                 monitorDelay: FiniteDuration = 15.millis,
                 buffer: Array[Byte] = new Array[Byte](1024),
                 closeOnComplete: Boolean = true): IO[Writer] = IO {
    val length = reader.length
    monitor.open(length)
    var total = 0L

    val delay = monitorDelay.toMillis
    var lastNotified = 0L

    @tailrec
    def recurse(): Unit = {
      val len = reader.read(buffer)
      if (len == -1) {
        writer.flush()
        if (closeOnComplete) {
          writer.close()
          reader.close()
          monitor.closed()
        }
        writer.complete()
        monitor.completed()
      } else {
        try {
          writer.write(buffer, 0, len)
          total += len
          val percentage = length.map { l =>
            (total.toDouble / l.toDouble) * 100.0
          }
          val now = System.currentTimeMillis()
          val elapsed = now - lastNotified
          if (elapsed >= delay) {
            monitor.written(len, total, percentage)
            lastNotified = now
          }
        } catch {
          case t: Throwable =>
            monitor.failure(t)
            throw new IOException(s"IO failed to write to writer with length: $len with reader: $reader, writer: $writer.", t)
        }
        recurse()
      }
    }

    recurse()
    writer
  }

  /**
    * Uses IO.stream, but supports recursive directory copying.
    *
    * @param source file or directory
    * @param destination file or directory
    */
  def copy(source: File, destination: File): Unit = if (source.isDirectory) {
    destination.mkdirs()
    assert(destination.isDirectory, s"Destination ${destination.getAbsolutePath} is a file, not a directory!")
    source.listFiles().foreach { file =>
      copy(file, new File(destination, file.getName))
    }
  } else if (source.isFile) {
    if (destination.isDirectory) {
      apply(source, new File(destination, source.getName))
    } else {
      apply(source, destination)
    }
  }

  def merge(sources: List[File], destination: File): Unit = {
    val outputStream = new FileOutputStream(destination)
    sources.foreach { source =>
      apply(source, outputStream, closeOnComplete = false)
    }
    outputStream.flush()
    outputStream.close()
  }

  def delete(file: File): Boolean = {
    if (file.isDirectory) {
      deleteFiles(file.listFiles().toList)
    }
    file.delete()
  }

  @tailrec
  final def deleteFiles(files: List[File]): Unit = {
    if (files.nonEmpty) {
      val f = files.head
      delete(f)
      deleteFiles(files.tail)
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy