From c60f820ccbf9f4d5f590bc9208baf23d24af8b58 Mon Sep 17 00:00:00 2001 From: Jesse Wilson Date: Tue, 5 Jan 2021 21:10:53 -0500 Subject: [PATCH] Syntactic sugar for writing files --- docs/index.md | 103 ++++++++++++++++++ okio/src/commonMain/kotlin/okio/FileSystem.kt | 20 ++++ .../kotlin/okio/AbstractFileSystemTest.kt | 27 +++++ 3 files changed, 150 insertions(+) diff --git a/docs/index.md b/docs/index.md index 1433c33ee9..2c1b850893 100644 --- a/docs/index.md +++ b/docs/index.md @@ -973,6 +973,108 @@ fun decryptAesToByteString(file: File, key: ByteArray, iv: ByteArray): ByteStrin } ``` +File System Examples +-------------------- + +Okio's recently gained a multiplatform file system API. These examples work on JVM, native, and +Node.js platforms. In the examples below `fileSystem` is an instance of [FileSystem] such as +`FileSystem.SYSTEM` or `FakeFileSystem`. + +Read all of `readme.md` as a string: + +``` +val path = "readme.md".toPath() +val entireFileString = fileSystem.read(path) { + readUtf8() +} +``` + +Read all of `thumbnail.png` as a [ByteString][3]: + +``` +val path = "thumbnail.png".toPath() +val entireFileByteString = fileSystem.read(path) { + readByteString() +} +``` + +Read all lines of `/etc/hosts` into a `List`: + +``` +val path = "/etc/hosts".toPath() +val allLines = fileSystem.read(path) { + generateSequence { readUtf8Line() }.toList() +} +``` + +Read the prefix of `index.html` that precedes the first `` substring: + +``` +val path = "index.html".toPath() +val untilHtmlTag = fileSystem.read(path) { + val htmlTag = indexOf("".encodeUtf8()) + if (htmlTag != -1L) readUtf8(htmlTag) else null +} +``` + +Write `readme.md` as a string: + +``` +val path = "readme.md".toPath() +fileSystem.write(path) { + writeUtf8( + """ + |Hello, World + |------------ + | + |This is a sample file. + |""".trimMargin() + ) +} +``` + +Write `data.bin` as a [ByteString][3]: + +``` +val path = "data.bin".toPath() +fileSystem.write(path) { + val byteString = "68656c6c6f20776f726c640a".decodeHex() + write(byteString) +} +``` + +Write `readme.md` from a `List`: + +``` +val path = "readme.md".toPath() +val lines = listOf( + "Hello, World", + "------------", + "", + "This is a sample file.", + "" +) +fileSystem.write(path) { + for (line in lines) { + writeUtf8(line) + writeUtf8("\n") + } +} +``` + +Generate `binary.txt` programmatically: + +``` +val path = "binary.txt".toPath() +fileSystem.write(path) { + for (i in 1 until 100) { + writeUtf8("$i ${i.toString(2)}") + writeUtf8("\n") + } +} +``` + + Releases -------- @@ -1049,6 +1151,7 @@ License [WriteFileKt]: https://github.com/square/okio/blob/master/samples/src/jvmMain/kotlin/okio/samples/WriteFile.kt [ExploreCharsets]: https://github.com/square/okio/blob/master/samples/src/jvmMain/java/okio/samples/ExploreCharsets.java [ExploreCharsetsKt]: https://github.com/square/okio/blob/master/samples/src/jvmMain/kotlin/okio/samples/ExploreCharsets.kt + [FileSystem]: https://square.github.io/okio/2.x/okio/okio/-file-system/index.html [GoldenValue]: https://github.com/square/okio/blob/master/samples/src/jvmMain/java/okio/samples/GoldenValue.java [GoldenValueKt]: https://github.com/square/okio/blob/master/samples/src/jvmMain/kotlin/okio/samples/GoldenValue.kt [BitmapEncoder]: https://github.com/square/okio/blob/master/samples/src/jvmMain/java/okio/samples/BitmapEncoder.java diff --git a/okio/src/commonMain/kotlin/okio/FileSystem.kt b/okio/src/commonMain/kotlin/okio/FileSystem.kt index acf8cc435b..8bf42f8a5b 100644 --- a/okio/src/commonMain/kotlin/okio/FileSystem.kt +++ b/okio/src/commonMain/kotlin/okio/FileSystem.kt @@ -153,6 +153,16 @@ abstract class FileSystem { @Throws(IOException::class) abstract fun source(file: Path): Source + /** + * Creates a source to read [file], executes [readerAction] to read it, and then closes the + * source. This is a compact way to read the contents of a file. + */ + inline fun read(file: Path, readerAction: BufferedSource.() -> T): T { + return source(file).buffer().use { + it.readerAction() + } + } + /** * Returns a sink that writes bytes to [file] from beginning to end. If [file] already exists it * will be replaced with the new data. @@ -164,6 +174,16 @@ abstract class FileSystem { @Throws(IOException::class) abstract fun sink(file: Path): Sink + /** + * Creates a sink to write [file], executes [writerAction] to write it, and then closes the sink. + * This is a compact way to write a file. + */ + inline fun write(file: Path, writerAction: BufferedSink.() -> T): T { + return sink(file).buffer().use { + it.writerAction() + } + } + /** * Returns a sink that appends bytes to the end of [file], creating it if it doesn't already * exist. diff --git a/okio/src/commonTest/kotlin/okio/AbstractFileSystemTest.kt b/okio/src/commonTest/kotlin/okio/AbstractFileSystemTest.kt index 90758d49c2..bbe36f93ab 100644 --- a/okio/src/commonTest/kotlin/okio/AbstractFileSystemTest.kt +++ b/okio/src/commonTest/kotlin/okio/AbstractFileSystemTest.kt @@ -131,6 +131,21 @@ abstract class AbstractFileSystemTest( source.close() } + @Test + fun readPath() { + val path = base / "read-path" + val string = "hello, read with a Path" + path.writeUtf8(string) + + val result = fileSystem.read(path) { + assertEquals("hello", readUtf8(5)) + assertEquals(", read with ", readUtf8(12)) + assertEquals("a Path", readUtf8()) + return@read "success" + } + assertEquals("success", result) + } + @Test fun fileSink() { val path = base / "file-sink" @@ -143,6 +158,18 @@ abstract class AbstractFileSystemTest( assertEquals("hello, world!", path.readUtf8()) } + @Test + fun writePath() { + val path = base / "write-path" + val content = fileSystem.write(path) { + val string = "hello, write with a Path" + writeUtf8(string) + return@write string + } + assertTrue(path in fileSystem.list(base)) + assertEquals(content, path.readUtf8()) + } + @Test fun appendingSinkAppendsToExistingFile() { val path = base / "appending-sink-appends-to-existing-file"