Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[#323] added Files.createTemp* Managed methods; added Files.deleteRec… #327

Merged
merged 3 commits into from
Apr 5, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
target
.sbtopts
project/.sbt
*.tmp
# if you are here to add your IDE's files please read this instead:
# https://stackoverflow.com/questions/7335420/global-git-ignore#22885996
34 changes: 32 additions & 2 deletions nio/src/main/scala/zio/nio/file/Files.scala
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,9 @@ import java.nio.file.{
}
import java.util.function.BiPredicate
import java.util.{ Iterator => JIterator }

import zio.blocking._
import zio.nio.core.file.Path
import zio.stream.ZStream
import zio.stream.{ ZSink, ZStream }
import zio.{ Chunk, UIO, ZIO, ZManaged }

import scala.jdk.CollectionConverters._
Expand Down Expand Up @@ -66,6 +65,14 @@ object Files {
effectBlocking(Path.fromJava(JFiles.createTempFile(dir.javaPath, prefix.orNull, suffix, fileAttributes.toSeq: _*)))
.refineToOrDie[IOException]

def createTempFileInManaged(
dir: Path,
suffix: String = ".tmp",
prefix: Option[String] = None,
fileAttributes: Iterable[FileAttribute[_]] = Nil
): ZManaged[Blocking, IOException, Path] =
ZManaged.make(createTempFileIn(dir, suffix, prefix, fileAttributes))(release = deleteIfExists(_).ignore)

def createTempFile(
suffix: String = ".tmp",
prefix: Option[String],
Expand All @@ -74,6 +81,13 @@ object Files {
effectBlocking(Path.fromJava(JFiles.createTempFile(prefix.orNull, suffix, fileAttributes.toSeq: _*)))
.refineToOrDie[IOException]

def createTempFileManaged(
suffix: String = ".tmp",
prefix: Option[String] = None,
fileAttributes: Iterable[FileAttribute[_]] = Nil
): ZManaged[Blocking, IOException, Path] =
ZManaged.make(createTempFile(suffix, prefix, fileAttributes))(release = deleteIfExists(_).ignore)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure what exactly should be used in the finalization block: ignore or orDie?
In most cases, it won't fail so I choose to ignore

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I'm not 100% sure either, but ignore seems good.


def createTempDirectory(
dir: Path,
prefix: Option[String],
Expand All @@ -82,13 +96,26 @@ object Files {
effectBlocking(Path.fromJava(JFiles.createTempDirectory(dir.javaPath, prefix.orNull, fileAttributes.toSeq: _*)))
.refineToOrDie[IOException]

def createTempDirectoryManaged(
dir: Path,
prefix: Option[String],
fileAttributes: Iterable[FileAttribute[_]]
): ZManaged[Blocking, IOException, Path] =
ZManaged.make(createTempDirectory(dir, prefix, fileAttributes))(release = deleteRecursive(_).ignore)

def createTempDirectory(
prefix: Option[String],
fileAttributes: Iterable[FileAttribute[_]]
): ZIO[Blocking, IOException, Path] =
effectBlocking(Path.fromJava(JFiles.createTempDirectory(prefix.orNull, fileAttributes.toSeq: _*)))
.refineToOrDie[IOException]

def createTempDirectoryManaged(
prefix: Option[String],
fileAttributes: Iterable[FileAttribute[_]]
): ZManaged[Blocking, IOException, Path] =
ZManaged.make(createTempDirectory(prefix, fileAttributes))(release = deleteRecursive(_).ignore)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not 100% sure about recursive directory deletion. But it seems to be reasonable in terms of temporary directories


def createSymbolicLink(
link: Path,
target: Path,
Expand All @@ -106,6 +133,9 @@ object Files {
def deleteIfExists(path: Path): ZIO[Blocking, IOException, Boolean] =
effectBlocking(JFiles.deleteIfExists(path.javaPath)).refineToOrDie[IOException]

def deleteRecursive(path: Path): ZIO[Blocking, IOException, Long] =
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO this method may be useful independently of the rest updates

newDirectoryStream(path).mapM(delete).run(ZSink.count) <* delete(path)

def copy(source: Path, target: Path, copyOptions: CopyOption*): ZIO[Blocking, IOException, Unit] =
effectBlocking(JFiles.copy(source.javaPath, target.javaPath, copyOptions: _*)).unit
.refineToOrDie[IOException]
Expand Down
85 changes: 85 additions & 0 deletions nio/src/test/scala/zio/nio/file/FilesSpec.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package zio.nio.file

import zio.{ Chunk, Ref }
import zio.nio.BaseSpec
import zio.nio.core.file.Path
import zio.test._
import zio.test.Assertion._

object FilesSpec extends BaseSpec {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚀


override def spec =
suite("FilesSpec")(
testM("createTempFileInManaged cleans up temp file") {
val sampleFileContent = Chunk.fromArray("createTempFileInManaged works!".getBytes)
for {
pathRef <- Ref.make[Option[Path]](None)
readBytes <- Files
.createTempFileInManaged(dir = Path("."))
.use { tmpFile =>
pathRef.set(Some(tmpFile)) *> writeAndThenRead(tmpFile)(sampleFileContent)
}
Some(tmpFilePath) <- pathRef.get
tmpFileExistsAfterUsage <- Files.exists(tmpFilePath)
} yield assert(readBytes)(equalTo(sampleFileContent)) &&
assert(tmpFileExistsAfterUsage)(isFalse)
},
testM("createTempFileManaged cleans up temp file") {
val sampleFileContent = Chunk.fromArray("createTempFileManaged works!".getBytes)
for {
pathRef <- Ref.make[Option[Path]](None)
readBytes <- Files
.createTempFileManaged()
.use { tmpFile =>
pathRef.set(Some(tmpFile)) *> writeAndThenRead(tmpFile)(sampleFileContent)
}
Some(tmpFilePath) <- pathRef.get
tmpFileExistsAfterUsage <- Files.exists(tmpFilePath)
} yield assert(readBytes)(equalTo(sampleFileContent)) &&
assert(tmpFileExistsAfterUsage)(isFalse)
},
testM("createTempDirectoryManaged cleans up temp dir") {
val sampleFileContent = Chunk.fromArray("createTempDirectoryManaged works!".getBytes)
for {
pathRef <- Ref.make[Option[Path]](None)
readBytes <- Files
.createTempDirectoryManaged(
prefix = None,
fileAttributes = Nil
)
.use { tmpDir =>
val sampleFile = tmpDir / "createTempDirectoryManaged"
pathRef.set(Some(tmpDir)) *> createAndWriteAndThenRead(sampleFile)(sampleFileContent)
}
Some(tmpFilePath) <- pathRef.get
tmpFileExistsAfterUsage <- Files.exists(tmpFilePath)
} yield assert(readBytes)(equalTo(sampleFileContent)) &&
assert(tmpFileExistsAfterUsage)(isFalse)
},
testM("createTempDirectoryManaged (dir) cleans up temp dir") {
val sampleFileContent = Chunk.fromArray("createTempDirectoryManaged(dir) works!".getBytes)
for {
pathRef <- Ref.make[Option[Path]](None)
readBytes <- Files
.createTempDirectoryManaged(
dir = Path("."),
prefix = None,
fileAttributes = Nil
)
.use { tmpDir =>
val sampleFile = tmpDir / "createTempDirectoryManaged2"
pathRef.set(Some(tmpDir)) *> createAndWriteAndThenRead(sampleFile)(sampleFileContent)
}
Some(tmpFilePath) <- pathRef.get
tmpFileExistsAfterUsage <- Files.exists(tmpFilePath)
} yield assert(readBytes)(equalTo(sampleFileContent)) &&
assert(tmpFileExistsAfterUsage)(isFalse)
}
)

private def createAndWriteAndThenRead(file: Path)(bytes: Chunk[Byte]) =
Files.createFile(file) *> writeAndThenRead(file)(bytes)

private def writeAndThenRead(file: Path)(bytes: Chunk[Byte]) =
Files.writeBytes(file, bytes) *> Files.readAllBytes(file)
}