Implement robust file downloading, uploading and opening

Signed-off-by: denis0001-dev <denis0001.dev@ya.ru>
This commit is contained in:
2026-05-29 20:23:52 +03:00
Unverified
parent 76645e2633
commit 70a9f8635e
104 changed files with 5767 additions and 881 deletions
@@ -7,11 +7,18 @@ import kotlinx.cinterop.addressOf
import kotlinx.cinterop.usePinned
import platform.Foundation.NSData
import platform.Foundation.NSFileManager
import platform.Foundation.NSNumber
import platform.Foundation.NSCachesDirectory
import platform.Foundation.NSSearchPathForDirectoriesInDomains
import platform.Foundation.NSUserDomainMask
import platform.Foundation.create
import platform.Foundation.writeToFile
import platform.posix.fclose
import platform.posix.fopen
import platform.posix.fwrite
import kotlinx.cinterop.addressOf
import kotlinx.cinterop.convert
import kotlinx.cinterop.usePinned
@OptIn(ExperimentalForeignApi::class)
internal actual fun expectExists(path: String): Boolean =
@@ -29,6 +36,32 @@ internal actual fun expectWriteBytes(path: String, bytes: ByteArray) {
nsData?.writeToFile(path, true)
}
@OptIn(ExperimentalForeignApi::class)
internal actual fun expectAppendBytes(path: String, bytes: ByteArray) {
val parent = path.substringBeforeLast('/', missingDelimiterValue = "")
if (parent.isNotEmpty()) {
NSFileManager.defaultManager.createDirectoryAtPath(parent, true, null, null)
}
val file = fopen(path, "ab") ?: error("Failed to open file for append")
try {
bytes.usePinned { pinned ->
val written = fwrite(pinned.addressOf(0), 1.convert(), bytes.size.convert(), file).toInt()
if (written != bytes.size) {
error("Short write")
}
}
} finally {
fclose(file)
}
}
@OptIn(ExperimentalForeignApi::class)
internal actual fun expectFileSize(path: String): Long {
if (!NSFileManager.defaultManager.fileExistsAtPath(path)) return 0L
val attrs = NSFileManager.defaultManager.attributesOfItemAtPath(path, null) ?: return 0L
return (attrs["NSFileSize"] as? NSNumber)?.longValue ?: 0L
}
internal actual fun expectDelete(path: String) {
NSFileManager.defaultManager.removeItemAtPath(path, null)
}
@@ -43,6 +76,12 @@ internal actual fun expectDeleteFilesWithPrefix(dirPath: String, namePrefix: Str
}
}
internal actual fun expectListFileNamesInDirectory(dirPath: String): List<String> {
val contents = NSFileManager.defaultManager.contentsOfDirectoryAtPath(dirPath, null)
?: return emptyList()
return (contents as List<*>).filterIsInstance<String>()
}
internal actual fun expectGetAppCacheDirectory(): String {
val paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, true)
return (paths.firstOrNull() as? String) ?: ""