Narrow file I/O catches and chain parse-failure exception causes

Read/decode loaders now catch IOException and SerializationException
instead of Exception (matching readJsonOrNull); filename parsers pass
the original exception as the cause of the typed filename error.
This commit is contained in:
Wavesonics 2026-06-06 15:41:54 -07:00
parent 52dbde1daf
commit 8c15ab68fb
11 changed files with 58 additions and 23 deletions

View file

@ -35,8 +35,10 @@ import io.github.aakira.napier.Napier
import korlibs.datastructure.iterators.parallelMap
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.first
import kotlinx.serialization.SerializationException
import net.peanuuutz.tomlkt.Toml
import okio.FileSystem
import okio.IOException
import okio.Path.Companion.toPath
import org.koin.core.component.inject
import org.koin.core.parameter.parametersOf
@ -192,7 +194,11 @@ class ProjectsListComponent(
val path = projectDef.path.toOkioPath() / ProjectDataDatasource.FILENAME
return try {
fileSystem.readToml<StoredProjectData>(path, toml).data
} catch (e: Exception) {
} catch (e: IOException) {
Napier.w("Failed to read stored project data for ${projectDef.name}, using defaults", e)
StoredData()
} catch (e: SerializationException) {
Napier.w("Failed to read stored project data for ${projectDef.name}, using defaults", e)
StoredData()
}
}

View file

@ -12,6 +12,7 @@ import com.darkrockstudios.apps.hammer.common.fileio.HPath
import com.darkrockstudios.apps.hammer.common.fileio.okio.toHPath
import com.darkrockstudios.apps.hammer.common.fileio.okio.toOkioPath
import io.github.aakira.napier.Napier
import kotlinx.serialization.SerializationException
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.encodeToString
import net.peanuuutz.tomlkt.Toml
@ -136,7 +137,9 @@ class EncyclopediaDatasource(
val entry: EntryContainer = toml.decodeFromString(contentToml)
return entry
} catch (e: Exception) {
} catch (e: IOException) {
throw EntryLoadError(entryPath, e)
} catch (e: SerializationException) {
throw EntryLoadError(entryPath, e)
}
}
@ -295,9 +298,9 @@ class EncyclopediaDatasource(
val entryId = captures.groupValues[2].toInt()
return entryId
} catch (e: NumberFormatException) {
throw InvalidSceneFilename("Number format exception", fileName)
throw InvalidSceneFilename("Number format exception", fileName, e)
} catch (e: IllegalStateException) {
throw InvalidSceneFilename("Invalid filename", fileName)
throw InvalidSceneFilename("Invalid filename", fileName, e)
}
}
@ -319,11 +322,11 @@ class EncyclopediaDatasource(
)
return def
} catch (e: NumberFormatException) {
throw InvalidEntryFilename("Number format exception", fileName)
throw InvalidEntryFilename("Number format exception", fileName, e)
} catch (e: IllegalStateException) {
throw InvalidEntryFilename("Invalid filename", fileName)
throw InvalidEntryFilename("Invalid filename", fileName, e)
} catch (e: IllegalArgumentException) {
throw InvalidEntryFilename(e.message ?: "Invalid filename argument", fileName)
throw InvalidEntryFilename(e.message ?: "Invalid filename argument", fileName, cause = e)
}
}
@ -365,5 +368,5 @@ fun Sequence<HPath>.filterEntryPaths() = filter {
!it.name.startsWith(".") && ENTRY_FILENAME_PATTERN.matches(it.name)
}.sortedBy { it.name }
open class InvalidEntryFilename(message: String, fileName: String) :
IllegalStateException("$fileName failed to parse because: $message")
open class InvalidEntryFilename(message: String, fileName: String, cause: Throwable? = null) :
IllegalStateException("$fileName failed to parse because: $message", cause)

View file

@ -99,9 +99,9 @@ class NotesDatasource(
val sceneId = captures.groupValues[1].toInt()
return sceneId
} catch (e: NumberFormatException) {
throw InvalidSceneFilename("Number format exception", fileName)
throw InvalidSceneFilename("Number format exception", fileName, e)
} catch (e: IllegalStateException) {
throw InvalidSceneFilename("Invalid filename", fileName)
throw InvalidSceneFilename("Invalid filename", fileName, e)
}
}

View file

@ -13,6 +13,7 @@ import io.github.aakira.napier.Napier
import kotlinx.datetime.*
import okio.FileNotFoundException
import okio.FileSystem
import okio.IOException
import okio.Path
import org.koin.core.component.KoinComponent
import kotlin.time.Clock
@ -93,7 +94,7 @@ open class ProjectBackupRepository(
} else {
Napier.w("Backup file not found: ${backup.path.name}")
}
} catch (e: Exception) {
} catch (e: IOException) {
Napier.e("Failed to delete backup: ${backup.path.name}", e)
throw e
}

View file

@ -13,8 +13,10 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.IO
import kotlinx.coroutines.withContext
import kotlinx.serialization.Serializable
import kotlinx.serialization.SerializationException
import net.peanuuutz.tomlkt.Toml
import okio.FileSystem
import okio.IOException
import okio.Path
/**
@ -38,7 +40,10 @@ class ProjectDataDatasource(
if (!fileSystem.exists(path)) return@withContext StoredProjectData()
try {
fileSystem.readToml<StoredProjectData>(path, toml)
} catch (e: Exception) {
} catch (e: IOException) {
Napier.e("Failed to load project_data.toml: $path", e)
StoredProjectData()
} catch (e: SerializationException) {
Napier.e("Failed to load project_data.toml: $path", e)
StoredProjectData()
}

View file

@ -3,8 +3,10 @@ package com.darkrockstudios.apps.hammer.common.data.projectstatistics
import com.darkrockstudios.apps.hammer.base.http.readToml
import com.darkrockstudios.apps.hammer.common.data.ProjectDef
import io.github.aakira.napier.Napier
import kotlinx.serialization.SerializationException
import net.peanuuutz.tomlkt.Toml
import okio.FileSystem
import okio.IOException
/** Reads the cached stats file directly, without opening a ProjectDefScope. */
class ProjectStatisticsCacheReader(
@ -18,7 +20,10 @@ class ProjectStatisticsCacheReader(
val stats = try {
fileSystem.readToml<ProjectStatistics>(file, toml)
} catch (e: Exception) {
} catch (e: IOException) {
Napier.d("Failed to read statistics cache for ${projectDef.name}", e)
return null
} catch (e: SerializationException) {
Napier.d("Failed to read statistics cache for ${projectDef.name}", e)
return null
}

View file

@ -8,8 +8,10 @@ import com.darkrockstudios.apps.hammer.common.dependencyinjection.ProjectDefScop
import com.darkrockstudios.apps.hammer.common.dependencyinjection.injectIoDispatcher
import io.github.aakira.napier.Napier
import kotlinx.coroutines.withContext
import kotlinx.serialization.SerializationException
import net.peanuuutz.tomlkt.Toml
import okio.FileSystem
import okio.IOException
class StatisticsDatasource(
private val fileSystem: FileSystem,
@ -25,7 +27,10 @@ class StatisticsDatasource(
return@withContext if (fileSystem.exists(file)) {
try {
fileSystem.readToml(file, toml)
} catch (e: Exception) {
} catch (e: IOException) {
Napier.e("Failed to load statistics cache", e)
null
} catch (e: SerializationException) {
Napier.e("Failed to load statistics cache", e)
null
}

View file

@ -9,8 +9,10 @@ import com.darkrockstudios.apps.hammer.common.dependencyinjection.injectIoDispat
import com.darkrockstudios.apps.hammer.common.getCacheDirectory
import io.github.aakira.napier.Napier
import kotlinx.coroutines.withContext
import kotlinx.serialization.SerializationException
import net.peanuuutz.tomlkt.Toml
import okio.FileSystem
import okio.IOException
import okio.Path
import okio.Path.Companion.toPath
@ -28,7 +30,10 @@ class ReferenceIndexDatasource(
return@withContext if (fileSystem.exists(file)) {
try {
fileSystem.readToml(file, toml)
} catch (e: Exception) {
} catch (e: IOException) {
Napier.e("Failed to load reference index cache", e)
null
} catch (e: SerializationException) {
Napier.e("Failed to load reference index cache", e)
null
}

View file

@ -83,9 +83,9 @@ class SceneDatasource(
val sceneId = captures.groupValues[1].toInt()
return sceneId
} catch (e: NumberFormatException) {
throw InvalidSceneBufferFilename("Number format exception", fileName)
throw InvalidSceneBufferFilename("Number format exception", fileName, e)
} catch (e: IllegalStateException) {
throw InvalidSceneBufferFilename("Invalid filename", fileName)
throw InvalidSceneBufferFilename("Invalid filename", fileName, e)
}
}

View file

@ -1,7 +1,7 @@
package com.darkrockstudios.apps.hammer.common.data.sceneeditorrepository
open class InvalidSceneFilename(message: String, fileName: String) :
IllegalStateException("$fileName failed to parse because: $message")
open class InvalidSceneFilename(message: String, fileName: String, cause: Throwable? = null) :
IllegalStateException("$fileName failed to parse because: $message", cause)
class InvalidSceneBufferFilename(message: String, fileName: String) :
InvalidSceneFilename(message, fileName)
class InvalidSceneBufferFilename(message: String, fileName: String, cause: Throwable? = null) :
InvalidSceneFilename(message, fileName, cause)

View file

@ -11,8 +11,10 @@ import com.darkrockstudios.apps.hammer.common.dependencyinjection.injectIoDispat
import com.darkrockstudios.apps.hammer.common.fileio.okio.toOkioPath
import io.github.aakira.napier.Napier
import kotlinx.coroutines.withContext
import kotlinx.serialization.SerializationException
import net.peanuuutz.tomlkt.Toml
import okio.FileSystem
import okio.IOException
import okio.Path
/**
@ -44,7 +46,10 @@ class WritingActivityDatasource(
if (!fileSystem.exists(path)) return@withContext null
try {
fileSystem.readToml(path, toml)
} catch (e: Exception) {
} catch (e: IOException) {
Napier.e("Failed to load writing activity log: $path", e)
null
} catch (e: SerializationException) {
Napier.e("Failed to load writing activity log: $path", e)
null
}