Skip to content

Commit ff6ec3d

Browse files
committed
Fixed/clarified KDoc and added/fixed related tests
1 parent aad5f56 commit ff6ec3d

File tree

4 files changed

+77
-21
lines changed

4 files changed

+77
-21
lines changed

core/common/src/-CommonPlatform.kt

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,16 @@ package kotlinx.io
2323

2424
internal expect fun String.asUtf8ToByteArray(): ByteArray
2525

26+
/**
27+
* Signals about a general issue occurred during I/O operation.
28+
*/
2629
public expect open class IOException(message: String?, cause: Throwable?) : Exception {
2730
public constructor(message: String? = null)
2831
}
2932

33+
/**
34+
* Signals that the end of the file or stream was reached unexpectedly during an input operation.
35+
*/
3036
public expect open class EOFException(message: String? = null) : IOException
3137

3238

@@ -35,4 +41,4 @@ public expect open class EOFException(message: String? = null) : IOException
3541
// This is a workaround that should be removed as soon as stdlib will support AutoCloseable
3642
// actual typealias on JVM.
3743
@OptIn(ExperimentalStdlibApi::class)
38-
internal typealias AutoCloseableAlias = AutoCloseable
44+
internal typealias AutoCloseableAlias = AutoCloseable

core/common/src/files/FileSystem.kt

Lines changed: 28 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import kotlinx.io.Sink
1010
import kotlinx.io.Source
1111

1212
/**
13-
* An interface providing basic operations on a file system.
13+
* An interface providing basic operations on a filesystem.
1414
*
1515
* **This API is unstable and subject to change.**
1616
*/
@@ -21,7 +21,7 @@ public sealed interface FileSystem {
2121
public val temporaryDirectory: Path
2222

2323
/**
24-
* Returns `true` if there is a file system entity corresponding to a [path],
24+
* Returns `true` if there is a filesystem entity a [path] points to,
2525
* otherwise returns `false`.
2626
*
2727
* @param path the path that should be checked for existence.
@@ -31,15 +31,18 @@ public sealed interface FileSystem {
3131
public fun exists(path: Path): Boolean
3232

3333
/**
34-
* Deletes [path] from a file system. If there is no file system entity
35-
* represented by the [path] this method throws [kotlinx.io.files.FileNotFoundException] when [mustExist]
36-
* is `true`.
34+
* Deletes a file or directory the [path] points to from a filesystem.
35+
* If there is no filesystem entity represented by the [path]
36+
* this method throws [kotlinx.io.files.FileNotFoundException] when [mustExist] is `true`.
3737
*
38-
* @param path the path to be deleted.
38+
* Note that in the case of a directory, this method will not attempt to delete it recursively,
39+
* so deletion of non-empty directory will fail.
40+
*
41+
* @param path the path to a file or directory to be deleted.
3942
* @param mustExist the flag indicating whether missing [path] is an error, `true` by default.
4043
*
4144
* @throws kotlinx.io.files.FileNotFoundException when [path] does not exist and [mustExist] is `true`.
42-
* @throws kotlinx.io.IOException if the [path] could not be deleted.
45+
* @throws kotlinx.io.IOException if deletion failed.
4346
*/
4447
public fun delete(path: Path, mustExist: Boolean = true)
4548

@@ -48,7 +51,7 @@ public sealed interface FileSystem {
4851
* If [path] already exists then the method throws [kotlinx.io.IOException] when [mustCreate] is `true`.
4952
* The call will attempt to create only missing directories.
5053
* The method is not atomic and if it fails after creating some
51-
* directories these directories will not be deleted automatically.
54+
* directories, these directories will not be deleted automatically.
5255
* Permissions for created directories are platform-specific.
5356
*
5457
* @param path the path to be created.
@@ -63,26 +66,28 @@ public sealed interface FileSystem {
6366
/**
6467
* Atomically renames [source] to [destination] overriding [destination] if it already exists.
6568
*
66-
* When the file system does not support atomic move of [source] and [destination] corresponds to different
67-
* file systems and the operation could not be performed atomically,
69+
* When the filesystem does not support atomic move of [source] and [destination] corresponds to different
70+
* filesystems and the operation could not be performed atomically,
6871
* [UnsupportedOperationException] is thrown.
6972
*
7073
* @param source the path to rename.
7174
* @param destination desired path name.
7275
*
7376
* @throws kotlinx.io.files.FileNotFoundException when the [source] does not exist.
7477
* @throws kotlinx.io.IOException when the move failed.
75-
* @throws kotlin.UnsupportedOperationException when the file system does not support atomic move.
78+
* @throws kotlin.UnsupportedOperationException when the filesystem does not support atomic move.
7679
*/
7780
public fun atomicMove(source: Path, destination: Path)
7881

7982
/**
80-
* Returns [Source] to read from a file represented by the [path].
83+
* Returns [Source] to read from a file the [path] points to.
8184
*
8285
* How a source will read the data is implementation-specific and failures caused
8386
* by the missing file or, for example, lack of permissions may not be reported immediately,
8487
* but postponed until the source will try to fetch data.
8588
*
89+
* If [path] points to a directory, this method will fail with [IOException].
90+
*
8691
* @param path the path to read from.
8792
*
8893
* @throws kotlinx.io.files.FileNotFoundException when the file does not exist.
@@ -91,19 +96,21 @@ public sealed interface FileSystem {
9196
public fun read(path: Path): Source
9297

9398
/**
94-
* Returns [Sink] to write into a file represented by the [path].
99+
* Returns [Sink] to write into a file the [path] points to.
95100
* File will be created if it does not exist yet.
96101
*
97102
* How a sink will write the data is implementation-specific and failures caused,
98103
* for example, by the lack of permissions may not be reported immediately,
99104
* but postponed until the sink will try to store data.
100105
*
106+
* If [path] points to a directory, this method will fail with [IOException].
107+
*
101108
* @throws kotlinx.io.IOException when it's not possible to open the file for writing.
102109
*/
103110
public fun write(path: Path): Sink
104111

105112
/**
106-
* Return [FileMetadata] associated with a file or directory represented by the [path].
113+
* Return [FileMetadata] associated with a file or directory the [path] points to.
107114
* If there is no such file or directory, or it's impossible to fetch metadata,
108115
* `null` is returned.
109116
*
@@ -113,7 +120,7 @@ public sealed interface FileSystem {
113120

114121
public companion object {
115122
/**
116-
* An instance of [FileSystem] representing a default system-wide file system.
123+
* An instance of [FileSystem] representing a default system-wide filesystem.
117124
*/
118125
public val System: FileSystem = SystemFileSystem
119126
}
@@ -129,6 +136,9 @@ internal abstract class SystemFileSystemImpl : FileSystem {
129136

130137
internal expect val SystemFileSystem: FileSystem
131138

139+
/**
140+
* Represents information about a file or directory obtainable from a filesystem.
141+
*/
132142
public class FileMetadata(
133143
/**
134144
* Flag indicating that the metadata was retrieved for a regular file.
@@ -144,4 +154,7 @@ public class FileMetadata(
144154
public val size: Long = 0L
145155
)
146156

157+
/**
158+
* Signals an I/O operation's failure due to a missing file or directory.
159+
*/
147160
public expect class FileNotFoundException(message: String?) : IOException

core/common/test/files/SmokeFileTest.kt

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,39 @@ class SmokeFileTest {
263263
assertNotEquals(p0, Path(p0, "d", ".."))
264264
}
265265

266+
@Test
267+
fun deleteNonEmptyDirectory() {
268+
val basePath = createTempPath()
269+
val childPath = Path(basePath, "child")
270+
271+
FileSystem.System.createDirectories(childPath, true)
272+
assertFailsWith<IOException> { FileSystem.System.delete(basePath) }
273+
274+
FileSystem.System.delete(childPath)
275+
FileSystem.System.delete(basePath)
276+
}
277+
278+
@Test
279+
fun readDirectory() {
280+
val dir = createTempPath()
281+
FileSystem.System.createDirectories(dir)
282+
283+
assertFailsWith<IOException> { FileSystem.System.read(dir).readByte() }
284+
}
285+
286+
@OptIn(ExperimentalStdlibApi::class)
287+
@Test
288+
fun writeDirectory() {
289+
val dir = createTempPath()
290+
FileSystem.System.createDirectories(dir)
291+
292+
assertFailsWith<IOException> {
293+
FileSystem.System.write(dir).use {
294+
it.writeByte(0)
295+
}
296+
}
297+
}
298+
266299
private fun constructAbsolutePath(vararg parts: String): String {
267300
return Path.separator.toString() + parts.joinToString(Path.separator.toString())
268301
}

core/js/src/files/PathsJs.kt

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -155,11 +155,15 @@ private class FileSink(private val path: Path) : RawSink {
155155
val segmentBytes = head.limit - head.pos
156156
val buf = buffer.Buffer.allocUnsafe(segmentBytes)
157157
buf.fill(head.data, head.pos, segmentBytes)
158-
if (append) {
159-
fs.appendFileSync(path.toString(), buf)
160-
} else {
161-
fs.writeFileSync(path.toString(), buf)
162-
append = true
158+
try {
159+
if (append) {
160+
fs.appendFileSync(path.toString(), buf)
161+
} else {
162+
fs.writeFileSync(path.toString(), buf)
163+
append = true
164+
}
165+
} catch (e: Throwable) {
166+
throw IOException("Write failed", e)
163167
}
164168

165169
source.skip(segmentBytes.toLong())

0 commit comments

Comments
 (0)