feat(zns-importer): add ZNSImportService with tests and REST controller

- Created `ZnsImportService` to handle uploading, parsing, and persisting ZNS data from legacy `.zip` files.
- Introduced corresponding test cases in `ZnsImportServiceTest` for handling edge cases including imports and updates.
- Added REST controller `ZnsImportController` for initiating import jobs and retrieving their status.
- Defined `ZnsImportResult` data structure for reporting results of import operations.
- Established database configuration specific to ZNS importer for development profile.
- Updated utility libraries with `FixedWidthLineReader` for fixed-width string parsing.
- Refactored architecture by placing parser logic in `core:zns-parser` for reuse across backend and Compose Desktop app.

Signed-off-by: Stefan Mogeritsch <stefan.mo.co@gmail.com>
This commit is contained in:
2026-03-25 14:43:01 +01:00
parent 4e8ed21ac0
commit 9d08cb0f72
21 changed files with 1653 additions and 22 deletions
@@ -0,0 +1,39 @@
package at.mocode.core.utils.parser
/**
* A simple utility to parse fixed-width strings based on 1-based start positions and lengths.
* This is particularly useful for parsing legacy data formats like the OePS ZNS formats.
*/
class FixedWidthLineReader(private val line: String) {
/**
* Extracts a string from the given 1-based start position with the given length.
* Trims leading and trailing whitespace.
* Returns an empty string if the position is out of bounds.
*/
fun getString(start1Based: Int, length: Int): String {
val start0Based = start1Based - 1
if (start0Based >= line.length) return ""
val end0Based = minOf(start0Based + length, line.length)
return line.substring(start0Based, end0Based).trim()
}
/**
* Extracts a string and parses it as a Long.
* Returns null if the field is empty or cannot be parsed.
*/
fun getLongOrNull(start1Based: Int, length: Int): Long? {
val str = getString(start1Based, length)
return str.toLongOrNull()
}
/**
* Extracts a string and parses it as an Int.
* Returns null if the field is empty or cannot be parsed.
*/
fun getIntOrNull(start1Based: Int, length: Int): Int? {
val str = getString(start1Based, length)
return str.toIntOrNull()
}
}