22
33package org.alexn.hook
44
5+ import arrow.core.Either
6+ import com.charleskorn.kaml.Yaml
7+ import com.charleskorn.kaml.YamlConfiguration
58import kotlinx.cinterop.ExperimentalForeignApi
69import kotlinx.cinterop.toKString
710import kotlinx.serialization.ExperimentalSerializationApi
811import kotlinx.serialization.Serializable
9- import kotlinx.serialization.json.Json
1012import platform.posix.fclose
1113import platform.posix.fgets
1214import platform.posix.fopen
@@ -43,13 +45,13 @@ data class AppConfig(
4345
4446 companion object {
4547 @OptIn(ExperimentalForeignApi ::class )
46- fun parseFile (filePath : String ): Result < AppConfig > {
48+ fun parseFile (filePath : String ): Either < ConfigException , AppConfig > {
4749 val extension = filePath.substringAfterLast(' .' , " " ).lowercase()
4850
4951 val content = try {
5052 readFile(filePath)
5153 } catch (ex: Exception ) {
52- return Result . Error (
54+ return Either . Left (
5355 ConfigException (
5456 " Failed to read configuration file: $filePath " ,
5557 ex,
@@ -58,113 +60,40 @@ data class AppConfig(
5860 }
5961
6062 return when (extension) {
61- " json" -> parseJson(content)
62- " yaml" , " yml" -> {
63- // For now, we'll convert simple YAML to JSON
64- // Full YAML parsing would require a native YAML library
65- parseSimpleYaml(content)
66- }
63+ " yaml" , " yml" -> parseYaml(content)
6764 else ->
68- Result . Error (
65+ Either . Left (
6966 ConfigException (
70- " Unsupported configuration file format: $extension " ,
67+ " Unsupported configuration file format: $extension (only YAML/YML supported for native) " ,
7168 ),
7269 )
7370 }
7471 }
7572
76- fun parseJson (string : String ): Result < AppConfig > =
73+ fun parseYaml (string : String ): Either < ConfigException , AppConfig > =
7774 try {
78- val config = jsonParser.decodeFromString(
79- serializer(),
80- string,
81- )
82- Result .Success (config)
83- } catch (ex: Exception ) {
84- Result .Error (
85- ConfigException (
86- " Failed to parse JSON configuration" ,
87- ex,
75+ Either .Right (
76+ yamlParser.decodeFromString(
77+ serializer(),
78+ string,
8879 ),
8980 )
90- }
91-
92- // Simple YAML parser for basic configurations
93- // This is a simplified version that handles the basic structure
94- private fun parseSimpleYaml (yaml : String ): Result <AppConfig > {
95- try {
96- val lines = yaml.lines().filter { it.isNotBlank() && ! it.trim().startsWith(" #" ) }
97- val json = buildString {
98- append(" {" )
99- var inHttp = false
100- var inProjects = false
101- var currentProject: String? = null
102- var indent = 0
103-
104- for ((index, line) in lines.withIndex()) {
105- val trimmed = line.trim()
106- val currentIndent = line.takeWhile { it == ' ' }.length
107-
108- when {
109- trimmed.startsWith(" http:" ) -> {
110- if (index > 0 ) append(" ," )
111- append(" \" http\" :{" )
112- inHttp = true
113- inProjects = false
114- currentProject = null
115- }
116- trimmed.startsWith(" projects:" ) -> {
117- if (inHttp) append(" }" )
118- append(" ,\" projects\" :{" )
119- inHttp = false
120- inProjects = true
121- currentProject = null
122- }
123- inHttp && trimmed.contains(" :" ) -> {
124- val (key, value) = trimmed.split(" :" , limit = 2 )
125- val cleanValue = value.trim().trim(' "' )
126- if (trimmed != lines.first { it.contains(" http:" ) }) append(" ," )
127- append(" \" ${key.trim()} \" :${if (cleanValue.toIntOrNull() != null ) cleanValue else " \" $cleanValue \" " } " )
128- }
129- inProjects && currentIndent == 2 && trimmed.contains(" :" ) && ! trimmed.contains(" " ) -> {
130- // Project name
131- if (currentProject != null ) append(" }" )
132- val projectName = trimmed.removeSuffix(" :" )
133- if (currentProject != null ) append(" ," )
134- append(" \" $projectName \" :{" )
135- currentProject = projectName
136- }
137- currentProject != null && trimmed.contains(" :" ) -> {
138- // Project property
139- val (key, value) = trimmed.split(" :" , limit = 2 )
140- val cleanValue = value.trim().trim(' "' )
141- if (trimmed != lines.first { it.contains(" $currentProject :" ) }.let { lines.indexOf(it) + 1 }.let { if (it < lines.size) lines[it] else trimmed }) append(" ," )
142- append(" \" ${key.trim()} \" :\" $cleanValue \" " )
143- }
144- }
145- }
146- if (currentProject != null ) append(" }" )
147- if (inProjects) append(" }" )
148- append(" }" )
149- }
150-
151- return parseJson(json)
15281 } catch (ex: Exception ) {
153- return Result . Error (
82+ Either . Left (
15483 ConfigException (
15584 " Failed to parse YAML configuration" ,
15685 ex,
15786 ),
15887 )
15988 }
160- }
16189
162- private val jsonParser =
163- Json {
164- isLenient = true
165- ignoreUnknownKeys = true
166- explicitNulls = false
167- }
90+ private val yamlParser =
91+ Yaml (
92+ configuration =
93+ YamlConfiguration (
94+ strictMode = false ,
95+ ),
96+ )
16897
16998 @OptIn(ExperimentalForeignApi ::class )
17099 private fun readFile (path : String ): String {
@@ -193,9 +122,3 @@ class ConfigException(
193122 message : String ,
194123 cause : Throwable ? = null ,
195124) : Exception(message, cause)
196-
197- // Simple Result type to replace Arrow's Either
198- sealed class Result <out T > {
199- data class Success <T >(val value : T ) : Result<T>()
200- data class Error (val exception : Exception ) : Result<Nothing>()
201- }
0 commit comments