Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package zio.stream

import java.nio.file.Files

import zio._
import zio.test.Assertion._
import zio.test._

object ZSinkPlatformSpecificSpec extends ZIOBaseSpec {
override def spec = suite("ZSink JVM")(
suite("fromFile")(
testM("writes to an existing file") {
val data = (0 to 100).mkString

Task(Files.createTempFile("stream", "fromFile"))
.bracket(path => Task(Files.delete(path)).orDie) { path =>
for {
bytes <- Task(data.getBytes("UTF-8"))
length <- ZStream.fromIterable(bytes).run(ZSink.fromFile(path))
str <- Task(new String(Files.readAllBytes(path)))
} yield assert(data)(equalTo(str)) && assert(bytes.length.toLong)(equalTo(length))
}

}
)
)
}
43 changes: 42 additions & 1 deletion streams/jvm/src/main/scala/zio/stream/platform.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ package zio.stream
import java.io.{ IOException, InputStream, OutputStream }
import java.nio.ByteBuffer
import java.nio.channels.FileChannel
import java.nio.file.Path
import java.nio.file.StandardOpenOption._
import java.nio.file.{ OpenOption, Path }
import java.{ util => ju }

import zio._
Expand All @@ -29,6 +30,46 @@ trait ZSinkPlatformSpecificConstructors { self: ZSink.type =>
case e: IOException => e
}
}

/**
* Uses the provided `Path` to create a [[ZSink]] that consumes byte chunks
* and writes them to the `File`. The sink will yield count of bytes written.
*/
final def fromFile(
path: => Path,
position: Long = 0L,
options: Set[OpenOption] = Set(WRITE, TRUNCATE_EXISTING, CREATE)
): ZSink[Blocking, Throwable, Byte, Long] =
ZSink {
for {
state <- Ref.make(0L).toManaged_
channel <- ZManaged.make(
blocking
.effectBlockingInterrupt(
FileChannel
.open(
path,
options.foldLeft(new ju.HashSet[OpenOption]()) { (acc, op) =>
acc.add(op); acc
} // for avoiding usage of different Java collection converters for different scala versions
)
.position(position)
)
.orDie
)(chan => blocking.effectBlocking(chan.close()).orDie)
push = (is: Option[Chunk[Byte]]) =>
is match {
case None => state.get.flatMap(Push.emit)
case Some(byteChunk) =>
for {
justWritten <- blocking.effectBlockingInterrupt {
channel.write(ByteBuffer.wrap(byteChunk.toArray))
}.mapError(Left(_))
more <- state.update(_ + justWritten) *> Push.more
} yield more
}
} yield push
}
}

trait ZStreamPlatformSpecificConstructors { self: ZStream.type =>
Expand Down