From b420d85be553896fb465d03acdf99c9ca97a90bf Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Tue, 22 Jun 2021 13:36:23 +0600 Subject: [PATCH] MPPFile --- CHANGELOG.md | 5 +++ common/build.gradle | 15 +++++++++ .../inmo/micro_utils/common/InputAllocator.kt | 10 +++++- .../dev/inmo/micro_utils/common/MPPFile.kt | 31 ++++++++++++++++++ .../dev/inmo/micro_utils/common/JSMPPFile.kt | 32 +++++++++++++++++++ .../inmo/micro_utils/common/PromiseAwait.kt | 8 +++++ .../dev/inmo/micro_utils/common/JVMMPPFile.kt | 20 ++++++++++++ 7 files changed, 120 insertions(+), 1 deletion(-) create mode 100644 common/src/commonMain/kotlin/dev/inmo/micro_utils/common/MPPFile.kt create mode 100644 common/src/jsMain/kotlin/dev/inmo/micro_utils/common/JSMPPFile.kt create mode 100644 common/src/jsMain/kotlin/dev/inmo/micro_utils/common/PromiseAwait.kt create mode 100644 common/src/jvmMain/kotlin/dev/inmo/micro_utils/common/JVMMPPFile.kt diff --git a/CHANGELOG.md b/CHANGELOG.md index 0fc94989c88..3bf3d09ab56 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,11 @@ ## 0.5.13 +* `Common`: + * Add functionality for multiplatform working with files: + * Main class for files `MPPFile` + * Inline class for filenames work encapsulation `FileName` + ## 0.5.12 * `Common`: diff --git a/common/build.gradle b/common/build.gradle index 7c54502f100..14324f2f227 100644 --- a/common/build.gradle +++ b/common/build.gradle @@ -5,3 +5,18 @@ plugins { } apply from: "$mppProjectWithSerializationPresetPath" + +kotlin { + sourceSets { + jvmMain { + dependencies { + api project(":micro_utils.coroutines") + } + } + androidMain { + dependencies { + api project(":micro_utils.coroutines") + } + } + } +} diff --git a/common/src/commonMain/kotlin/dev/inmo/micro_utils/common/InputAllocator.kt b/common/src/commonMain/kotlin/dev/inmo/micro_utils/common/InputAllocator.kt index 2fd48505694..b33b406805d 100644 --- a/common/src/commonMain/kotlin/dev/inmo/micro_utils/common/InputAllocator.kt +++ b/common/src/commonMain/kotlin/dev/inmo/micro_utils/common/InputAllocator.kt @@ -7,9 +7,17 @@ import kotlinx.serialization.encoding.Decoder import kotlinx.serialization.encoding.Encoder typealias ByteArrayAllocator = () -> ByteArray +typealias SuspendByteArrayAllocator = suspend () -> ByteArray val ByteArray.asAllocator: ByteArrayAllocator get() = { this } +val ByteArray.asSuspendAllocator: SuspendByteArrayAllocator + get() = { this } +val ByteArrayAllocator.asSuspendAllocator: SuspendByteArrayAllocator + get() = { this() } +suspend fun SuspendByteArrayAllocator.asAllocator(): ByteArrayAllocator { + return invoke().asAllocator +} object ByteArrayAllocatorSerializer : KSerializer { private val realSerializer = ByteArraySerializer() @@ -17,7 +25,7 @@ object ByteArrayAllocatorSerializer : KSerializer { override fun deserialize(decoder: Decoder): ByteArrayAllocator { val bytes = realSerializer.deserialize(decoder) - return { bytes } + return bytes.asAllocator } override fun serialize(encoder: Encoder, value: ByteArrayAllocator) { diff --git a/common/src/commonMain/kotlin/dev/inmo/micro_utils/common/MPPFile.kt b/common/src/commonMain/kotlin/dev/inmo/micro_utils/common/MPPFile.kt new file mode 100644 index 00000000000..c60eb0d6b80 --- /dev/null +++ b/common/src/commonMain/kotlin/dev/inmo/micro_utils/common/MPPFile.kt @@ -0,0 +1,31 @@ +package dev.inmo.micro_utils.common + +import kotlinx.serialization.Serializable +import kotlin.jvm.JvmInline + +@Serializable +@JvmInline +value class FileName(val string: String) { + val name: String + get() = string.takeLastWhile { it != '/' } + val extension: String + get() = name.takeLastWhile { it != '.' } + val nameWithoutExtension: String + get() { + val filename = name + return filename.indexOfLast { it == '.' }.takeIf { it > -1 } ?.let { + filename.substring(0, it) + } ?: filename + } + override fun toString(): String = string +} + + +@PreviewFeature +expect class MPPFile + +expect val MPPFile.filename: FileName +expect val MPPFile.filesize: Long +expect val MPPFile.bytesAllocator: SuspendByteArrayAllocator +suspend fun MPPFile.bytes() = bytesAllocator() + diff --git a/common/src/jsMain/kotlin/dev/inmo/micro_utils/common/JSMPPFile.kt b/common/src/jsMain/kotlin/dev/inmo/micro_utils/common/JSMPPFile.kt new file mode 100644 index 00000000000..b163f489506 --- /dev/null +++ b/common/src/jsMain/kotlin/dev/inmo/micro_utils/common/JSMPPFile.kt @@ -0,0 +1,32 @@ +package dev.inmo.micro_utils.common + +import org.khronos.webgl.ArrayBuffer +import org.w3c.dom.ErrorEvent +import org.w3c.files.File +import org.w3c.files.FileReader +import kotlin.js.Promise + +actual typealias MPPFile = File + +fun MPPFile.readBytesPromise() = Promise { success, failure -> + val reader = FileReader() + reader.onload = { + success((reader.result as ArrayBuffer).toByteArray()) + Unit + } + reader.onerror = { + failure(Exception((it as ErrorEvent).message)) + Unit + } + reader.readAsArrayBuffer(this) +} + +private suspend fun MPPFile.dirtyReadBytes(): ByteArray = readBytesPromise().await() + +actual val MPPFile.filename: FileName + get() = FileName(name) +actual val MPPFile.filesize: Long + get() = size.toLong() +@Warning("That is not optimized version of bytes allocator. Use asyncBytesAllocator everywhere you can") +actual val MPPFile.bytesAllocator: SuspendByteArrayAllocator + get() = ::dirtyReadBytes diff --git a/common/src/jsMain/kotlin/dev/inmo/micro_utils/common/PromiseAwait.kt b/common/src/jsMain/kotlin/dev/inmo/micro_utils/common/PromiseAwait.kt new file mode 100644 index 00000000000..6c2f3bfcd16 --- /dev/null +++ b/common/src/jsMain/kotlin/dev/inmo/micro_utils/common/PromiseAwait.kt @@ -0,0 +1,8 @@ +package dev.inmo.micro_utils.common + +import kotlin.coroutines.* +import kotlin.js.Promise + +suspend fun Promise.await(): T = suspendCoroutine { cont -> + then({ cont.resume(it) }, { cont.resumeWithException(it) }) +} diff --git a/common/src/jvmMain/kotlin/dev/inmo/micro_utils/common/JVMMPPFile.kt b/common/src/jvmMain/kotlin/dev/inmo/micro_utils/common/JVMMPPFile.kt new file mode 100644 index 00000000000..770dfc95f7f --- /dev/null +++ b/common/src/jvmMain/kotlin/dev/inmo/micro_utils/common/JVMMPPFile.kt @@ -0,0 +1,20 @@ +package dev.inmo.micro_utils.common + +import dev.inmo.micro_utils.coroutines.doInIO +import dev.inmo.micro_utils.coroutines.doOutsideOfCoroutine +import java.io.File + +actual typealias MPPFile = File + +actual val MPPFile.filename: FileName + get() = FileName(name) +actual val MPPFile.filesize: Long + get() = length() +actual val MPPFile.bytesAllocator: SuspendByteArrayAllocator + get() = { + doInIO { + doOutsideOfCoroutine { + readBytes() + } + } + }