Optimize server dispatcher
This commit is contained in:
parent
5632ec7c85
commit
a84c170396
12 changed files with 483 additions and 459 deletions
|
|
@ -12,6 +12,8 @@ import com.pixelized.server.lwa.model.tag.TagService
|
|||
import com.pixelized.server.lwa.model.tag.TagStore
|
||||
import com.pixelized.server.lwa.server.Engine
|
||||
import com.pixelized.shared.lwa.utils.PathProvider
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Job
|
||||
import org.koin.core.module.dsl.createdAtStart
|
||||
import org.koin.core.module.dsl.singleOf
|
||||
import org.koin.dsl.module
|
||||
|
|
@ -29,6 +31,9 @@ val toolsDependencies
|
|||
single {
|
||||
PathProvider(appName = "LwaServer")
|
||||
}
|
||||
factory<CoroutineScope> {
|
||||
CoroutineScope(Job())
|
||||
}
|
||||
}
|
||||
|
||||
val engineDependencies
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ class AlterationService(
|
|||
}
|
||||
|
||||
@Throws
|
||||
fun save(
|
||||
suspend fun save(
|
||||
json: AlterationJson,
|
||||
create: Boolean,
|
||||
) {
|
||||
|
|
|
|||
|
|
@ -12,11 +12,11 @@ import com.pixelized.shared.lwa.protocol.rest.APIResponse
|
|||
import com.pixelized.shared.lwa.utils.PathProvider
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import kotlinx.serialization.json.Json
|
||||
import java.io.File
|
||||
import java.text.Collator
|
||||
|
|
@ -24,15 +24,14 @@ import java.text.Collator
|
|||
class AlterationStore(
|
||||
private val pathProvider: PathProvider,
|
||||
private val factory: AlterationJsonFactory,
|
||||
private val json: Json,
|
||||
private val jsonSerializer: Json,
|
||||
scope: CoroutineScope,
|
||||
) {
|
||||
private val directory = File(pathProvider.alterationsPath()).also { it.mkdirs() }
|
||||
|
||||
private val alterationFlow = MutableStateFlow<List<Alteration>>(emptyList())
|
||||
|
||||
init {
|
||||
// build a coroutine scope for async calls
|
||||
val scope = CoroutineScope(Dispatchers.IO + Job())
|
||||
// load the initial data
|
||||
scope.launch {
|
||||
updateAlterationFlow()
|
||||
|
|
@ -41,7 +40,7 @@ class AlterationStore(
|
|||
|
||||
fun alterationsFlow(): StateFlow<List<Alteration>> = alterationFlow
|
||||
|
||||
fun updateAlterationFlow() {
|
||||
suspend fun updateAlterationFlow() {
|
||||
alterationFlow.value = try {
|
||||
load()
|
||||
} catch (exception: Exception) {
|
||||
|
|
@ -52,104 +51,110 @@ class AlterationStore(
|
|||
|
||||
@Throws(
|
||||
FileReadException::class,
|
||||
JsonCodingException::class,
|
||||
JsonConversionException::class,
|
||||
)
|
||||
private fun load(
|
||||
private suspend fun load(
|
||||
directory: File = this.directory,
|
||||
): List<Alteration> {
|
||||
return directory
|
||||
.listFiles()
|
||||
?.mapNotNull { file ->
|
||||
val json = try {
|
||||
file.readText(charset = Charsets.UTF_8)
|
||||
} catch (exception: Exception) {
|
||||
throw FileReadException(root = exception)
|
||||
return withContext(Dispatchers.IO) {
|
||||
directory
|
||||
.listFiles()
|
||||
?.mapNotNull { file ->
|
||||
val json = try {
|
||||
file.readText(charset = Charsets.UTF_8)
|
||||
} catch (exception: Exception) {
|
||||
throw FileReadException(root = exception)
|
||||
}
|
||||
// Guard, if the json is blank no alteration have been save, ignore this file.
|
||||
if (json.isBlank()) {
|
||||
return@mapNotNull null
|
||||
}
|
||||
// decode the file
|
||||
val data = try {
|
||||
jsonSerializer.decodeFromString<AlterationJson>(json)
|
||||
} catch (exception: Exception) {
|
||||
throw JsonCodingException(root = exception)
|
||||
}
|
||||
// parse the json string.
|
||||
val alterations = try {
|
||||
factory.convertFromJson(data)
|
||||
} catch (exception: Exception) {
|
||||
throw JsonConversionException(root = exception)
|
||||
}
|
||||
return@mapNotNull alterations
|
||||
}
|
||||
// Guard, if the json is blank no alteration have been save, ignore this file.
|
||||
if (json.isBlank()) {
|
||||
return@mapNotNull null
|
||||
}
|
||||
// decode the file
|
||||
val data = try {
|
||||
this.json.decodeFromString<AlterationJson>(json)
|
||||
} catch (exception: Exception) {
|
||||
throw JsonCodingException(root = exception)
|
||||
}
|
||||
// parse the json string.
|
||||
try {
|
||||
factory.convertFromJson(data)
|
||||
} catch (exception: Exception) {
|
||||
throw JsonConversionException(root = exception)
|
||||
}
|
||||
}
|
||||
?.sortedWith(compareBy(Collator.getInstance()) { it.metadata.name })
|
||||
?: emptyList()
|
||||
?.sortedWith(compareBy(Collator.getInstance()) { it.metadata.name })
|
||||
?: emptyList()
|
||||
}
|
||||
}
|
||||
|
||||
@Throws(
|
||||
BusinessException::class,
|
||||
JsonConversionException::class,
|
||||
JsonCodingException::class,
|
||||
FileWriteException::class,
|
||||
JsonCodingException::class,
|
||||
JsonConversionException::class,
|
||||
)
|
||||
fun save(
|
||||
suspend fun save(
|
||||
json: AlterationJson,
|
||||
create: Boolean,
|
||||
) {
|
||||
val file = alterationFile(id = json.id)
|
||||
// Guard case on update alteration
|
||||
if (create && file.exists()) {
|
||||
throw BusinessException(
|
||||
message = "Alteration already exist, creation is impossible.",
|
||||
)
|
||||
}
|
||||
// Transform the json into the model.
|
||||
val alteration = try {
|
||||
factory.convertFromJson(json)
|
||||
} catch (exception: Exception) {
|
||||
throw JsonConversionException(root = exception)
|
||||
}
|
||||
if (alteration.id.isEmpty()) {
|
||||
throw BusinessException(
|
||||
message = "Alteration 'id' is a mandatory field.",
|
||||
code = APIResponse.ErrorCode.AlterationId,
|
||||
)
|
||||
}
|
||||
if (alteration.metadata.name.isEmpty()) {
|
||||
throw BusinessException(
|
||||
message = "Alteration 'name' is a mandatory field.",
|
||||
code = APIResponse.ErrorCode.AlterationName,
|
||||
)
|
||||
}
|
||||
// Encode the json into a string.
|
||||
val data = try {
|
||||
this.json.encodeToString(json)
|
||||
} catch (exception: Exception) {
|
||||
throw JsonCodingException(root = exception)
|
||||
}
|
||||
// Write the alteration into a file.
|
||||
try {
|
||||
file.writeText(
|
||||
text = data,
|
||||
charset = Charsets.UTF_8,
|
||||
)
|
||||
} catch (exception: Exception) {
|
||||
throw FileWriteException(root = exception)
|
||||
}
|
||||
// Update the dataflow.
|
||||
alterationFlow.update { alterations ->
|
||||
val index = alterations.indexOfFirst { it.id == json.id }
|
||||
alterations.toMutableList()
|
||||
.also {
|
||||
if (index >= 0) {
|
||||
it[index] = alteration
|
||||
} else {
|
||||
it.add(alteration)
|
||||
withContext(Dispatchers.IO) {
|
||||
val file = alterationFile(id = json.id)
|
||||
// Guard case on update alteration
|
||||
if (create && file.exists()) {
|
||||
throw BusinessException(
|
||||
message = "Alteration already exist, creation is impossible.",
|
||||
)
|
||||
}
|
||||
// Transform the json into the model.
|
||||
val alteration = try {
|
||||
factory.convertFromJson(json)
|
||||
} catch (exception: Exception) {
|
||||
throw JsonConversionException(root = exception)
|
||||
}
|
||||
if (alteration.id.isEmpty()) {
|
||||
throw BusinessException(
|
||||
message = "Alteration 'id' is a mandatory field.",
|
||||
code = APIResponse.ErrorCode.AlterationId,
|
||||
)
|
||||
}
|
||||
if (alteration.metadata.name.isEmpty()) {
|
||||
throw BusinessException(
|
||||
message = "Alteration 'name' is a mandatory field.",
|
||||
code = APIResponse.ErrorCode.AlterationName,
|
||||
)
|
||||
}
|
||||
// Encode the json into a string.
|
||||
val data = try {
|
||||
jsonSerializer.encodeToString(json)
|
||||
} catch (exception: Exception) {
|
||||
throw JsonCodingException(root = exception)
|
||||
}
|
||||
// Write the alteration into a file.
|
||||
try {
|
||||
file.writeText(
|
||||
text = data,
|
||||
charset = Charsets.UTF_8,
|
||||
)
|
||||
} catch (exception: Exception) {
|
||||
throw FileWriteException(root = exception)
|
||||
}
|
||||
// Update the dataflow.
|
||||
alterationFlow.update { alterations ->
|
||||
val index = alterations.indexOfFirst { it.id == json.id }
|
||||
alterations.toMutableList()
|
||||
.also {
|
||||
if (index >= 0) {
|
||||
it[index] = alteration
|
||||
} else {
|
||||
it.add(alteration)
|
||||
}
|
||||
}
|
||||
}
|
||||
.sortedWith(compareBy(Collator.getInstance()) {
|
||||
it.metadata.name
|
||||
})
|
||||
.sortedWith(compareBy(Collator.getInstance()) {
|
||||
it.metadata.name
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ class CampaignService(
|
|||
}
|
||||
|
||||
@Throws
|
||||
fun addCharacter(
|
||||
suspend fun addCharacter(
|
||||
characterSheetId: String,
|
||||
) {
|
||||
// Check if the character is already in the campaign.
|
||||
|
|
@ -57,7 +57,7 @@ class CampaignService(
|
|||
}
|
||||
|
||||
@Throws
|
||||
fun addNpc(
|
||||
suspend fun addNpc(
|
||||
characterSheetId: String,
|
||||
) {
|
||||
// Check if the character is already in the campaign.
|
||||
|
|
@ -75,7 +75,7 @@ class CampaignService(
|
|||
}
|
||||
|
||||
@Throws
|
||||
fun removeCharacter(
|
||||
suspend fun removeCharacter(
|
||||
characterSheetId: String,
|
||||
) {
|
||||
// Check if the character is in the campaign.
|
||||
|
|
@ -93,7 +93,7 @@ class CampaignService(
|
|||
}
|
||||
|
||||
@Throws
|
||||
fun removeNpc(
|
||||
suspend fun removeNpc(
|
||||
characterSheetId: String,
|
||||
) {
|
||||
// Check if the character is in the campaign.
|
||||
|
|
@ -111,7 +111,7 @@ class CampaignService(
|
|||
}
|
||||
|
||||
@Throws
|
||||
fun setScene(
|
||||
suspend fun setScene(
|
||||
scene: Campaign.Scene,
|
||||
) {
|
||||
// save the campaign to the disk + update the flow.
|
||||
|
|
@ -122,7 +122,7 @@ class CampaignService(
|
|||
|
||||
// Data manipulation through WebSocket.
|
||||
|
||||
fun updateToggleParty() {
|
||||
suspend fun updateToggleParty() {
|
||||
store.save(
|
||||
campaign = campaign.copy(
|
||||
options = campaign.options.copy(
|
||||
|
|
@ -132,7 +132,7 @@ class CampaignService(
|
|||
)
|
||||
}
|
||||
|
||||
fun updateToggleNpc() {
|
||||
suspend fun updateToggleNpc() {
|
||||
store.save(
|
||||
campaign = campaign.copy(
|
||||
options = campaign.options.copy(
|
||||
|
|
|
|||
|
|
@ -15,21 +15,21 @@ import kotlinx.coroutines.flow.MutableStateFlow
|
|||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import kotlinx.serialization.json.Json
|
||||
import java.io.File
|
||||
|
||||
class CampaignStore(
|
||||
private val pathProvider: PathProvider,
|
||||
private val factory: CampaignJsonFactory,
|
||||
private val json: Json,
|
||||
private val jsonSerializer: Json,
|
||||
scope: CoroutineScope,
|
||||
) {
|
||||
private val campaignFlow = MutableStateFlow(value = Campaign.empty())
|
||||
|
||||
init {
|
||||
// create the directory if needed.
|
||||
File(pathProvider.campaignPath()).also { it.mkdirs() }
|
||||
// build a coroutine scope for async calls
|
||||
val scope = CoroutineScope(Dispatchers.IO + Job())
|
||||
// load the initial data
|
||||
scope.launch {
|
||||
updateCampaignFlow()
|
||||
|
|
@ -38,7 +38,7 @@ class CampaignStore(
|
|||
|
||||
fun campaignFlow(): StateFlow<Campaign> = campaignFlow
|
||||
|
||||
fun updateCampaignFlow() {
|
||||
suspend fun updateCampaignFlow() {
|
||||
campaignFlow.value = try {
|
||||
load()
|
||||
} catch (exception: Exception) {
|
||||
|
|
@ -52,29 +52,31 @@ class CampaignStore(
|
|||
JsonCodingException::class,
|
||||
JsonConversionException::class,
|
||||
)
|
||||
fun load(): Campaign {
|
||||
val file = campaignFile()
|
||||
// Read the campaign file.
|
||||
val data = try {
|
||||
file.readText(charset = Charsets.UTF_8)
|
||||
} catch (exception: Exception) {
|
||||
throw FileReadException(root = exception)
|
||||
suspend fun load(): Campaign {
|
||||
return withContext(Dispatchers.IO) {
|
||||
val file = campaignFile()
|
||||
// Read the campaign file.
|
||||
val data = try {
|
||||
file.readText(charset = Charsets.UTF_8)
|
||||
} catch (exception: Exception) {
|
||||
throw FileReadException(root = exception)
|
||||
}
|
||||
// Guard, if the file is empty we load a default campaign.
|
||||
if (data.isBlank()) return@withContext Campaign.empty()
|
||||
// Decode the json into a string.
|
||||
val json = try {
|
||||
jsonSerializer.decodeFromString<CampaignJson>(data)
|
||||
} catch (exception: Exception) {
|
||||
throw JsonCodingException(root = exception)
|
||||
}
|
||||
// Convert from the Json format
|
||||
val campaign = try {
|
||||
factory.convertFromJson(json)
|
||||
} catch (exception: Exception) {
|
||||
throw JsonConversionException(root = exception)
|
||||
}
|
||||
return@withContext campaign
|
||||
}
|
||||
// Guard, if the file is empty we load a default campaign.
|
||||
if (data.isBlank()) return Campaign.empty()
|
||||
// Decode the json into a string.
|
||||
val json = try {
|
||||
this.json.decodeFromString<CampaignJson>(data)
|
||||
} catch (exception: Exception) {
|
||||
throw JsonCodingException(root = exception)
|
||||
}
|
||||
// Convert from the Json format
|
||||
val campaign = try {
|
||||
factory.convertFromJson(json)
|
||||
} catch (exception: Exception) {
|
||||
throw JsonConversionException(root = exception)
|
||||
}
|
||||
return campaign
|
||||
}
|
||||
|
||||
@Throws(
|
||||
|
|
@ -82,31 +84,33 @@ class CampaignStore(
|
|||
JsonCodingException::class,
|
||||
FileWriteException::class,
|
||||
)
|
||||
fun save(campaign: Campaign) {
|
||||
// Transform the json into the model.
|
||||
val json = try {
|
||||
factory.convertToJson(campaign = campaign)
|
||||
} catch (exception: Exception) {
|
||||
throw JsonConversionException(root = exception)
|
||||
suspend fun save(campaign: Campaign) {
|
||||
withContext(Dispatchers.IO) {
|
||||
// Transform the json into the model.
|
||||
val json = try {
|
||||
factory.convertToJson(campaign = campaign)
|
||||
} catch (exception: Exception) {
|
||||
throw JsonConversionException(root = exception)
|
||||
}
|
||||
// Encode the json into a string.
|
||||
val data = try {
|
||||
jsonSerializer.encodeToString(json)
|
||||
} catch (exception: Exception) {
|
||||
throw JsonCodingException(root = exception)
|
||||
}
|
||||
// Write the file
|
||||
try {
|
||||
val file = campaignFile()
|
||||
file.writeText(
|
||||
text = data,
|
||||
charset = Charsets.UTF_8,
|
||||
)
|
||||
} catch (exception: Exception) {
|
||||
throw FileWriteException(root = exception)
|
||||
}
|
||||
// Update the dataflow.
|
||||
campaignFlow.update { campaign }
|
||||
}
|
||||
// Encode the json into a string.
|
||||
val data = try {
|
||||
this.json.encodeToString(json)
|
||||
} catch (exception: Exception) {
|
||||
throw JsonCodingException(root = exception)
|
||||
}
|
||||
// Write the file
|
||||
try {
|
||||
val file = campaignFile()
|
||||
file.writeText(
|
||||
text = data,
|
||||
charset = Charsets.UTF_8,
|
||||
)
|
||||
} catch (exception: Exception) {
|
||||
throw FileWriteException(root = exception)
|
||||
}
|
||||
// Update the dataflow.
|
||||
campaignFlow.update { campaign }
|
||||
}
|
||||
|
||||
private fun campaignFile(): File {
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ class CharacterSheetService(
|
|||
}
|
||||
|
||||
@Throws
|
||||
fun deleteCharacterSheet(
|
||||
suspend fun deleteCharacterSheet(
|
||||
characterSheetId: String,
|
||||
) {
|
||||
characterStore.delete(
|
||||
|
|
@ -65,7 +65,7 @@ class CharacterSheetService(
|
|||
|
||||
// Data manipulation through WebSocket.
|
||||
|
||||
fun updateAlteration(
|
||||
suspend fun updateAlteration(
|
||||
characterSheetId: String,
|
||||
alterationId: String,
|
||||
active: Boolean,
|
||||
|
|
@ -97,7 +97,7 @@ class CharacterSheetService(
|
|||
}
|
||||
}
|
||||
|
||||
fun updateDamage(
|
||||
suspend fun updateDamage(
|
||||
characterSheetId: String,
|
||||
damage: Int,
|
||||
) {
|
||||
|
|
@ -110,7 +110,7 @@ class CharacterSheetService(
|
|||
}
|
||||
}
|
||||
|
||||
fun updateDiminished(
|
||||
suspend fun updateDiminished(
|
||||
characterSheetId: String,
|
||||
diminished: Int,
|
||||
) {
|
||||
|
|
@ -123,7 +123,7 @@ class CharacterSheetService(
|
|||
}
|
||||
}
|
||||
|
||||
fun updateFatigue(
|
||||
suspend fun updateFatigue(
|
||||
characterSheetId: String,
|
||||
fatigue: Int,
|
||||
) {
|
||||
|
|
@ -136,7 +136,7 @@ class CharacterSheetService(
|
|||
}
|
||||
}
|
||||
|
||||
fun updateCharacterSkillUsage(
|
||||
suspend fun updateCharacterSkillUsage(
|
||||
characterSheetId: String,
|
||||
skillId: String,
|
||||
used: Boolean,
|
||||
|
|
|
|||
|
|
@ -12,11 +12,11 @@ import com.pixelized.shared.lwa.protocol.rest.APIResponse
|
|||
import com.pixelized.shared.lwa.utils.PathProvider
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import kotlinx.serialization.json.Json
|
||||
import java.io.File
|
||||
import java.text.Collator
|
||||
|
|
@ -24,14 +24,13 @@ import java.text.Collator
|
|||
class CharacterSheetStore(
|
||||
private val pathProvider: PathProvider,
|
||||
private val factory: CharacterSheetJsonFactory,
|
||||
private val json: Json,
|
||||
private val jsonSerializer: Json,
|
||||
scope: CoroutineScope,
|
||||
) {
|
||||
private val directory = File(pathProvider.characterStorePath()).also { it.mkdirs() }
|
||||
private val characterSheetsFlow = MutableStateFlow<List<CharacterSheet>>(value = emptyList())
|
||||
|
||||
init {
|
||||
// build a coroutine scope for async calls
|
||||
val scope = CoroutineScope(Dispatchers.IO + Job())
|
||||
// load the initial data
|
||||
scope.launch {
|
||||
updateCharacterFlow()
|
||||
|
|
@ -40,7 +39,7 @@ class CharacterSheetStore(
|
|||
|
||||
fun characterSheetsFlow(): StateFlow<List<CharacterSheet>> = characterSheetsFlow
|
||||
|
||||
fun updateCharacterFlow() {
|
||||
suspend fun updateCharacterFlow() {
|
||||
characterSheetsFlow.value = try {
|
||||
load()
|
||||
} catch (exception: Exception) {
|
||||
|
|
@ -54,112 +53,118 @@ class CharacterSheetStore(
|
|||
JsonCodingException::class,
|
||||
JsonConversionException::class,
|
||||
)
|
||||
fun load(
|
||||
suspend fun load(
|
||||
directory: File = this.directory,
|
||||
): List<CharacterSheet> {
|
||||
return directory
|
||||
.listFiles()
|
||||
?.mapNotNull { file ->
|
||||
val json = try {
|
||||
file.readText(charset = Charsets.UTF_8)
|
||||
} catch (exception: Exception) {
|
||||
throw FileReadException(root = exception)
|
||||
return withContext(Dispatchers.IO) {
|
||||
directory
|
||||
.listFiles()
|
||||
?.mapNotNull { file ->
|
||||
val json = try {
|
||||
file.readText(charset = Charsets.UTF_8)
|
||||
} catch (exception: Exception) {
|
||||
throw FileReadException(root = exception)
|
||||
}
|
||||
// Guard, if the json is blank no character have been save, ignore this file.
|
||||
if (json.isBlank()) {
|
||||
return@mapNotNull null
|
||||
}
|
||||
// decode the file
|
||||
val data = try {
|
||||
jsonSerializer.decodeFromString<CharacterSheetJson>(json)
|
||||
} catch (exception: Exception) {
|
||||
throw JsonCodingException(root = exception)
|
||||
}
|
||||
// parse the json string.
|
||||
try {
|
||||
factory.convertFromJson(data)
|
||||
} catch (exception: Exception) {
|
||||
throw JsonConversionException(root = exception)
|
||||
}
|
||||
}
|
||||
// Guard, if the json is blank no character have been save, ignore this file.
|
||||
if (json.isBlank()) {
|
||||
return@mapNotNull null
|
||||
}
|
||||
// decode the file
|
||||
val data = try {
|
||||
this.json.decodeFromString<CharacterSheetJson>(json)
|
||||
} catch (exception: Exception) {
|
||||
throw JsonCodingException(root = exception)
|
||||
}
|
||||
// parse the json string.
|
||||
try {
|
||||
factory.convertFromJson(data)
|
||||
} catch (exception: Exception) {
|
||||
throw JsonConversionException(root = exception)
|
||||
}
|
||||
}
|
||||
?.sortedWith(compareBy(Collator.getInstance()) { it.name })
|
||||
?: emptyList()
|
||||
?.sortedWith(compareBy(Collator.getInstance()) { it.name })
|
||||
?: emptyList()
|
||||
}
|
||||
}
|
||||
|
||||
@Throws(
|
||||
BusinessException::class,
|
||||
JsonConversionException::class,
|
||||
JsonCodingException::class,
|
||||
FileWriteException::class,
|
||||
JsonCodingException::class,
|
||||
JsonConversionException::class,
|
||||
)
|
||||
fun save(
|
||||
suspend fun save(
|
||||
sheet: CharacterSheet,
|
||||
create: Boolean,
|
||||
) {
|
||||
val file = characterSheetFile(id = sheet.id)
|
||||
// Guard case on update alteration
|
||||
if (create && file.exists()) {
|
||||
throw BusinessException(message = "Character already exist, creation is impossible.")
|
||||
}
|
||||
// Transform the json into the model.
|
||||
val json = try {
|
||||
factory.convertToJson(sheet = sheet)
|
||||
} catch (exception: Exception) {
|
||||
throw JsonConversionException(root = exception)
|
||||
}
|
||||
// Encode the json into a string.
|
||||
val data = try {
|
||||
this.json.encodeToString(json)
|
||||
} catch (exception: Exception) {
|
||||
throw JsonCodingException(root = exception)
|
||||
}
|
||||
// write the character file.
|
||||
try {
|
||||
file.writeText(
|
||||
text = data,
|
||||
charset = Charsets.UTF_8,
|
||||
)
|
||||
} catch (exception: Exception) {
|
||||
throw FileWriteException(root = exception)
|
||||
}
|
||||
// Update the dataflow.
|
||||
characterSheetsFlow.update { characters ->
|
||||
characters.toMutableList()
|
||||
.also { data ->
|
||||
val index = data.indexOfFirst { it.id == sheet.id }
|
||||
if (index >= 0) {
|
||||
data[index] = sheet
|
||||
} else {
|
||||
data.add(sheet)
|
||||
withContext(Dispatchers.IO) {
|
||||
val file = characterSheetFile(id = sheet.id)
|
||||
// Guard case on update alteration
|
||||
if (create && file.exists()) {
|
||||
throw BusinessException(message = "Character already exist, creation is impossible.")
|
||||
}
|
||||
// Transform the json into the model.
|
||||
val json = try {
|
||||
factory.convertToJson(sheet = sheet)
|
||||
} catch (exception: Exception) {
|
||||
throw JsonConversionException(root = exception)
|
||||
}
|
||||
// Encode the json into a string.
|
||||
val data = try {
|
||||
jsonSerializer.encodeToString(json)
|
||||
} catch (exception: Exception) {
|
||||
throw JsonCodingException(root = exception)
|
||||
}
|
||||
// write the character file.
|
||||
try {
|
||||
file.writeText(
|
||||
text = data,
|
||||
charset = Charsets.UTF_8,
|
||||
)
|
||||
} catch (exception: Exception) {
|
||||
throw FileWriteException(root = exception)
|
||||
}
|
||||
// Update the dataflow.
|
||||
characterSheetsFlow.update { characters ->
|
||||
characters.toMutableList()
|
||||
.also { data ->
|
||||
val index = data.indexOfFirst { it.id == sheet.id }
|
||||
if (index >= 0) {
|
||||
data[index] = sheet
|
||||
} else {
|
||||
data.add(sheet)
|
||||
}
|
||||
}
|
||||
}
|
||||
.sortedWith(compareBy(Collator.getInstance()) {
|
||||
it.name
|
||||
})
|
||||
.sortedWith(compareBy(Collator.getInstance()) {
|
||||
it.name
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Throws(BusinessException::class)
|
||||
fun delete(characterSheetId: String) {
|
||||
val file = characterSheetFile(id = characterSheetId)
|
||||
// Guard case on the file existence.
|
||||
if (file.exists().not()) {
|
||||
throw BusinessException(
|
||||
message = "Character file with id:$characterSheetId doesn't not exist.",
|
||||
code = APIResponse.ErrorCode.CharacterSheetId
|
||||
)
|
||||
}
|
||||
// Guard case on the file deletion
|
||||
if (file.delete().not()) {
|
||||
throw BusinessException(
|
||||
message = "Character file have not been deleted for unknown reason.",
|
||||
)
|
||||
}
|
||||
// Update the data model with the deleted character.
|
||||
characterSheetsFlow.update { characters ->
|
||||
characters.toMutableList()
|
||||
.also { data -> data.removeIf { it.id == characterSheetId } }
|
||||
.sortedWith(compareBy(Collator.getInstance()) { it.name })
|
||||
suspend fun delete(characterSheetId: String) {
|
||||
withContext(Dispatchers.IO) {
|
||||
val file = characterSheetFile(id = characterSheetId)
|
||||
// Guard case on the file existence.
|
||||
if (file.exists().not()) {
|
||||
throw BusinessException(
|
||||
message = "Character file with id:$characterSheetId doesn't not exist.",
|
||||
code = APIResponse.ErrorCode.CharacterSheetId
|
||||
)
|
||||
}
|
||||
// Guard case on the file deletion
|
||||
if (file.delete().not()) {
|
||||
throw BusinessException(
|
||||
message = "Character file have not been deleted for unknown reason.",
|
||||
)
|
||||
}
|
||||
// Update the data model with the deleted character.
|
||||
characterSheetsFlow.update { characters ->
|
||||
characters.toMutableList()
|
||||
.also { data -> data.removeIf { it.id == characterSheetId } }
|
||||
.sortedWith(compareBy(Collator.getInstance()) { it.name })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ class InventoryService(
|
|||
}
|
||||
|
||||
@Throws
|
||||
fun updatePurse(
|
||||
suspend fun updatePurse(
|
||||
purse: ApiPurseJson,
|
||||
) {
|
||||
val inventory = inventory(
|
||||
|
|
@ -59,7 +59,7 @@ class InventoryService(
|
|||
}
|
||||
|
||||
@Throws
|
||||
fun save(
|
||||
suspend fun save(
|
||||
inventoryJson: InventoryJson,
|
||||
create: Boolean,
|
||||
) {
|
||||
|
|
@ -70,12 +70,12 @@ class InventoryService(
|
|||
}
|
||||
|
||||
@Throws
|
||||
fun delete(characterSheetId: String) {
|
||||
suspend fun delete(characterSheetId: String) {
|
||||
inventoryStore.delete(characterSheetId = characterSheetId)
|
||||
}
|
||||
|
||||
@Throws
|
||||
fun createInventoryItem(
|
||||
suspend fun createInventoryItem(
|
||||
characterSheetId: String,
|
||||
itemId: String,
|
||||
count: Float,
|
||||
|
|
@ -107,7 +107,7 @@ class InventoryService(
|
|||
}
|
||||
|
||||
@Throws
|
||||
fun changeInventoryItemCount(
|
||||
suspend fun changeInventoryItemCount(
|
||||
characterSheetId: String,
|
||||
inventoryId: String,
|
||||
count: Float,
|
||||
|
|
@ -145,7 +145,7 @@ class InventoryService(
|
|||
}
|
||||
|
||||
@Throws
|
||||
fun consumeInventoryItem(
|
||||
suspend fun consumeInventoryItem(
|
||||
characterSheetId: String,
|
||||
inventoryId: String,
|
||||
) {
|
||||
|
|
@ -185,7 +185,7 @@ class InventoryService(
|
|||
}
|
||||
|
||||
@Throws
|
||||
fun equipInventoryItem(
|
||||
suspend fun equipInventoryItem(
|
||||
characterSheetId: String,
|
||||
inventoryId: String,
|
||||
equip: Boolean,
|
||||
|
|
@ -223,7 +223,7 @@ class InventoryService(
|
|||
}
|
||||
|
||||
@Throws
|
||||
fun deleteInventoryItem(
|
||||
suspend fun deleteInventoryItem(
|
||||
characterSheetId: String,
|
||||
inventoryId: String,
|
||||
) {
|
||||
|
|
|
|||
|
|
@ -12,26 +12,24 @@ import com.pixelized.shared.lwa.protocol.rest.APIResponse
|
|||
import com.pixelized.shared.lwa.utils.PathProvider
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import kotlinx.serialization.json.Json
|
||||
import java.io.File
|
||||
import java.util.UUID
|
||||
|
||||
class InventoryStore(
|
||||
private val pathProvider: PathProvider,
|
||||
private val factory: InventoryJsonFactory,
|
||||
private val json: Json,
|
||||
private val jsonSerializer: Json,
|
||||
scope: CoroutineScope,
|
||||
) {
|
||||
private val directory = File(pathProvider.inventoryPath()).also { it.mkdirs() }
|
||||
private val inventoryFlow = MutableStateFlow<Map<String, Inventory>>(value = emptyMap())
|
||||
|
||||
init {
|
||||
// build a coroutine scope for async calls
|
||||
val scope = CoroutineScope(Dispatchers.IO + Job())
|
||||
// load the initial data
|
||||
scope.launch {
|
||||
updateInventoryFlow()
|
||||
|
|
@ -40,7 +38,7 @@ class InventoryStore(
|
|||
|
||||
fun inventoryFlow(): StateFlow<Map<String, Inventory>> = inventoryFlow
|
||||
|
||||
fun updateInventoryFlow() {
|
||||
suspend fun updateInventoryFlow() {
|
||||
inventoryFlow.value = try {
|
||||
load()
|
||||
} catch (exception: Exception) {
|
||||
|
|
@ -54,102 +52,104 @@ class InventoryStore(
|
|||
JsonCodingException::class,
|
||||
JsonConversionException::class,
|
||||
)
|
||||
private fun load(
|
||||
private suspend fun load(
|
||||
directory: File = this.directory,
|
||||
): Map<String, Inventory> {
|
||||
return directory
|
||||
.listFiles()
|
||||
?.mapNotNull { file ->
|
||||
val json = try {
|
||||
file.readText(charset = Charsets.UTF_8)
|
||||
} catch (exception: Exception) {
|
||||
throw FileReadException(root = exception)
|
||||
return withContext(Dispatchers.IO) {
|
||||
directory
|
||||
.listFiles()
|
||||
?.mapNotNull { file ->
|
||||
val json = try {
|
||||
file.readText(charset = Charsets.UTF_8)
|
||||
} catch (exception: Exception) {
|
||||
throw FileReadException(root = exception)
|
||||
}
|
||||
// Guard, if the json is blank no character have been save, ignore this file.
|
||||
if (json.isBlank()) {
|
||||
return@mapNotNull null
|
||||
}
|
||||
// decode the file
|
||||
val data = try {
|
||||
jsonSerializer.decodeFromString<InventoryJson>(json)
|
||||
} catch (exception: Exception) {
|
||||
throw JsonCodingException(root = exception)
|
||||
}
|
||||
// parse the json string.
|
||||
val inventory = try {
|
||||
factory.convertFromJson(data)
|
||||
} catch (exception: Exception) {
|
||||
throw JsonConversionException(root = exception)
|
||||
}
|
||||
inventory.characterSheetId to inventory
|
||||
}
|
||||
// Guard, if the json is blank no character have been save, ignore this file.
|
||||
if (json.isBlank()) {
|
||||
return@mapNotNull null
|
||||
}
|
||||
// decode the file
|
||||
val data = try {
|
||||
this.json.decodeFromString<InventoryJson>(json)
|
||||
} catch (exception: Exception) {
|
||||
throw JsonCodingException(root = exception)
|
||||
}
|
||||
// parse the json string.
|
||||
val inventory = try {
|
||||
factory.convertFromJson(data)
|
||||
} catch (exception: Exception) {
|
||||
throw JsonConversionException(root = exception)
|
||||
}
|
||||
inventory.characterSheetId to inventory
|
||||
}
|
||||
?.toMap()
|
||||
?: emptyMap()
|
||||
?.toMap()
|
||||
?: emptyMap()
|
||||
}
|
||||
}
|
||||
|
||||
@Throws(
|
||||
BusinessException::class,
|
||||
JsonConversionException::class,
|
||||
JsonCodingException::class,
|
||||
FileWriteException::class,
|
||||
JsonCodingException::class,
|
||||
JsonConversionException::class,
|
||||
)
|
||||
fun save(
|
||||
suspend fun save(
|
||||
inventory: Inventory,
|
||||
create: Boolean,
|
||||
) {
|
||||
val file = inventoryFile(id = inventory.characterSheetId)
|
||||
|
||||
if (create && file.exists()) {
|
||||
throw BusinessException(message = "Inventory already exist, creation is impossible.")
|
||||
}
|
||||
|
||||
val json = try {
|
||||
factory.convertToJson(inventory = inventory)
|
||||
} catch (exception: Exception) {
|
||||
throw JsonConversionException(root = exception)
|
||||
}
|
||||
|
||||
val data = try {
|
||||
this.json.encodeToString(json)
|
||||
} catch (exception: Exception) {
|
||||
throw JsonCodingException(root = exception)
|
||||
}
|
||||
|
||||
try {
|
||||
file.writeText(
|
||||
text = data,
|
||||
charset = Charsets.UTF_8,
|
||||
withContext(Dispatchers.IO) {
|
||||
val file = inventoryFile(
|
||||
id = inventory.characterSheetId
|
||||
)
|
||||
} catch (exception: Exception) {
|
||||
throw FileWriteException(root = exception)
|
||||
}
|
||||
|
||||
inventoryFlow.update { flow ->
|
||||
flow.toMutableMap().also { data ->
|
||||
data[inventory.characterSheetId] = inventory
|
||||
if (create && file.exists()) {
|
||||
throw BusinessException(message = "Inventory already exist, creation is impossible.")
|
||||
}
|
||||
val json = try {
|
||||
factory.convertToJson(inventory = inventory)
|
||||
} catch (exception: Exception) {
|
||||
throw JsonConversionException(root = exception)
|
||||
}
|
||||
val data = try {
|
||||
jsonSerializer.encodeToString(json)
|
||||
} catch (exception: Exception) {
|
||||
throw JsonCodingException(root = exception)
|
||||
}
|
||||
try {
|
||||
file.writeText(
|
||||
text = data,
|
||||
charset = Charsets.UTF_8,
|
||||
)
|
||||
} catch (exception: Exception) {
|
||||
throw FileWriteException(root = exception)
|
||||
}
|
||||
inventoryFlow.update { flow ->
|
||||
flow.toMutableMap().also { data ->
|
||||
data[inventory.characterSheetId] = inventory
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Throws(BusinessException::class)
|
||||
fun delete(characterSheetId: String) {
|
||||
val file = inventoryFile(id = characterSheetId)
|
||||
|
||||
if (file.exists().not()) {
|
||||
throw BusinessException(
|
||||
message = "Inventory file with id:$characterSheetId doesn't not exist.",
|
||||
code = APIResponse.ErrorCode.CharacterSheetId
|
||||
suspend fun delete(characterSheetId: String) {
|
||||
withContext(Dispatchers.IO) {
|
||||
val file = inventoryFile(
|
||||
id = characterSheetId,
|
||||
)
|
||||
}
|
||||
|
||||
if (file.delete().not()) {
|
||||
throw BusinessException(
|
||||
message = "Inventory file have not been deleted for unknown reason.",
|
||||
)
|
||||
}
|
||||
|
||||
inventoryFlow.update { characters ->
|
||||
characters.toMutableMap().also { data -> data.remove(characterSheetId) }
|
||||
if (file.exists().not()) {
|
||||
throw BusinessException(
|
||||
message = "Inventory file with id:$characterSheetId doesn't not exist.",
|
||||
code = APIResponse.ErrorCode.CharacterSheetId
|
||||
)
|
||||
}
|
||||
if (file.delete().not()) {
|
||||
throw BusinessException(
|
||||
message = "Inventory file have not been deleted for unknown reason.",
|
||||
)
|
||||
}
|
||||
inventoryFlow.update { characters ->
|
||||
characters.toMutableMap().also { data -> data.remove(characterSheetId) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ class ItemService(
|
|||
}
|
||||
|
||||
@Throws
|
||||
fun save(
|
||||
suspend fun save(
|
||||
json: ItemJson,
|
||||
create: Boolean,
|
||||
) {
|
||||
|
|
@ -43,7 +43,7 @@ class ItemService(
|
|||
}
|
||||
|
||||
@Throws
|
||||
fun delete(itemId: String) {
|
||||
suspend fun delete(itemId: String) {
|
||||
itemStore.delete(id = itemId)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,11 +12,11 @@ import com.pixelized.shared.lwa.protocol.rest.APIResponse
|
|||
import com.pixelized.shared.lwa.utils.PathProvider
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import kotlinx.serialization.json.Json
|
||||
import java.io.File
|
||||
import java.text.Collator
|
||||
|
|
@ -24,14 +24,13 @@ import java.text.Collator
|
|||
class ItemStore(
|
||||
private val pathProvider: PathProvider,
|
||||
private val factory: ItemJsonFactory,
|
||||
private val json: Json,
|
||||
private val jsonSerializer: Json,
|
||||
scope: CoroutineScope,
|
||||
) {
|
||||
private val directory = File(pathProvider.itemsPath()).also { it.mkdirs() }
|
||||
private val itemFlow = MutableStateFlow<List<Item>>(emptyList())
|
||||
|
||||
init {
|
||||
val scope = CoroutineScope(Dispatchers.IO + Job())
|
||||
|
||||
scope.launch {
|
||||
updateItemsFlow()
|
||||
}
|
||||
|
|
@ -43,7 +42,7 @@ class ItemStore(
|
|||
|
||||
fun itemsFlow(): StateFlow<List<Item>> = itemFlow
|
||||
|
||||
fun updateItemsFlow() {
|
||||
suspend fun updateItemsFlow() {
|
||||
itemFlow.value = try {
|
||||
load()
|
||||
} catch (exception: Exception) {
|
||||
|
|
@ -54,131 +53,138 @@ class ItemStore(
|
|||
|
||||
@Throws(
|
||||
FileReadException::class,
|
||||
JsonCodingException::class,
|
||||
JsonConversionException::class,
|
||||
)
|
||||
private fun load(
|
||||
private suspend fun load(
|
||||
directory: File = this.directory,
|
||||
): List<Item> {
|
||||
return directory
|
||||
.listFiles()
|
||||
?.mapNotNull { file ->
|
||||
val json = try {
|
||||
file.readText(charset = Charsets.UTF_8)
|
||||
} catch (exception: Exception) {
|
||||
throw FileReadException(root = exception)
|
||||
return withContext(Dispatchers.IO) {
|
||||
directory
|
||||
.listFiles()
|
||||
?.mapNotNull { file ->
|
||||
val json = try {
|
||||
file.readText(charset = Charsets.UTF_8)
|
||||
} catch (exception: Exception) {
|
||||
throw FileReadException(root = exception)
|
||||
}
|
||||
// Guard, if the json is blank no item have been save, ignore this file.
|
||||
if (json.isBlank()) {
|
||||
return@mapNotNull null
|
||||
}
|
||||
// decode the file
|
||||
val data = try {
|
||||
jsonSerializer.decodeFromString<ItemJson>(json)
|
||||
} catch (exception: Exception) {
|
||||
throw JsonCodingException(root = exception)
|
||||
}
|
||||
// parse the json string.
|
||||
try {
|
||||
factory.convertFromJson(data)
|
||||
} catch (exception: Exception) {
|
||||
throw JsonConversionException(root = exception)
|
||||
}
|
||||
}
|
||||
// Guard, if the json is blank no item have been save, ignore this file.
|
||||
if (json.isBlank()) {
|
||||
return@mapNotNull null
|
||||
}
|
||||
// decode the file
|
||||
val data = try {
|
||||
this.json.decodeFromString<ItemJson>(json)
|
||||
} catch (exception: Exception) {
|
||||
throw JsonCodingException(root = exception)
|
||||
}
|
||||
// parse the json string.
|
||||
try {
|
||||
factory.convertFromJson(data)
|
||||
} catch (exception: Exception) {
|
||||
throw JsonConversionException(root = exception)
|
||||
}
|
||||
}
|
||||
?.sortedWith(compareBy(Collator.getInstance()) { it.metadata.label })
|
||||
?: emptyList()
|
||||
?.sortedWith(compareBy(Collator.getInstance()) { it.metadata.label })
|
||||
?: emptyList()
|
||||
}
|
||||
}
|
||||
|
||||
@Throws(
|
||||
BusinessException::class,
|
||||
JsonConversionException::class,
|
||||
JsonCodingException::class,
|
||||
FileWriteException::class,
|
||||
JsonCodingException::class,
|
||||
JsonConversionException::class,
|
||||
)
|
||||
fun save(
|
||||
suspend fun save(
|
||||
json: ItemJson,
|
||||
create: Boolean,
|
||||
) {
|
||||
val file = itemFile(id = json.id)
|
||||
// Guard case on update alteration
|
||||
if (create && file.exists()) {
|
||||
throw BusinessException(
|
||||
message = "Item already exist, creation is impossible.",
|
||||
)
|
||||
}
|
||||
// Transform the json into the model.
|
||||
val item = try {
|
||||
factory.convertFromJson(json)
|
||||
} catch (exception: Exception) {
|
||||
throw JsonConversionException(root = exception)
|
||||
}
|
||||
if (item.id.isEmpty()) {
|
||||
throw BusinessException(
|
||||
message = "Item 'id' is a mandatory field.",
|
||||
code = APIResponse.ErrorCode.ItemId,
|
||||
)
|
||||
}
|
||||
if (item.metadata.label.isEmpty()) {
|
||||
throw BusinessException(
|
||||
message = "Item 'name' is a mandatory field.",
|
||||
code = APIResponse.ErrorCode.ItemName,
|
||||
)
|
||||
}
|
||||
// Encode the json into a string.
|
||||
val data = try {
|
||||
this.json.encodeToString(json)
|
||||
} catch (exception: Exception) {
|
||||
throw JsonCodingException(root = exception)
|
||||
}
|
||||
// Write the alteration into a file.
|
||||
try {
|
||||
file.writeText(
|
||||
text = data,
|
||||
charset = Charsets.UTF_8,
|
||||
)
|
||||
} catch (exception: Exception) {
|
||||
throw FileWriteException(root = exception)
|
||||
}
|
||||
// Update the dataflow.
|
||||
itemFlow.update { items ->
|
||||
val index = items.indexOfFirst { it.id == json.id }
|
||||
items.toMutableList()
|
||||
.also {
|
||||
if (index >= 0) {
|
||||
it[index] = item
|
||||
} else {
|
||||
it.add(item)
|
||||
withContext(Dispatchers.IO) {
|
||||
val file = itemFile(id = json.id)
|
||||
// Guard case on update alteration
|
||||
if (create && file.exists()) {
|
||||
throw BusinessException(
|
||||
message = "Item already exist, creation is impossible.",
|
||||
)
|
||||
}
|
||||
// Transform the json into the model.
|
||||
val item = try {
|
||||
factory.convertFromJson(json)
|
||||
} catch (exception: Exception) {
|
||||
throw JsonConversionException(root = exception)
|
||||
}
|
||||
if (item.id.isEmpty()) {
|
||||
throw BusinessException(
|
||||
message = "Item 'id' is a mandatory field.",
|
||||
code = APIResponse.ErrorCode.ItemId,
|
||||
)
|
||||
}
|
||||
if (item.metadata.label.isEmpty()) {
|
||||
throw BusinessException(
|
||||
message = "Item 'name' is a mandatory field.",
|
||||
code = APIResponse.ErrorCode.ItemName,
|
||||
)
|
||||
}
|
||||
// Encode the json into a string.
|
||||
val data = try {
|
||||
jsonSerializer.encodeToString(json)
|
||||
} catch (exception: Exception) {
|
||||
throw JsonCodingException(root = exception)
|
||||
}
|
||||
// Write the alteration into a file.
|
||||
try {
|
||||
file.writeText(
|
||||
text = data,
|
||||
charset = Charsets.UTF_8,
|
||||
)
|
||||
} catch (exception: Exception) {
|
||||
throw FileWriteException(root = exception)
|
||||
}
|
||||
// Update the dataflow.
|
||||
itemFlow.update { items ->
|
||||
val index = items.indexOfFirst { it.id == json.id }
|
||||
items.toMutableList()
|
||||
.also {
|
||||
if (index >= 0) {
|
||||
it[index] = item
|
||||
} else {
|
||||
it.add(item)
|
||||
}
|
||||
}
|
||||
}
|
||||
.sortedWith(compareBy(Collator.getInstance()) {
|
||||
it.metadata.label
|
||||
})
|
||||
.sortedWith(compareBy(Collator.getInstance()) {
|
||||
it.metadata.label
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Throws(BusinessException::class)
|
||||
fun delete(id: String) {
|
||||
val file = itemFile(id = id)
|
||||
// Guard case on the file existence.
|
||||
if (file.exists().not()) {
|
||||
throw BusinessException(
|
||||
message = "Item doesn't not exist, deletion is impossible.",
|
||||
)
|
||||
}
|
||||
// Guard case on the file deletion
|
||||
if (file.delete().not()) {
|
||||
throw BusinessException(
|
||||
message = "Item file have not been deleted for unknown reason.",
|
||||
)
|
||||
}
|
||||
// Update the data model with the deleted alteration.
|
||||
itemFlow.update { items ->
|
||||
items.toMutableList()
|
||||
.also { item ->
|
||||
item.removeIf { it.id == id }
|
||||
}
|
||||
.sortedWith(compareBy(Collator.getInstance()) {
|
||||
it.metadata.label
|
||||
})
|
||||
suspend fun delete(id: String) {
|
||||
withContext(Dispatchers.IO) {
|
||||
val file = itemFile(id = id)
|
||||
// Guard case on the file existence.
|
||||
if (file.exists().not()) {
|
||||
throw BusinessException(
|
||||
message = "Item doesn't not exist, deletion is impossible.",
|
||||
)
|
||||
}
|
||||
// Guard case on the file deletion
|
||||
if (file.delete().not()) {
|
||||
throw BusinessException(
|
||||
message = "Item file have not been deleted for unknown reason.",
|
||||
)
|
||||
}
|
||||
// Update the data model with the deleted alteration.
|
||||
itemFlow.update { items ->
|
||||
items.toMutableList()
|
||||
.also { item ->
|
||||
item.removeIf { it.id == id }
|
||||
}
|
||||
.sortedWith(compareBy(Collator.getInstance()) {
|
||||
it.metadata.label
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -20,7 +20,8 @@ private const val ITEM = "item"
|
|||
|
||||
class TagStore(
|
||||
private val pathProvider: PathProvider,
|
||||
private val json: Json,
|
||||
private val jsonSerializer: Json,
|
||||
scope: CoroutineScope,
|
||||
) {
|
||||
private val alterationTagsFlow = MutableStateFlow<Map<String, TagJson>>(emptyMap())
|
||||
private val characterTagsFlow = MutableStateFlow<Map<String, TagJson>>(emptyMap())
|
||||
|
|
@ -29,8 +30,6 @@ class TagStore(
|
|||
init {
|
||||
// make the file path.
|
||||
File(pathProvider.tagsPath()).mkdirs()
|
||||
// build a coroutine scope for async calls
|
||||
val scope = CoroutineScope(Dispatchers.IO + Job())
|
||||
// load the initial data
|
||||
scope.launch {
|
||||
updateTagFlow()
|
||||
|
|
@ -41,7 +40,7 @@ class TagStore(
|
|||
fun characterTags(): StateFlow<Map<String, TagJson>> = characterTagsFlow
|
||||
fun itemTags(): StateFlow<Map<String, TagJson>> = itemTagsFlow
|
||||
|
||||
fun updateTagFlow() {
|
||||
suspend fun updateTagFlow() {
|
||||
update(
|
||||
flow = alterationTagsFlow,
|
||||
file = alterationFile(),
|
||||
|
|
@ -56,7 +55,7 @@ class TagStore(
|
|||
)
|
||||
}
|
||||
|
||||
private fun update(
|
||||
private suspend fun update(
|
||||
flow: MutableStateFlow<Map<String, TagJson>>,
|
||||
file: File,
|
||||
) {
|
||||
|
|
@ -69,7 +68,7 @@ class TagStore(
|
|||
}
|
||||
|
||||
@Throws(FileReadException::class, JsonConversionException::class)
|
||||
private fun File.readTags(): List<TagJson> {
|
||||
private suspend fun File.readTags(): List<TagJson> {
|
||||
// read the file (force the UTF8 format)
|
||||
val data = try {
|
||||
readText(charset = Charsets.UTF_8)
|
||||
|
|
@ -81,7 +80,7 @@ class TagStore(
|
|||
return emptyList()
|
||||
}
|
||||
return try {
|
||||
json.decodeFromString<List<TagJson>>(data)
|
||||
jsonSerializer.decodeFromString<List<TagJson>>(data)
|
||||
} catch (exception: Exception) {
|
||||
throw JsonConversionException(
|
||||
root = exception
|
||||
|
|
@ -90,10 +89,10 @@ class TagStore(
|
|||
}
|
||||
|
||||
@Throws(JsonConversionException::class, FileWriteException::class)
|
||||
private fun saveAlterationTags(tags: List<TagJson>) {
|
||||
private suspend fun saveAlterationTags(tags: List<TagJson>) {
|
||||
// convert the data to json format
|
||||
val json = try {
|
||||
this.json.encodeToString(tags)
|
||||
this.jsonSerializer.encodeToString(tags)
|
||||
} catch (exception: Exception) {
|
||||
throw JsonConversionException(root = exception)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue