full reborn

This commit is contained in:
2021-11-24 13:52:27 +06:00
parent 0ac6b0a4df
commit 6a6a197041
246 changed files with 4327 additions and 6952 deletions

View File

@@ -0,0 +1,19 @@
plugins {
id "org.jetbrains.kotlin.multiplatform"
id "org.jetbrains.kotlin.plugin.serialization"
id "com.android.library"
}
apply from: "$mppProjectWithSerializationPresetPath"
kotlin {
sourceSets {
commonMain {
dependencies {
api project(":postssystem.features.common.common")
api "dev.inmo:micro_utils.mime_types:$microutils_version"
api "dev.inmo:micro_utils.repos.common:$microutils_version"
}
}
}
}

View File

@@ -0,0 +1,6 @@
package dev.inmo.postssystem.features.files.common
const val filesRootPathPart = "files"
const val filesGetFilesPathPart = "getFiles"
const val filesGetFullFileInfoPathPart = "getFullFileInfo"
const val filesFileIdParameter = "fileId"

View File

@@ -0,0 +1,35 @@
package dev.inmo.postssystem.features.files.common
import dev.inmo.micro_utils.common.*
import dev.inmo.micro_utils.mime_types.MimeType
import dev.inmo.micro_utils.serialization.typed_serializer.TypedSerializer
import kotlinx.serialization.KSerializer
import kotlinx.serialization.Serializable
@Serializable(FileInfoSerializer::class)
sealed interface FileInfo {
val name: FileName
val mimeType: MimeType
companion object {
fun serializer(): KSerializer<FileInfo> = FileInfoSerializer
}
}
object FileInfoSerializer : KSerializer<FileInfo> by TypedSerializer(
"meta" to MetaFileInfo.serializer(),
"full" to FullFileInfo.serializer(),
)
@Serializable
data class MetaFileInfo(override val name: FileName, override val mimeType: MimeType) : FileInfo
@Serializable
data class FullFileInfo(
override val name: FileName,
override val mimeType: MimeType,
@Serializable(ByteArrayAllocatorSerializer::class)
val byteArrayAllocator: ByteArrayAllocator
) : FileInfo
fun FullFileInfo.toMetaFileInfo() = MetaFileInfo(name, mimeType)

View File

@@ -0,0 +1,21 @@
package dev.inmo.postssystem.features.files.common
import kotlinx.serialization.Serializable
import kotlin.jvm.JvmInline
@Serializable
@JvmInline
value class FileId(val string: String) {
override fun toString(): String = string.toString()
}
@Serializable
sealed class FileInfoStorageWrapper {
abstract val id: FileId
abstract val fileInfo: FileInfo
}
@Serializable
data class MetaFileInfoStorageWrapper(override val id: FileId, override val fileInfo: MetaFileInfo) : FileInfoStorageWrapper()
@Serializable
data class FullFileInfoStorageWrapper(override val id: FileId, override val fileInfo: FullFileInfo) : FileInfoStorageWrapper()

View File

@@ -0,0 +1,9 @@
package dev.inmo.postssystem.features.files.common.storage
import dev.inmo.postssystem.features.files.common.*
import dev.inmo.micro_utils.repos.ReadCRUDRepo
interface FilesStorage : ReadCRUDRepo<MetaFileInfoStorageWrapper, FileId> {
suspend fun getBytes(id: FileId): ByteArray
suspend fun getFullFileInfo(id: FileId): FullFileInfoStorageWrapper?
}

View File

@@ -0,0 +1,8 @@
package dev.inmo.postssystem.features.files.common.storage
interface FullFilesStorage : FilesStorage, WriteFilesStorage
class DefaultFullFilesStorage(
filesStorage: FilesStorage,
writeFilesStorage: WriteFilesStorage
) : FullFilesStorage, FilesStorage by filesStorage, WriteFilesStorage by writeFilesStorage

View File

@@ -0,0 +1,6 @@
package dev.inmo.postssystem.features.files.common.storage
import dev.inmo.postssystem.features.files.common.*
import dev.inmo.micro_utils.repos.WriteCRUDRepo
interface WriteFilesStorage : WriteCRUDRepo<FullFileInfoStorageWrapper, FileId, FullFileInfo>

View File

@@ -0,0 +1,63 @@
package dev.inmo.postssystem.features.files.common
import dev.inmo.postssystem.features.files.common.storage.FilesStorage
import dev.inmo.micro_utils.pagination.*
import dev.inmo.micro_utils.repos.ReadKeyValueRepo
import java.io.File
class DiskFilesStorage(
private val filesFolder: File,
private val metasKeyValueRepo: ReadKeyValueRepo<FileId, MetaFileInfo>
) : FilesStorage {
private val FileId.file
get() = File(filesFolder, string)
init {
if (!filesFolder.exists()) {
filesFolder.mkdirs()
} else {
require(filesFolder.isDirectory) { "$filesFolder must be a directory" }
}
}
private suspend fun FileId.meta(): MetaFileInfoStorageWrapper? {
return MetaFileInfoStorageWrapper(
this,
metasKeyValueRepo.get(this) ?: return null
)
}
override suspend fun getBytes(id: FileId): ByteArray = id.file.readBytes()
override suspend fun getFullFileInfo(id: FileId): FullFileInfoStorageWrapper? = getById(
id
) ?.let {
FullFileInfoStorageWrapper(
id,
FullFileInfo(
it.fileInfo.name,
it.fileInfo.mimeType
) {
id.file.readBytes()
}
)
}
override suspend fun contains(id: FileId): Boolean = metasKeyValueRepo.contains(id)
override suspend fun count(): Long = metasKeyValueRepo.count()
override suspend fun getById(id: FileId): MetaFileInfoStorageWrapper? = id.meta()
override suspend fun getByPagination(pagination: Pagination): PaginationResult<MetaFileInfoStorageWrapper> {
val keys = metasKeyValueRepo.keys(pagination)
return keys.changeResults(
keys.results.mapNotNull {
MetaFileInfoStorageWrapper(
it,
metasKeyValueRepo.get(it) ?: return@mapNotNull null
)
}
)
}
}

View File

@@ -0,0 +1,15 @@
package dev.inmo.postssystem.features.files.common
import dev.inmo.micro_utils.repos.KeyValueRepo
import dev.inmo.micro_utils.repos.mappers.withMapper
import kotlinx.serialization.StringFormat
fun MetasKeyValueRepo(
serialFormat: StringFormat,
originalRepo: KeyValueRepo<String, String>
) = originalRepo.withMapper<FileId, MetaFileInfo, String, String>(
{ string },
{ serialFormat.encodeToString(MetaFileInfo.serializer(), this) },
{ FileId(this) },
{ serialFormat.decodeFromString(MetaFileInfo.serializer(), this) }
)

View File

@@ -0,0 +1,69 @@
package dev.inmo.postssystem.features.files.common
import com.benasher44.uuid.uuid4
import dev.inmo.postssystem.features.files.common.storage.WriteFilesStorage
import dev.inmo.micro_utils.repos.*
import kotlinx.coroutines.flow.*
import java.io.File
class WriteDistFilesStorage(
private val filesFolder: File,
private val metasKeyValueRepo: WriteKeyValueRepo<FileId, MetaFileInfo>
) : WriteFilesStorage {
private val FileId.file
get() = File(filesFolder, string)
private val _deletedObjectsIdsFlow = MutableSharedFlow<FileId>()
private val _newObjectsFlow = MutableSharedFlow<FullFileInfoStorageWrapper>()
private val _updatedObjectsFlow = MutableSharedFlow<FullFileInfoStorageWrapper>()
override val deletedObjectsIdsFlow: Flow<FileId> = _deletedObjectsIdsFlow.asSharedFlow()
override val newObjectsFlow: Flow<FullFileInfoStorageWrapper> = _newObjectsFlow.asSharedFlow()
override val updatedObjectsFlow: Flow<FullFileInfoStorageWrapper> = _updatedObjectsFlow.asSharedFlow()
init {
if (!filesFolder.exists()) {
filesFolder.mkdirs()
} else {
require(filesFolder.isDirectory) { "$filesFolder must be a directory" }
}
}
override suspend fun create(values: List<FullFileInfo>): List<FullFileInfoStorageWrapper> = values.map {
var newId: FileId
var file: File
do {
newId = FileId(uuid4().toString())
file = newId.file
} while (file.exists())
metasKeyValueRepo.set(newId, it.toMetaFileInfo())
file.writeBytes(it.byteArrayAllocator())
FullFileInfoStorageWrapper(newId, it)
}
override suspend fun deleteById(ids: List<FileId>) {
ids.forEach {
if (it.file.delete()) {
metasKeyValueRepo.unset(it)
_deletedObjectsIdsFlow.emit(it)
}
}
}
override suspend fun update(
id: FileId,
value: FullFileInfo
): FullFileInfoStorageWrapper? = id.file.takeIf { it.exists() } ?.writeBytes(value.byteArrayAllocator()) ?.let {
val result = FullFileInfoStorageWrapper(id, value.copy())
metasKeyValueRepo.set(id, value.toMetaFileInfo())
_updatedObjectsFlow.emit(result)
result
}
override suspend fun update(
values: List<UpdatedValuePair<FileId, FullFileInfo>>
): List<FullFileInfoStorageWrapper> = values.mapNotNull { (id, file) ->
update(id, file)
}
}

View File

@@ -0,0 +1 @@
<manifest package="dev.inmo.postssystem.features.files.common"/>