diff options
4 files changed, 815 insertions, 2 deletions
diff --git a/kvision-modules/kvision-cordova/src/main/kotlin/pl/treksoft/kvision/cordova/File.kt b/kvision-modules/kvision-cordova/src/main/kotlin/pl/treksoft/kvision/cordova/File.kt new file mode 100644 index 00000000..df176e84 --- /dev/null +++ b/kvision-modules/kvision-cordova/src/main/kotlin/pl/treksoft/kvision/cordova/File.kt @@ -0,0 +1,542 @@ +@file:Suppress( + "TooManyFunctions", "TooGenericExceptionCaught" +) + +/* + * Copyright (c) 2017-present Robert Jaros + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package pl.treksoft.kvision.cordova + +import org.khronos.webgl.ArrayBuffer +import org.khronos.webgl.Uint8Array +import org.khronos.webgl.set +import org.w3c.files.Blob +import org.w3c.files.BlobPropertyBag +import org.w3c.files.FileReader +import pl.treksoft.kvision.utils.obj +import kotlin.browser.window +import kotlin.coroutines.resume +import kotlin.coroutines.suspendCoroutine + +/** + * Exception class for file errors. + */ +class FileException(code: Int) : Exception("Error: $code") + +/** + * System directories class. + */ +external class SystemDirs { + val applicationDirectory: String + val applicationStorageDirectory: String + val dataDirectory: String + val cacheDirectory: String + val externalApplicationStorageDirectory: String? + val externalDataDirectory: String? + val externalCacheDirectory: String? + val externalRootDirectory: String? + val tempDirectory: String? + val syncedDataDirectory: String? + val documentsDirectory: String? + val sharedDirectory: String? +} + +/** + * Main object for Cordova file. + */ +object File { + + const val NOT_FOUND_ERR = 1 + const val SECURITY_ERR = 2 + const val ABORT_ERR = 3 + const val NOT_READABLE_ERR = 4 + const val ENCODING_ERR = 5 + const val NO_MODIFICATION_ALLOWED_ERR = 6 + const val INVALID_STATE_ERR = 7 + const val SYNTAX_ERR = 8 + const val INVALID_MODIFICATION_ERR = 9 + const val QUOTA_EXCEEDED_ERR = 10 + const val TYPE_MISMATCH_ERR = 11 + const val PATH_EXISTS_ERR = 12 + + /** + * File system types. + */ + enum class FileSystemType { + TEMPORARY, + PERSISTENT + } + + /** + * Get system directories. + */ + suspend fun getSystemDirectories(): SystemDirs { + return suspendCoroutine { continuation -> + addDeviceReadyListener { + continuation.resume(window.asDynamic().cordova.file) + } + } + } + + /** + * Resolve given path to a file or directory entry. + * @param url directory path + */ + @Suppress("UnsafeCastFromDynamic") + suspend fun resolveLocalFileSystemURL(url: String): Result<Entry, FileException> { + return suspendCoroutine { continuation -> + addDeviceReadyListener { + window.asDynamic().resolveLocalFileSystemURL(url, { entry -> + continuation.resume(Result.Success(entry)) + }, { error -> + continuation.resume(Result.error(FileException(error.code))) + }) + } + } + } + + /** + * Resolve given path to a directory entry. + * @param url directory path + */ + @Suppress("UnsafeCastFromDynamic") + suspend fun resolveLocalFileSystemURLForDir(url: String): Result<DirectoryEntry, FileException> { + return when (val result = resolveLocalFileSystemURL(url)) { + is Result.Success -> { + if (result.value.isDirectory) { + result.map { + @Suppress("UNCHECKED_CAST_TO_EXTERNAL_INTERFACE") + it as DirectoryEntry + } + } else { + Result.error(FileException(TYPE_MISMATCH_ERR)) + } + } + is Result.Failure -> result + } + } + + /** + * Resolve given path to a file entry. + * @param url file path + */ + @Suppress("UnsafeCastFromDynamic") + suspend fun resolveLocalFileSystemURLForFile(url: String): Result<FileEntry, FileException> { + return when (val result = resolveLocalFileSystemURL(url)) { + is Result.Success -> { + if (result.value.isFile) { + result.map { + @Suppress("UNCHECKED_CAST_TO_EXTERNAL_INTERFACE") + it as FileEntry + } + } else { + Result.error(FileException(TYPE_MISMATCH_ERR)) + } + } + is Result.Failure -> result + } + } + + /** + * Request a file system of a given type. + * @param fileSystemType file system type (temporary or persistent) + * @param size file system size (quota) + */ + @Suppress("UnsafeCastFromDynamic") + suspend fun requestFileSystem( + fileSystemType: File.FileSystemType, + size: Long = 0 + ): Result<FileSystem, FileException> { + return suspendCoroutine { continuation -> + val type = when (fileSystemType) { + File.FileSystemType.TEMPORARY -> LocalFileSystem.TEMPORARY + File.FileSystemType.PERSISTENT -> LocalFileSystem.PERSISTENT + } + addDeviceReadyListener { + window.asDynamic().requestFileSystem(type, size, { fs -> + continuation.resume(Result.Success(fs)) + }, { error -> + continuation.resume(Result.error(FileException(error.code))) + }) + } + } + } + + internal fun dataURLtoBlob(dataURI: String): Blob { + val byteString = window.atob(dataURI.split(',')[1]) + val mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0] + val ab = ArrayBuffer(byteString.length) + val ia = Uint8Array(ab) + for (i in 0 until byteString.length) { + ia[i] = byteString[i].toByte() + } + return Blob(arrayOf(ab), BlobPropertyBag(type = mimeString)) + } + +} + +/** + * Extension function to convert String to a directory entry. + */ +suspend fun String.toDirectoryEntry(): Result<DirectoryEntry, FileException> { + return File.resolveLocalFileSystemURLForDir(this) +} + +/** + * Extension function to convert String to a file entry. + */ +suspend fun String.toFileEntry(): Result<FileEntry, FileException> { + return File.resolveLocalFileSystemURLForFile(this) +} + +/** + * Get file or directory metadata. + */ +@Suppress("UNCHECKED_CAST_TO_EXTERNAL_INTERFACE", "UnsafeCastFromDynamic") +suspend fun Entry.getMetadata(): Result<Metadata, FileException> { + return suspendCoroutine { continuation -> + this.getMetadata({ metadata: Metadata -> + continuation.resume(Result.success(metadata)) + } as MetadataCallback, { error: dynamic -> + continuation.resume(Result.error(FileException(error.code))) + } as ErrorCallback) + } +} + +/** + * Get file or directory parent directory entry. + */ +@Suppress("UNCHECKED_CAST_TO_EXTERNAL_INTERFACE", "UnsafeCastFromDynamic") +suspend fun Entry.getParent(): Result<DirectoryEntry, FileException> { + return suspendCoroutine { continuation -> + this.getParent({ directoryEntry: DirectoryEntry -> + continuation.resume(Result.success(directoryEntry)) + } as DirectoryEntryCallback, { error: dynamic -> + continuation.resume(Result.error(FileException(error.code))) + } as ErrorCallback) + } +} + +/** + * Remove given file or directory. + */ +@Suppress("UNCHECKED_CAST_TO_EXTERNAL_INTERFACE", "UnsafeCastFromDynamic") +suspend fun Entry.remove(): Result<Entry, FileException> { + return suspendCoroutine { continuation -> + this.remove({ + continuation.resume(Result.success(this)) + } as VoidCallback, { error: dynamic -> + continuation.resume(Result.error(FileException(error.code))) + } as ErrorCallback) + } +} + +/** + * Move given file or directory to a new location. + * @param parent new location parent directory entry + * @param newName new location name + */ +@Suppress("UNCHECKED_CAST_TO_EXTERNAL_INTERFACE", "UnsafeCastFromDynamic") +suspend fun Entry.moveTo(parent: DirectoryEntry, newName: String? = null): Result<Entry, FileException> { + return suspendCoroutine { continuation -> + this.moveTo(parent, newName, { entry: Entry -> + continuation.resume(Result.success(entry)) + } as EntryCallback, { error: dynamic -> + continuation.resume(Result.error(FileException(error.code))) + } as ErrorCallback) + } +} + +/** + * Copy given file or directory to a new location. + * @param parent new location parent directory entry + * @param newName new location name + */ +@Suppress("UNCHECKED_CAST_TO_EXTERNAL_INTERFACE", "UnsafeCastFromDynamic") +suspend fun Entry.copyTo(parent: DirectoryEntry, newName: String? = null): Result<Entry, FileException> { + return suspendCoroutine { continuation -> + this.copyTo(parent, newName, { entry: Entry -> + continuation.resume(Result.success(entry)) + } as EntryCallback, { error: dynamic -> + continuation.resume(Result.error(FileException(error.code))) + } as ErrorCallback) + } +} + +/** + * Create a FileWriter object for a given file entry. + */ +@Suppress("UNCHECKED_CAST_TO_EXTERNAL_INTERFACE", "UnsafeCastFromDynamic") +suspend fun FileEntry.createWriter(): Result<FileWriter, FileException> { + return suspendCoroutine { continuation -> + this.createWriter({ fileWriter: FileWriter -> + continuation.resume(Result.success(fileWriter)) + } as FileWriterCallback, { error: dynamic -> + continuation.resume(Result.error(FileException(error.code))) + } as ErrorCallback) + } +} + +/** + * Get a File object for a given file entry. + */ +@Suppress("UNCHECKED_CAST_TO_EXTERNAL_INTERFACE", "UnsafeCastFromDynamic") +suspend fun FileEntry.file(): Result<org.w3c.files.File, FileException> { + return suspendCoroutine { continuation -> + this.file({ file: org.w3c.files.File -> + continuation.resume(Result.success(file)) + } as FileCallback, { error: dynamic -> + continuation.resume(Result.error(FileException(error.code))) + } as ErrorCallback) + } +} + +/** + * Get or create a file in a given parent directory. + * @param path target file path + * @param create Used to indicate that the user wants to create a file if it was not previously there. + * @param exclusive Fail if the target path file already exists. + */ +@Suppress("UNCHECKED_CAST_TO_EXTERNAL_INTERFACE", "UnsafeCastFromDynamic") +suspend fun DirectoryEntry.getFile( + path: String, + create: Boolean = true, + exclusive: Boolean = false +): Result<FileEntry, FileException> { + return suspendCoroutine { continuation -> + this.getFile(path, obj { + this.create = create + this.exclusive = exclusive + }, { fileEntry: FileEntry -> + continuation.resume(Result.success(fileEntry)) + } as FileEntryCallback, { error: dynamic -> + continuation.resume(Result.error(FileException(error.code))) + } as ErrorCallback) + } +} + +/** + * Get or create a directory in a given parent directory. + * @param path target directory path + * @param create Used to indicate that the user wants to create a directory if it was not previously there. + * @param exclusive Fail if the target path directory already exists. + */ +@Suppress("UNCHECKED_CAST_TO_EXTERNAL_INTERFACE", "UnsafeCastFromDynamic") +suspend fun DirectoryEntry.getDirectory( + path: String, + create: Boolean = true, + exclusive: Boolean = false +): Result<DirectoryEntry, FileException> { + return suspendCoroutine { continuation -> + this.getDirectory(path, obj { + this.create = create + this.exclusive = exclusive + }, { directoryEntry: DirectoryEntry -> + continuation.resume(Result.success(directoryEntry)) + } as DirectoryEntryCallback, { error: dynamic -> + continuation.resume(Result.error(FileException(error.code))) + } as ErrorCallback) + } +} + +/** + * Remove given directory recursively. + */ +@Suppress("UNCHECKED_CAST_TO_EXTERNAL_INTERFACE", "UnsafeCastFromDynamic") +suspend fun DirectoryEntry.removeRecursively(): Result<DirectoryEntry, FileException> { + return suspendCoroutine { continuation -> + this.removeRecursively({ + continuation.resume(Result.success(this)) + } as VoidCallback, { error: dynamic -> + continuation.resume(Result.error(FileException(error.code))) + } as ErrorCallback) + } +} + +/** + * List directory entries for a given DirectoryReader. + */ +@Suppress("UNCHECKED_CAST_TO_EXTERNAL_INTERFACE", "UnsafeCastFromDynamic") +suspend fun DirectoryReader.readEntries(): Result<List<Entry>, FileException> { + return suspendCoroutine { continuation -> + this.readEntries({ entries: Array<Entry> -> + continuation.resume(Result.success(entries.toList())) + } as EntriesCallback, { error: dynamic -> + continuation.resume(Result.error(FileException(error.code))) + } as ErrorCallback) + } +} + +/** + * List directory entries for a given parent directory entry. + */ +suspend fun DirectoryEntry.readEntries(): Result<List<Entry>, FileException> { + return this.createReader().readEntries() +} + +/** + * Read file content as a plain string. + */ +@Suppress("UnsafeCastFromDynamic") +suspend fun FileEntry.readAsText(): Result<String, FileException> { + return this.file().flatMap { file -> + suspendCoroutine<Result<String, FileException>> { continuation -> + val reader = FileReader() + reader.onloadend = { e -> + continuation.resume(Result.success(e.target.asDynamic().result)) + } + reader.onerror = { error: dynamic -> + continuation.resume(Result.error(FileException(error.code))) + } + reader.readAsText(file) + } + } +} + +/** + * Read file content as a data url. + */ +@Suppress("UnsafeCastFromDynamic") +suspend fun FileEntry.readAsDataURL(): Result<String, FileException> { + return this.file().flatMap { file -> + suspendCoroutine<Result<String, FileException>> { continuation -> + val reader = FileReader() + reader.onloadend = { e -> + continuation.resume(Result.success(e.target.asDynamic().result)) + } + reader.onerror = { error: dynamic -> + continuation.resume(Result.error(FileException(error.code))) + } + reader.readAsDataURL(file) + } + } +} + +/** + * Read file content as an array buffer. + */ +@Suppress("UnsafeCastFromDynamic") +suspend fun FileEntry.readAsArrayBuffer(): Result<ArrayBuffer, FileException> { + return this.file().flatMap { file -> + suspendCoroutine<Result<ArrayBuffer, FileException>> { continuation -> + val reader = FileReader() + reader.onloadend = { e -> + continuation.resume(Result.success(e.target.asDynamic().result)) + } + reader.onerror = { error: dynamic -> + continuation.resume(Result.error(FileException(error.code))) + } + reader.onabort = { _ -> + continuation.resume(Result.error(FileException(File.ABORT_ERR))) + } + reader.readAsArrayBuffer(file) + } + } +} + +/** + * Write file content from a Blob. + * @param data a data Blob to be written. + */ +@Suppress("UnsafeCastFromDynamic") +suspend fun FileEntry.write(data: Blob): Result<FileEntry, FileException> { + return this.createWriter().flatMap { writer -> + suspendCoroutine<Result<FileEntry, FileException>> { continuation -> + writer.onwriteend = { + continuation.resume(Result.success(this)) + } + writer.onerror = { error: dynamic -> + continuation.resume(Result.error(FileException(error.code))) + } + writer.onabort = { _ -> + continuation.resume(Result.error(FileException(File.ABORT_ERR))) + } + writer.write(data) + } + } +} + +/** + * Write file content from a plain string. + * @param data a data string to be written. + */ +suspend fun FileEntry.write(data: String): Result<FileEntry, FileException> { + return this.write(Blob(arrayOf(data))) +} + +/** + * Write file content from a data url. + * @param dataUrl a data url to be written. + */ +suspend fun FileEntry.writeDataUrL(dataUrl: String): Result<FileEntry, FileException> { + return this.write(File.dataURLtoBlob(dataUrl)) +} + +/** + * Write file content from an array buffer. + * @param data an array buffer to be written. + */ +suspend fun FileEntry.write(data: ArrayBuffer): Result<FileEntry, FileException> { + return this.write(data.toBlob()) +} + +/** + * Append file content from a Blob. + * @param data a data Blob to be appended. + */ +@Suppress("UnsafeCastFromDynamic") +suspend fun FileEntry.append(data: Blob): Result<FileEntry, FileException> { + return this.createWriter().flatMap { writer -> + try { + writer.seek(writer.length) + } catch (e: Exception) { + console.log("File doesn't exist!") + } + suspendCoroutine<Result<FileEntry, FileException>> { continuation -> + writer.onwriteend = { + continuation.resume(Result.success(this)) + } + writer.onerror = { error: dynamic -> + continuation.resume(Result.error(FileException(error.code))) + } + writer.onabort = { _ -> + continuation.resume(Result.error(FileException(File.ABORT_ERR))) + } + writer.write(data) + } + } +} + +/** + * Append file content from a plain string. + * @param data a string to be appended. + */ +suspend fun FileEntry.append(data: String): Result<FileEntry, FileException> { + return this.append(Blob(arrayOf(data))) +} + +/** + * Convert array buffer to a blob. + */ +fun ArrayBuffer.toBlob(): Blob { + return Blob(arrayOf(Uint8Array(this))) +} diff --git a/kvision-modules/kvision-cordova/src/main/kotlin/pl/treksoft/kvision/cordova/FileSystem.kt b/kvision-modules/kvision-cordova/src/main/kotlin/pl/treksoft/kvision/cordova/FileSystem.kt new file mode 100644 index 00000000..48f4f3d2 --- /dev/null +++ b/kvision-modules/kvision-cordova/src/main/kotlin/pl/treksoft/kvision/cordova/FileSystem.kt @@ -0,0 +1,221 @@ +@file:Suppress( + "INTERFACE_WITH_SUPERCLASS", + "OVERRIDING_FINAL_MEMBER", + "RETURN_TYPE_MISMATCH_ON_OVERRIDE", + "CONFLICTING_OVERLOADS", + "EXTERNAL_DELEGATION", + "NESTED_CLASS_IN_EXTERNAL_INTERFACE", + "unused", "PropertyName", "TooManyFunctions", "VariableNaming", "MaxLineLength" +) + +package pl.treksoft.kvision.cordova + +import org.w3c.files.File +import kotlin.js.Date + +external object LocalFileSystem { + var TEMPORARY: Number + var PERSISTENT: Number + fun requestFileSystem( + type: Number, + size: Number, + successCallback: FileSystemCallback, + errorCallback: ErrorCallback? = definedExternally /* null */ + ) + + fun resolveLocalFileSystemURL( + url: String, + successCallback: EntryCallback, + errorCallback: ErrorCallback? = definedExternally /* null */ + ) + + fun webkitRequestFileSystem( + type: Number, + size: Number, + successCallback: FileSystemCallback, + errorCallback: ErrorCallback? = definedExternally /* null */ + ) +} + +external object LocalFileSystemSync { + var TEMPORARY: Number + var PERSISTENT: Number + fun requestFileSystemSync(type: Number, size: Number): FileSystemSync + fun resolveLocalFileSystemSyncURL(url: String): EntrySync + fun webkitRequestFileSystemSync(type: Number, size: Number): FileSystemSync +} + +external interface Metadata { + var modificationTime: Date + var size: Number +} + +external interface Flags { + var create: Boolean? get() = definedExternally; set(value) = definedExternally + var exclusive: Boolean? get() = definedExternally; set(value) = definedExternally +} + +external interface FileSystem { + var name: String + var root: DirectoryEntry +} + +external interface Entry { + var isFile: Boolean + var isDirectory: Boolean + fun getMetadata(successCallback: MetadataCallback, errorCallback: ErrorCallback? = definedExternally /* null */) + var name: String + var fullPath: String + var filesystem: FileSystem + fun moveTo( + parent: DirectoryEntry, + newName: String? = definedExternally /* null */, + successCallback: EntryCallback? = definedExternally /* null */, + errorCallback: ErrorCallback? = definedExternally /* null */ + ) + + fun copyTo( + parent: DirectoryEntry, + newName: String? = definedExternally /* null */, + successCallback: EntryCallback? = definedExternally /* null */, + errorCallback: ErrorCallback? = definedExternally /* null */ + ) + + fun toURL(): String + fun toInternalURL(): String + fun remove(successCallback: VoidCallback, errorCallback: ErrorCallback? = definedExternally /* null */) + fun getParent(successCallback: DirectoryEntryCallback, errorCallback: ErrorCallback? = definedExternally /* null */) +} + +external interface DirectoryEntry : Entry { + fun createReader(): DirectoryReader + fun getFile( + path: String, + options: Flags? = definedExternally /* null */, + successCallback: FileEntryCallback?, + errorCallback: ErrorCallback? = definedExternally /* null */ + ) + + fun getDirectory( + path: String, + options: Flags? = definedExternally /* null */, + successCallback: DirectoryEntryCallback?, + errorCallback: ErrorCallback? = definedExternally /* null */ + ) + + fun removeRecursively(successCallback: VoidCallback, errorCallback: ErrorCallback? = definedExternally /* null */) +} + +external interface DirectoryReader { + fun readEntries(successCallback: EntriesCallback, errorCallback: ErrorCallback? = definedExternally /* null */) +} + +external interface FileEntry : Entry { + fun createWriter(successCallback: FileWriterCallback, errorCallback: ErrorCallback? = definedExternally /* null */) + fun file(successCallback: FileCallback, errorCallback: ErrorCallback? = definedExternally /* null */) +} + +external interface FileSystemCallback + +@Suppress("NOTHING_TO_INLINE") +inline operator fun FileSystemCallback.invoke(filesystem: FileSystem) { + asDynamic()(filesystem) +} + +external interface EntryCallback + +@Suppress("NOTHING_TO_INLINE") +inline operator fun EntryCallback.invoke(entry: Entry) { + asDynamic()(entry) +} + +external interface FileEntryCallback + +@Suppress("NOTHING_TO_INLINE") +inline operator fun FileEntryCallback.invoke(entry: FileEntry) { + asDynamic()(entry) +} + +external interface DirectoryEntryCallback + +@Suppress("NOTHING_TO_INLINE") +inline operator fun DirectoryEntryCallback.invoke(entry: DirectoryEntry) { + asDynamic()(entry) +} + +external interface EntriesCallback + +@Suppress("NOTHING_TO_INLINE") +inline operator fun EntriesCallback.invoke(entries: Array<Entry>) { + asDynamic()(entries) +} + +external interface MetadataCallback + +@Suppress("NOTHING_TO_INLINE") +inline operator fun MetadataCallback.invoke(metadata: Metadata) { + asDynamic()(metadata) +} + +external interface FileWriterCallback + +@Suppress("NOTHING_TO_INLINE") +inline operator fun FileWriterCallback.invoke(fileWriter: FileWriter) { + asDynamic()(fileWriter) +} + +external interface FileCallback + +@Suppress("NOTHING_TO_INLINE") +inline operator fun FileCallback.invoke(file: File) { + asDynamic()(file) +} + +external interface VoidCallback + +@Suppress("NOTHING_TO_INLINE") +inline operator fun VoidCallback.invoke() { + asDynamic()() +} + +external interface ErrorCallback + +@Suppress("NOTHING_TO_INLINE") +inline operator fun ErrorCallback.invoke(err: Error) { + asDynamic()(err) +} + +external interface FileSystemSync { + var name: String + var root: DirectoryEntrySync +} + +external interface EntrySync { + var isFile: Boolean + var isDirectory: Boolean + fun getMetadata(): Metadata + var name: String + var fullPath: String + var filesystem: FileSystemSync + fun moveTo(parent: DirectoryEntrySync, newName: String? = definedExternally /* null */): EntrySync + fun copyTo(parent: DirectoryEntrySync, newName: String? = definedExternally /* null */): EntrySync + fun toURL(): String + fun remove() + fun getParent(): DirectoryEntrySync +} + +external interface DirectoryEntrySync : EntrySync { + fun createReader(): DirectoryReaderSync + fun getFile(path: String, options: Flags? = definedExternally /* null */): FileEntrySync + fun getDirectory(path: String, options: Flags? = definedExternally /* null */): DirectoryEntrySync + fun removeRecursively() +} + +external interface DirectoryReaderSync { + fun readEntries(): Array<EntrySync> +} + +external interface FileEntrySync : EntrySync { + fun createWriter(): FileWriterSync + fun file(): File +} diff --git a/kvision-modules/kvision-cordova/src/main/kotlin/pl/treksoft/kvision/cordova/FileWriter.kt b/kvision-modules/kvision-cordova/src/main/kotlin/pl/treksoft/kvision/cordova/FileWriter.kt new file mode 100644 index 00000000..26215aa8 --- /dev/null +++ b/kvision-modules/kvision-cordova/src/main/kotlin/pl/treksoft/kvision/cordova/FileWriter.kt @@ -0,0 +1,47 @@ +@file:Suppress( + "INTERFACE_WITH_SUPERCLASS", + "OVERRIDING_FINAL_MEMBER", + "RETURN_TYPE_MISMATCH_ON_OVERRIDE", + "CONFLICTING_OVERLOADS", + "EXTERNAL_DELEGATION", + "NESTED_CLASS_IN_EXTERNAL_INTERFACE", + "unused", "PropertyName", "TooManyFunctions", "VariableNaming", "MaxLineLength" +) + +package pl.treksoft.kvision.cordova + +import org.w3c.dom.events.EventTarget +import org.w3c.files.Blob +import org.w3c.xhr.ProgressEvent + +external interface FileSaver : EventTarget { + fun abort() + var INIT: Number + var WRITING: Number + var DONE: Number + var readyState: Number + var error: Error + var onwritestart: (event: ProgressEvent) -> Unit + var onprogress: (event: ProgressEvent) -> Unit + var onwrite: (event: ProgressEvent) -> Unit + var onabort: (event: ProgressEvent) -> Unit + var onerror: (event: ProgressEvent) -> Unit + var onwriteend: (event: ProgressEvent) -> Unit + +} + +external interface FileWriter : FileSaver { + var position: Number + var length: Number + fun write(data: Blob) + fun seek(offset: Number) + fun truncate(size: Number) +} + +external interface FileWriterSync { + var position: Number + var length: Number + fun write(data: Blob) + fun seek(offset: Number) + fun truncate(size: Number) +} diff --git a/kvision-modules/kvision-cordova/src/main/kotlin/pl/treksoft/kvision/cordova/Result.kt b/kvision-modules/kvision-cordova/src/main/kotlin/pl/treksoft/kvision/cordova/Result.kt index bb125e24..83a1987e 100644 --- a/kvision-modules/kvision-cordova/src/main/kotlin/pl/treksoft/kvision/cordova/Result.kt +++ b/kvision-modules/kvision-cordova/src/main/kotlin/pl/treksoft/kvision/cordova/Result.kt @@ -1,3 +1,6 @@ +@file:Suppress( + "TooManyFunctions", "TooGenericExceptionCaught" +) /* * Source: https://github.com/kittinunf/Result * @@ -31,9 +34,9 @@ inline fun <reified X> Result<*, *>.getAs() = when (this) { is Result.Failure -> error as? X } -fun <V : Any> Result<V, *>.success(f: (V) -> Unit) = fold(f, {}) +inline fun <V : Any> Result<V, *>.success(f: (V) -> Unit) = fold(f, {}) -fun <E : Exception> Result<*, E>.failure(f: (E) -> Unit) = fold({}, f) +inline fun <E : Exception> Result<*, E>.failure(f: (E) -> Unit) = fold({}, f) infix fun <V : Any, E : Exception> Result<V, E>.or(fallback: V) = when (this) { is Result.Success -> this |