Introduce Path.createParentDirectories function #KT-53263

This commit is contained in:
Ilya Gorbunov
2023-01-19 19:30:20 +01:00
committed by Space Team
parent cf10bb8871
commit d2dc23d8bb
5 changed files with 85 additions and 11 deletions
@@ -19,10 +19,10 @@ import java.nio.file.attribute.BasicFileAttributes
* Note that if this function throws, partial copying may have taken place.
*
* Unlike `File.copyRecursively`, if some directories on the way to the [target] are missing, then they won't be created automatically.
* You can use the following approach to ensure that required intermediate directories are created:
* You can use the [createParentDirectories] function to ensure that required intermediate directories are created:
* ```
* sourcePath.copyToRecursively(
* destinationPath.apply { parent?.createDirectories() },
* destinationPath.createParentDirectories(),
* followLinks = false
* )
* ```
@@ -93,10 +93,10 @@ public fun Path.copyToRecursively(
* Note that if this function throws, partial copying may have taken place.
*
* Unlike `File.copyRecursively`, if some directories on the way to the [target] are missing, then they won't be created automatically.
* You can use the following approach to ensure that required intermediate directories are created:
* You can use the [createParentDirectories] function to ensure that required intermediate directories are created:
* ```
* sourcePath.copyToRecursively(
* destinationPath.apply { parent?.createDirectories() },
* destinationPath.createParentDirectories(),
* followLinks = false
* )
* ```
@@ -189,9 +189,9 @@ private object PathRelativizer {
* Copies a file or directory located by this path to the given [target] path.
*
* Unlike `File.copyTo`, if some directories on the way to the [target] are missing, then they won't be created automatically.
* You can use the following approach to ensure that required intermediate directories are created:
* You can use the [createParentDirectories] function to ensure that required intermediate directories are created:
* ```
* sourcePath.copyTo(destinationPath.apply { parent?.createDirectories() })
* sourcePath.copyTo(destinationPath.createParentDirectories())
* ```
*
* If the [target] path already exists, this function will fail unless [overwrite] argument is set to `true`.
@@ -226,9 +226,9 @@ public inline fun Path.copyTo(target: Path, overwrite: Boolean = false): Path {
* Copies a file or directory located by this path to the given [target] path.
*
* Unlike `File.copyTo`, if some directories on the way to the [target] are missing, then they won't be created automatically.
* You can use the following approach to ensure that required intermediate directories are created:
* You can use the [createParentDirectories] function to ensure that required intermediate directories are created:
* ```
* sourcePath.copyTo(destinationPath.apply { parent?.createDirectories() })
* sourcePath.copyTo(destinationPath.createParentDirectories())
* ```
*
* If the [target] path already exists, this function will fail unless the
@@ -500,6 +500,8 @@ public inline fun Path.deleteIfExists() =
* when creating the directory.
*
* @see Files.createDirectory
* @see Path.createDirectories
* @see Path.createParentDirectories
*/
@SinceKotlin("1.5")
@WasExperimental(ExperimentalPathApi::class)
@@ -513,15 +515,20 @@ public inline fun Path.createDirectory(vararg attributes: FileAttribute<*>): Pat
*
* If the directory already exists, this function does not throw an exception, unlike [Path.createDirectory].
*
* @return the path of this directory if it already exists or has been created successfully.
* The returned path can be converted [Path.toAbsolutePath][to absolute path] if it was relative.
*
* @param attributes an optional list of file attributes to set atomically when creating the directory.
*
* @throws FileAlreadyExistsException if there is already a file located by this path
* @throws FileAlreadyExistsException if there is already a file located by this path or one of its parent paths
* (optional specific exception, some implementations may throw more general [IOException]).
* @throws IOException if an I/O error occurs.
* @throws UnsupportedOperationException if the [attributes ]array contains an attribute that cannot be set atomically
* @throws UnsupportedOperationException if the [attributes] array contains an attribute that cannot be set atomically
* when creating the directory.
*
* @see Files.createDirectories
* @see Path.createDirectory
* @see Path.createParentDirectories
*/
@SinceKotlin("1.5")
@WasExperimental(ExperimentalPathApi::class)
@@ -530,6 +537,33 @@ public inline fun Path.createDirectory(vararg attributes: FileAttribute<*>): Pat
public inline fun Path.createDirectories(vararg attributes: FileAttribute<*>): Path =
Files.createDirectories(this, *attributes)
/**
* Ensures that all parent directories of this path exist, creating them if required.
*
* If the parent directory already exists, this function does nothing.
*
* Note that the [parent][Path.getParent] directory is not always the directory that contains the entry specified by this path.
* For example, the parent of the path `x/y/.` is `x/y`, which is logically the same directory,
* and the parent of `x/y/..` (which means just `x/`) is also `x/y`.
* Use the function [Path.normalize] to eliminate redundant name elements from the path.
*
* @param attributes an optional list of file attributes to set atomically when creating the missing parent directories.
*
* @return this path unchanged if all parent directories already exist or have been created successfully.
*
* @throws FileAlreadyExistsException if there is already a file located by the [parent][Path.getParent] path or one of its parent paths
* (optional specific exception, some implementations may throw more general [IOException]).
* @throws IOException if an I/O error occurs.
* @throws UnsupportedOperationException if the [attributes] array contains an attribute that cannot be set atomically
* when creating the directory.
*
* @see Path.getParent
* @see Path.createDirectories
*/
@SinceKotlin("1.9")
@Throws(IOException::class)
public fun Path.createParentDirectories(vararg attributes: FileAttribute<*>): Path =
this.apply { parent?.createDirectories(*attributes) }
/**
* Moves or renames the file located by this path to the [target] path.
@@ -59,6 +59,45 @@ class PathExtensionsTest : AbstractPathTest() {
assertFailsWith<FileAlreadyExistsException> { file.createFile() }
}
@Test
fun createParentDirectories() {
val dir = createTempDirectory().cleanupRecursively()
val file = dir / "test-dir" / "sub-dir" / "new-file"
val parent = file.parent!!
assertTrue(file.notExists())
assertTrue(parent.notExists())
val result = file.createParentDirectories()
assertTrue(file.notExists())
assertTrue(parent.isDirectory())
assertEquals(file, result)
file.createFile()
file.createParentDirectories()
assertTrue(file.exists())
assertTrue(parent.isDirectory())
}
@Test
fun createParentDirectoriesRelativePath() {
run {
val path = Path("test_path_without_parent")
path.createParentDirectories()
assertTrue(path.toAbsolutePath().parent!!.isDirectory())
}
run {
val path = Path("build/test_subdirectory/test_path")
val parent = path.parent!!
assertTrue(parent.notExists())
parent.cleanupRecursively()
path.createParentDirectories()
assertTrue(path.notExists())
assertTrue(parent.isDirectory())
}
}
@Test
fun createTempFileDefaultDir() {
val file1 = createTempFile().cleanup()
@@ -359,7 +359,7 @@ class PathRecursiveFunctionsTest : AbstractPathTest() {
OnErrorResult.SKIP_SUBTREE
})
src.copyToRecursively(dst.apply { parent?.createDirectories() }, followLinks = false)
src.copyToRecursively(dst.createParentDirectories(), followLinks = false)
}
@Test
@@ -3361,6 +3361,7 @@ public final class kotlin/io/path/PathsKt {
public static final fun copyToRecursively (Ljava/nio/file/Path;Ljava/nio/file/Path;Lkotlin/jvm/functions/Function3;ZZ)Ljava/nio/file/Path;
public static synthetic fun copyToRecursively$default (Ljava/nio/file/Path;Ljava/nio/file/Path;Lkotlin/jvm/functions/Function3;ZLkotlin/jvm/functions/Function3;ILjava/lang/Object;)Ljava/nio/file/Path;
public static synthetic fun copyToRecursively$default (Ljava/nio/file/Path;Ljava/nio/file/Path;Lkotlin/jvm/functions/Function3;ZZILjava/lang/Object;)Ljava/nio/file/Path;
public static final fun createParentDirectories (Ljava/nio/file/Path;[Ljava/nio/file/attribute/FileAttribute;)Ljava/nio/file/Path;
public static final fun createTempDirectory (Ljava/nio/file/Path;Ljava/lang/String;[Ljava/nio/file/attribute/FileAttribute;)Ljava/nio/file/Path;
public static synthetic fun createTempDirectory$default (Ljava/nio/file/Path;Ljava/lang/String;[Ljava/nio/file/attribute/FileAttribute;ILjava/lang/Object;)Ljava/nio/file/Path;
public static final fun createTempFile (Ljava/nio/file/Path;Ljava/lang/String;Ljava/lang/String;[Ljava/nio/file/attribute/FileAttribute;)Ljava/nio/file/Path;