mirror of
https://github.com/InsanusMokrassar/MicroUtils.git
synced 2024-12-18 14:47:15 +00:00
experimentally add linuxx64 and mingwx64 as target platforms
This commit is contained in:
parent
d23e005985
commit
617dfb54e0
@ -20,5 +20,16 @@ kotlin {
|
|||||||
}
|
}
|
||||||
dependsOn jvmMain
|
dependsOn jvmMain
|
||||||
}
|
}
|
||||||
|
|
||||||
|
linuxX64Main {
|
||||||
|
dependencies {
|
||||||
|
api libs.okio
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mingwX64Main {
|
||||||
|
dependencies {
|
||||||
|
api libs.okio
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,146 +1,36 @@
|
|||||||
package dev.inmo.micro_utils.common
|
package dev.inmo.micro_utils.common
|
||||||
|
|
||||||
import kotlinx.cinterop.ByteVar
|
import okio.FileSystem
|
||||||
import kotlinx.cinterop.CPointer
|
import okio.Path
|
||||||
import kotlinx.cinterop.alloc
|
import okio.use
|
||||||
import kotlinx.cinterop.allocArray
|
|
||||||
import kotlinx.cinterop.convert
|
|
||||||
import kotlinx.cinterop.memScoped
|
|
||||||
import kotlinx.cinterop.ptr
|
|
||||||
import kotlinx.cinterop.readBytes
|
|
||||||
import platform.linux.statvfs
|
|
||||||
import platform.posix.ACCESSPERMS
|
|
||||||
import platform.posix.FILE
|
|
||||||
import platform.posix.F_OK
|
|
||||||
import platform.posix.SEEK_END
|
|
||||||
import platform.posix.S_IFDIR
|
|
||||||
import platform.posix.S_IFMT
|
|
||||||
import platform.posix.access
|
|
||||||
import platform.posix.fgets
|
|
||||||
import platform.posix.fopen
|
|
||||||
import platform.posix.fseek
|
|
||||||
import platform.posix.ftell
|
|
||||||
import platform.posix.remove
|
|
||||||
import platform.posix.rename
|
|
||||||
import platform.posix.rmdir
|
|
||||||
import platform.posix.stat
|
|
||||||
|
|
||||||
actual data class MPPFile(internal val filename: FileName) {
|
actual typealias MPPFile = Path
|
||||||
val path = filename.string
|
|
||||||
val isFile: Boolean
|
|
||||||
get() = memScoped {
|
|
||||||
val stat = alloc<stat>()
|
|
||||||
if (stat(path, stat.ptr) != 0)
|
|
||||||
return@memScoped false
|
|
||||||
(S_IFDIR != (stat.st_mode and S_IFMT.convert()).convert<Int>())
|
|
||||||
}
|
|
||||||
|
|
||||||
val isDirectory: Boolean
|
|
||||||
get() = memScoped {
|
|
||||||
val stat = alloc<stat>()
|
|
||||||
if (stat(path, stat.ptr) != 0)
|
|
||||||
return@memScoped false
|
|
||||||
S_IFDIR == (stat.st_mode and S_IFMT.convert()).convert<Int>()
|
|
||||||
}
|
|
||||||
val size: Long
|
|
||||||
get() = memScoped {
|
|
||||||
val stat = alloc<stat>()
|
|
||||||
if (stat(path, stat.ptr) != 0)
|
|
||||||
return@memScoped 0
|
|
||||||
return stat.st_size.convert()
|
|
||||||
}
|
|
||||||
val lastModified: Long
|
|
||||||
get() = memScoped {
|
|
||||||
val stat = alloc<stat>()
|
|
||||||
if (stat(path, stat.ptr) != 0)
|
|
||||||
return@memScoped 0
|
|
||||||
return stat.st_ctim.tv_nsec / 1000L
|
|
||||||
}
|
|
||||||
|
|
||||||
val freeSpace: Long
|
|
||||||
get() =
|
|
||||||
memScoped {
|
|
||||||
val stat = alloc<statvfs>()
|
|
||||||
statvfs(path, stat.ptr)
|
|
||||||
(stat.f_bfree.toULong() * stat.f_bsize.toULong()).toLong()
|
|
||||||
}
|
|
||||||
|
|
||||||
val availableSpace: Long
|
|
||||||
get() =
|
|
||||||
memScoped {
|
|
||||||
val stat = alloc<statvfs>()
|
|
||||||
statvfs(path, stat.ptr)
|
|
||||||
(stat.f_bavail.toULong() * stat.f_bsize.toULong()).toLong()
|
|
||||||
}
|
|
||||||
|
|
||||||
val totalSpace: Long
|
|
||||||
get() = memScoped {
|
|
||||||
val stat = alloc<statvfs>()
|
|
||||||
statvfs(path, stat.ptr)
|
|
||||||
(stat.f_blocks.toULong() * stat.f_frsize.toULong()).toLong()
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor(vararg path: String) : this(FileName(path.map { it.removeSuffix(SEPARATOR_STRING) }.joinToString(SEPARATOR_STRING)))
|
|
||||||
constructor(parent: MPPFile, subpath: String) : this("${parent.filename.withoutSlashAtTheEnd}$SEPARATOR${subpath}")
|
|
||||||
|
|
||||||
fun createPointer(mode: String = "r") = fopen(filename.name, mode)
|
|
||||||
|
|
||||||
fun delete(): Boolean {
|
|
||||||
return when {
|
|
||||||
isDirectory -> rmdir(path) == 0
|
|
||||||
isFile -> remove(path) == 0
|
|
||||||
else -> false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun mkdir(): Boolean = platform.posix.mkdir(path, ACCESSPERMS) == 0
|
|
||||||
|
|
||||||
override fun toString(): String = path
|
|
||||||
override fun equals(other: Any?): Boolean {
|
|
||||||
if (other !is MPPFile) return false
|
|
||||||
return path == other.path
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun hashCode(): Int = 31 + path.hashCode()
|
|
||||||
|
|
||||||
fun renameTo(newPath: MPPFile): Boolean = rename(path, newPath.path) == 0
|
|
||||||
|
|
||||||
fun list(): List<MPPFile> {
|
|
||||||
val out = ArrayList<MPPFile>()
|
|
||||||
FileIterator(this).forEach { file ->
|
|
||||||
out += file
|
|
||||||
}
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
|
|
||||||
fun fileChannel(mode: String = "r") = FileChannel(this, mode)
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
val SEPARATOR: Char = '/'
|
|
||||||
val SEPARATOR_STRING: String = SEPARATOR.toString()
|
|
||||||
val temporalDirectory: MPPFile?
|
|
||||||
get() = MPPFile("/tmp").takeIf {
|
|
||||||
it.isDirectory
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @suppress
|
||||||
|
*/
|
||||||
actual val MPPFile.filename: FileName
|
actual val MPPFile.filename: FileName
|
||||||
get() = this.filename
|
get() = FileName(toString())
|
||||||
|
/**
|
||||||
|
* @suppress
|
||||||
|
*/
|
||||||
actual val MPPFile.filesize: Long
|
actual val MPPFile.filesize: Long
|
||||||
get() = memScoped {
|
get() = FileSystem.SYSTEM.openReadOnly(this).use {
|
||||||
val pointer = createPointer()
|
it.size()
|
||||||
fseek(pointer, 0L, SEEK_END)
|
|
||||||
ftell(pointer)
|
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* @suppress
|
||||||
|
*/
|
||||||
actual val MPPFile.bytesAllocatorSync: ByteArrayAllocator
|
actual val MPPFile.bytesAllocatorSync: ByteArrayAllocator
|
||||||
get() = {
|
get() = {
|
||||||
memScoped {
|
FileSystem.SYSTEM.read(this) {
|
||||||
fileChannel().readFully()
|
readByteArray()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* @suppress
|
||||||
|
*/
|
||||||
actual val MPPFile.bytesAllocator: SuspendByteArrayAllocator
|
actual val MPPFile.bytesAllocator: SuspendByteArrayAllocator
|
||||||
get() = {
|
get() = {
|
||||||
bytesAllocatorSync()
|
bytesAllocatorSync()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,107 +0,0 @@
|
|||||||
package dev.inmo.micro_utils.common
|
|
||||||
|
|
||||||
import kotlinx.cinterop.ByteVar
|
|
||||||
import kotlinx.cinterop.allocArray
|
|
||||||
import kotlinx.cinterop.convert
|
|
||||||
import kotlinx.cinterop.memScoped
|
|
||||||
import kotlinx.cinterop.toCValues
|
|
||||||
import kotlinx.cinterop.toKString
|
|
||||||
import platform.posix.SEEK_END
|
|
||||||
import platform.posix.SEEK_SET
|
|
||||||
import platform.posix.fclose
|
|
||||||
import platform.posix.feof
|
|
||||||
import platform.posix.fread
|
|
||||||
import platform.posix.fseek
|
|
||||||
import platform.posix.ftell
|
|
||||||
import platform.posix.fwrite
|
|
||||||
import platform.posix.size_t
|
|
||||||
|
|
||||||
class FileChannel (file: MPPFile, mode: String) : RandomAccess {
|
|
||||||
internal val handler = file.createPointer(mode)
|
|
||||||
|
|
||||||
private var position: ULong
|
|
||||||
get() = ftell(handler).convert()
|
|
||||||
set(value) {
|
|
||||||
fseek(handler, value.convert(), SEEK_SET)
|
|
||||||
}
|
|
||||||
private val size: ULong
|
|
||||||
get() {
|
|
||||||
val pos = position
|
|
||||||
gotoEnd()
|
|
||||||
val result = position
|
|
||||||
position = pos
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
fun skip(length: Long): Long {
|
|
||||||
checkClosed()
|
|
||||||
memScoped { }
|
|
||||||
if (length == 0L)
|
|
||||||
return 0L
|
|
||||||
if (feof(handler) != 0)
|
|
||||||
return 0L
|
|
||||||
val endOfFile = size
|
|
||||||
val position = minOf(endOfFile, this.position + length.toULong())
|
|
||||||
this.position = position
|
|
||||||
return (endOfFile - position).toLong()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun read(dest: ByteArray): Int {
|
|
||||||
checkClosed()
|
|
||||||
if (feof(handler) != 0)
|
|
||||||
return 0
|
|
||||||
|
|
||||||
memScoped {
|
|
||||||
val tmp = allocArray<ByteVar>(dest.size);
|
|
||||||
fread(tmp, Byte.SIZE_BYTES.convert<size_t>(), dest.size.convert(), handler).convert<Int>()
|
|
||||||
tmp.toKString()
|
|
||||||
}
|
|
||||||
return fread(dest.toCValues(), Byte.SIZE_BYTES.convert<size_t>(), dest.size.convert(), handler).convert<Int>()
|
|
||||||
}
|
|
||||||
|
|
||||||
private var closed = false
|
|
||||||
|
|
||||||
private fun checkClosed() {
|
|
||||||
if (closed) {
|
|
||||||
error("File channel has been closed already")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun close() {
|
|
||||||
checkClosed()
|
|
||||||
fclose(handler)
|
|
||||||
closed = true
|
|
||||||
}
|
|
||||||
|
|
||||||
fun write(data: ByteArray): Int {
|
|
||||||
checkClosed()
|
|
||||||
if (feof(handler) != 0)
|
|
||||||
return 0
|
|
||||||
|
|
||||||
return fwrite(data.toCValues(), 1.convert<size_t>(), data.size.convert<size_t>(), handler).convert<Int>()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun flush() {
|
|
||||||
checkClosed()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun gotoEnd() {
|
|
||||||
fseek(handler, 0, SEEK_END)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun readFully(): ByteArray {
|
|
||||||
var result = ByteArray(0)
|
|
||||||
memScoped {
|
|
||||||
val tmp = ByteArray(64 * 1024)
|
|
||||||
|
|
||||||
do {
|
|
||||||
val read = read(tmp)
|
|
||||||
result += tmp.take(read)
|
|
||||||
} while (read > 0)
|
|
||||||
}
|
|
||||||
val tmp = ByteArray(64 * 1024)
|
|
||||||
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,56 +0,0 @@
|
|||||||
package dev.inmo.micro_utils.common
|
|
||||||
|
|
||||||
import kotlinx.cinterop.pointed
|
|
||||||
import kotlinx.cinterop.toKString
|
|
||||||
import platform.posix.closedir
|
|
||||||
import platform.posix.dirent
|
|
||||||
import platform.posix.opendir
|
|
||||||
import platform.posix.readdir
|
|
||||||
import kotlin.native.internal.createCleaner
|
|
||||||
|
|
||||||
class FileIterator internal constructor(private val file: MPPFile) : Iterator<MPPFile> {
|
|
||||||
init {
|
|
||||||
if (!file.isDirectory)
|
|
||||||
error("\"${file.path}\" is not direction")
|
|
||||||
}
|
|
||||||
|
|
||||||
private val handler = opendir(file.path)
|
|
||||||
private var next: dirent? = null
|
|
||||||
private var end = false
|
|
||||||
|
|
||||||
override fun hasNext(): Boolean {
|
|
||||||
while (true) {
|
|
||||||
if (end)
|
|
||||||
return false
|
|
||||||
|
|
||||||
if (next == null) {
|
|
||||||
next = readdir(handler)?.pointed
|
|
||||||
if (next == null) {
|
|
||||||
end = true
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
val name = next!!.d_name.toKString()
|
|
||||||
if (name == "." || name == "..") {
|
|
||||||
next = null
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun next(): MPPFile {
|
|
||||||
if (!hasNext())
|
|
||||||
throw NoSuchElementException()
|
|
||||||
val result = MPPFile(file, next!!.d_name.toKString())
|
|
||||||
next = null
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
@OptIn(ExperimentalStdlibApi::class)
|
|
||||||
private val cleaner = createCleaner(handler) {
|
|
||||||
closedir(it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,36 +0,0 @@
|
|||||||
package dev.inmo.micro_utils.common
|
|
||||||
|
|
||||||
import dev.inmo.micro_utils.common.MPPFile
|
|
||||||
import kotlin.test.Test
|
|
||||||
import kotlin.test.assertEquals
|
|
||||||
|
|
||||||
fun createTestFile(content: String): MPPFile = MPPFile(
|
|
||||||
MPPFile.temporalDirectory!!,
|
|
||||||
"tmp.file.txt"
|
|
||||||
).apply {
|
|
||||||
val channel = fileChannel("wc")
|
|
||||||
runCatching {
|
|
||||||
channel.write(content.encodeToByteArray())
|
|
||||||
}
|
|
||||||
channel.close()
|
|
||||||
}
|
|
||||||
fun MPPFile.removeTestFile() {
|
|
||||||
delete()
|
|
||||||
}
|
|
||||||
|
|
||||||
class FileTests {
|
|
||||||
@Test
|
|
||||||
fun testReadFromFile() {
|
|
||||||
val testContent = "Test"
|
|
||||||
val file = createTestFile(testContent)
|
|
||||||
|
|
||||||
try {
|
|
||||||
val content = runCatching {
|
|
||||||
file.fileChannel().readFully().contentToString()
|
|
||||||
}.getOrThrow()
|
|
||||||
assertEquals(testContent, content)
|
|
||||||
} finally {
|
|
||||||
file.removeTestFile()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
36
common/src/mingwX64Main/kotlin/ActualMPPFile.kt
Normal file
36
common/src/mingwX64Main/kotlin/ActualMPPFile.kt
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
package dev.inmo.micro_utils.common
|
||||||
|
|
||||||
|
import okio.FileSystem
|
||||||
|
import okio.Path
|
||||||
|
import okio.use
|
||||||
|
|
||||||
|
actual typealias MPPFile = Path
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @suppress
|
||||||
|
*/
|
||||||
|
actual val MPPFile.filename: FileName
|
||||||
|
get() = FileName(toString())
|
||||||
|
/**
|
||||||
|
* @suppress
|
||||||
|
*/
|
||||||
|
actual val MPPFile.filesize: Long
|
||||||
|
get() = FileSystem.SYSTEM.openReadOnly(this).use {
|
||||||
|
it.size()
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @suppress
|
||||||
|
*/
|
||||||
|
actual val MPPFile.bytesAllocatorSync: ByteArrayAllocator
|
||||||
|
get() = {
|
||||||
|
FileSystem.SYSTEM.read(this) {
|
||||||
|
readByteArray()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @suppress
|
||||||
|
*/
|
||||||
|
actual val MPPFile.bytesAllocator: SuspendByteArrayAllocator
|
||||||
|
get() = {
|
||||||
|
bytesAllocatorSync()
|
||||||
|
}
|
26
common/src/mingwX64Main/kotlin/fixed.kt
Normal file
26
common/src/mingwX64Main/kotlin/fixed.kt
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
package dev.inmo.micro_utils.common
|
||||||
|
|
||||||
|
import kotlinx.cinterop.ByteVar
|
||||||
|
import kotlinx.cinterop.allocArray
|
||||||
|
import kotlinx.cinterop.memScoped
|
||||||
|
import kotlinx.cinterop.toKString
|
||||||
|
import platform.posix.snprintf
|
||||||
|
import platform.posix.sprintf
|
||||||
|
|
||||||
|
actual fun Float.fixed(signs: Int): Float {
|
||||||
|
return memScoped {
|
||||||
|
val buff = allocArray<ByteVar>(Float.SIZE_BYTES * 2)
|
||||||
|
|
||||||
|
sprintf(buff, "%.${signs}f", this@fixed)
|
||||||
|
buff.toKString().toFloat()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
actual fun Double.fixed(signs: Int): Double {
|
||||||
|
return memScoped {
|
||||||
|
val buff = allocArray<ByteVar>(Double.SIZE_BYTES * 2)
|
||||||
|
|
||||||
|
sprintf(buff, "%.${signs}f", this@fixed)
|
||||||
|
buff.toKString().toDouble()
|
||||||
|
}
|
||||||
|
}
|
@ -11,6 +11,7 @@ kotlin {
|
|||||||
commonMain {
|
commonMain {
|
||||||
dependencies {
|
dependencies {
|
||||||
api project(":micro_utils.common")
|
api project(":micro_utils.common")
|
||||||
|
api libs.krypto
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
jsMain {
|
jsMain {
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
package dev.inmo.micro_utils.crypto
|
package dev.inmo.micro_utils.crypto
|
||||||
|
|
||||||
|
import com.soywiz.krypto.md5
|
||||||
|
|
||||||
typealias MD5 = String
|
typealias MD5 = String
|
||||||
|
|
||||||
expect fun SourceBytes.md5(): MD5
|
fun SourceBytes.md5(): MD5 = md5().hexLower
|
||||||
fun SourceString.md5(): MD5 = encodeToByteArray().md5()
|
fun SourceString.md5(): MD5 = encodeToByteArray().md5().hexLower
|
||||||
|
@ -1,6 +0,0 @@
|
|||||||
package dev.inmo.micro_utils.crypto
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @suppress
|
|
||||||
*/
|
|
||||||
actual fun SourceBytes.md5(): MD5 = CryptoJS.MD5(decodeToString())
|
|
@ -1,12 +0,0 @@
|
|||||||
package dev.inmo.micro_utils.crypto
|
|
||||||
|
|
||||||
import java.math.BigInteger
|
|
||||||
import java.security.MessageDigest
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @suppress
|
|
||||||
*/
|
|
||||||
actual fun SourceBytes.md5(): MD5 = BigInteger(
|
|
||||||
1,
|
|
||||||
MessageDigest.getInstance("MD5").digest(this)
|
|
||||||
).toString(16)
|
|
@ -10,7 +10,7 @@ jb-compose = "1.3.1"
|
|||||||
jb-exposed = "0.41.1"
|
jb-exposed = "0.41.1"
|
||||||
jb-dokka = "1.8.10"
|
jb-dokka = "1.8.10"
|
||||||
|
|
||||||
klock = "3.4.0"
|
korlibs = "3.4.0"
|
||||||
uuid = "0.7.0"
|
uuid = "0.7.0"
|
||||||
|
|
||||||
ktor = "2.2.4"
|
ktor = "2.2.4"
|
||||||
@ -19,6 +19,8 @@ gh-release = "2.4.1"
|
|||||||
|
|
||||||
koin = "3.3.2"
|
koin = "3.3.2"
|
||||||
|
|
||||||
|
okio = "3.3.0"
|
||||||
|
|
||||||
ksp = "1.8.10-1.0.9"
|
ksp = "1.8.10-1.0.9"
|
||||||
kotlin-poet = "1.12.0"
|
kotlin-poet = "1.12.0"
|
||||||
|
|
||||||
@ -67,7 +69,8 @@ ktor-server-content-negotiation = { module = "io.ktor:ktor-server-content-negoti
|
|||||||
|
|
||||||
kslog = { module = "dev.inmo:kslog", version.ref = "kslog" }
|
kslog = { module = "dev.inmo:kslog", version.ref = "kslog" }
|
||||||
|
|
||||||
klock = { module = "com.soywiz.korlibs.klock:klock", version.ref = "klock" }
|
klock = { module = "com.soywiz.korlibs.klock:klock", version.ref = "korlibs" }
|
||||||
|
krypto = { module = "com.soywiz.korlibs.krypto:krypto", version.ref = "korlibs" }
|
||||||
uuid = { module = "com.benasher44:uuid", version.ref = "uuid" }
|
uuid = { module = "com.benasher44:uuid", version.ref = "uuid" }
|
||||||
koin = { module = "io.insert-koin:koin-core", version.ref = "koin" }
|
koin = { module = "io.insert-koin:koin-core", version.ref = "koin" }
|
||||||
|
|
||||||
@ -91,6 +94,8 @@ kt-test-junit = { module = "org.jetbrains.kotlin:kotlin-test-junit", version.ref
|
|||||||
kotlin-poet = { module = "com.squareup:kotlinpoet-ksp", version.ref = "kotlin-poet" }
|
kotlin-poet = { module = "com.squareup:kotlinpoet-ksp", version.ref = "kotlin-poet" }
|
||||||
ksp = { module = "com.google.devtools.ksp:symbol-processing-api", version.ref = "ksp" }
|
ksp = { module = "com.google.devtools.ksp:symbol-processing-api", version.ref = "ksp" }
|
||||||
|
|
||||||
|
okio = { module = "com.squareup.okio:okio", version.ref = "okio" }
|
||||||
|
|
||||||
# Buildscript
|
# Buildscript
|
||||||
|
|
||||||
buildscript-kt-gradle = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kt" }
|
buildscript-kt-gradle = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kt" }
|
||||||
|
@ -18,5 +18,17 @@ kotlin {
|
|||||||
androidMain {
|
androidMain {
|
||||||
dependsOn jvmMain
|
dependsOn jvmMain
|
||||||
}
|
}
|
||||||
|
|
||||||
|
linuxX64Main {
|
||||||
|
dependencies {
|
||||||
|
api internalProject("micro_utils.mime_types")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mingwX64Main {
|
||||||
|
dependencies {
|
||||||
|
api internalProject("micro_utils.mime_types")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,10 @@
|
|||||||
package dev.inmo.micro_utils.ktor.client
|
package dev.inmo.micro_utils.ktor.client
|
||||||
|
|
||||||
import dev.inmo.micro_utils.common.MPPFile
|
import dev.inmo.micro_utils.common.MPPFile
|
||||||
|
import dev.inmo.micro_utils.common.filesize
|
||||||
|
import dev.inmo.micro_utils.ktor.common.input
|
||||||
import io.ktor.client.request.forms.InputProvider
|
import io.ktor.client.request.forms.InputProvider
|
||||||
|
|
||||||
expect suspend fun MPPFile.inputProvider(): InputProvider
|
fun MPPFile.inputProvider(): InputProvider = InputProvider(filesize) {
|
||||||
|
input()
|
||||||
|
}
|
||||||
|
@ -1,11 +0,0 @@
|
|||||||
package dev.inmo.micro_utils.ktor.client
|
|
||||||
|
|
||||||
import dev.inmo.micro_utils.common.*
|
|
||||||
import io.ktor.client.request.forms.InputProvider
|
|
||||||
import io.ktor.utils.io.core.ByteReadPacket
|
|
||||||
|
|
||||||
actual suspend fun MPPFile.inputProvider(): InputProvider = bytes().let {
|
|
||||||
InputProvider(it.size.toLong()) {
|
|
||||||
ByteReadPacket(it)
|
|
||||||
}
|
|
||||||
}
|
|
@ -7,5 +7,3 @@ import io.ktor.utils.io.streams.asInput
|
|||||||
fun MPPFile.inputProviderSync(): InputProvider = InputProvider(length()) {
|
fun MPPFile.inputProviderSync(): InputProvider = InputProvider(length()) {
|
||||||
inputStream().asInput()
|
inputStream().asInput()
|
||||||
}
|
}
|
||||||
|
|
||||||
actual suspend fun MPPFile.inputProvider(): InputProvider = inputProviderSync()
|
|
||||||
|
40
ktor/client/src/linuxX64Main/kotlin/ActualTemporalUpload.kt
Normal file
40
ktor/client/src/linuxX64Main/kotlin/ActualTemporalUpload.kt
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
package dev.inmo.micro_utils.ktor.client
|
||||||
|
|
||||||
|
import dev.inmo.micro_utils.common.MPPFile
|
||||||
|
import dev.inmo.micro_utils.common.filename
|
||||||
|
import dev.inmo.micro_utils.ktor.common.TemporalFileId
|
||||||
|
import dev.inmo.micro_utils.mime_types.getMimeTypeOrAny
|
||||||
|
import io.ktor.client.HttpClient
|
||||||
|
import io.ktor.client.plugins.onUpload
|
||||||
|
import io.ktor.client.request.forms.formData
|
||||||
|
import io.ktor.client.request.forms.submitFormWithBinaryData
|
||||||
|
import io.ktor.client.statement.bodyAsText
|
||||||
|
import io.ktor.http.Headers
|
||||||
|
import io.ktor.http.HttpHeaders
|
||||||
|
|
||||||
|
internal val MPPFile.mimeType: String
|
||||||
|
get() = getMimeTypeOrAny(filename.extension).raw
|
||||||
|
|
||||||
|
actual suspend fun HttpClient.tempUpload(
|
||||||
|
fullTempUploadDraftPath: String,
|
||||||
|
file: MPPFile,
|
||||||
|
onUpload: OnUploadCallback
|
||||||
|
): TemporalFileId {
|
||||||
|
val inputProvider = file.inputProvider()
|
||||||
|
val fileId = submitFormWithBinaryData(
|
||||||
|
fullTempUploadDraftPath,
|
||||||
|
formData = formData {
|
||||||
|
append(
|
||||||
|
"data",
|
||||||
|
inputProvider,
|
||||||
|
Headers.build {
|
||||||
|
append(HttpHeaders.ContentType, file.mimeType)
|
||||||
|
append(HttpHeaders.ContentDisposition, "filename=\"${file.filename.string}\"")
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
onUpload(onUpload)
|
||||||
|
}.bodyAsText()
|
||||||
|
return TemporalFileId(fileId)
|
||||||
|
}
|
107
ktor/client/src/linuxX64Main/kotlin/ActualUniUpload.kt
Normal file
107
ktor/client/src/linuxX64Main/kotlin/ActualUniUpload.kt
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
package dev.inmo.micro_utils.ktor.client
|
||||||
|
|
||||||
|
import dev.inmo.micro_utils.common.MPPFile
|
||||||
|
import dev.inmo.micro_utils.common.Progress
|
||||||
|
import io.ktor.client.HttpClient
|
||||||
|
import io.ktor.client.engine.mergeHeaders
|
||||||
|
import io.ktor.client.plugins.onUpload
|
||||||
|
import io.ktor.client.request.HttpRequestBuilder
|
||||||
|
import io.ktor.client.request.forms.InputProvider
|
||||||
|
import io.ktor.client.request.forms.formData
|
||||||
|
import io.ktor.client.request.forms.submitForm
|
||||||
|
import io.ktor.client.request.forms.submitFormWithBinaryData
|
||||||
|
import io.ktor.client.request.headers
|
||||||
|
import io.ktor.client.statement.bodyAsText
|
||||||
|
import io.ktor.http.Headers
|
||||||
|
import io.ktor.http.HttpHeaders
|
||||||
|
import io.ktor.http.HttpStatusCode
|
||||||
|
import io.ktor.http.Parameters
|
||||||
|
import io.ktor.http.content.PartData
|
||||||
|
import kotlinx.serialization.DeserializationStrategy
|
||||||
|
import kotlinx.serialization.InternalSerializationApi
|
||||||
|
import kotlinx.serialization.SerializationStrategy
|
||||||
|
import kotlinx.serialization.StringFormat
|
||||||
|
import kotlinx.serialization.encodeToString
|
||||||
|
import kotlinx.serialization.serializer
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Will execute submitting of multipart data request
|
||||||
|
*
|
||||||
|
* @param data [Map] where keys will be used as names for multipart parts and values as values. If you will pass
|
||||||
|
* [dev.inmo.micro_utils.common.MPPFile] (File from JS or JVM platform). Also you may pass [UniUploadFileInfo] as value
|
||||||
|
* in case you wish to pass other source of multipart binary data than regular file
|
||||||
|
* @suppress
|
||||||
|
*/
|
||||||
|
@OptIn(InternalSerializationApi::class)
|
||||||
|
actual suspend fun <T> HttpClient.uniUpload(
|
||||||
|
url: String,
|
||||||
|
data: Map<String, Any>,
|
||||||
|
resultDeserializer: DeserializationStrategy<T>,
|
||||||
|
headers: Headers,
|
||||||
|
stringFormat: StringFormat,
|
||||||
|
onUpload: OnUploadCallback
|
||||||
|
): T? {
|
||||||
|
val withBinary = data.values.any { it is MPPFile || it is UniUploadFileInfo }
|
||||||
|
|
||||||
|
val formData = formData {
|
||||||
|
for (k in data.keys) {
|
||||||
|
val v = data[k] ?: continue
|
||||||
|
when (v) {
|
||||||
|
is MPPFile -> append(
|
||||||
|
k,
|
||||||
|
v.inputProvider(),
|
||||||
|
Headers.build {
|
||||||
|
append(HttpHeaders.ContentType, v.mimeType)
|
||||||
|
append(HttpHeaders.ContentDisposition, "filename=\"${v.name}\"")
|
||||||
|
}
|
||||||
|
)
|
||||||
|
is UniUploadFileInfo -> append(
|
||||||
|
k,
|
||||||
|
InputProvider(block = v.inputAllocator),
|
||||||
|
Headers.build {
|
||||||
|
append(HttpHeaders.ContentType, v.mimeType)
|
||||||
|
append(HttpHeaders.ContentDisposition, "filename=\"${v.fileName.name}\"")
|
||||||
|
}
|
||||||
|
)
|
||||||
|
else -> append(
|
||||||
|
k,
|
||||||
|
stringFormat.encodeToString(v::class.serializer() as SerializationStrategy<in Any>, v)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val requestBuilder: HttpRequestBuilder.() -> Unit = {
|
||||||
|
headers {
|
||||||
|
appendAll(headers)
|
||||||
|
}
|
||||||
|
onUpload { bytesSentTotal, contentLength ->
|
||||||
|
onUpload(bytesSentTotal, contentLength)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val response = if (withBinary) {
|
||||||
|
submitFormWithBinaryData(
|
||||||
|
url,
|
||||||
|
formData,
|
||||||
|
block = requestBuilder
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
submitForm(
|
||||||
|
url,
|
||||||
|
Parameters.build {
|
||||||
|
for (it in formData) {
|
||||||
|
val formItem = (it as PartData.FormItem)
|
||||||
|
append(it.name!!, it.value)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
block = requestBuilder
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return if (response.status == HttpStatusCode.OK) {
|
||||||
|
stringFormat.decodeFromString(resultDeserializer, response.bodyAsText())
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
40
ktor/client/src/mingwX64Main/kotlin/ActualTemporalUpload.kt
Normal file
40
ktor/client/src/mingwX64Main/kotlin/ActualTemporalUpload.kt
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
package dev.inmo.micro_utils.ktor.client
|
||||||
|
|
||||||
|
import dev.inmo.micro_utils.common.MPPFile
|
||||||
|
import dev.inmo.micro_utils.common.filename
|
||||||
|
import dev.inmo.micro_utils.ktor.common.TemporalFileId
|
||||||
|
import dev.inmo.micro_utils.mime_types.getMimeTypeOrAny
|
||||||
|
import io.ktor.client.HttpClient
|
||||||
|
import io.ktor.client.plugins.onUpload
|
||||||
|
import io.ktor.client.request.forms.formData
|
||||||
|
import io.ktor.client.request.forms.submitFormWithBinaryData
|
||||||
|
import io.ktor.client.statement.bodyAsText
|
||||||
|
import io.ktor.http.Headers
|
||||||
|
import io.ktor.http.HttpHeaders
|
||||||
|
|
||||||
|
internal val MPPFile.mimeType: String
|
||||||
|
get() = getMimeTypeOrAny(filename.extension).raw
|
||||||
|
|
||||||
|
actual suspend fun HttpClient.tempUpload(
|
||||||
|
fullTempUploadDraftPath: String,
|
||||||
|
file: MPPFile,
|
||||||
|
onUpload: OnUploadCallback
|
||||||
|
): TemporalFileId {
|
||||||
|
val inputProvider = file.inputProvider()
|
||||||
|
val fileId = submitFormWithBinaryData(
|
||||||
|
fullTempUploadDraftPath,
|
||||||
|
formData = formData {
|
||||||
|
append(
|
||||||
|
"data",
|
||||||
|
inputProvider,
|
||||||
|
Headers.build {
|
||||||
|
append(HttpHeaders.ContentType, file.mimeType)
|
||||||
|
append(HttpHeaders.ContentDisposition, "filename=\"${file.filename.string}\"")
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
onUpload(onUpload)
|
||||||
|
}.bodyAsText()
|
||||||
|
return TemporalFileId(fileId)
|
||||||
|
}
|
107
ktor/client/src/mingwX64Main/kotlin/ActualUniUpload.kt
Normal file
107
ktor/client/src/mingwX64Main/kotlin/ActualUniUpload.kt
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
package dev.inmo.micro_utils.ktor.client
|
||||||
|
|
||||||
|
import dev.inmo.micro_utils.common.MPPFile
|
||||||
|
import dev.inmo.micro_utils.common.Progress
|
||||||
|
import io.ktor.client.HttpClient
|
||||||
|
import io.ktor.client.engine.mergeHeaders
|
||||||
|
import io.ktor.client.plugins.onUpload
|
||||||
|
import io.ktor.client.request.HttpRequestBuilder
|
||||||
|
import io.ktor.client.request.forms.InputProvider
|
||||||
|
import io.ktor.client.request.forms.formData
|
||||||
|
import io.ktor.client.request.forms.submitForm
|
||||||
|
import io.ktor.client.request.forms.submitFormWithBinaryData
|
||||||
|
import io.ktor.client.request.headers
|
||||||
|
import io.ktor.client.statement.bodyAsText
|
||||||
|
import io.ktor.http.Headers
|
||||||
|
import io.ktor.http.HttpHeaders
|
||||||
|
import io.ktor.http.HttpStatusCode
|
||||||
|
import io.ktor.http.Parameters
|
||||||
|
import io.ktor.http.content.PartData
|
||||||
|
import kotlinx.serialization.DeserializationStrategy
|
||||||
|
import kotlinx.serialization.InternalSerializationApi
|
||||||
|
import kotlinx.serialization.SerializationStrategy
|
||||||
|
import kotlinx.serialization.StringFormat
|
||||||
|
import kotlinx.serialization.encodeToString
|
||||||
|
import kotlinx.serialization.serializer
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Will execute submitting of multipart data request
|
||||||
|
*
|
||||||
|
* @param data [Map] where keys will be used as names for multipart parts and values as values. If you will pass
|
||||||
|
* [dev.inmo.micro_utils.common.MPPFile] (File from JS or JVM platform). Also you may pass [UniUploadFileInfo] as value
|
||||||
|
* in case you wish to pass other source of multipart binary data than regular file
|
||||||
|
* @suppress
|
||||||
|
*/
|
||||||
|
@OptIn(InternalSerializationApi::class)
|
||||||
|
actual suspend fun <T> HttpClient.uniUpload(
|
||||||
|
url: String,
|
||||||
|
data: Map<String, Any>,
|
||||||
|
resultDeserializer: DeserializationStrategy<T>,
|
||||||
|
headers: Headers,
|
||||||
|
stringFormat: StringFormat,
|
||||||
|
onUpload: OnUploadCallback
|
||||||
|
): T? {
|
||||||
|
val withBinary = data.values.any { it is MPPFile || it is UniUploadFileInfo }
|
||||||
|
|
||||||
|
val formData = formData {
|
||||||
|
for (k in data.keys) {
|
||||||
|
val v = data[k] ?: continue
|
||||||
|
when (v) {
|
||||||
|
is MPPFile -> append(
|
||||||
|
k,
|
||||||
|
v.inputProvider(),
|
||||||
|
Headers.build {
|
||||||
|
append(HttpHeaders.ContentType, v.mimeType)
|
||||||
|
append(HttpHeaders.ContentDisposition, "filename=\"${v.name}\"")
|
||||||
|
}
|
||||||
|
)
|
||||||
|
is UniUploadFileInfo -> append(
|
||||||
|
k,
|
||||||
|
InputProvider(block = v.inputAllocator),
|
||||||
|
Headers.build {
|
||||||
|
append(HttpHeaders.ContentType, v.mimeType)
|
||||||
|
append(HttpHeaders.ContentDisposition, "filename=\"${v.fileName.name}\"")
|
||||||
|
}
|
||||||
|
)
|
||||||
|
else -> append(
|
||||||
|
k,
|
||||||
|
stringFormat.encodeToString(v::class.serializer() as SerializationStrategy<in Any>, v)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val requestBuilder: HttpRequestBuilder.() -> Unit = {
|
||||||
|
headers {
|
||||||
|
appendAll(headers)
|
||||||
|
}
|
||||||
|
onUpload { bytesSentTotal, contentLength ->
|
||||||
|
onUpload(bytesSentTotal, contentLength)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val response = if (withBinary) {
|
||||||
|
submitFormWithBinaryData(
|
||||||
|
url,
|
||||||
|
formData,
|
||||||
|
block = requestBuilder
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
submitForm(
|
||||||
|
url,
|
||||||
|
Parameters.build {
|
||||||
|
for (it in formData) {
|
||||||
|
val formItem = (it as PartData.FormItem)
|
||||||
|
append(it.name!!, it.value)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
block = requestBuilder
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return if (response.status == HttpStatusCode.OK) {
|
||||||
|
stringFormat.decodeFromString(resultDeserializer, response.bodyAsText())
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
33
ktor/common/src/linuxX64Main/kotlin/ActualMPPFileInput.kt
Normal file
33
ktor/common/src/linuxX64Main/kotlin/ActualMPPFileInput.kt
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
package dev.inmo.micro_utils.ktor.common
|
||||||
|
|
||||||
|
import dev.inmo.micro_utils.common.MPPFile
|
||||||
|
import io.ktor.utils.io.bits.Memory
|
||||||
|
import io.ktor.utils.io.bits.loadByteArray
|
||||||
|
import io.ktor.utils.io.core.Input
|
||||||
|
import okio.FileSystem
|
||||||
|
import okio.Path
|
||||||
|
|
||||||
|
private class FileInput(
|
||||||
|
private val path: Path
|
||||||
|
) : Input() {
|
||||||
|
private val openedFile = FileSystem.SYSTEM.openReadOnly(path)
|
||||||
|
override fun closeSource() {
|
||||||
|
openedFile.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun fill(destination: Memory, offset: Int, length: Int): Int {
|
||||||
|
val byteArray = ByteArray(length)
|
||||||
|
val read = openedFile.read(offset.toLong(), byteArray, 0, length)
|
||||||
|
destination.loadByteArray(
|
||||||
|
offset,
|
||||||
|
byteArray,
|
||||||
|
count = length
|
||||||
|
)
|
||||||
|
return read
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
actual fun MPPFile.input(): Input {
|
||||||
|
return FileInput(this)
|
||||||
|
}
|
||||||
|
|
33
ktor/common/src/mingwX64Main/kotlin/ActualMPPFileInput.kt
Normal file
33
ktor/common/src/mingwX64Main/kotlin/ActualMPPFileInput.kt
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
package dev.inmo.micro_utils.ktor.common
|
||||||
|
|
||||||
|
import dev.inmo.micro_utils.common.MPPFile
|
||||||
|
import io.ktor.utils.io.bits.Memory
|
||||||
|
import io.ktor.utils.io.bits.loadByteArray
|
||||||
|
import io.ktor.utils.io.core.Input
|
||||||
|
import okio.FileSystem
|
||||||
|
import okio.Path
|
||||||
|
|
||||||
|
private class FileInput(
|
||||||
|
private val path: Path
|
||||||
|
) : Input() {
|
||||||
|
private val openedFile = FileSystem.SYSTEM.openReadOnly(path)
|
||||||
|
override fun closeSource() {
|
||||||
|
openedFile.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun fill(destination: Memory, offset: Int, length: Int): Int {
|
||||||
|
val byteArray = ByteArray(length)
|
||||||
|
val read = openedFile.read(offset.toLong(), byteArray, 0, length)
|
||||||
|
destination.loadByteArray(
|
||||||
|
offset,
|
||||||
|
byteArray,
|
||||||
|
count = length
|
||||||
|
)
|
||||||
|
return read
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
actual fun MPPFile.input(): Input {
|
||||||
|
return FileInput(this)
|
||||||
|
}
|
||||||
|
|
@ -1,3 +1,5 @@
|
|||||||
|
import math
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
from bs4 import BeautifulSoup
|
from bs4 import BeautifulSoup
|
||||||
import pandas as pd
|
import pandas as pd
|
||||||
@ -17,33 +19,45 @@ def fix_name(category, raw_name):
|
|||||||
result += out1
|
result += out1
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
def remove_prefix(text, prefix):
|
||||||
|
if text.startswith(prefix):
|
||||||
|
return text[len(prefix):]
|
||||||
|
return text # or whatever
|
||||||
|
|
||||||
|
def extensionPreparationFun(extension):
|
||||||
|
return "\"%s\"" % (remove_prefix(extension, "."))
|
||||||
|
|
||||||
# https://www.freeformatter.com/mime-types-list.html
|
# https://www.freeformatter.com/mime-types-list.html
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
df = pd.read_html(open('table.html', 'r'))
|
df = pd.read_html(open('local.table.html', 'r'))
|
||||||
mimes = []
|
mimes = []
|
||||||
for row in df[0].drop_duplicates(subset=['MIME Type / Internet Media Type'], keep='first').iterrows():
|
for row in df[0].drop_duplicates(subset=['MIME Type / Internet Media Type'], keep='first').iterrows():
|
||||||
mime = row[1][1]
|
mime = row[1][1]
|
||||||
|
extensions = list()
|
||||||
|
if isinstance(row[1][2], str):
|
||||||
|
extensions = list(map(extensionPreparationFun, row[1][2].split(", ")))
|
||||||
mime_category = mime.split('/', 1)[0]
|
mime_category = mime.split('/', 1)[0]
|
||||||
mime_name = mime.split('/', 1)[1]
|
mime_name = mime.split('/', 1)[1]
|
||||||
mimes.append({
|
mimes.append([
|
||||||
'mime_category': mime_category,
|
mime_category,
|
||||||
'mime_name': mime_name,
|
mime_name,
|
||||||
})
|
extensions
|
||||||
|
])
|
||||||
|
|
||||||
# codegen
|
# codegen
|
||||||
|
|
||||||
mimes.sort(key=lambda x: x['mime_category'])
|
mimes.sort(key=lambda x: x[0])
|
||||||
grouped = itertools.groupby(mimes, lambda x: x['mime_category'])
|
grouped = itertools.groupby(mimes, lambda x: x[0])
|
||||||
code = ''
|
code = ''
|
||||||
code2 = 'internal val knownMimeTypes: Set<MimeType> = setOf(\n'
|
code2 = 'internal val knownMimeTypes: Set<MimeType> = setOf(\n'
|
||||||
code2 += ' KnownMimeTypes.Any,\n'
|
code2 += ' KnownMimeTypes.Any,\n'
|
||||||
for key, group in grouped:
|
for key, group in grouped:
|
||||||
group_name = fix_name(group, key)
|
group_name = fix_name(group, key)
|
||||||
code += '@Serializable(MimeTypeSerializer::class)\nsealed class %s(raw: String) : MimeType, KnownMimeTypes(raw) {\n' % group_name
|
code += '@Serializable(MimeTypeSerializer::class)\nsealed class %s(raw: String, extensions: Array<String> = emptyArray()) : MimeType, KnownMimeTypes(raw, extensions) {\n' % group_name
|
||||||
code += ' @Serializable(MimeTypeSerializer::class)\n object Any: %s ("%s/*")\n' % (group_name, key)
|
code += ' @Serializable(MimeTypeSerializer::class)\n object Any: %s ("%s/*")\n' % (group_name, key)
|
||||||
for mime in group:
|
for mime in group:
|
||||||
name = fix_name(mime['mime_category'], mime['mime_name'])
|
name = fix_name(mime[0], mime[1])
|
||||||
code += ' @Serializable(MimeTypeSerializer::class)\n object %s: %s ("%s/%s")\n' % (name, group_name, mime['mime_category'], mime['mime_name'])
|
code += ' @Serializable(MimeTypeSerializer::class)\n object %s: %s ("%s/%s", arrayOf(%s))\n' % (name, group_name, mime[0], mime[1], ", ".join(mime[2]))
|
||||||
code2 += ' KnownMimeTypes.%s.%s,\n' % (group_name, name)
|
code2 += ' KnownMimeTypes.%s.%s,\n' % (group_name, name)
|
||||||
code += '}\n\n'
|
code += '}\n\n'
|
||||||
code2 += ')\n'
|
code2 += ')\n'
|
||||||
|
@ -0,0 +1,24 @@
|
|||||||
|
package dev.inmo.micro_utils.mime_types
|
||||||
|
|
||||||
|
val mimeTypesByExtensions: Map<String, Array<MimeType>> by lazy {
|
||||||
|
val extensionsMap = mutableMapOf<String, MutableList<MimeType>>()
|
||||||
|
|
||||||
|
knownMimeTypes.forEach { mimeType ->
|
||||||
|
mimeType.extensions.forEach {
|
||||||
|
extensionsMap.getOrPut(it) { mutableListOf() }.add(mimeType)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extensionsMap.mapValues {
|
||||||
|
it.value.toTypedArray()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fun getMimeType(
|
||||||
|
stringWithExtension: String,
|
||||||
|
selector: (Array<MimeType>) -> MimeType? = { it.firstOrNull() }
|
||||||
|
) = mimeTypesByExtensions[stringWithExtension.takeLastWhile { it != '.' }] ?.takeIf { it.isNotEmpty() } ?.let(selector)
|
||||||
|
inline fun getMimeTypeOrAny(
|
||||||
|
stringWithExtension: String,
|
||||||
|
selector: (Array<MimeType>) -> MimeType? = { it.firstOrNull() }
|
||||||
|
) = getMimeType(stringWithExtension, selector) ?: KnownMimeTypes.Any
|
File diff suppressed because it is too large
Load Diff
@ -5,4 +5,6 @@ import kotlinx.serialization.Serializable
|
|||||||
@Serializable(MimeTypeSerializer::class)
|
@Serializable(MimeTypeSerializer::class)
|
||||||
interface MimeType {
|
interface MimeType {
|
||||||
val raw: String
|
val raw: String
|
||||||
|
val extensions: Array<String>
|
||||||
|
get() = emptyArray()
|
||||||
}
|
}
|
||||||
|
@ -54,7 +54,11 @@ kotlin {
|
|||||||
}
|
}
|
||||||
mingwX64Test {
|
mingwX64Test {
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation kotlin('test-js')
|
implementation kotlin('test-junit')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
linuxX64Test {
|
||||||
|
dependencies {
|
||||||
implementation kotlin('test-junit')
|
implementation kotlin('test-junit')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user