spice.streamer.Streamer.scala Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of spice-core_3 Show documentation
Show all versions of spice-core_3 Show documentation
Core functionality leveraged and shared by most other sub-projects of YouI.
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