Fix FileAccess for good, add new 2 verify methods

This commit is contained in:
JeremyStar™ 2024-12-17 03:26:39 +01:00
parent 32816ecce1
commit c13eaead94
Signed by: JeremyStarTM
GPG key ID: E366BAEF67E4704D

View file

@ -357,7 +357,7 @@ class FileAccess {
*/ */
@Throws(IOAccessException::class) @Throws(IOAccessException::class)
fun getType(): Type { fun getType(): Type {
return if (!exists()) Type.VOID return if (!exists(noFollowSymbolicLink = true)) Type.VOID
else if (Files.isRegularFile(path)) Type.FILE else if (Files.isRegularFile(path)) Type.FILE
else if (Files.isDirectory(path)) Type.DIRECTORY else if (Files.isDirectory(path)) Type.DIRECTORY
else Type.UNKNOWN else Type.UNKNOWN
@ -566,7 +566,7 @@ class FileAccess {
*/ */
@Throws(IOAccessException::class) @Throws(IOAccessException::class)
fun createFile(): FileAccess { fun createFile(): FileAccess {
if (!exists()) if (!exists(noFollowSymbolicLink = true))
try { try {
logger.diag("Creating a file at '${unformatFromPath(path)}'") logger.diag("Creating a file at '${unformatFromPath(path)}'")
file.parentFile.mkdirs() file.parentFile.mkdirs()
@ -591,7 +591,7 @@ class FileAccess {
*/ */
@Throws(IOAccessException::class) @Throws(IOAccessException::class)
fun createDirectory(): FileAccess { fun createDirectory(): FileAccess {
if (!exists()) if (!exists(noFollowSymbolicLink = true))
try { try {
logger.diag("Creating a directory at '${unformatFromPath(path)}'") logger.diag("Creating a directory at '${unformatFromPath(path)}'")
file.mkdirs() file.mkdirs()
@ -619,7 +619,7 @@ class FileAccess {
*/ */
@Throws(IOAccessException::class) @Throws(IOAccessException::class)
fun createLink(destination: FileAccess, hard: Boolean): FileAccess { fun createLink(destination: FileAccess, hard: Boolean): FileAccess {
if (!exists()) if (!exists(noFollowSymbolicLink = true))
try { try {
logger.diag("Creating a ${if (hard) "hard" else "symbolic"} link at '${unformatFromPath(path)}'") logger.diag("Creating a ${if (hard) "hard" else "symbolic"} link at '${unformatFromPath(path)}'")
if (hard) Files.createLink(path, destination.path) if (hard) Files.createLink(path, destination.path)
@ -707,7 +707,7 @@ class FileAccess {
*/ */
@Throws(IOAccessException::class) @Throws(IOAccessException::class)
fun delete(): FileAccess { fun delete(): FileAccess {
if (exists()) if (exists(noFollowSymbolicLink = true))
try { try {
logger.diag("Deleting '${unformatFromPath(path)}'") logger.diag("Deleting '${unformatFromPath(path)}'")
@ -825,12 +825,17 @@ class FileAccess {
@Throws(IOAccessException::class) @Throws(IOAccessException::class)
fun writeBytes(bytes: ByteArray, async: Boolean = false): FileAccess { fun writeBytes(bytes: ByteArray, async: Boolean = false): FileAccess {
try { try {
if (getType() != Type.FILE) when (getType()) {
return this Type.UNKNOWN, Type.DIRECTORY -> return this
else -> {}
}
logger.diag("Writing to file '${path}' (bytes, ${if (async) "async" else ""})") logger.diag("Writing to file '${path}' (bytes, ${if (async) "async" else ""})")
createFile() createFile()
Files.write(path, bytes, StandardOpenOption.WRITE, if (async) StandardOpenOption.DSYNC else StandardOpenOption.SYNC) if (async)
Files.write(path, bytes, StandardOpenOption.WRITE)
else
Files.write(path, bytes, StandardOpenOption.WRITE, StandardOpenOption.SYNC)
} catch (exception: Exception) { } catch (exception: Exception) {
throw IOAccessException("Unable to read file '${path}' (bytes, ${if (async) "async" else ""})", exception) throw IOAccessException("Unable to read file '${path}' (bytes, ${if (async) "async" else ""})", exception)
} }
@ -855,12 +860,17 @@ class FileAccess {
@Throws(IOAccessException::class) @Throws(IOAccessException::class)
fun writeLines(lines: List<String>, async: Boolean = false): FileAccess { fun writeLines(lines: List<String>, async: Boolean = false): FileAccess {
try { try {
if (getType() != Type.FILE) when (getType()) {
return this Type.UNKNOWN, Type.DIRECTORY -> return this
else -> {}
}
logger.diag("Writing to file '${path}' (lines, ${if (async) "async" else ""})") logger.diag("Writing to file '${path}' (lines, ${if (async) "async" else ""})")
createFile() createFile()
Files.write(path, lines, StandardOpenOption.WRITE, if (async) StandardOpenOption.DSYNC else StandardOpenOption.SYNC) if (async)
Files.write(path, lines, StandardOpenOption.WRITE)
else
Files.write(path, lines, StandardOpenOption.WRITE, StandardOpenOption.SYNC)
} catch (exception: Exception) { } catch (exception: Exception) {
throw IOAccessException("Unable to read file '${path}' (lines, ${if (async) "async" else ""})", exception) throw IOAccessException("Unable to read file '${path}' (lines, ${if (async) "async" else ""})", exception)
} }
@ -885,12 +895,17 @@ class FileAccess {
@Throws(IOAccessException::class) @Throws(IOAccessException::class)
fun writeString(string: String, async: Boolean = false): FileAccess { fun writeString(string: String, async: Boolean = false): FileAccess {
try { try {
if (getType() != Type.FILE) when (getType()) {
return this Type.UNKNOWN, Type.DIRECTORY -> return this
else -> {}
}
logger.diag("Writing to file '${path}' (string, ${if (async) "async" else ""})") logger.diag("Writing to file '${path}' (string, ${if (async) "async" else ""})")
createFile() createFile()
Files.writeString(path, string, StandardOpenOption.WRITE, if (async) StandardOpenOption.DSYNC else StandardOpenOption.SYNC) if (async)
Files.writeString(path, string, StandardOpenOption.WRITE)
else
Files.writeString(path, string, StandardOpenOption.WRITE, StandardOpenOption.SYNC)
} catch (exception: Exception) { } catch (exception: Exception) {
throw IOAccessException("Unable to read file '${path}' (string, ${if (async) "async" else ""})", exception) throw IOAccessException("Unable to read file '${path}' (string, ${if (async) "async" else ""})", exception)
} }
@ -915,12 +930,17 @@ class FileAccess {
@Throws(IOAccessException::class) @Throws(IOAccessException::class)
fun appendBytes(bytes: ByteArray, async: Boolean = false): FileAccess { fun appendBytes(bytes: ByteArray, async: Boolean = false): FileAccess {
try { try {
if (getType() != Type.FILE) when (getType()) {
return this Type.UNKNOWN, Type.DIRECTORY -> return this
else -> {}
}
logger.diag("Appending to file '${path}' (bytes, ${if (async) "async" else ""})") logger.diag("Appending to file '${path}' (bytes, ${if (async) "async" else ""})")
createFile() createFile()
Files.write(path, bytes, StandardOpenOption.APPEND, if (async) StandardOpenOption.DSYNC else StandardOpenOption.SYNC) if (async)
Files.write(path, bytes, StandardOpenOption.APPEND)
else
Files.write(path, bytes, StandardOpenOption.APPEND, StandardOpenOption.SYNC)
} catch (exception: Exception) { } catch (exception: Exception) {
throw IOAccessException("Unable to read file '${path}' (bytes, ${if (async) "async" else ""})", exception) throw IOAccessException("Unable to read file '${path}' (bytes, ${if (async) "async" else ""})", exception)
} }
@ -945,12 +965,17 @@ class FileAccess {
@Throws(IOAccessException::class) @Throws(IOAccessException::class)
fun appendLines(lines: List<String>, async: Boolean = false): FileAccess { fun appendLines(lines: List<String>, async: Boolean = false): FileAccess {
try { try {
if (getType() != Type.FILE) when (getType()) {
return this Type.UNKNOWN, Type.DIRECTORY -> return this
else -> {}
}
logger.diag("Appending to file '${path}' (lines, ${if (async) "async" else ""})") logger.diag("Appending to file '${path}' (lines, ${if (async) "async" else ""})")
createFile() createFile()
Files.write(path, lines, StandardOpenOption.APPEND, if (async) StandardOpenOption.DSYNC else StandardOpenOption.SYNC) if (async)
Files.write(path, lines, StandardOpenOption.APPEND)
else
Files.write(path, lines, StandardOpenOption.APPEND, StandardOpenOption.SYNC)
} catch (exception: Exception) { } catch (exception: Exception) {
throw IOAccessException("Unable to read file '${path}' (lines, ${if (async) "async" else ""})", exception) throw IOAccessException("Unable to read file '${path}' (lines, ${if (async) "async" else ""})", exception)
} }
@ -975,12 +1000,17 @@ class FileAccess {
@Throws(IOAccessException::class) @Throws(IOAccessException::class)
fun appendString(string: String, async: Boolean = false): FileAccess { fun appendString(string: String, async: Boolean = false): FileAccess {
try { try {
if (getType() != Type.FILE) when (getType()) {
return this Type.UNKNOWN, Type.DIRECTORY -> return this
else -> {}
}
logger.diag("Appending to file '${path}' (string, ${if (async) "async" else ""})") logger.diag("Appending to file '${path}' (string, ${if (async) "async" else ""})")
createFile() createFile()
Files.writeString(path, string, StandardOpenOption.APPEND, if (async) StandardOpenOption.DSYNC else StandardOpenOption.SYNC) if (async)
Files.writeString(path, string, StandardOpenOption.APPEND)
else
Files.writeString(path, string, StandardOpenOption.APPEND, StandardOpenOption.SYNC)
} catch (exception: Exception) { } catch (exception: Exception) {
throw IOAccessException("Unable to read file '${path}' (string, ${if (async) "async" else ""})", exception) throw IOAccessException("Unable to read file '${path}' (string, ${if (async) "async" else ""})", exception)
} }
@ -1105,14 +1135,21 @@ class FileAccess {
* Verifies that something * Verifies that something
* exists at this location. * exists at this location.
* *
* This method does not follow symbolic links
* and instead looks at the file directly. This
* method should likely be used to check if
* something exists at that exact location.
* In case you may need to follow symbolic links,
* use [verifyExistsLink] instead.
*
* @return this instance * @return this instance
* @throws IOAccessException on IO error * @throws IOAccessException on IO error
* @throws VerificationFailedException if the verification fails * @throws VerificationFailedException if the verification fails
* @since v1-alpha10 * @since v1-alpha10
*/ */
@Throws(IOAccessException::class, VerificationFailedException::class) @Throws(IOAccessException::class, VerificationFailedException::class)
fun verifyExists(noFollowSymbolicLink: Boolean = false): FileAccess { fun verifyExists(): FileAccess {
if (!exists(noFollowSymbolicLink = noFollowSymbolicLink)) if (!exists(noFollowSymbolicLink = true))
throw VerificationFailedException("Expected that something exists at '${unformatFromPath(path)}'") throw VerificationFailedException("Expected that something exists at '${unformatFromPath(path)}'")
return this return this
@ -1122,19 +1159,76 @@ class FileAccess {
* Verifies that something does * Verifies that something does
* not exist at this location. * not exist at this location.
* *
* This method does not follow symbolic links
* and instead looks at the file directly. This
* method should likely be used to check if
* something exists at that exact location.
* In case you may need to follow symbolic links,
* use [verifyNotExistsLink] instead.
*
* @return this instance * @return this instance
* @throws IOAccessException on IO error * @throws IOAccessException on IO error
* @throws VerificationFailedException if the verification fails * @throws VerificationFailedException if the verification fails
* @since v1-alpha10 * @since v1-alpha10
*/ */
@Throws(IOAccessException::class, VerificationFailedException::class) @Throws(IOAccessException::class, VerificationFailedException::class)
fun verifyNotExists(noFollowSymbolicLink: Boolean = false): FileAccess { fun verifyNotExists(): FileAccess {
if (exists(noFollowSymbolicLink = noFollowSymbolicLink)) if (exists(noFollowSymbolicLink = true))
throw VerificationFailedException("Expected that nothing exists at '${unformatFromPath(path)}'") throw VerificationFailedException("Expected that nothing exists at '${unformatFromPath(path)}'")
return this return this
} }
/**
* Verifies that something
* exists at this location.
*
* This method follows symbolic links during
* the verification process, which should
* only be used to check if you can read/write
* from a file. In case you need to verify the
* existence of this very file and do not
* require following symbolic links, use the
* [verifyNotExists] method instead.
*
* @return this instance
* @throws IOAccessException on IO error
* @throws VerificationFailedException if the verification fails
* @since v1-alpha10
*/
@Throws(IOAccessException::class, VerificationFailedException::class)
fun verifyExistsLink(): FileAccess {
if (!exists(noFollowSymbolicLink = false))
throw VerificationFailedException("Expected the link destination of '${unformatFromPath(path)}' is dead")
return this
}
/**
* Verifies that something does
* not exist at this location.
*
* This method follows symbolic links during
* the verification process, which should
* only be used to check if you can read/write
* from a file. In case you need to verify the
* existence of this very file and do not
* require following symbolic links, use the
* [verifyExists] method instead.
*
* @return this instance
* @throws IOAccessException on IO error
* @throws VerificationFailedException if the verification fails
* @since v1-alpha10
*/
@Throws(IOAccessException::class, VerificationFailedException::class)
fun verifyNotExistsLink(): FileAccess {
if (exists(noFollowSymbolicLink = false))
throw VerificationFailedException("Expected that the link destination of '${unformatFromPath(path)}' exists")
return this
}
/** /**
* Verifies that a file * Verifies that a file
* is at this location. * is at this location.
@ -1214,7 +1308,7 @@ class FileAccess {
*/ */
@Throws(IOAccessException::class, VerificationFailedException::class) @Throws(IOAccessException::class, VerificationFailedException::class)
fun verifyIsLink(): FileAccess { fun verifyIsLink(): FileAccess {
if (exists() && isSymbolicLink()) if (exists(noFollowSymbolicLink = true) && isSymbolicLink())
throw VerificationFailedException("Expected that '${unformatFromPath(path)}' is a link") throw VerificationFailedException("Expected that '${unformatFromPath(path)}' is a link")
return this return this
@ -1231,7 +1325,7 @@ class FileAccess {
*/ */
@Throws(IOAccessException::class, VerificationFailedException::class) @Throws(IOAccessException::class, VerificationFailedException::class)
fun verifyIsNotLink(): FileAccess { fun verifyIsNotLink(): FileAccess {
if (exists() && !isSymbolicLink()) if (exists(noFollowSymbolicLink = true) && !isSymbolicLink())
throw VerificationFailedException("Expected that '${unformatFromPath(path)}' is not a link") throw VerificationFailedException("Expected that '${unformatFromPath(path)}' is not a link")
return this return this
@ -1248,7 +1342,7 @@ class FileAccess {
*/ */
@Throws(IOAccessException::class, VerificationFailedException::class) @Throws(IOAccessException::class, VerificationFailedException::class)
fun verifyIsValidLink(): FileAccess { fun verifyIsValidLink(): FileAccess {
if (exists() && (isSymbolicLink() || getLinkDestination() != null)) if (exists(noFollowSymbolicLink = true) && (isSymbolicLink() || getLinkDestination() != null))
throw VerificationFailedException("Expected that '${unformatFromPath(path)}' is a link") throw VerificationFailedException("Expected that '${unformatFromPath(path)}' is a link")
return this return this
@ -1265,7 +1359,7 @@ class FileAccess {
*/ */
@Throws(IOAccessException::class, VerificationFailedException::class) @Throws(IOAccessException::class, VerificationFailedException::class)
fun verifyIsNotValidLink(): FileAccess { fun verifyIsNotValidLink(): FileAccess {
if (exists() && !(isSymbolicLink() || getLinkDestination() != null)) if (exists(noFollowSymbolicLink = true) && !(isSymbolicLink() || getLinkDestination() != null))
throw VerificationFailedException("Expected that '${unformatFromPath(path)}' is not a link") throw VerificationFailedException("Expected that '${unformatFromPath(path)}' is not a link")
return this return this
@ -1387,6 +1481,7 @@ class FileAccess {
} }
override fun visitFile(path: Path, attributes: BasicFileAttributes): FileVisitResult { override fun visitFile(path: Path, attributes: BasicFileAttributes): FileVisitResult {
path.toFile().delete()
return FileVisitResult.CONTINUE return FileVisitResult.CONTINUE
} }