Skip to content

Commit

Permalink
Filesystem.exists and Filesystem.metadataOrNull
Browse files Browse the repository at this point in the history
  • Loading branch information
squarejesse committed Dec 27, 2020
1 parent 8a0a518 commit d3f0c32
Show file tree
Hide file tree
Showing 12 changed files with 102 additions and 27 deletions.
6 changes: 3 additions & 3 deletions okio-testing/src/commonMain/kotlin/okio/FakeFilesystem.kt
Original file line number Diff line number Diff line change
Expand Up @@ -79,10 +79,10 @@ class FakeFilesystem(
return canonicalPath
}

override fun metadata(path: Path): FileMetadata {
override fun metadataOrNull(path: Path): FileMetadata? {
val canonicalPath = workingDirectory / path
val element = elements[canonicalPath] ?: throw IOException("no such file: $path")
return element.metadata
val element = elements[canonicalPath]
return element?.metadata
}

override fun list(dir: Path): List<Path> {
Expand Down
4 changes: 3 additions & 1 deletion okio/src/appleMain/kotlin/okio/posixVariant.kt
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,19 @@ package okio
import kotlinx.cinterop.alloc
import kotlinx.cinterop.memScoped
import kotlinx.cinterop.ptr
import platform.posix.ENOENT
import platform.posix.S_IFDIR
import platform.posix.S_IFMT
import platform.posix.S_IFREG
import platform.posix.errno
import platform.posix.stat

@ExperimentalFilesystem
internal actual fun PosixSystemFilesystem.variantMetadata(path: Path): FileMetadata {
internal actual fun PosixSystemFilesystem.variantMetadataOrNull(path: Path): FileMetadata? {
return memScoped {
val stat = alloc<stat>()
if (platform.posix.lstat(path.toString(), stat.ptr) != 0) {
if (errno == ENOENT) return null
throw IOException(errnoString(errno))
}
return@memScoped FileMetadata(
Expand Down
29 changes: 26 additions & 3 deletions okio/src/commonMain/kotlin/okio/Filesystem.kt
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@ package okio
*
* * A remote filesystem could access files over the network.
*
* * A decorating filesystem could apply monitoring, encryption, compression, or filtering to
* another filesystem implementation.
* * A [decorating filesystem][ForwardingFilesystem] could apply monitoring, encryption,
* compression, or filtering to another filesystem implementation.
*
* For improved capability and testability, consider structuring your classes to dependency inject
* a `Filesystem` rather than using [SYSTEM] directly.
Expand Down Expand Up @@ -106,7 +106,30 @@ abstract class Filesystem {
* @throws IOException if [path] does not exist or its metadata cannot be read.
*/
@Throws(IOException::class)
abstract fun metadata(path: Path): FileMetadata
fun metadata(path: Path): FileMetadata {
return metadataOrNull(path) ?: throw IOException("no such file: $path")
}

/**
* Returns metadata of the file, directory, or object identified by [path]. This returns null if
* there is no file at [path].
*
* @throws IOException if [path] cannot be accessed due to a connectivity problem, permissions
* problem, or other issue.
*/
@Throws(IOException::class)
abstract fun metadataOrNull(path: Path): FileMetadata?

/**
* Returns true if [path] identifies an object on this filesystem.
*
* @throws IOException if [path] cannot be accessed due to a connectivity problem, permissions
* problem, or other issue.
*/
@Throws(IOException::class)
fun exists(path: Path): Boolean {
return metadataOrNull(path) != null
}

/**
* Returns the children of the directory identified by [dir].
Expand Down
11 changes: 6 additions & 5 deletions okio/src/commonMain/kotlin/okio/ForwardingFilesystem.kt
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,8 @@ import kotlin.jvm.JvmName
* non-abstract functions in the [Filesystem] interface.
*
* **This class forwards only the abstract functions;** non-abstract functions delegate to the
* other functions of this class.
* other functions of this class. If desired, subclasses may override non-abstract functions to
* forward them.
*/
@ExperimentalFilesystem
abstract class ForwardingFilesystem(
Expand All @@ -121,7 +122,7 @@ abstract class ForwardingFilesystem(
* file and once for the target file.
*
* @param path the path passed to any of the functions of this.
* @param functionName a string like "canonicalize", "metadata", or "appendingSink".
* @param functionName a string like "canonicalize", "metadataOrNull", or "appendingSink".
* @param parameterName a string like "path", "file", "source", or "target".
* @return the path to pass to [delegate] for the same parameter.
*/
Expand Down Expand Up @@ -150,9 +151,9 @@ abstract class ForwardingFilesystem(
}

@Throws(IOException::class)
override fun metadata(path: Path): FileMetadata {
val path = onPathParameter(path, "metadata", "path")
return delegate.metadata(path)
override fun metadataOrNull(path: Path): FileMetadata? {
val path = onPathParameter(path, "metadataOrNull", "path")
return delegate.metadataOrNull(path)
}

@Throws(IOException::class)
Expand Down
31 changes: 31 additions & 0 deletions okio/src/commonTest/kotlin/okio/AbstractFilesystemTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,12 @@ import okio.ByteString.Companion.toByteString
import okio.Path.Companion.toPath
import kotlin.random.Random
import kotlin.test.BeforeTest
import kotlin.test.Ignore
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertFailsWith
import kotlin.test.assertFalse
import kotlin.test.assertNull
import kotlin.test.assertTrue
import kotlin.time.ExperimentalTime
import kotlin.time.seconds
Expand Down Expand Up @@ -432,6 +434,19 @@ abstract class AbstractFilesystemTest(
assertInRange(metadata.lastAccessedAt, minTime, maxTime)
}

@Test
fun absentMetadataOrNull() {
val path = base / "no-such-file"
assertNull(filesystem.metadataOrNull(path))
}

@Test
@Ignore
fun inaccessibleMetadata() {
// TODO(swankjesse): configure a test directory in CI that exists, but that this process doesn't
// have permission to read metadata of. Perhaps a file in another user's /home directory?
}

@Test
fun absentMetadata() {
val path = base / "no-such-file"
Expand All @@ -440,6 +455,22 @@ abstract class AbstractFilesystemTest(
}
}

@Test
fun fileExists() {
val path = base / "file-exists"
assertFalse(filesystem.exists(path))
path.writeUtf8("hello, world!")
assertTrue(filesystem.exists(path))
}

@Test
fun directoryExists() {
val path = base / "directory-exists"
assertFalse(filesystem.exists(path))
filesystem.createDirectory(path)
assertTrue(filesystem.exists(path))
}

@Test
fun deleteOpenForWritingFailsOnWindows() {
val file = base / "file.txt"
Expand Down
13 changes: 12 additions & 1 deletion okio/src/jsMain/kotlin/okio/NodeJsSystemFilesystem.kt
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,11 @@ internal object NodeJsSystemFilesystem : Filesystem() {
}
}

override fun metadata(path: Path): FileMetadata {
override fun metadataOrNull(path: Path): FileMetadata? {
val stat = try {
statSync(path.toString())
} catch (e: Throwable) {
if (e.errorCode == "ENOENT") return null // "No such file or directory".
throw IOException(e.message)
}
return FileMetadata(
Expand All @@ -62,6 +63,16 @@ internal object NodeJsSystemFilesystem : Filesystem() {
)
}

/**
* Returns the error code on this `SystemError`. This uses `asDynamic()` because our JS bindings
* don't (yet) include the `SystemError` type.
*
* https://nodejs.org/dist/latest-v14.x/docs/api/errors.html#errors_class_systemerror
* https://nodejs.org/dist/latest-v14.x/docs/api/errors.html#errors_common_system_errors
*/
private val Throwable.errorCode
get() = asDynamic().code

override fun list(dir: Path): List<Path> {
try {
val opendir = opendirSync(dir.toString())
Expand Down
4 changes: 2 additions & 2 deletions okio/src/jvmMain/kotlin/okio/JvmSystemFilesystem.kt
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ internal open class JvmSystemFilesystem : Filesystem() {
return canonicalFile.toOkioPath()
}

override fun metadata(path: Path): FileMetadata {
override fun metadataOrNull(path: Path): FileMetadata? {
val file = path.toFile()
val isRegularFile = file.isFile
val isDirectory = file.isDirectory
Expand All @@ -42,7 +42,7 @@ internal open class JvmSystemFilesystem : Filesystem() {
size == 0L &&
!file.exists()
) {
throw IOException("no such file")
return null
}

return FileMetadata(
Expand Down
18 changes: 11 additions & 7 deletions okio/src/jvmMain/kotlin/okio/NioSystemFilesystem.kt
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package okio
import org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement
import java.nio.file.Files
import java.nio.file.LinkOption
import java.nio.file.NoSuchFileException
import java.nio.file.StandardCopyOption.ATOMIC_MOVE
import java.nio.file.StandardCopyOption.REPLACE_EXISTING
import java.nio.file.attribute.BasicFileAttributes
Expand All @@ -30,14 +31,18 @@ import java.nio.file.attribute.FileTime
@ExperimentalFilesystem
@IgnoreJRERequirement // Only used on platforms that support java.nio.file.
internal class NioSystemFilesystem : JvmSystemFilesystem() {
override fun metadata(path: Path): FileMetadata {
override fun metadataOrNull(path: Path): FileMetadata? {
val nioPath = path.toNioPath()

val attributes = Files.readAttributes(
nioPath,
BasicFileAttributes::class.java,
LinkOption.NOFOLLOW_LINKS
)
val attributes = try {
Files.readAttributes(
nioPath,
BasicFileAttributes::class.java,
LinkOption.NOFOLLOW_LINKS
)
} catch (_: NoSuchFileException) {
return null
}

return FileMetadata(
isRegularFile = attributes.isRegularFile,
Expand All @@ -53,7 +58,6 @@ internal class NioSystemFilesystem : JvmSystemFilesystem() {
* Returns this time as a epoch millis. If this is 0L this returns null, because epoch time 0L is
* a special value that indicates the requested time was not available.
*/
@IgnoreJRERequirement
private fun FileTime.zeroToNull(): Long? {
return toMillis().takeIf { it != 0L }
}
Expand Down
4 changes: 3 additions & 1 deletion okio/src/linuxX64Main/kotlin/okio/posixVariant.kt
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,19 @@ package okio
import kotlinx.cinterop.alloc
import kotlinx.cinterop.memScoped
import kotlinx.cinterop.ptr
import platform.posix.ENOENT
import platform.posix.S_IFDIR
import platform.posix.S_IFMT
import platform.posix.S_IFREG
import platform.posix.errno
import platform.posix.stat

@ExperimentalFilesystem
internal actual fun PosixSystemFilesystem.variantMetadata(path: Path): FileMetadata {
internal actual fun PosixSystemFilesystem.variantMetadataOrNull(path: Path): FileMetadata? {
return memScoped {
val stat = alloc<stat>()
if (platform.posix.lstat(path.toString(), stat.ptr) != 0) {
if (errno == ENOENT) return null
throw IOException(errnoString(errno))
}
return@memScoped FileMetadata(
Expand Down
3 changes: 2 additions & 1 deletion okio/src/mingwX64Main/kotlin/okio/posixVariant.kt
Original file line number Diff line number Diff line change
Expand Up @@ -71,10 +71,11 @@ internal actual fun PosixSystemFilesystem.variantCanonicalize(path: Path): Path
}

@ExperimentalFilesystem
internal actual fun PosixSystemFilesystem.variantMetadata(path: Path): FileMetadata {
internal actual fun PosixSystemFilesystem.variantMetadataOrNull(path: Path): FileMetadata? {
return memScoped {
val stat = alloc<_stat64>()
if (_stat64(path.toString(), stat.ptr) != 0) {
if (errno == ENOENT) return null
throw IOException(errnoString(errno))
}
return@memScoped FileMetadata(
Expand Down
4 changes: 2 additions & 2 deletions okio/src/nativeMain/kotlin/okio/PosixSystemFilesystem.kt
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@ internal object PosixSystemFilesystem : Filesystem() {

override fun canonicalize(path: Path) = variantCanonicalize(path)

override fun metadata(path: Path): FileMetadata {
return variantMetadata(path)
override fun metadataOrNull(path: Path): FileMetadata? {
return variantMetadataOrNull(path)
}

override fun list(dir: Path): List<Path> {
Expand Down
2 changes: 1 addition & 1 deletion okio/src/nativeMain/kotlin/okio/posixVariant.kt
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ internal expect fun PosixSystemFilesystem.variantMkdir(dir: Path): Int
internal expect fun PosixSystemFilesystem.variantCanonicalize(path: Path): Path

@ExperimentalFilesystem
internal expect fun PosixSystemFilesystem.variantMetadata(path: Path): FileMetadata
internal expect fun PosixSystemFilesystem.variantMetadataOrNull(path: Path): FileMetadata?

@ExperimentalFilesystem
internal expect fun PosixSystemFilesystem.variantMove(source: Path, target: Path)

0 comments on commit d3f0c32

Please sign in to comment.