diff --git a/kson-http/build.gradle.kts b/kson-http/build.gradle.kts new file mode 100644 index 00000000..d33cb1f7 --- /dev/null +++ b/kson-http/build.gradle.kts @@ -0,0 +1,28 @@ +plugins { + kotlin("multiplatform") + kotlin("plugin.serialization") +} + +repositories { + mavenCentral() +} + +val ktorVersion = "3.4.0" + +kotlin { + jvm { + } + + sourceSets { + commonMain { + dependencies { + implementation(project(":kson-service-api")) + + implementation("io.ktor:ktor-client-core:$ktorVersion") + implementation("io.ktor:ktor-client-cio:$ktorVersion") + implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.8.1") + } + } + } +} + diff --git a/kson-http/kson-api-schema.json b/kson-http/kson-api-schema.json new file mode 100644 index 00000000..02cddad2 --- /dev/null +++ b/kson-http/kson-api-schema.json @@ -0,0 +1,595 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://kson.org/api/schema", + "title": "KSON API Schema", + "description": "Schema for the KSON public API, supporting format, toJson, toYaml, analyze, parseSchema, validate, and validateEmbedRule commands.", + + "$defs": { + "Position": { + "type": "object", + "description": "A zero-based line/column position in a document.", + "properties": { + "line": { + "type": "integer", + "minimum": 0, + "description": "The line number (0-based)" + }, + "column": { + "type": "integer", + "minimum": 0, + "description": "The column number (0-based)" + } + }, + "required": ["line", "column"], + "additionalProperties": false + }, + + "Message": { + "type": "object", + "description": "A message logged during KSON processing.", + "properties": { + "message": { + "type": "string" + }, + "severity": { + "$ref": "#/$defs/MessageSeverity" + }, + "start": { + "$ref": "#/$defs/Position" + }, + "end": { + "$ref": "#/$defs/Position" + } + }, + "required": ["message", "severity", "start", "end"], + "additionalProperties": false + }, + + "MessageSeverity": { + "type": "string", + "enum": ["ERROR", "WARNING"] + }, + + "IndentType": { + "oneOf": [ + { + "type": "object", + "description": "Use spaces for indentation.", + "properties": { + "type": { "const": "spaces" }, + "size": { + "type": "integer", + "minimum": 1, + "default": 2, + "description": "Number of spaces per indent level" + } + }, + "required": ["type"], + "additionalProperties": false + }, + { + "type": "object", + "description": "Use tabs for indentation.", + "properties": { + "type": { "const": "tabs" } + }, + "required": ["type"], + "additionalProperties": false + } + ] + }, + + "FormattingStyle": { + "type": "string", + "enum": ["PLAIN", "DELIMITED", "COMPACT", "CLASSIC"] + }, + + "EmbedRule": { + "type": "object", + "description": "A rule for formatting string values at specific paths as embed blocks.", + "properties": { + "pathPattern": { + "type": "string", + "description": "A JsonPointerGlob pattern (e.g., \"/scripts/*\", \"/queries/**\")" + }, + "tag": { + "type": ["string", "null"], + "description": "Optional embed tag (e.g., \"yaml\", \"sql\", \"bash\")" + } + }, + "required": ["pathPattern"], + "additionalProperties": false + }, + + "FormatOptions": { + "type": "object", + "description": "Options for formatting KSON output.", + "properties": { + "indentType": { + "$ref": "#/$defs/IndentType" + }, + "formattingStyle": { + "$ref": "#/$defs/FormattingStyle" + }, + "embedBlockRules": { + "type": "array", + "items": { "$ref": "#/$defs/EmbedRule" } + } + }, + "additionalProperties": false + }, + + "TokenType": { + "type": "string", + "enum": [ + "CURLY_BRACE_L", + "CURLY_BRACE_R", + "SQUARE_BRACKET_L", + "SQUARE_BRACKET_R", + "ANGLE_BRACKET_L", + "ANGLE_BRACKET_R", + "COLON", + "DOT", + "END_DASH", + "COMMA", + "COMMENT", + "EMBED_OPEN_DELIM", + "EMBED_CLOSE_DELIM", + "EMBED_TAG", + "EMBED_PREAMBLE_NEWLINE", + "EMBED_CONTENT", + "FALSE", + "UNQUOTED_STRING", + "ILLEGAL_CHAR", + "LIST_DASH", + "NULL", + "NUMBER", + "STRING_OPEN_QUOTE", + "STRING_CLOSE_QUOTE", + "STRING_CONTENT", + "TRUE", + "WHITESPACE", + "EOF" + ] + }, + + "Token": { + "type": "object", + "description": "A token produced by the lexing phase.", + "properties": { + "tokenType": { "$ref": "#/$defs/TokenType" }, + "text": { "type": "string" }, + "start": { "$ref": "#/$defs/Position" }, + "end": { "$ref": "#/$defs/Position" } + }, + "required": ["tokenType", "text", "start", "end"], + "additionalProperties": false + }, + + "KsonValue": { + "oneOf": [ + { "$ref": "#/$defs/KsonObject" }, + { "$ref": "#/$defs/KsonArray" }, + { "$ref": "#/$defs/KsonString" }, + { "$ref": "#/$defs/KsonInteger" }, + { "$ref": "#/$defs/KsonDecimal" }, + { "$ref": "#/$defs/KsonBoolean" }, + { "$ref": "#/$defs/KsonNull" }, + { "$ref": "#/$defs/KsonEmbed" } + ] + }, + + "KsonObject": { + "type": "object", + "properties": { + "type": { "const": "OBJECT" }, + "properties": { + "type": "object", + "additionalProperties": { "$ref": "#/$defs/KsonValue" }, + "description": "Key-value pairs of the object" + }, + "propertyKeys": { + "type": "object", + "additionalProperties": { "$ref": "#/$defs/KsonString" }, + "description": "Property key metadata (including positions)" + }, + "start": { "$ref": "#/$defs/Position" }, + "end": { "$ref": "#/$defs/Position" } + }, + "required": ["type", "properties", "propertyKeys", "start", "end"], + "additionalProperties": false + }, + + "KsonArray": { + "type": "object", + "properties": { + "type": { "const": "ARRAY" }, + "elements": { + "type": "array", + "items": { "$ref": "#/$defs/KsonValue" } + }, + "start": { "$ref": "#/$defs/Position" }, + "end": { "$ref": "#/$defs/Position" } + }, + "required": ["type", "elements", "start", "end"], + "additionalProperties": false + }, + + "KsonString": { + "type": "object", + "properties": { + "type": { "const": "STRING" }, + "value": { "type": "string" }, + "start": { "$ref": "#/$defs/Position" }, + "end": { "$ref": "#/$defs/Position" } + }, + "required": ["type", "value", "start", "end"], + "additionalProperties": false + }, + + "KsonInteger": { + "type": "object", + "properties": { + "type": { "const": "INTEGER" }, + "value": { "type": "integer" }, + "start": { "$ref": "#/$defs/Position" }, + "end": { "$ref": "#/$defs/Position" } + }, + "required": ["type", "value", "start", "end"], + "additionalProperties": false + }, + + "KsonDecimal": { + "type": "object", + "properties": { + "type": { "const": "DECIMAL" }, + "value": { "type": "number" }, + "start": { "$ref": "#/$defs/Position" }, + "end": { "$ref": "#/$defs/Position" } + }, + "required": ["type", "value", "start", "end"], + "additionalProperties": false + }, + + "KsonBoolean": { + "type": "object", + "properties": { + "type": { "const": "BOOLEAN" }, + "value": { "type": "boolean" }, + "start": { "$ref": "#/$defs/Position" }, + "end": { "$ref": "#/$defs/Position" } + }, + "required": ["type", "value", "start", "end"], + "additionalProperties": false + }, + + "KsonNull": { + "type": "object", + "properties": { + "type": { "const": "NULL" }, + "start": { "$ref": "#/$defs/Position" }, + "end": { "$ref": "#/$defs/Position" } + }, + "required": ["type", "start", "end"], + "additionalProperties": false + }, + + "KsonEmbed": { + "type": "object", + "properties": { + "type": { "const": "EMBED" }, + "tag": { + "type": ["string", "null"], + "description": "Optional embed tag" + }, + "content": { "type": "string" }, + "start": { "$ref": "#/$defs/Position" }, + "end": { "$ref": "#/$defs/Position" } + }, + "required": ["type", "content", "start", "end"], + "additionalProperties": false + }, + + "FormatCommand": { + "type": "object", + "description": "Formats KSON source with the specified formatting options.", + "properties": { + "command": { "const": "format" }, + "kson": { + "type": "string", + "description": "The KSON source to format" + }, + "formatOptions": { + "$ref": "#/$defs/FormatOptions" + } + }, + "required": ["command", "kson"], + "additionalProperties": false + }, + + "ToJsonCommand": { + "type": "object", + "description": "Converts KSON to JSON.", + "properties": { + "command": { "const": "toJson" }, + "kson": { + "type": "string", + "description": "The KSON source to convert" + }, + "retainEmbedTags": { + "type": "boolean", + "default": true, + "description": "Whether to retain embed tags in the JSON output" + } + }, + "required": ["command", "kson"], + "additionalProperties": false + }, + + "ToYamlCommand": { + "type": "object", + "description": "Converts KSON to YAML, preserving comments.", + "properties": { + "command": { "const": "toYaml" }, + "kson": { + "type": "string", + "description": "The KSON source to convert" + }, + "retainEmbedTags": { + "type": "boolean", + "default": true, + "description": "Whether to retain embed tags in the YAML output" + } + }, + "required": ["command", "kson"], + "additionalProperties": false + }, + + "AnalyzeCommand": { + "type": "object", + "description": "Statically analyze KSON and return messages, tokens, and parsed value.", + "properties": { + "command": { "const": "analyze" }, + "kson": { + "type": "string", + "description": "The KSON source to analyze" + }, + "filepath": { + "type": ["string", "null"], + "description": "Optional filepath of the document being analyzed" + } + }, + "required": ["command", "kson"], + "additionalProperties": false + }, + + "ParseSchemaCommand": { + "type": "object", + "description": "Parses a KSON schema definition.", + "properties": { + "command": { "const": "parseSchema" }, + "schemaKson": { + "type": "string", + "description": "The KSON source defining a JSON Schema" + } + }, + "required": ["command", "schemaKson"], + "additionalProperties": false + }, + + "ValidateCommand": { + "type": "object", + "description": "Parses a schema and validates a KSON document against it in a single step.", + "properties": { + "command": { "const": "validate" }, + "schemaKson": { + "type": "string", + "description": "The KSON source defining a JSON Schema" + }, + "kson": { + "type": "string", + "description": "The KSON source to validate against the schema" + }, + "filepath": { + "type": ["string", "null"], + "description": "Optional filepath of the document being validated" + } + }, + "required": ["command", "schemaKson", "kson"], + "additionalProperties": false + }, + + "ValidateEmbedRuleCommand": { + "type": "object", + "description": "Validates that the given EmbedRule has a valid JsonPointerGlob pathPattern.", + "properties": { + "command": { "const": "validateEmbedRule" }, + "embedBlockRule": { + "$ref": "#/$defs/EmbedRule", + "description": "The embed rule to validate" + } + }, + "required": ["command", "embedBlockRule"], + "additionalProperties": false + }, + + "FormatResult": { + "type": "object", + "description": "Result of the format command.", + "properties": { + "command": { "const": "format" }, + "success": { "const": true }, + "output": { + "type": "string", + "description": "The formatted KSON source" + } + }, + "required": ["command", "success", "output"], + "additionalProperties": false + }, + + "TranspileSuccessResult": { + "type": "object", + "properties": { + "command": { + "type": "string", + "enum": ["toJson", "toYaml"] + }, + "success": { "const": true }, + "output": { + "type": "string", + "description": "The transpiled output (JSON or YAML)" + } + }, + "required": ["command", "success", "output"], + "additionalProperties": false + }, + + "TranspileFailureResult": { + "type": "object", + "properties": { + "command": { + "type": "string", + "enum": ["toJson", "toYaml"] + }, + "success": { "const": false }, + "errors": { + "type": "array", + "items": { "$ref": "#/$defs/Message" } + } + }, + "required": ["command", "success", "errors"], + "additionalProperties": false + }, + + "AnalyzeResult": { + "type": "object", + "description": "Result of the analyze command.", + "properties": { + "command": { "const": "analyze" }, + "errors": { + "type": "array", + "items": { "$ref": "#/$defs/Message" } + }, + "tokens": { + "type": "array", + "items": { "$ref": "#/$defs/Token" } + }, + "ksonValue": { + "oneOf": [ + { "$ref": "#/$defs/KsonValue" }, + { "type": "null" } + ] + } + }, + "required": ["command", "errors", "tokens", "ksonValue"], + "additionalProperties": false + }, + + "ParseSchemaSuccessResult": { + "type": "object", + "properties": { + "command": { "const": "parseSchema" }, + "success": { "const": true } + }, + "required": ["command", "success"], + "additionalProperties": false + }, + + "ParseSchemaFailureResult": { + "type": "object", + "properties": { + "command": { "const": "parseSchema" }, + "success": { "const": false }, + "errors": { + "type": "array", + "items": { "$ref": "#/$defs/Message" } + } + }, + "required": ["command", "success", "errors"], + "additionalProperties": false + }, + + "ValidateResult": { + "type": "object", + "description": "Result of the validate command.", + "properties": { + "command": { "const": "validate" }, + "success": { + "type": "boolean", + "description": "True if both schema parsing and validation succeeded with no errors" + }, + "errors": { + "type": "array", + "items": { "$ref": "#/$defs/Message" }, + "description": "Schema parsing errors or validation errors" + } + }, + "required": ["command", "success", "errors"], + "additionalProperties": false + }, + + "EmbedRuleSuccessResult": { + "type": "object", + "description": "Successful result of the validateEmbedRule command.", + "properties": { + "command": { "const": "validateEmbedRule" }, + "success": { "const": true } + }, + "required": ["command", "success"], + "additionalProperties": false + }, + + "EmbedRuleFailureResult": { + "type": "object", + "description": "Failed result of the validateEmbedRule command.", + "properties": { + "command": { "const": "validateEmbedRule" }, + "success": { "const": false }, + "error": { + "type": "string", + "description": "Error message for the invalid pathPattern" + } + }, + "required": ["command", "success", "error"], + "additionalProperties": false + } + }, + + "type": "object", + "properties": { + "request": { + "description": "A KSON API command request.", + "oneOf": [ + { "$ref": "#/$defs/FormatCommand" }, + { "$ref": "#/$defs/ToJsonCommand" }, + { "$ref": "#/$defs/ToYamlCommand" }, + { "$ref": "#/$defs/AnalyzeCommand" }, + { "$ref": "#/$defs/ParseSchemaCommand" }, + { "$ref": "#/$defs/ValidateCommand" }, + { "$ref": "#/$defs/ValidateEmbedRuleCommand" } + ], + "discriminator": { + "propertyName": "command" + } + }, + "response": { + "description": "A KSON API command response.", + "oneOf": [ + { "$ref": "#/$defs/FormatResult" }, + { "$ref": "#/$defs/TranspileSuccessResult" }, + { "$ref": "#/$defs/TranspileFailureResult" }, + { "$ref": "#/$defs/AnalyzeResult" }, + { "$ref": "#/$defs/ParseSchemaSuccessResult" }, + { "$ref": "#/$defs/ParseSchemaFailureResult" }, + { "$ref": "#/$defs/ValidateResult" }, + { "$ref": "#/$defs/EmbedRuleSuccessResult" }, + { "$ref": "#/$defs/EmbedRuleFailureResult" } + ], + "discriminator": { + "propertyName": "command" + } + } + }, + "additionalProperties": false +} diff --git a/kson-http/readme.md b/kson-http/readme.md new file mode 100644 index 00000000..bc6f59a2 --- /dev/null +++ b/kson-http/readme.md @@ -0,0 +1,24 @@ +# kson-http + +A reimplementation of KSON's public API (see [kson-lib](../kson-lib)) based on +HTTP requests to a server that respects the [kson-api-schema](./kson-api-schema.json). +This code is meant to facilitate testing, so users of KSON should ignore it +and rely on `kson-lib` for all their KSON needs. We might implement a +user-facing, HTTP-based KSON API in the future. + +> [!WARNING] +> The JSON Schema is highly experimental, possibly inaccurate in some places, and +> subject to change without notice + +### Testing KSON implementations + +If you have an implementation of the KSON public API that you would like to +test, all you need to do is create an HTTP server that handles KSON requests +according to the [schema](./kson-api-schema.json) and internally dispatches +them to your actual KSON implementation. This sounds complicated, but a good +LLM can easily one-shot server creation based on the schema and your +implementation's source code. + +With that in place, you then need to instruct Gradle to run the `kson-lib` +tests against your server. For reference, see the [test project](../lib-python/kson-lib-tests/) +we use to run the `kson-lib` tests against our Python bindings. diff --git a/kson-http/src/commonMain/kotlin/org/kson/Kson.kt b/kson-http/src/commonMain/kotlin/org/kson/Kson.kt new file mode 100644 index 00000000..82eed8a1 --- /dev/null +++ b/kson-http/src/commonMain/kotlin/org/kson/Kson.kt @@ -0,0 +1,201 @@ +package org.kson + +import io.ktor.client.* +import io.ktor.client.engine.cio.* +import io.ktor.client.request.* +import io.ktor.client.statement.* +import io.ktor.http.* +import kotlinx.coroutines.runBlocking +import kotlinx.serialization.json.* +import org.kson.api.* + +object Kson : KsonService { + private val client = HttpClient(CIO) + private val json = Json { ignoreUnknownKeys = true } + private var baseUrl = "http://localhost:8080" + + fun setPort(port: Int) { + baseUrl = "http://localhost:$port" + } + + override fun format(kson: String, formatOptions: FormatOptions): String { + val request = buildJsonObject { + put("command", "format") + put("kson", kson) + put("formatOptions", formatOptions.toJson()) + } + val response = post(request) + return response["output"]!!.jsonPrimitive.content + } + + + override fun toJson(kson: String, options: TranspileOptions.Json): Result { + val request = buildJsonObject { + put("command", "toJson") + put("kson", kson) + put("retainEmbedTags", options.retainEmbedTags) + } + return parseTranspileResult(post(request)) + } + + + override fun toYaml(kson: String, options: TranspileOptions.Yaml): Result { + val request = buildJsonObject { + put("command", "toYaml") + put("kson", kson) + put("retainEmbedTags", options.retainEmbedTags) + } + return parseTranspileResult(post(request)) + } + + + override fun analyze(kson: String, filepath: String?): Analysis { + val request = buildJsonObject { + put("command", "analyze") + put("kson", kson) + filepath?.let { put("filepath", it) } + } + val response = post(request) + val errors = parseMessages(response["errors"]!!.jsonArray) + val tokens = parseTokens(response["tokens"]!!.jsonArray) + val ksonValue = response["ksonValue"]?.let { + if (it is JsonNull) null else parseKsonValue(it.jsonObject) + } + return Analysis(errors, tokens, ksonValue) + } + + override fun parseSchema(schemaKson: String): SchemaResult { + val request = buildJsonObject { + put("command", "parseSchema") + put("schemaKson", schemaKson) + } + val response = post(request) + val success = response["success"]!!.jsonPrimitive.boolean + return if (success) { + SchemaResult.Success(SchemaValidator(schemaKson)) + } else { + SchemaResult.Failure(parseMessages(response["errors"]!!.jsonArray)) + } + } + + internal fun post(body: JsonObject): JsonObject = runBlocking { + val response = client.post(baseUrl) { + contentType(ContentType.Application.Json) + setBody(body.toString()) + } + json.parseToJsonElement(response.bodyAsText()).jsonObject + } + + private fun parseTranspileResult(response: JsonObject): Result { + val success = response["success"]!!.jsonPrimitive.boolean + return if (success) { + Result.Success(response["output"]!!.jsonPrimitive.content) + } else { + Result.Failure(parseMessages(response["errors"]!!.jsonArray)) + } + } +} + +class SchemaValidator internal constructor(private val schemaKson: String) : SchemaValidatorService { + + override fun validate(kson: String, filepath: String?): List { + val request = buildJsonObject { + put("command", "validate") + put("schemaKson", schemaKson) + put("kson", kson) + filepath?.let { put("filepath", it) } + } + val response = Kson.post(request) + return parseMessages(response["errors"]!!.jsonArray) + } +} + +internal fun FormatOptions.toJson(): JsonObject = buildJsonObject { + put("indentType", when (indentType) { + is IndentType.Spaces -> buildJsonObject { + put("type", "spaces") + put("size", (indentType as IndentType.Spaces).size) + } + is IndentType.Tabs -> buildJsonObject { + put("type", "tabs") + } + }) + put("formattingStyle", formattingStyle.name) + put("embedBlockRules", buildJsonArray { + for (rule in embedBlockRules) { + add(buildJsonObject { + put("pathPattern", rule.pathPattern) + rule.tag?.let { put("tag", it) } + }) + } + }) +} + +// --- JSON response parsing helpers --- + +internal fun parsePosition(obj: JsonObject): Position { + return Position( + line = obj["line"]!!.jsonPrimitive.int, + column = obj["column"]!!.jsonPrimitive.int + ) +} + +internal fun parseMessages(array: JsonArray): List { + return array.map { element -> + val obj = element.jsonObject + Message( + message = obj["message"]!!.jsonPrimitive.content, + severity = MessageSeverity.valueOf(obj["severity"]!!.jsonPrimitive.content), + start = parsePosition(obj["start"]!!.jsonObject), + end = parsePosition(obj["end"]!!.jsonObject) + ) + } +} + +internal fun parseTokens(array: JsonArray): List { + return array.map { element -> + val obj = element.jsonObject + Token( + tokenType = TokenType.valueOf(obj["tokenType"]!!.jsonPrimitive.content), + text = obj["text"]!!.jsonPrimitive.content, + start = parsePosition(obj["start"]!!.jsonObject), + end = parsePosition(obj["end"]!!.jsonObject) + ) + } +} + +internal fun parseKsonValue(obj: JsonObject): KsonValue { + val start = parsePosition(obj["start"]!!.jsonObject) + val end = parsePosition(obj["end"]!!.jsonObject) + + return when (val type = obj["type"]!!.jsonPrimitive.content) { + "OBJECT" -> { + val properties = obj["properties"]!!.jsonObject.mapValues { (_, v) -> parseKsonValue(v.jsonObject) } + val propertyKeys = obj["propertyKeys"]!!.jsonObject.mapValues { (_, v) -> + val keyObj = v.jsonObject + KsonValue.KsonString( + value = keyObj["value"]!!.jsonPrimitive.content, + internalStart = parsePosition(keyObj["start"]!!.jsonObject), + internalEnd = parsePosition(keyObj["end"]!!.jsonObject) + ) + } + KsonValue.KsonObject(properties, propertyKeys, start, end) + } + "ARRAY" -> { + val elements = obj["elements"]!!.jsonArray.map { parseKsonValue(it.jsonObject) } + KsonValue.KsonArray(elements, start, end) + } + "STRING" -> KsonValue.KsonString(obj["value"]!!.jsonPrimitive.content, start, end) + "INTEGER" -> KsonValue.KsonNumber.Integer(obj["value"]!!.jsonPrimitive.int, start, end) + "DECIMAL" -> KsonValue.KsonNumber.Decimal(obj["value"]!!.jsonPrimitive.double, start, end) + "BOOLEAN" -> KsonValue.KsonBoolean(obj["value"]!!.jsonPrimitive.boolean, start, end) + "NULL" -> KsonValue.KsonNull(start, end) + "EMBED" -> KsonValue.KsonEmbed( + tag = obj["tag"]?.let { if (it is JsonNull) null else it.jsonPrimitive.content }, + content = obj["content"]!!.jsonPrimitive.content, + internalStart = start, + internalEnd = end + ) + else -> throw IllegalArgumentException("Unknown KsonValue type: $type") + } +} diff --git a/kson-lib/build.gradle.kts b/kson-lib/build.gradle.kts index 26d76bcf..25efb263 100644 --- a/kson-lib/build.gradle.kts +++ b/kson-lib/build.gradle.kts @@ -10,7 +10,7 @@ plugins { kotlin("multiplatform") id("com.vanniktech.maven.publish") version "0.30.0" id("org.jetbrains.dokka") version "2.0.0" - id("nl.ochagavia.krossover") version "1.0.5" + id("nl.ochagavia.krossover") version "1.0.7" } repositories { @@ -40,13 +40,24 @@ kotlin { val commonMain by getting { dependencies { implementation(project(":")) + implementation(project(":kson-service-api")) } } val commonTest by getting { + dependencies { + implementation(project(":kson-service-tests")) + } + } + val jsTest by getting { dependencies { implementation(kotlin("test")) } } + val jvmTest by getting { + dependencies { + implementation(kotlin("test-junit5")) + } + } } } @@ -65,8 +76,8 @@ krossover { jniSysModule = "kson_sys" outputDir = Path("${rootProject.projectDir}/lib-rust/kson/src/generated") returnTypeMappings = listOf( - ReturnTypeMapping("org.kson.Result", "std::result::Result", "crate::kson_result_into_rust_result"), - ReturnTypeMapping("org.kson.SchemaResult", "std::result::Result", "crate::kson_schema_result_into_rust_result") + ReturnTypeMapping("org.kson.api.Result", "std::result::Result", "crate::kson_result_into_rust_result"), + ReturnTypeMapping("org.kson.api.SchemaResult", "std::result::Result", "crate::kson_schema_result_into_rust_result") ) } } @@ -114,7 +125,7 @@ tasks.register("copyNodeDistribution") { * * This task is used when you need to prepare the production library with its dependencies * installed. - * + * * This will: * 1. Build the production library (via jsNodeProductionLibraryDistribution) * 2. Run 'npm install' in build/dist/js/productionLibrary diff --git a/kson-lib/src/commonMain/kotlin/org/kson/Kson.kt b/kson-lib/src/commonMain/kotlin/org/kson/Kson.kt index 58f20f13..af29c108 100644 --- a/kson-lib/src/commonMain/kotlin/org/kson/Kson.kt +++ b/kson-lib/src/commonMain/kotlin/org/kson/Kson.kt @@ -5,18 +5,19 @@ package org.kson import org.kson.Kson.parseSchema import org.kson.Kson.publishMessages +import org.kson.api.* import org.kson.parser.* import org.kson.parser.messages.MessageSeverity as InternalMessageSeverity import org.kson.schema.JsonSchema -import org.kson.tools.InternalEmbedRule -import org.kson.tools.FormattingStyle as InternalFormattingStyle import org.kson.tools.IndentType as InternalIndentType +import org.kson.tools.FormattingStyle as InternalFormattingStyle +import org.kson.tools.InternalEmbedRule import org.kson.tools.KsonFormatterConfig import org.kson.value.navigation.json_pointer.ExperimentalJsonPointerGlobLanguage import org.kson.value.navigation.json_pointer.JsonPointerGlob import org.kson.parser.TokenType as InternalTokenType import org.kson.parser.Token as InternalToken -import org.kson.validation.SourceContext +import org.kson.validation.SourceContext as InternalSourceContext import org.kson.value.KsonValue as InternalKsonValue import org.kson.value.KsonObject as InternalKsonObject import org.kson.value.KsonList as InternalKsonList @@ -30,7 +31,7 @@ import kotlin.js.JsExport /** * The [Kson](https://kson.org) language */ -object Kson { +object Kson : KsonService { /** * Formats Kson source with the specified formatting options. * @@ -38,7 +39,7 @@ object Kson { * @param formatOptions The formatting options to apply * @return The formatted Kson source */ - fun format(kson: String, formatOptions: FormatOptions = FormatOptions()): String { + override fun format(kson: String, formatOptions: FormatOptions): String { return org.kson.tools.format(kson, formatOptions.toInternal()) } @@ -49,7 +50,7 @@ object Kson { * @param options Options for the JSON transpilation * @return A Result containing either the Json output or error messages */ - fun toJson(kson: String, options: TranspileOptions.Json = TranspileOptions.Json()): Result { + override fun toJson(kson: String, options: TranspileOptions.Json): Result { val compileConfig = Json( retainEmbedTags = options.retainEmbedTags, ) @@ -68,7 +69,7 @@ object Kson { * @param options Options for the YAML transpilation * @return A Result containing either the Yaml output or error messages */ - fun toYaml(kson: String, options: TranspileOptions.Yaml = TranspileOptions.Yaml()): Result { + override fun toYaml(kson: String, options: TranspileOptions.Yaml): Result { val compileConfig = CompileTarget.Yaml( retainEmbedTags = options.retainEmbedTags, ) @@ -86,10 +87,10 @@ object Kson { * @param kson The Kson source to analyze * @param filepath Filepath of the document being analyzed */ - fun analyze(kson: String, filepath: String? = null) : Analysis { + override fun analyze(kson: String, filepath: String?) : Analysis { val parseResult = KsonCore.parseToAst( kson, - CoreCompileConfig(sourceContext = SourceContext(filepath)) + CoreCompileConfig(sourceContext = InternalSourceContext(filepath)) ) val tokens = convertTokens(parseResult.lexedTokens) val messages = publishMessages(parseResult.messages) @@ -103,7 +104,7 @@ object Kson { * @param schemaKson The Kson source defining a Json Schema * @return A SchemaValidator that can validate Kson documents against the schema */ - fun parseSchema(schemaKson: String): SchemaResult { + override fun parseSchema(schemaKson: String): SchemaResult { val schemaParseResult = KsonCore.parseSchema(schemaKson) val messages = publishMessages(schemaParseResult.messages) val jsonSchema = schemaParseResult.jsonSchema @@ -129,34 +130,17 @@ object Kson { Message( message = it.message.toString(), severity = severity, - start = Position(it.location.start), - end = Position(it.location.end) + start = it.location.start.toPosition(), + end = it.location.end.toPosition() ) } } } - -/** - * Result of a Kson conversion operation - */ -sealed class Result { - class Success(val output: String) : Result() - class Failure(val errors: List) : Result() -} - -/** - * A [parseSchema] result - */ -sealed class SchemaResult { - class Success(val schemaValidator: SchemaValidator) : SchemaResult() - class Failure(val errors: List) : SchemaResult() -} - /** * A validator that can check if Kson source conforms to a schema. */ -class SchemaValidator internal constructor(private val schema: JsonSchema) { +class SchemaValidator internal constructor(private val schema: JsonSchema) : SchemaValidatorService { /** * Validates the given Kson source against this validator's schema. * @param kson The Kson source to validate @@ -164,10 +148,10 @@ class SchemaValidator internal constructor(private val schema: JsonSchema) { * * @return A list of validation error messages, or empty list if valid */ - fun validate(kson: String, filepath: String? = null): List { + override fun validate(kson: String, filepath: String?): List { val astParseResult = KsonCore.parseToAst( kson, - CoreCompileConfig(sourceContext = SourceContext(filepath)) + CoreCompileConfig(sourceContext = InternalSourceContext(filepath)) ) if (astParseResult.hasErrors()) { return publishMessages(astParseResult.messages) @@ -176,7 +160,7 @@ class SchemaValidator internal constructor(private val schema: JsonSchema) { val messageSink = MessageSink() val ksonValue = astParseResult.ksonValue if (ksonValue != null) { - schema.validate(ksonValue, messageSink, SourceContext(filepath)) + schema.validate(ksonValue, messageSink, InternalSourceContext(filepath)) } return publishMessages(messageSink.loggedMessages()) @@ -184,211 +168,46 @@ class SchemaValidator internal constructor(private val schema: JsonSchema) { } /** - * A rule for formatting string values at specific paths as embed blocks. - * - * When formatting KSON, strings at paths matching [pathPattern] will be rendered - * as embed blocks instead of regular strings. + * Map [FormatOptions] to [org.kson.tools.KsonFormatterConfig] that is used internally to format a Kson document. * - * **Warning:** JsonPointerGlob syntax is experimental and may change in future versions. + * @throws IllegalArgumentException when one or more embed rules have an invalid [JsonPointerGlob] */ -class EmbedRule private constructor( - val pathPattern: String, - val tag: String? = null -) { - @OptIn(ExperimentalJsonPointerGlobLanguage::class) - internal val parsedPathPattern: JsonPointerGlob = JsonPointerGlob(pathPattern) - - companion object { - /** - * Builds a new [EmbedRule]. - * - * @param pathPattern A JsonPointerGlob pattern (e.g., "/scripts/ *", "/queries/ **") - * @param tag Optional embed tag to include (e.g., "yaml", "sql", "bash") - * @return [EmbedRuleResult.Success] if [pathPattern] is a valid JsonPointerGlob, otherwise [EmbedRuleResult.Failure] - * - * Example: - * ```kotlin - * EmbedRule.fromPathPattern("/scripts/ *", tag = "bash") // Match all values under "scripts" - * EmbedRule.fromPathPattern("/config/description") // Match exact path, no tag - * ``` - */ - fun fromPathPattern(pathPattern: String, tag: String? = null): EmbedRuleResult { - return try { - EmbedRuleResult.Success(EmbedRule(pathPattern, tag)) - } catch (e: IllegalArgumentException) { - EmbedRuleResult.Failure(e.toString()) - } - } +@OptIn(ExperimentalJsonPointerGlobLanguage::class) +internal fun FormatOptions.toInternal(): KsonFormatterConfig { + val indentType = when (indentType) { + is IndentType.Spaces -> InternalIndentType.Space((indentType as IndentType.Spaces).size) + is IndentType.Tabs -> InternalIndentType.Tab() } -} - -sealed class EmbedRuleResult { - data class Success(val embedRule: EmbedRule) : EmbedRuleResult() - data class Failure(val message: String) : EmbedRuleResult() -} - - -/** - * Options for formatting Kson output. - * - * @param indentType The type of indentation to use (spaces or tabs) - * @param formattingStyle The formatting style (PLAIN, DELIMITED, COMPACT, CLASSIC) - * @param embedBlockRules Rules for formatting specific paths as embed blocks - */ -class FormatOptions( - val indentType: IndentType = IndentType.Spaces(2), - val formattingStyle: FormattingStyle = FormattingStyle.PLAIN, - val embedBlockRules: List = emptyList() -) { - /** - * Map [FormatOptions] to [KsonFormatterConfig] that is used internally to format a Kson document. - */ - internal fun toInternal(): KsonFormatterConfig { - val indentType = when (indentType) { - is IndentType.Spaces -> InternalIndentType.Space(indentType.size) - is IndentType.Tabs -> InternalIndentType.Tab() - } - val formattingStyle = when (formattingStyle){ - FormattingStyle.PLAIN -> InternalFormattingStyle.PLAIN - FormattingStyle.DELIMITED -> InternalFormattingStyle.DELIMITED - FormattingStyle.COMPACT -> InternalFormattingStyle.COMPACT - FormattingStyle.CLASSIC -> InternalFormattingStyle.CLASSIC - } + val formattingStyle = when (formattingStyle){ + FormattingStyle.PLAIN -> InternalFormattingStyle.PLAIN + FormattingStyle.DELIMITED -> InternalFormattingStyle.DELIMITED + FormattingStyle.COMPACT -> InternalFormattingStyle.COMPACT + FormattingStyle.CLASSIC -> InternalFormattingStyle.CLASSIC + } - val internalEmbedRules = embedBlockRules.map { rule -> - InternalEmbedRule( - pathPattern = rule.parsedPathPattern, - tag = rule.tag - ) + val internalEmbedRules = embedBlockRules.mapNotNull { rule -> + val pathPattern = try { + JsonPointerGlob(rule.pathPattern) + } catch (e: IllegalArgumentException) { + // Discard invalid `jsonPointerGlob` + return@mapNotNull null } - - return KsonFormatterConfig( - indentType = indentType, - formattingStyle = formattingStyle, - embedBlockRules = internalEmbedRules + InternalEmbedRule( + pathPattern = pathPattern, + tag = rule.tag ) } -} -/** - * Core interface for transpilation options shared across all output formats. - */ -sealed class TranspileOptions { - abstract val retainEmbedTags: Boolean - - /** - * Options for transpiling Kson to JSON. - */ - class Json( - override val retainEmbedTags: Boolean = true - ) : TranspileOptions() - - /** - * Options for transpiling Kson to YAML. - */ - class Yaml( - override val retainEmbedTags: Boolean = true - ) : TranspileOptions() -} - -/** - * [FormattingStyle] options for Kson Output - */ -enum class FormattingStyle{ - /** - * These values map to [InternalFormattingStyle] - */ - PLAIN, - DELIMITED, - COMPACT, - CLASSIC -} - -/** - * Options for indenting Kson Output - */ -sealed class IndentType { - /** Use spaces for indentation with the specified count */ - class Spaces(val size: Int = 2) : IndentType() - - /** Use tabs for indentation */ - data object Tabs : IndentType() -} - -/** - * The result of statically analyzing a Kson document - */ -class Analysis internal constructor( - val errors: List, - val tokens: List, - val ksonValue: KsonValue? -) - -/** - * [Token] produced by the lexing phase of a Kson parse - */ -class Token internal constructor( - val tokenType: TokenType, - val text: String, - val start: Position, - val end: Position) - -enum class TokenType { - /** - * See [convertTokens] for the mapping from our [org.kson.parser.TokenType]/[InternalTokenType] tokens - */ - CURLY_BRACE_L, - CURLY_BRACE_R, - SQUARE_BRACKET_L, - SQUARE_BRACKET_R, - ANGLE_BRACKET_L, - ANGLE_BRACKET_R, - COLON, - DOT, - END_DASH, - COMMA, - COMMENT, - EMBED_OPEN_DELIM, - EMBED_CLOSE_DELIM, - EMBED_TAG, - EMBED_PREAMBLE_NEWLINE, - EMBED_CONTENT, - FALSE, - UNQUOTED_STRING, - ILLEGAL_CHAR, - LIST_DASH, - NULL, - NUMBER, - STRING_OPEN_QUOTE, - STRING_CLOSE_QUOTE, - STRING_CONTENT, - TRUE, - WHITESPACE, - EOF + return KsonFormatterConfig( + indentType = indentType, + formattingStyle = formattingStyle, + embedBlockRules = internalEmbedRules + ) } -/** - * Represents a message logged during Kson processing - */ -class Message internal constructor(val message: String, val severity: MessageSeverity, val start: Position, val end: Position) - -/** - * Represents the severity of a [Message] - */ -enum class MessageSeverity{ - ERROR, - WARNING, -} - -/** - * A zero-based line/column position in a document - * - * @param line The line number where the error occurred (0-based) - * @param column The column number where the error occurred (0-based) - */ -class Position internal constructor(val line: Int, val column: Int) { - internal constructor(coordinates: Coordinates) : this(coordinates.line, coordinates.column) +internal fun Coordinates.toPosition(): Position { + return Position(line, column) } /** @@ -505,8 +324,8 @@ private fun createPublicToken(publicTokenType: TokenType, internalToken: Interna return Token( publicTokenType, internalToken.lexeme.text, - Position(internalToken.lexeme.location.start), - Position(internalToken.lexeme.location.end) + internalToken.lexeme.location.start.toPosition(), + internalToken.lexeme.location.end.toPosition() ) } @@ -527,22 +346,22 @@ internal fun convertValue(ksonValue: InternalKsonValue): KsonValue { it.value.propName ) as KsonValue.KsonString) }.toMap(), - internalStart = Position(ksonValue.location.start), - internalEnd = Position(ksonValue.location.end) + internalStart = ksonValue.location.start.toPosition(), + internalEnd = ksonValue.location.end.toPosition() ) } is InternalKsonList -> { KsonValue.KsonArray( elements = ksonValue.elements.map { convertValue(it) }, - internalStart = Position(ksonValue.location.start), - internalEnd = Position(ksonValue.location.end) + internalStart = ksonValue.location.start.toPosition(), + internalEnd = ksonValue.location.end.toPosition() ) } is InternalKsonString -> { KsonValue.KsonString( value = ksonValue.value, - internalStart = Position(ksonValue.location.start), - internalEnd = Position(ksonValue.location.end) + internalStart = ksonValue.location.start.toPosition(), + internalEnd = ksonValue.location.end.toPosition() ) } is InternalKsonNumber -> { @@ -550,151 +369,37 @@ internal fun convertValue(ksonValue: InternalKsonValue): KsonValue { if (isInteger) { KsonValue.KsonNumber.Integer( value = ksonValue.value.asString.toInt(), - internalStart = Position(ksonValue.location.start), - internalEnd = Position(ksonValue.location.end) + internalStart = ksonValue.location.start.toPosition(), + internalEnd = ksonValue.location.end.toPosition() ) } else { KsonValue.KsonNumber.Decimal( value = ksonValue.value.asString.toDouble(), - internalStart = Position(ksonValue.location.start), - internalEnd = Position(ksonValue.location.end) + internalStart = ksonValue.location.start.toPosition(), + internalEnd = ksonValue.location.end.toPosition() ) } } is InternalKsonBoolean -> { KsonValue.KsonBoolean( value = ksonValue.value, - internalStart = Position(ksonValue.location.start), - internalEnd = Position(ksonValue.location.end) + internalStart = ksonValue.location.start.toPosition(), + internalEnd = ksonValue.location.end.toPosition() ) } is InternalKsonNull -> { KsonValue.KsonNull( - internalStart = Position(ksonValue.location.start), - internalEnd = Position(ksonValue.location.end) + internalStart = ksonValue.location.start.toPosition(), + internalEnd = ksonValue.location.end.toPosition() ) } is InternalEmbedBlock -> { KsonValue.KsonEmbed( tag = ksonValue.embedTag?.value, content = ksonValue.embedContent.value, - internalStart = Position(ksonValue.location.start), - internalEnd = Position(ksonValue.location.end) + internalStart = ksonValue.location.start.toPosition(), + internalEnd = ksonValue.location.end.toPosition() ) } } } - -/** - * Type discriminator for KsonValue subclasses - */ -enum class KsonValueType { - OBJECT, - ARRAY, - STRING, - INTEGER, - DECIMAL, - BOOLEAN, - NULL, - EMBED -} - -/** - * Represents a parsed [InternalKsonValue] in the public API - */ -sealed class KsonValue(val start: Position, val end: Position) { - /** - * Type discriminator for easier type checking in TypeScript/JavaScript - */ - abstract val type: KsonValueType - /** - * A Kson object with key-value pairs - */ - @ConsistentCopyVisibility - data class KsonObject internal constructor( - val properties: Map, - val propertyKeys: Map, - private val internalStart: Position, - private val internalEnd: Position - ) : KsonValue(internalStart, internalEnd) { - override val type = KsonValueType.OBJECT - } - - /** - * A Kson array with elements - */ - class KsonArray internal constructor( - val elements: List, - internalStart: Position, - internalEnd: Position - ) : KsonValue(internalStart, internalEnd) { - override val type = KsonValueType.ARRAY - } - - /** - * A Kson string value - */ - class KsonString internal constructor( - val value: String, - internalStart: Position, - internalEnd: Position - ) : KsonValue(internalStart, internalEnd) { - override val type = KsonValueType.STRING - } - - /** - * A Kson number value. - */ - sealed class KsonNumber(start: Position, end: Position) : KsonValue(start, end) { - - class Integer internal constructor( - val value: Int, - val internalStart: Position, - val internalEnd: Position - ) : KsonNumber(internalStart, internalEnd){ - override val type = KsonValueType.INTEGER - } - - class Decimal internal constructor( - val value: Double, - internalStart: Position, - internalEnd: Position - ) : KsonNumber(internalStart, internalEnd) { - override val type = KsonValueType.DECIMAL - } - } - - - /** - * A Kson boolean value - */ - class KsonBoolean internal constructor( - val value: Boolean, - internalStart: Position, - internalEnd: Position - ) : KsonValue(internalStart, internalEnd) { - override val type = KsonValueType.BOOLEAN - } - - /** - * A Kson null value - */ - class KsonNull internal constructor( - internalStart: Position, - internalEnd: Position - ) : KsonValue(internalStart, internalEnd) { - override val type = KsonValueType.NULL - } - - /** - * A Kson embed block - */ - class KsonEmbed internal constructor( - val tag: String?, - val content: String, - internalStart: Position, - internalEnd: Position - ) : KsonValue(internalStart, internalEnd) { - override val type = KsonValueType.EMBED - } -} diff --git a/kson-lib/src/commonTest/kotlin/org/kson/KsonSmokeTest.kt b/kson-lib/src/commonTest/kotlin/org/kson/KsonSmokeTest.kt index d79f3b6f..c7c34384 100644 --- a/kson-lib/src/commonTest/kotlin/org/kson/KsonSmokeTest.kt +++ b/kson-lib/src/commonTest/kotlin/org/kson/KsonSmokeTest.kt @@ -1,401 +1,11 @@ package org.kson +import org.kson.api.* import kotlin.test.* -/** - * Tests for the public [Kson] interface. Note we explicitly call this out as a [KsonSmokeTest]: since the underlying - * code that [Kson] puts an interface on it well-tested, we only need to smoke test each [Kson] method to be - * confident in this code - */ -class KsonSmokeTest { - - @Test - fun testFormat_withDefaultOptions() { - val input = """{"name": "test", "value": 123}""" - val formatted = Kson.format(input) - assertEquals(""" - name: test - value: 123 - """.trimIndent(), - formatted) - } - - @Test - fun testFormat_withSpacesOption() { - val input = """{"name": "test", "value": 123}""" - val formatted = Kson.format(input, FormatOptions(IndentType.Spaces(6))) - assertEquals(""" - name: test - value: 123 - """.trimIndent(), - formatted) - } - - @Test - fun testFormat_withDelimitedOption() { - val input = """{"name": "test", "list": [1, 2, 3]}""" - val formatted = Kson.format(input, FormatOptions(formattingStyle = FormattingStyle.DELIMITED)) - assertEquals( - """ - { - name: test - list: < - - 1 - - 2 - - 3 - > - } - """.trimIndent(), - formatted - ) - } - - @Test - fun testFormat_withTabsOption() { - val input = """{"name": "test", "value": 123}""" - val result = Kson.format(input, FormatOptions(IndentType.Tabs)) - assertIs(result) - assertTrue(result.isNotEmpty()) - } - - @Test - fun testToJson_success() { - val input = """{"name": "test", "value": 123}""" - val result = Kson.toJson(input) - assertIs(result) - assertTrue(result.output.isNotEmpty()) - } - - @Test - fun testToJson_failure() { - val input = """{"invalid": }""" - val result = Kson.toJson(input) - assertIs(result) - assertTrue(result.errors.isNotEmpty()) - val error = result.errors.first() - assertIs(error.message) - assertIs(error.start) - assertIs(error.end) - assertTrue(error.start.line == 0) - assertTrue(error.start.column > 0) - } - - @Test - fun testToYaml_success() { - val input = """{"name": "test", "value": 123}""" - val result = Kson.toYaml(input) - assertIs(result) - assertTrue(result.output.isNotEmpty()) - } - - @Test - fun testToYaml_failure() { - val input = """{"invalid": }""" - val result = Kson.toYaml(input) - assertIs(result) - assertTrue(result.errors.isNotEmpty()) - } - - @Test - fun testAnalyze() { - val input = """{"name": "test", "value": 123}""" - val analysis = Kson.analyze(input) - assertIs(analysis) - assertIs>(analysis.errors) - assertIs>(analysis.tokens) - assertTrue(analysis.tokens.isNotEmpty()) - - val token = analysis.tokens.first() - assertIs(token.tokenType) - assertIs(token.text) - assertIs(token.start) - assertIs(token.end) - } - - @Test - fun testAnalysisUnclosedString() { - val analysis = Kson.analyze("'unclosed string") - assertIs(analysis) - assertIs>(analysis.errors) - assertIs>(analysis.tokens) - assertTrue(analysis.tokens.isNotEmpty()) - - val token = analysis.tokens.first() - assertIs(token.tokenType) - assertIs(token.text) - assertIs(token.start) - assertIs(token.end) +class KsonSmokeTest : KsonServiceSmokeTest() { + override fun createService(): KsonService { + return Kson } - @Test - fun testAnalyze_tokens() { - val input = """name: test, complexString: "this has legal \n and illegal \x escapes and \u3456 unicode"""" - val tokens = Kson.analyze(input).tokens - assertEquals( - listOf(TokenType.UNQUOTED_STRING, - TokenType.COLON, - TokenType.UNQUOTED_STRING, - TokenType.COMMA, - TokenType.UNQUOTED_STRING, - TokenType.COLON, - TokenType.STRING_OPEN_QUOTE, - TokenType.STRING_CONTENT, - TokenType.STRING_CLOSE_QUOTE, - TokenType.EOF), - tokens.map { it.tokenType }) - } - - @Test - fun testAnalyze_value() { - val input = """ - key: value - list: - - 1 - - 2.1 - - 3E5 - embed:%tag - %%""".trimIndent() - val value = Kson.analyze(input).ksonValue - assertNotNull(value) - assertTrue(value is KsonValue.KsonObject) - - assertEquals(3, value.properties.size) - - // Check root object location (should span entire document) - assertEquals(0, value.start.line) - assertEquals(0, value.start.column) - assertEquals(6, value.end.line) - assertEquals(2, value.end.column) - - // Check "key" property - val keyValue = value.properties.get("key") - assertTrue(keyValue is KsonValue.KsonString) - assertEquals("value", keyValue.value) - assertEquals(0, keyValue.start.line) - assertEquals(5, keyValue.start.column) - assertEquals(0, keyValue.end.line) - assertEquals(10, keyValue.end.column) - - // Check "list" property - val listValue = value.properties.get("list") - assertTrue(listValue is KsonValue.KsonArray) - assertEquals(3, listValue.elements.size) - assertEquals(2, listValue.start.line) - assertEquals(2, listValue.start.column) - assertEquals(4, listValue.end.line) - assertEquals(7, listValue.end.column) - - // Check list elements - val firstElement = listValue.elements[0] - assertTrue(firstElement is KsonValue.KsonNumber.Integer) - assertEquals(1, firstElement.value) - assertEquals(2, firstElement.start.line) - assertEquals(4, firstElement.start.column) - assertEquals(2, firstElement.end.line) - assertEquals(5, firstElement.end.column) - - val secondElement = listValue.elements[1] - assertTrue(secondElement is KsonValue.KsonNumber.Decimal) - assertEquals(2.1, secondElement.value) - assertEquals(3, secondElement.start.line) - assertEquals(4, secondElement.start.column) - assertEquals(3, secondElement.end.line) - assertEquals(7, secondElement.end.column) - - val thirdElement = listValue.elements[2] - assertTrue(thirdElement is KsonValue.KsonNumber.Decimal) - assertEquals(3e5, thirdElement.value) - assertEquals(4, thirdElement.start.line) - assertEquals(4, thirdElement.start.column) - assertEquals(4, thirdElement.end.line) - assertEquals(7, thirdElement.end.column) - - // Check "embed" property - val embedValue = value.properties.get("embed") - assertTrue(embedValue is KsonValue.KsonEmbed) - assertEquals("tag", embedValue.tag) - assertEquals("", embedValue.content) - assertEquals(5, embedValue.start.line) - assertEquals(6, embedValue.start.column) - assertEquals(6, embedValue.end.line) - assertEquals(2, embedValue.end.column) - } - - @Test - fun testParseSchema_success() { - val schemaKson = """{ - "type": "object", - "properties": { - "name": {"type": "string"}, - "age": {"type": "number"} - } - }""" - val result = Kson.parseSchema(schemaKson) - assertIs(result) - assertIs(result.schemaValidator) - } - - @Test - fun testParseSchema_failure() { - val invalidSchema = """{"type": }""" - val result = Kson.parseSchema(invalidSchema) - assertIs(result) - assertTrue(result.errors.isNotEmpty()) - } - - @Test - fun testSchemaValidator_validInput() { - val schemaKson = """{ - "type": "object", - "properties": { - "name": {"type": "string"}, - "age": {"type": "number"} - } - }""" - val schemaResult = Kson.parseSchema(schemaKson) - assertIs(schemaResult) - - val validator = schemaResult.schemaValidator - val validKson = """{"name": "John", "age": 30}""" - val errors = validator.validate(validKson) - assertTrue(errors.isEmpty()) - } - - @Test - fun testSchemaValidator_invalidInput() { - val schemaKson = """{ - "type": "object", - "properties": { - "name": {"type": "string"}, - "age": {"type": "number"} - }, - "required": ["name", "age"] - }""" - val schemaResult = Kson.parseSchema(schemaKson) - assertIs(schemaResult) - - val validator = schemaResult.schemaValidator - val invalidKson = """{"name": "John"}""" - val errors = validator.validate(invalidKson) - assertTrue(errors.isNotEmpty()) - } - - @Test - fun testSchemaValidator_validateWithParseErrors() { - val schemaKson = """{"type": "object"}""" - val schemaResult = Kson.parseSchema(schemaKson) - assertIs(schemaResult) - - val validator = schemaResult.schemaValidator - val invalidKson = """{"invalid": }""" - val errors = validator.validate(invalidKson) - assertTrue(errors.isNotEmpty()) - } - - @Test - fun testPropertyKeys_basicAccess() { - val input = """ - name: John - age: 30 - city: 'New York' - """.trimIndent() - val analysis = Kson.analyze(input) - val value = analysis.ksonValue - assertNotNull(value) - assertTrue(value is KsonValue.KsonObject) - - // Verify all keys are present in propertyKeys - assertEquals(3, value.propertyKeys.size) - assertTrue(value.propertyKeys.containsKey("name")) - assertTrue(value.propertyKeys.containsKey("age")) - assertTrue(value.propertyKeys.containsKey("city")) - - // Verify propertyKeys contains KsonString values - val nameKey = value.propertyKeys.get("name") - assertNotNull(nameKey) - assertEquals("name", nameKey.value) - - val ageKey = value.propertyKeys.get("age") - assertNotNull(ageKey) - assertEquals("age", ageKey.value) - } - - @Test - fun testPropertyKeys_withPositionInformation() { - val input = """ - name: John - age: 30 - """.trimIndent() - val analysis = Kson.analyze(input) - val value = analysis.ksonValue - assertNotNull(value) - assertTrue(value is KsonValue.KsonObject) - - // Verify position information for keys - val nameKey = value.propertyKeys.get("name") - assertNotNull(nameKey) - assertEquals(0, nameKey.start.line) - assertEquals(0, nameKey.start.column) - assertEquals(0, nameKey.end.line) - assertEquals(4, nameKey.end.column) - - val ageKey = value.propertyKeys.get("age") - assertNotNull(ageKey) - assertEquals(1, ageKey.start.line) - assertEquals(0, ageKey.start.column) - assertEquals(1, ageKey.end.line) - assertEquals(3, ageKey.end.column) - } - - @Test - fun testPropertyKeys_emptyObject() { - val input = "{}" - val analysis = Kson.analyze(input) - val value = analysis.ksonValue - assertNotNull(value) - assertTrue(value is KsonValue.KsonObject) - - // Empty object should have no propertyKeys - assertEquals(0, value.propertyKeys.size) - assertEquals(0, value.properties.size) - } - - @Test - fun testEmbedRule_validPattern() { - val ruleResult = EmbedRule.fromPathPattern("/scripts/*", tag = "bash") - val rule = assertIs(ruleResult).embedRule - assertEquals("/scripts/*", rule.pathPattern) - assertEquals("bash", rule.tag) - } - - @Test - fun testEmbedRule_invalidPattern() { - val ruleResult = EmbedRule.fromPathPattern("invalid[pattern") - assertIs(ruleResult) - } - - @Test - fun testFormat_withEmbedBlockRules() { - val input = """ - scripts: - build: "make all" - """.trimIndent() - - val rule = (EmbedRule.fromPathPattern("/scripts/build", tag = "bash") as EmbedRuleResult.Success).embedRule - val formatted = Kson.format( - input, - FormatOptions( - embedBlockRules = listOf(rule) - ) - ) - assertEquals( - """ - scripts: - build: %bash - make all - %% - """.trimIndent(), - formatted - ) - } } diff --git a/kson-service-api/build.gradle.kts b/kson-service-api/build.gradle.kts new file mode 100644 index 00000000..471cbb37 --- /dev/null +++ b/kson-service-api/build.gradle.kts @@ -0,0 +1,59 @@ +plugins { + kotlin("multiplatform") + id("com.vanniktech.maven.publish") version "0.30.0" + id("org.jetbrains.dokka") version "2.0.0" +} + +repositories { + mavenCentral() +} + +group = "org.kson" +// [[kson-version-num]] - base version defined in buildSrc/src/main/kotlin/org/kson/KsonVersion.kt +val isRelease = project.findProperty("release") == "true" +version = org.kson.KsonVersion.getVersion(isRelease = isRelease) + +kotlin { + jvm() + js(IR) { + browser() + nodejs() + binaries.library() + useEsModules() + generateTypeScriptDefinitions() + } +} + +mavenPublishing { + publishToMavenCentral(com.vanniktech.maven.publish.SonatypeHost.CENTRAL_PORTAL, automaticRelease = false) + signAllPublications() + + coordinates("org.kson", "kson-service-api", org.kson.KsonVersion.getPublishVersion(rootProject.projectDir, isRelease = isRelease)) + + pom { + name.set("KSON API") + description.set("A 💌 to the humans maintaining computer configurations") + url.set("https://kson.org") + + licenses { + license { + name.set("Apache-2.0") + url.set("https://www.apache.org/licenses/LICENSE-2.0.txt") + } + } + + developers { + developer { + id.set("dmarcotte") + name.set("Daniel Marcotte") + email.set("kson@kson.org") + } + } + + scm { + connection.set("scm:git:https://github.com/kson-org/kson.git") + developerConnection.set("scm:git:git@github.com:kson-org/kson.git") + url.set("https://github.com/kson-org/kson") + } + } +} diff --git a/kson-service-api/readme.md b/kson-service-api/readme.md new file mode 100644 index 00000000..99cb187f --- /dev/null +++ b/kson-service-api/readme.md @@ -0,0 +1,4 @@ +# kson-service-api + +KSON's public interface for Kotlin, decoupled from any specific implementation. See `kson-lib` for +the main implementation. diff --git a/kson-service-api/src/commonMain/kotlin/org/kson/api/FormatOptions.kt b/kson-service-api/src/commonMain/kotlin/org/kson/api/FormatOptions.kt new file mode 100644 index 00000000..56cddf5f --- /dev/null +++ b/kson-service-api/src/commonMain/kotlin/org/kson/api/FormatOptions.kt @@ -0,0 +1,48 @@ +package org.kson.api + +/** + * Options for formatting Kson output. + * + * @param indentType The type of indentation to use (spaces or tabs) + * @param formattingStyle The formatting style (PLAIN, DELIMITED, COMPACT, CLASSIC) + * @param embedBlockRules Rules for formatting specific paths as embed blocks + */ +class FormatOptions( + val indentType: IndentType = IndentType.Spaces(2), + val formattingStyle: FormattingStyle = FormattingStyle.PLAIN, + val embedBlockRules: List = emptyList() +) + +/** + * [FormattingStyle] options for Kson Output + */ +enum class FormattingStyle { + PLAIN, + DELIMITED, + COMPACT, + CLASSIC +} + +/** + * Options for indenting Kson Output + */ +sealed class IndentType { + /** Use spaces for indentation with the specified count */ + class Spaces(val size: Int = 2) : IndentType() + + /** Use tabs for indentation */ + data object Tabs : IndentType() +} + +/** + * A rule for formatting string values at specific paths as embed blocks. + * + * When formatting KSON, strings at paths matching [pathPattern] will be rendered + * as embed blocks instead of regular strings. + * + * **Warning:** JsonPointerGlob syntax is experimental and may change in future versions. + */ +class EmbedRule( + val pathPattern: String, + val tag: String? = null +) diff --git a/kson-service-api/src/commonMain/kotlin/org/kson/api/KsonService.kt b/kson-service-api/src/commonMain/kotlin/org/kson/api/KsonService.kt new file mode 100644 index 00000000..8beca85f --- /dev/null +++ b/kson-service-api/src/commonMain/kotlin/org/kson/api/KsonService.kt @@ -0,0 +1,261 @@ +package org.kson.api + +interface KsonService { + /** + * Formats Kson source with the specified formatting options. + * + * @param kson The Kson source to format + * @param formatOptions The formatting options to apply + * @return The formatted Kson source + */ + fun format(kson: String, formatOptions: FormatOptions = FormatOptions()): String + + /** + * Converts Kson to Json. + * + * @param kson The Kson source to convert + * @param options Options for the JSON transpilation + * @return A Result containing either the Json output or error messages + */ + fun toJson(kson: String, options: TranspileOptions.Json = TranspileOptions.Json()): Result + + /** + * Converts Kson to Yaml, preserving comments + * + * @param kson The Kson source to convert + * @param options Options for the YAML transpilation + * @return A Result containing either the Yaml output or error messages + */ + fun toYaml(kson: String, options: TranspileOptions.Yaml = TranspileOptions.Yaml()): Result + + /** + * Statically analyze the given Kson and return an [Analysis] object containing any messages generated along with a + * tokenized version of the source. Useful for tooling/editor support. + * @param kson The Kson source to analyze + * @param filepath Filepath of the document being analyzed + */ + fun analyze(kson: String, filepath: String? = null): Analysis + + /** + * Parses a Kson schema definition and returns a validator for that schema. + * + * @param schemaKson The Kson source defining a Json Schema + * @return A SchemaValidator that can validate Kson documents against the schema + */ + fun parseSchema(schemaKson: String): SchemaResult +} + +/** + * Result of a Kson conversion operation + */ +sealed class Result { + class Success(val output: String) : Result() + class Failure(val errors: List) : Result() +} + +/** + * A [KsonService.parseSchema] result + */ +sealed class SchemaResult { + class Success(val schemaValidator: SchemaValidatorService) : SchemaResult() + class Failure(val errors: List) : SchemaResult() +} + +/** + * A validator that can check if Kson source conforms to a schema. + */ +interface SchemaValidatorService { + /** + * Validates the given Kson source against this validator's schema. + * @param kson The Kson source to validate + * @param filepath Optional filepath of the document being validated, used by validators to determine which rules to apply + * + * @return A list of validation error messages, or empty list if valid + */ + fun validate(kson: String, filepath: String? = null): List +} + +/** + * Represents a message logged during Kson processing + */ +class Message(val message: String, val severity: MessageSeverity, val start: Position, val end: Position) + +/** + * Represents the severity of a [Message] + */ +enum class MessageSeverity { + ERROR, + WARNING, +} + +/** + * A zero-based line/column position in a document + * + * @param line The line number where the error occurred (0-based) + * @param column The column number where the error occurred (0-based) + */ +class Position(val line: Int, val column: Int) + +class SourceContext(val filepath: String?) + +/** + * The result of statically analyzing a Kson document + */ +class Analysis( + val errors: List, + val tokens: List, + val ksonValue: KsonValue? +) + +/** + * [Token] produced by the lexing phase of a Kson parse + */ +class Token( + val tokenType: TokenType, + val text: String, + val start: Position, + val end: Position) + +enum class TokenType { + CURLY_BRACE_L, + CURLY_BRACE_R, + SQUARE_BRACKET_L, + SQUARE_BRACKET_R, + ANGLE_BRACKET_L, + ANGLE_BRACKET_R, + COLON, + DOT, + END_DASH, + COMMA, + COMMENT, + EMBED_OPEN_DELIM, + EMBED_CLOSE_DELIM, + EMBED_TAG, + EMBED_PREAMBLE_NEWLINE, + EMBED_CONTENT, + FALSE, + UNQUOTED_STRING, + ILLEGAL_CHAR, + LIST_DASH, + NULL, + NUMBER, + STRING_OPEN_QUOTE, + STRING_CLOSE_QUOTE, + STRING_CONTENT, + TRUE, + WHITESPACE, + EOF +} + +/** + * Type discriminator for KsonValue subclasses + */ +enum class KsonValueType { + OBJECT, + ARRAY, + STRING, + INTEGER, + DECIMAL, + BOOLEAN, + NULL, + EMBED +} + +/** + * Represents a parsed KSON document + */ +sealed class KsonValue(val start: Position, val end: Position) { + /** + * Type discriminator for easier type checking in TypeScript/JavaScript + */ + abstract val type: KsonValueType + /** + * A Kson object with key-value pairs + */ + data class KsonObject( + val properties: Map, + val propertyKeys: Map, + private val internalStart: Position, + private val internalEnd: Position + ) : KsonValue(internalStart, internalEnd) { + override val type = KsonValueType.OBJECT + } + + /** + * A Kson array with elements + */ + class KsonArray( + val elements: List, + internalStart: Position, + internalEnd: Position + ) : KsonValue(internalStart, internalEnd) { + override val type = KsonValueType.ARRAY + } + + /** + * A Kson string value + */ + class KsonString( + val value: String, + internalStart: Position, + internalEnd: Position + ) : KsonValue(internalStart, internalEnd) { + override val type = KsonValueType.STRING + } + + /** + * A Kson number value. + */ + sealed class KsonNumber(start: Position, end: Position) : KsonValue(start, end) { + + class Integer( + val value: Int, + val internalStart: Position, + val internalEnd: Position + ) : KsonNumber(internalStart, internalEnd){ + override val type = KsonValueType.INTEGER + } + + class Decimal( + val value: Double, + internalStart: Position, + internalEnd: Position + ) : KsonNumber(internalStart, internalEnd) { + override val type = KsonValueType.DECIMAL + } + } + + + /** + * A Kson boolean value + */ + class KsonBoolean( + val value: Boolean, + internalStart: Position, + internalEnd: Position + ) : KsonValue(internalStart, internalEnd) { + override val type = KsonValueType.BOOLEAN + } + + /** + * A Kson null value + */ + class KsonNull( + internalStart: Position, + internalEnd: Position + ) : KsonValue(internalStart, internalEnd) { + override val type = KsonValueType.NULL + } + + /** + * A Kson embed block + */ + class KsonEmbed( + val tag: String?, + val content: String, + internalStart: Position, + internalEnd: Position + ) : KsonValue(internalStart, internalEnd) { + override val type = KsonValueType.EMBED + } +} diff --git a/kson-service-api/src/commonMain/kotlin/org/kson/api/TranspileOptions.kt b/kson-service-api/src/commonMain/kotlin/org/kson/api/TranspileOptions.kt new file mode 100644 index 00000000..6477fe5d --- /dev/null +++ b/kson-service-api/src/commonMain/kotlin/org/kson/api/TranspileOptions.kt @@ -0,0 +1,22 @@ +package org.kson.api + +/** + * Core interface for transpilation options shared across all output formats. + */ +sealed class TranspileOptions { + abstract val retainEmbedTags: Boolean + + /** + * Options for transpiling Kson to JSON. + */ + class Json( + override val retainEmbedTags: Boolean = true + ) : TranspileOptions() + + /** + * Options for transpiling Kson to YAML. + */ + class Yaml( + override val retainEmbedTags: Boolean = true + ) : TranspileOptions() +} diff --git a/kson-service-tests/build.gradle.kts b/kson-service-tests/build.gradle.kts new file mode 100644 index 00000000..3b04ff28 --- /dev/null +++ b/kson-service-tests/build.gradle.kts @@ -0,0 +1,30 @@ +plugins { + kotlin("multiplatform") +} + +repositories { + mavenCentral() +} + +kotlin { + jvm() + js(IR) { + browser() + nodejs() + } + + sourceSets { + val commonMain by getting { + dependencies { + api(project(":kson-service-api")) + api(kotlin("test")) + } + } + + val jvmMain by getting { + dependencies { + api(kotlin("test-junit5")) + } + } + } +} diff --git a/kson-service-tests/readme.md b/kson-service-tests/readme.md new file mode 100644 index 00000000..a8c8d3f8 --- /dev/null +++ b/kson-service-tests/readme.md @@ -0,0 +1,4 @@ +# kson-service-tests + +Tests for KSON's public Kotlin interface, decoupled from any specific implementation. The provided +test classes can be used through inheritance (see e.g., tests in `kson-lib`). diff --git a/kson-service-tests/src/commonMain/kotlin/org/kson/api/KsonServiceSmokeTest.kt b/kson-service-tests/src/commonMain/kotlin/org/kson/api/KsonServiceSmokeTest.kt new file mode 100644 index 00000000..c67dbbcd --- /dev/null +++ b/kson-service-tests/src/commonMain/kotlin/org/kson/api/KsonServiceSmokeTest.kt @@ -0,0 +1,412 @@ +package org.kson.api + +import kotlin.test.* + +/** + * Tests for the public [KsonService] interface. Note we explicitly call this out as a smoke test: since the underlying + * code that Kson puts an interface on is well-tested, we only need to smoke test each Kson method to be + * confident in this code. + * + * Implementations should subclass this and provide a [KsonService] instance via [createService]. + */ +abstract class KsonServiceSmokeTest { + abstract fun createService(): KsonService + + @Test + fun testFormat_withDefaultOptions() { + val input = """{"name": "test", "value": 123}""" + val formatted = createService().format(input) + assertEquals(""" + name: test + value: 123 + """.trimIndent(), + formatted) + } + + @Test + fun testFormat_withSpacesOption() { + val input = """{"name": "test", "value": 123}""" + val formatted = createService().format(input, FormatOptions(IndentType.Spaces(6))) + assertEquals(""" + name: test + value: 123 + """.trimIndent(), + formatted) + } + + @Test + fun testFormat_withDelimitedOption() { + val input = """{"name": "test", "list": [1, 2, 3]}""" + val formatted = createService().format(input, FormatOptions(formattingStyle = FormattingStyle.DELIMITED)) + assertEquals( + """ + { + name: test + list: < + - 1 + - 2 + - 3 + > + } + """.trimIndent(), + formatted + ) + } + + @Test + fun testFormat_withTabsOption() { + val input = """{"name": "test", "value": 123}""" + val result = createService().format(input, FormatOptions(IndentType.Tabs)) + assertIs(result) + assertTrue(result.isNotEmpty()) + } + + @Test + fun testToJson_success() { + val input = """{"name": "test", "value": 123}""" + val result = createService().toJson(input) + assertIs(result) + assertTrue(result.output.isNotEmpty()) + } + + @Test + fun testToJson_failure() { + val input = """{"invalid": }""" + val result = createService().toJson(input) + assertIs(result) + assertTrue(result.errors.isNotEmpty()) + val error = result.errors.first() + assertIs(error.message) + assertIs(error.start) + assertIs(error.end) + assertTrue(error.start.line == 0) + assertTrue(error.start.column > 0) + } + + @Test + fun testToYaml_success() { + val input = """{"name": "test", "value": 123}""" + val result = createService().toYaml(input) + assertIs(result) + assertTrue(result.output.isNotEmpty()) + } + + @Test + fun testToYaml_failure() { + val input = """{"invalid": }""" + val result = createService().toYaml(input) + assertIs(result) + assertTrue(result.errors.isNotEmpty()) + } + + @Test + fun testAnalyze() { + val input = """{"name": "test", "value": 123}""" + val analysis = createService().analyze(input) + assertIs(analysis) + assertIs>(analysis.errors) + assertIs>(analysis.tokens) + assertTrue(analysis.tokens.isNotEmpty()) + + val token = analysis.tokens.first() + assertIs(token.tokenType) + assertIs(token.text) + assertIs(token.start) + assertIs(token.end) + } + + @Test + fun testAnalysisUnclosedString() { + val analysis = createService().analyze("'unclosed string") + assertIs(analysis) + assertIs>(analysis.errors) + assertIs>(analysis.tokens) + assertTrue(analysis.tokens.isNotEmpty()) + + val token = analysis.tokens.first() + assertIs(token.tokenType) + assertIs(token.text) + assertIs(token.start) + assertIs(token.end) + } + + @Test + fun testAnalyze_tokens() { + val input = """name: test, complexString: "this has legal \n and illegal \x escapes and \u3456 unicode"""" + val tokens = createService().analyze(input).tokens + assertEquals( + listOf(TokenType.UNQUOTED_STRING, + TokenType.COLON, + TokenType.UNQUOTED_STRING, + TokenType.COMMA, + TokenType.UNQUOTED_STRING, + TokenType.COLON, + TokenType.STRING_OPEN_QUOTE, + TokenType.STRING_CONTENT, + TokenType.STRING_CLOSE_QUOTE, + TokenType.EOF), + tokens.map { it.tokenType }) + } + + @Test + fun testAnalyze_value() { + val input = """ + key: value + list: + - 1 + - 2.1 + - 3E5 + embed:%tag + %%""".trimIndent() + val value = createService().analyze(input).ksonValue + assertNotNull(value) + assertTrue(value is KsonValue.KsonObject) + + assertEquals(3, value.properties.size) + + // Check root object location (should span entire document) + assertEquals(0, value.start.line) + assertEquals(0, value.start.column) + assertEquals(6, value.end.line) + assertEquals(2, value.end.column) + + // Check "key" property + val keyValue = value.properties.get("key") + assertTrue(keyValue is KsonValue.KsonString) + assertEquals("value", keyValue.value) + assertEquals(0, keyValue.start.line) + assertEquals(5, keyValue.start.column) + assertEquals(0, keyValue.end.line) + assertEquals(10, keyValue.end.column) + + // Check "list" property + val listValue = value.properties.get("list") + assertTrue(listValue is KsonValue.KsonArray) + assertEquals(3, listValue.elements.size) + assertEquals(2, listValue.start.line) + assertEquals(2, listValue.start.column) + assertEquals(4, listValue.end.line) + assertEquals(7, listValue.end.column) + + // Check list elements + val firstElement = listValue.elements[0] + assertTrue(firstElement is KsonValue.KsonNumber.Integer) + assertEquals(1, firstElement.value) + assertEquals(2, firstElement.start.line) + assertEquals(4, firstElement.start.column) + assertEquals(2, firstElement.end.line) + assertEquals(5, firstElement.end.column) + + val secondElement = listValue.elements[1] + assertTrue(secondElement is KsonValue.KsonNumber.Decimal) + assertEquals(2.1, secondElement.value) + assertEquals(3, secondElement.start.line) + assertEquals(4, secondElement.start.column) + assertEquals(3, secondElement.end.line) + assertEquals(7, secondElement.end.column) + + val thirdElement = listValue.elements[2] + assertTrue(thirdElement is KsonValue.KsonNumber.Decimal) + assertEquals(3e5, thirdElement.value) + assertEquals(4, thirdElement.start.line) + assertEquals(4, thirdElement.start.column) + assertEquals(4, thirdElement.end.line) + assertEquals(7, thirdElement.end.column) + + // Check "embed" property + val embedValue = value.properties.get("embed") + assertTrue(embedValue is KsonValue.KsonEmbed) + assertEquals("tag", embedValue.tag) + assertEquals("", embedValue.content) + assertEquals(5, embedValue.start.line) + assertEquals(6, embedValue.start.column) + assertEquals(6, embedValue.end.line) + assertEquals(2, embedValue.end.column) + } + + @Test + fun testParseSchema_success() { + val schemaKson = """{ + "type": "object", + "properties": { + "name": {"type": "string"}, + "age": {"type": "number"} + } + }""" + val result = createService().parseSchema(schemaKson) + assertIs(result) + } + + @Test + fun testParseSchema_failure() { + val invalidSchema = """{"type": }""" + val result = createService().parseSchema(invalidSchema) + assertIs(result) + assertTrue(result.errors.isNotEmpty()) + } + + @Test + fun testSchemaValidator_validInput() { + val schemaKson = """{ + "type": "object", + "properties": { + "name": {"type": "string"}, + "age": {"type": "number"} + } + }""" + val schemaResult = createService().parseSchema(schemaKson) + assertIs(schemaResult) + + val validator = schemaResult.schemaValidator + val validKson = """{"name": "John", "age": 30}""" + val errors = validator.validate(validKson) + assertTrue(errors.isEmpty()) + } + + @Test + fun testSchemaValidator_invalidInput() { + val schemaKson = """{ + "type": "object", + "properties": { + "name": {"type": "string"}, + "age": {"type": "number"} + }, + "required": ["name", "age"] + }""" + val schemaResult = createService().parseSchema(schemaKson) + assertIs(schemaResult) + + val validator = schemaResult.schemaValidator + val invalidKson = """{"name": "John"}""" + val errors = validator.validate(invalidKson) + assertTrue(errors.isNotEmpty()) + } + + @Test + fun testSchemaValidator_validateWithParseErrors() { + val schemaKson = """{"type": "object"}""" + val schemaResult = createService().parseSchema(schemaKson) + assertIs(schemaResult) + + val validator = schemaResult.schemaValidator + val invalidKson = """{"invalid": }""" + val errors = validator.validate(invalidKson) + assertTrue(errors.isNotEmpty()) + } + + @Test + fun testPropertyKeys_basicAccess() { + val input = """ + name: John + age: 30 + city: 'New York' + """.trimIndent() + val analysis = createService().analyze(input) + val value = analysis.ksonValue + assertNotNull(value) + assertTrue(value is KsonValue.KsonObject) + + // Verify all keys are present in propertyKeys + assertEquals(3, value.propertyKeys.size) + assertTrue(value.propertyKeys.containsKey("name")) + assertTrue(value.propertyKeys.containsKey("age")) + assertTrue(value.propertyKeys.containsKey("city")) + + // Verify propertyKeys contains KsonString values + val nameKey = value.propertyKeys.get("name") + assertNotNull(nameKey) + assertEquals("name", nameKey.value) + + val ageKey = value.propertyKeys.get("age") + assertNotNull(ageKey) + assertEquals("age", ageKey.value) + } + + @Test + fun testPropertyKeys_withPositionInformation() { + val input = """ + name: John + age: 30 + """.trimIndent() + val analysis = createService().analyze(input) + val value = analysis.ksonValue + assertNotNull(value) + assertTrue(value is KsonValue.KsonObject) + + // Verify position information for keys + val nameKey = value.propertyKeys.get("name") + assertNotNull(nameKey) + assertEquals(0, nameKey.start.line) + assertEquals(0, nameKey.start.column) + assertEquals(0, nameKey.end.line) + assertEquals(4, nameKey.end.column) + + val ageKey = value.propertyKeys.get("age") + assertNotNull(ageKey) + assertEquals(1, ageKey.start.line) + assertEquals(0, ageKey.start.column) + assertEquals(1, ageKey.end.line) + assertEquals(3, ageKey.end.column) + } + + @Test + fun testPropertyKeys_emptyObject() { + val input = "{}" + val analysis = createService().analyze(input) + val value = analysis.ksonValue + assertNotNull(value) + assertTrue(value is KsonValue.KsonObject) + + // Empty object should have no propertyKeys + assertEquals(0, value.propertyKeys.size) + assertEquals(0, value.properties.size) + } + + @Test + fun testFormat_withValidEmbedBlockRules() { + val input = """ + scripts: + build: "make all" + """.trimIndent() + + val rule = EmbedRule("/scripts/build", tag = "bash") + val formatted = createService().format( + input, + FormatOptions( + embedBlockRules = listOf(rule) + ) + ) + assertEquals( + """ + scripts: + build: %bash + make all + %% + """.trimIndent(), + formatted + ) + } + + @Test + fun testFormat_withInvalidEmbedBlockRules() { + val input = """ + scripts: + build: "make all" + """.trimIndent() + + val rule = EmbedRule("invalid[pattern", tag = "bash") + val formatted = createService().format( + input, + FormatOptions( + embedBlockRules = listOf(rule) + ) + ) + assertEquals( + """ + scripts: + build: 'make all' + """.trimIndent(), + formatted + ) + } +} diff --git a/lib-python/kson-lib-tests/build.gradle.kts b/lib-python/kson-lib-tests/build.gradle.kts new file mode 100644 index 00000000..f84c05fe --- /dev/null +++ b/lib-python/kson-lib-tests/build.gradle.kts @@ -0,0 +1,39 @@ +plugins { + kotlin("multiplatform") +} + +repositories { + mavenCentral() +} + +tasks.withType { + dependsOn(":lib-python:build") + + systemProperty("libPythonDir", project(":lib-python").projectDir.absolutePath) + + useJUnitPlatform() + jvmArgs("-Djunit.jupiter.extensions.autodetection.enabled=true") +} + +kotlin { + jvm() + + sourceSets { + commonTest { + dependencies { + implementation(project(":kson-service-tests")) + implementation(project(":kson-http")) + } + } + jvmTest { + dependencies { + implementation(kotlin("test-junit5")) + + // Important: this ensures we have a recent-enough version of JUnit, supporting the `AutoCloseable` + // interface (otherwise test runs never finish because the HTTP server doesn't get closed) + implementation(project.dependencies.platform("org.junit:junit-bom:5.14.3")) + } + } + } +} + diff --git a/lib-python/kson-lib-tests/src/jvmTest/kotlin/ServerExtension.kt b/lib-python/kson-lib-tests/src/jvmTest/kotlin/ServerExtension.kt new file mode 100644 index 00000000..1ab56b14 --- /dev/null +++ b/lib-python/kson-lib-tests/src/jvmTest/kotlin/ServerExtension.kt @@ -0,0 +1,56 @@ +import org.junit.jupiter.api.extension.BeforeAllCallback +import org.junit.jupiter.api.extension.ExtensionContext +import org.kson.Kson +import java.io.File +import java.net.Socket + +/** + * A JUnit extension that makes sure the Python KSON server is running before all tests and gets + * shut down at the end + */ +class ServerExtension : BeforeAllCallback, AutoCloseable { + + companion object { + private var started = false + private var process: Process? = null + } + + override fun beforeAll(context: ExtensionContext) { + if (!started) { + started = true + + // Register for cleanup when the root context closes (after all tests) + context.root.getStore(ExtensionContext.Namespace.GLOBAL) + .put("serverExtension", this) + + val libPythonDir = File(System.getProperty("libPythonDir")) + val isWindows = System.getProperty("os.name").lowercase().contains("win") + val uvwCommand = if (isWindows) listOf("cmd", "/c", "uvw.bat") else listOf("./uvw") + val port = 8082 + + Kson.setPort(port) + + process = ProcessBuilder(uvwCommand + listOf("run", "python", "tests/api_server.py", port.toString())) + .directory(libPythonDir) + .redirectOutput(ProcessBuilder.Redirect.INHERIT) + .redirectErrorStream(true) + .start() + + // Wait for readiness + repeat(30) { + try { + Socket("localhost", port).close() + return + } catch (_: Exception) { + Thread.sleep(1000) + } + } + throw RuntimeException("Server did not start in time") + } + } + + override fun close() { + process?.destroy() + process?.waitFor() + } +} diff --git a/lib-python/kson-lib-tests/src/jvmTest/kotlin/SmokeTest.kt b/lib-python/kson-lib-tests/src/jvmTest/kotlin/SmokeTest.kt new file mode 100644 index 00000000..cbd81068 --- /dev/null +++ b/lib-python/kson-lib-tests/src/jvmTest/kotlin/SmokeTest.kt @@ -0,0 +1,9 @@ +import org.kson.Kson +import org.kson.api.KsonService +import org.kson.api.KsonServiceSmokeTest + +class SmokeTest : KsonServiceSmokeTest() { + override fun createService(): KsonService { + return Kson + } +} \ No newline at end of file diff --git a/lib-python/kson-lib-tests/src/jvmTest/resources/META-INF/services/org.junit.jupiter.api.extension.Extension b/lib-python/kson-lib-tests/src/jvmTest/resources/META-INF/services/org.junit.jupiter.api.extension.Extension new file mode 100644 index 00000000..849fdf0a --- /dev/null +++ b/lib-python/kson-lib-tests/src/jvmTest/resources/META-INF/services/org.junit.jupiter.api.extension.Extension @@ -0,0 +1 @@ +ServerExtension diff --git a/lib-python/pyproject.toml b/lib-python/pyproject.toml index 6fceaa8b..792d1c85 100644 --- a/lib-python/pyproject.toml +++ b/lib-python/pyproject.toml @@ -48,6 +48,9 @@ package-dir = {"" = "src"} [tool.setuptools.package-data] kson = ["kson_api.h", "kson.dll", "libkson.dylib", "libkson.so", "jni_simplified.h"] +[tool.pyright] +stubPath = "stubs" + [dependency-groups] dev = [ "pyright>=1.1.403", diff --git a/lib-python/src/kson/__init__.py b/lib-python/src/kson/__init__.py index 0f46f3c2..2b41bf07 100644 --- a/lib-python/src/kson/__init__.py +++ b/lib-python/src/kson/__init__.py @@ -8,7 +8,6 @@ from enum import Enum tls = threading.local() -tls.attached_jni_thread = None JNI_OK = 0 @@ -61,31 +60,38 @@ def _raise_if_null(env: Any, ptr: Any): # JNI Helpers # ############### -def _attach_jni_thread() -> Any: - if tls.attached_jni_thread: - return tls.attached_jni_thread +class AttachedJniThread: + """Automatically attaches/detaches the thread to the JNI.""" - env_ptr = ffi.new("JNIEnv **") - if jvm[0].AttachCurrentThread(jvm, ffi.cast("void **", env_ptr), ffi.NULL) != JNI_OK: - raise RuntimeError("failed to attach JNI thread") + should_detach: bool - tls.attached_jni_thread = env_ptr[0] - return env_ptr[0] + def __enter__(self): + self.should_detach = False + + if getattr(tls, 'attached_jni_thread', None): + return tls.attached_jni_thread + + env_ptr = ffi.new("JNIEnv **") + if jvm[0].AttachCurrentThread(jvm, ffi.cast("void **", env_ptr), ffi.NULL) != JNI_OK: + raise RuntimeError("failed to attach JNI thread") + + tls.attached_jni_thread = env_ptr[0] + self.should_detach = True + return env_ptr[0] + + def __exit__(self, exc_type, exc_val, exc_tb): + if self.should_detach: + if jvm[0].DetachCurrentThread(jvm) != JNI_OK: + raise RuntimeError("failed to detach JNI thread") + tls.attached_jni_thread = None -def _detach_jni_thread(): - if jvm[0].DetachCurrentThread(jvm) != JNI_OK: - raise RuntimeError("failed to detach JNI thread") - tls.attached_jni_thread = None def _delete_local_ref(env, jni_ref: Any): env[0].DeleteLocalRef(env, ffi.cast("jobject", jni_ref)) def _delete_global_ref(jni_ref): - should_detach = tls.attached_jni_thread is None - env = _attach_jni_thread() - env[0].DeleteGlobalRef(env, ffi.cast("jobject", jni_ref)) - if should_detach: - _detach_jni_thread() + with AttachedJniThread() as env: + env[0].DeleteGlobalRef(env, ffi.cast("jobject", jni_ref)) def _to_gc_global_ref(env, jni_ref: Any) -> Any: global_jni_ref = env[0].NewGlobalRef(env, jni_ref) @@ -106,30 +112,27 @@ def _get_method(env, clazz: Any, method_name: bytes, method_signature: bytes) -> return method def _construct(class_name: bytes, constructor_signature: bytes, args: Any) -> Any: - env = _attach_jni_thread() - clazz = _get_class(env, class_name) - constructor = _get_method(env, clazz, b"", constructor_signature) - jni_ref = env[0].NewObject(env, clazz, constructor, *args) - _raise_exception_if_any(env) - jni_ref_global = _to_gc_global_ref(env, jni_ref) - _detach_jni_thread() - return jni_ref_global + with AttachedJniThread() as env: + clazz = _get_class(env, class_name) + constructor = _get_method(env, clazz, b"", constructor_signature) + jni_ref = env[0].NewObject(env, clazz, constructor, *args) + _raise_exception_if_any(env) + return _to_gc_global_ref(env, jni_ref) def _access_static_field(class_name: bytes, field_name: bytes, field_type: bytes) -> Any: - env = _attach_jni_thread() - c = _get_class(env, class_name) - signature_cstr = ffi.new("char[]", field_type) - field_name_cstr = ffi.new("char[]", field_name) + with AttachedJniThread() as env: + c = _get_class(env, class_name) + signature_cstr = ffi.new("char[]", field_type) + field_name_cstr = ffi.new("char[]", field_name) - # Get static id - field = env[0].GetStaticFieldID(env, c, field_name_cstr, signature_cstr) - _raise_if_null(env, field) + # Get static id + field = env[0].GetStaticFieldID(env, c, field_name_cstr, signature_cstr) + _raise_if_null(env, field) - # Access field - field_value = _to_gc_global_ref(env, env[0].GetStaticObjectField(env, c, field)) - _raise_if_null(env, field_value) - _detach_jni_thread() - return field_value + # Access field + field_value = _to_gc_global_ref(env, env[0].GetStaticObjectField(env, c, field)) + _raise_if_null(env, field_value) + return field_value def _call_method_raw(env: Any, class_name: bytes, jni_ref: Any, func_name: bytes, func_signature: bytes, jni_call_name: str, args: List[Any]) -> Any: clazz = _get_class(env, class_name) @@ -139,12 +142,11 @@ def _call_method_raw(env: Any, class_name: bytes, jni_ref: Any, func_name: bytes return result def _call_method(class_name: bytes, jni_ref: Any, func_name: bytes, func_signature: bytes, jni_call_name: str, args: List[Any]) -> Any: - env = _attach_jni_thread() - result = _call_method_raw(env, class_name, jni_ref, func_name, func_signature, jni_call_name, args) - if jni_call_name == "ObjectMethod": - result = _to_gc_global_ref(env, result) - _detach_jni_thread() - return result + with AttachedJniThread() as env: + result = _call_method_raw(env, class_name, jni_ref, func_name, func_signature, jni_call_name, args) + if jni_call_name == "ObjectMethod": + result = _to_gc_global_ref(env, result) + return result def _python_str_to_java_string(s: str) -> Any: utf16_bytes = s.encode("utf-16-le") @@ -155,41 +157,46 @@ def _python_str_to_java_string(s: str) -> Any: raise RuntimeError("entered unreachable code: raw string length was not divisible by 2") utf16_str = ffi.new("char[]", utf16_bytes) - env = _attach_jni_thread() - jni_ref = env[0].NewString(env, ffi.cast("jchar *", utf16_str), utf16_str_len) - _raise_if_null(env, jni_ref) - jni_ref = _to_gc_global_ref(env, jni_ref) - _detach_jni_thread() - return jni_ref + with AttachedJniThread() as env: + jni_ref = env[0].NewString(env, ffi.cast("jchar *", utf16_str), utf16_str_len) + _raise_if_null(env, jni_ref) + return _to_gc_global_ref(env, jni_ref) def _java_string_to_python_str(jni_ref: Any) -> str: - env = _attach_jni_thread() - native_chars = env[0].GetStringChars(env, jni_ref, ffi.NULL) - _raise_if_null(env, native_chars) - native_chars_byte_len = env[0].GetStringLength(env, jni_ref) * 2 - python_str = bytes(cast(Any, ffi.buffer(native_chars, native_chars_byte_len))).decode("utf-16-le", "strict") - env[0].ReleaseStringChars(env, jni_ref, native_chars) - _detach_jni_thread() - return python_str + with AttachedJniThread() as env: + native_chars = env[0].GetStringChars(env, jni_ref, ffi.NULL) + _raise_if_null(env, native_chars) + native_chars_byte_len = env[0].GetStringLength(env, jni_ref) * 2 + python_str = bytes(cast(Any, ffi.buffer(native_chars, native_chars_byte_len))).decode("utf-16-le", "strict") + env[0].ReleaseStringChars(env, jni_ref, native_chars) + return python_str def _jni_class_name(jni_ref: Any): if jni_ref == ffi.NULL: raise RuntimeError("entered unreachable code: attempted to obtain class name of null object") - env = _attach_jni_thread() - clazz = env[0].GetObjectClass(env, jni_ref) - _raise_if_null(env, clazz) - name_local = _call_method_raw(env, b"java/lang/Class", clazz, b"getName", b"()Ljava/lang/String;", "ObjectMethod", []) - name = _to_gc_global_ref(env, name_local) - _delete_local_ref(env, clazz) - _detach_jni_thread() - return _java_string_to_python_str(name) + with AttachedJniThread() as env: + clazz = env[0].GetObjectClass(env, jni_ref) + _raise_if_null(env, clazz) + name_local = _call_method_raw(env, b"java/lang/Class", clazz, b"getName", b"()Ljava/lang/String;", "ObjectMethod", []) + name = _to_gc_global_ref(env, name_local) + _delete_local_ref(env, clazz) + return _java_string_to_python_str(name) def _from_kotlin_object(python_class, jni_ref): obj = object.__new__(python_class) obj._jni_ref = jni_ref return obj +def _get_jni_ref(object) -> Any: + if hasattr(object, '_jni_ref'): + return object._jni_ref + + if isinstance(object, str): + return _python_str_to_java_string(object) + + raise TypeError(f"Cannot convert value to Kotlin: expected object with _jni_ref attribute or something convertible to a Kotlin type, got {type(object).__name__}") + def _from_kotlin_list( jni_ref: Any, wrap_item_fn: Callable[[Any], Any] ) -> List[Any]: @@ -207,28 +214,24 @@ def _from_kotlin_list( return python_list def _to_kotlin_list(list: List[Any]) -> Any: - env = _attach_jni_thread() + with AttachedJniThread() as env: - # Create a new ArrayList - array_list_class = _get_class(env, b"java/util/ArrayList") - constructor = _get_method(env, array_list_class, b"", b"()V") - array_list = env[0].NewObject(env, array_list_class, constructor) - _raise_exception_if_any(env) - array_list = _to_gc_global_ref(env, array_list) - - # Add each element to the list - for item in list: - if not hasattr(item, '_jni_ref'): - raise TypeError(f"Cannot convert item to Kotlin: expected object with _jni_ref attribute, got {type(item).__name__}") - item_ref = item._jni_ref - _call_method_raw(env, b"java/util/ArrayList", array_list, b"add", b"(Ljava/lang/Object;)Z", "BooleanMethod", [item_ref]) + # Create a new ArrayList + array_list_class = _get_class(env, b"java/util/ArrayList") + constructor = _get_method(env, array_list_class, b"", b"()V") + array_list = env[0].NewObject(env, array_list_class, constructor) _raise_exception_if_any(env) + array_list = _to_gc_global_ref(env, array_list) - _detach_jni_thread() - return array_list + # Add each element to the list + for item in list: + if not hasattr(item, '_jni_ref'): + raise TypeError(f"Cannot convert item to Kotlin: expected object with _jni_ref attribute, got {type(item).__name__}") + item_ref = item._jni_ref + _call_method_raw(env, b"java/util/ArrayList", array_list, b"add", b"(Ljava/lang/Object;)Z", "BooleanMethod", [item_ref]) + _raise_exception_if_any(env) -def _to_kotlin_map(list: Dict[Any, Any]) -> Any: - raise RuntimeError("not implemented") + return array_list def _from_kotlin_map( jni_ref: Any, @@ -253,41 +256,68 @@ def _from_kotlin_map( return python_dict +def _to_kotlin_map(items: Dict[Any, Any]) -> Any: + with AttachedJniThread() as env: + + # Create a new HashMap + map_class = _get_class(env, b"java/util/HashMap") + constructor = _get_method(env, map_class, b"", b"()V") + map = env[0].NewObject(env, map_class, constructor) + _raise_exception_if_any(env) + map = _to_gc_global_ref(env, map) + + # Add entries to it + for key, value in items.items(): + key_ref = _get_jni_ref(key) + value_ref = _get_jni_ref(value) + _call_method_raw(env, b"java/util/HashMap", map, b"put", b"(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;", "ObjectMethod", [key_ref, value_ref]) + _raise_exception_if_any(env) + + return map + ############ # Wrappers # ############ -class FormatOptions: - """Options for formatting Kson output. +class SchemaResult: + + _jni_ref: Any + + Success: TypeAlias + Failure: TypeAlias + def __eq__(self, other): + return _call_method(b"java/lang/Object", self._jni_ref, b"equals", b"(Ljava/lang/Object;)Z", "BooleanMethod", [other._jni_ref]) + + def __hash__(self): + return _call_method(b"java/lang/Object", self._jni_ref, b"hashCode", b"()I", "IntMethod", []) + + @staticmethod + def _downcast(jni_ref) -> Any: + match _jni_class_name(jni_ref): + + case "org.kson.api.SchemaResult$Success": + return _from_kotlin_object(_SchemaResult_Success, jni_ref) - @param indentType The type of indentation to use (spaces or tabs) - @param formattingStyle The formatting style (PLAIN, DELIMITED, COMPACT, CLASSIC) - @param embedBlockRules Rules for formatting specific paths as embed blocks - """ + case "org.kson.api.SchemaResult$Failure": + return _from_kotlin_object(_SchemaResult_Failure, jni_ref) + +class _SchemaResult_Success(SchemaResult): _jni_ref: Any def __init__( self, - indent_type: IndentType, - formatting_style: FormattingStyle, - embed_block_rules: List[EmbedRule], + schema_validator: SchemaValidatorService, ): - if indent_type is None: - raise ValueError("`indent_type` cannot be None") - if formatting_style is None: - raise ValueError("`formatting_style` cannot be None") - if embed_block_rules is None: - raise ValueError("`embed_block_rules` cannot be None") + if schema_validator is None: + raise ValueError("`schema_validator` cannot be None") self._jni_ref = _construct( - b"org/kson/FormatOptions", - b"(Lorg/kson/IndentType;Lorg/kson/FormattingStyle;Ljava/util/List;)V", + b"org/kson/api/SchemaResult$Success", + b"(Lorg/kson/api/SchemaValidatorService;)V", [ - indent_type._jni_ref, - formatting_style._to_kotlin_enum(), - _to_kotlin_list(embed_block_rules), + schema_validator._jni_ref, ] ) def __eq__(self, other): @@ -297,67 +327,92 @@ def __hash__(self): return _call_method(b"java/lang/Object", self._jni_ref, b"hashCode", b"()I", "IntMethod", []) - def indent_type( + def schema_validator( self, - ) -> IndentType: + ) -> SchemaValidatorService: jni_ref = self._jni_ref result = _call_method( - b"org/kson/FormatOptions", + b"org/kson/api/SchemaResult$Success", jni_ref, - b"getIndentType", - b"()Lorg/kson/IndentType;", + b"getSchemaValidator", + b"()Lorg/kson/api/SchemaValidatorService;", "ObjectMethod", [] ) - return cast(Any, (lambda x0: IndentType._downcast(x0))(result)) + return cast(Any, (lambda x0: _from_kotlin_object(SchemaValidatorService, x0))(result)) +SchemaResult.Success = _SchemaResult_Success - def formatting_style( - self, - ) -> FormattingStyle: +class _SchemaResult_Failure(SchemaResult): - jni_ref = self._jni_ref - result = _call_method( - b"org/kson/FormatOptions", - jni_ref, - b"getFormattingStyle", - b"()Lorg/kson/FormattingStyle;", - "ObjectMethod", - [] + _jni_ref: Any + + def __init__( + self, + errors: List[Message], + ): + if errors is None: + raise ValueError("`errors` cannot be None") + self._jni_ref = _construct( + b"org/kson/api/SchemaResult$Failure", + b"(Ljava/util/List;)V", + [ + + _to_kotlin_list(errors), + ] ) + def __eq__(self, other): + return _call_method(b"java/lang/Object", self._jni_ref, b"equals", b"(Ljava/lang/Object;)Z", "BooleanMethod", [other._jni_ref]) - return cast(Any, (lambda x0: FormattingStyle._from_kotlin_enum(x0))(result)) + def __hash__(self): + return _call_method(b"java/lang/Object", self._jni_ref, b"hashCode", b"()I", "IntMethod", []) - def embed_block_rules( + + def errors( self, - ) -> List[EmbedRule]: + ) -> List[Message]: jni_ref = self._jni_ref result = _call_method( - b"org/kson/FormatOptions", + b"org/kson/api/SchemaResult$Failure", jni_ref, - b"getEmbedBlockRules", + b"getErrors", b"()Ljava/util/List;", "ObjectMethod", [] ) - return cast(Any, (lambda x0: _from_kotlin_list(x0, lambda x1: _from_kotlin_object(EmbedRule, x1)))(result)) + return cast(Any, (lambda x0: _from_kotlin_list(x0, lambda x1: _from_kotlin_object(Message, x1)))(result)) +SchemaResult.Failure = _SchemaResult_Failure -class Position: - """A zero-based line/column position in a document - @param line The line number where the error occurred (0-based) - @param column The column number where the error occurred (0-based) - """ +class Position: _jni_ref: Any + def __init__( + self, + line: int, + column: int, + ): + if line is None: + raise ValueError("`line` cannot be None") + if column is None: + raise ValueError("`column` cannot be None") + self._jni_ref = _construct( + b"org/kson/api/Position", + b"(II)V", + [ + + ffi.cast('jint', line), + ffi.cast('jint', column), + ] + ) def __eq__(self, other): return _call_method(b"java/lang/Object", self._jni_ref, b"equals", b"(Ljava/lang/Object;)Z", "BooleanMethod", [other._jni_ref]) @@ -372,7 +427,7 @@ def line( jni_ref = self._jni_ref result = _call_method( - b"org/kson/Position", + b"org/kson/api/Position", jni_ref, b"getLine", b"()I", @@ -389,7 +444,7 @@ def column( jni_ref = self._jni_ref result = _call_method( - b"org/kson/Position", + b"org/kson/api/Position", jni_ref, b"getColumn", b"()I", @@ -400,11 +455,135 @@ def column( return cast(Any, (lambda x0: x0)(result)) +class IndentType: + + _jni_ref: Any + + Spaces: TypeAlias + Tabs: TypeAlias + def __eq__(self, other): + return _call_method(b"java/lang/Object", self._jni_ref, b"equals", b"(Ljava/lang/Object;)Z", "BooleanMethod", [other._jni_ref]) + + def __hash__(self): + return _call_method(b"java/lang/Object", self._jni_ref, b"hashCode", b"()I", "IntMethod", []) + + @staticmethod + def _downcast(jni_ref) -> Any: + match _jni_class_name(jni_ref): + + case "org.kson.api.IndentType$Spaces": + return _from_kotlin_object(_IndentType_Spaces, jni_ref) + + case "org.kson.api.IndentType$Tabs": + return _from_kotlin_object(_IndentType_Tabs, jni_ref) + +class _IndentType_Spaces(IndentType): + + _jni_ref: Any + + def __init__( + self, + size: int, + ): + if size is None: + raise ValueError("`size` cannot be None") + self._jni_ref = _construct( + b"org/kson/api/IndentType$Spaces", + b"(I)V", + [ + + ffi.cast('jint', size), + ] + ) + def __eq__(self, other): + return _call_method(b"java/lang/Object", self._jni_ref, b"equals", b"(Ljava/lang/Object;)Z", "BooleanMethod", [other._jni_ref]) + + def __hash__(self): + return _call_method(b"java/lang/Object", self._jni_ref, b"hashCode", b"()I", "IntMethod", []) + + + def size( + self, + ) -> int: + + + jni_ref = self._jni_ref + result = _call_method( + b"org/kson/api/IndentType$Spaces", + jni_ref, + b"getSize", + b"()I", + "IntMethod", + [] + ) + + return cast(Any, (lambda x0: x0)(result)) +IndentType.Spaces = _IndentType_Spaces + + +class _IndentType_Tabs(IndentType): + + _jni_ref: Any + + def __init__(self): + self._jni_ref = _access_static_field(b"org/kson/api/IndentType$Tabs", b"INSTANCE", b"Lorg/kson/api/IndentType$Tabs;") + def __eq__(self, other): + return _call_method(b"java/lang/Object", self._jni_ref, b"equals", b"(Ljava/lang/Object;)Z", "BooleanMethod", [other._jni_ref]) + + def __hash__(self): + return _call_method(b"java/lang/Object", self._jni_ref, b"hashCode", b"()I", "IntMethod", []) + + + @staticmethod + def to_string( + ) -> str: + + + jni_ref = _access_static_field(b"org/kson/api/IndentType$Tabs", b"INSTANCE", b"Lorg/kson/api/IndentType$Tabs;") + result = _call_method( + b"org/kson/api/IndentType$Tabs", + jni_ref, + b"toString", + b"()Ljava/lang/String;", + "ObjectMethod", + [] + ) + + return cast(Any, (_java_string_to_python_str)(result)) +IndentType.Tabs = _IndentType_Tabs + + + class Message: - """Represents a message logged during Kson processing""" _jni_ref: Any + def __init__( + self, + message: str, + severity: MessageSeverity, + start: Position, + end: Position, + ): + if message is None: + raise ValueError("`message` cannot be None") + if severity is None: + raise ValueError("`severity` cannot be None") + if start is None: + raise ValueError("`start` cannot be None") + if end is None: + raise ValueError("`end` cannot be None") + self._jni_ref = _construct( + b"org/kson/api/Message", + b"(Ljava/lang/String;Lorg/kson/api/MessageSeverity;Lorg/kson/api/Position;Lorg/kson/api/Position;)V", + [ + + _python_str_to_java_string(message), + severity._to_kotlin_enum(), + start._jni_ref, + end._jni_ref, + ] + ) def __eq__(self, other): return _call_method(b"java/lang/Object", self._jni_ref, b"equals", b"(Ljava/lang/Object;)Z", "BooleanMethod", [other._jni_ref]) @@ -419,7 +598,7 @@ def message( jni_ref = self._jni_ref result = _call_method( - b"org/kson/Message", + b"org/kson/api/Message", jni_ref, b"getMessage", b"()Ljava/lang/String;", @@ -436,10 +615,10 @@ def severity( jni_ref = self._jni_ref result = _call_method( - b"org/kson/Message", + b"org/kson/api/Message", jni_ref, b"getSeverity", - b"()Lorg/kson/MessageSeverity;", + b"()Lorg/kson/api/MessageSeverity;", "ObjectMethod", [] ) @@ -453,10 +632,10 @@ def start( jni_ref = self._jni_ref result = _call_method( - b"org/kson/Message", + b"org/kson/api/Message", jni_ref, b"getStart", - b"()Lorg/kson/Position;", + b"()Lorg/kson/api/Position;", "ObjectMethod", [] ) @@ -470,10 +649,10 @@ def end( jni_ref = self._jni_ref result = _call_method( - b"org/kson/Message", + b"org/kson/api/Message", jni_ref, b"getEnd", - b"()Lorg/kson/Position;", + b"()Lorg/kson/api/Position;", "ObjectMethod", [] ) @@ -481,11 +660,19 @@ def end( return cast(Any, (lambda x0: _from_kotlin_object(Position, x0))(result)) -class Token: - """[Token] produced by the lexing phase of a Kson parse""" +class KsonValue: _jni_ref: Any + KsonArray: TypeAlias + KsonNull: TypeAlias + KsonNumber: TypeAlias + KsonObject: TypeAlias + KsonString: TypeAlias + KsonBoolean: TypeAlias + KsonEmbed: TypeAlias + Decimal: TypeAlias + Integer: TypeAlias def __eq__(self, other): return _call_method(b"java/lang/Object", self._jni_ref, b"equals", b"(Ljava/lang/Object;)Z", "BooleanMethod", [other._jni_ref]) @@ -493,160 +680,51 @@ def __hash__(self): return _call_method(b"java/lang/Object", self._jni_ref, b"hashCode", b"()I", "IntMethod", []) - def token_type( + def start( self, - ) -> TokenType: + ) -> Position: jni_ref = self._jni_ref result = _call_method( - b"org/kson/Token", + b"org/kson/api/KsonValue", jni_ref, - b"getTokenType", - b"()Lorg/kson/TokenType;", + b"getStart", + b"()Lorg/kson/api/Position;", "ObjectMethod", [] ) - return cast(Any, (lambda x0: TokenType._from_kotlin_enum(x0))(result)) + return cast(Any, (lambda x0: _from_kotlin_object(Position, x0))(result)) - def text( + def end( self, - ) -> str: + ) -> Position: jni_ref = self._jni_ref result = _call_method( - b"org/kson/Token", + b"org/kson/api/KsonValue", jni_ref, - b"getText", - b"()Ljava/lang/String;", + b"getEnd", + b"()Lorg/kson/api/Position;", "ObjectMethod", [] ) - return cast(Any, (_java_string_to_python_str)(result)) + return cast(Any, (lambda x0: _from_kotlin_object(Position, x0))(result)) - def start( + def type( self, - ) -> Position: + ) -> KsonValueType: jni_ref = self._jni_ref result = _call_method( - b"org/kson/Token", + b"org/kson/api/KsonValue", jni_ref, - b"getStart", - b"()Lorg/kson/Position;", - "ObjectMethod", - [] - ) - - return cast(Any, (lambda x0: _from_kotlin_object(Position, x0))(result)) - - def end( - self, - ) -> Position: - - - jni_ref = self._jni_ref - result = _call_method( - b"org/kson/Token", - jni_ref, - b"getEnd", - b"()Lorg/kson/Position;", - "ObjectMethod", - [] - ) - - return cast(Any, (lambda x0: _from_kotlin_object(Position, x0))(result)) - - -class KsonValue: - """Represents a parsed [InternalKsonValue] in the public API""" - - _jni_ref: Any - - KsonNull: TypeAlias - KsonArray: TypeAlias - KsonString: TypeAlias - KsonEmbed: TypeAlias - KsonBoolean: TypeAlias - KsonObject: TypeAlias - KsonNumber: TypeAlias - Decimal: TypeAlias - Integer: TypeAlias - def __init__( - self, - start: Position, - end: Position, - ): - if start is None: - raise ValueError("`start` cannot be None") - if end is None: - raise ValueError("`end` cannot be None") - self._jni_ref = _construct( - b"org/kson/KsonValue", - b"(Lorg/kson/Position;Lorg/kson/Position;)V", - [ - - start._jni_ref, - end._jni_ref, - ] - ) - def __eq__(self, other): - return _call_method(b"java/lang/Object", self._jni_ref, b"equals", b"(Ljava/lang/Object;)Z", "BooleanMethod", [other._jni_ref]) - - def __hash__(self): - return _call_method(b"java/lang/Object", self._jni_ref, b"hashCode", b"()I", "IntMethod", []) - - - def start( - self, - ) -> Position: - - - jni_ref = self._jni_ref - result = _call_method( - b"org/kson/KsonValue", - jni_ref, - b"getStart", - b"()Lorg/kson/Position;", - "ObjectMethod", - [] - ) - - return cast(Any, (lambda x0: _from_kotlin_object(Position, x0))(result)) - - def end( - self, - ) -> Position: - - - jni_ref = self._jni_ref - result = _call_method( - b"org/kson/KsonValue", - jni_ref, - b"getEnd", - b"()Lorg/kson/Position;", - "ObjectMethod", - [] - ) - - return cast(Any, (lambda x0: _from_kotlin_object(Position, x0))(result)) - - def type( - self, - ) -> KsonValueType: - """Type discriminator for easier type checking in TypeScript/JavaScript""" - - - jni_ref = self._jni_ref - result = _call_method( - b"org/kson/KsonValue", - jni_ref, - b"getType", - b"()Lorg/kson/KsonValueType;", + b"getType", + b"()Lorg/kson/api/KsonValueType;", "ObjectMethod", [] ) @@ -656,38 +734,63 @@ def type( def _downcast(jni_ref) -> Any: match _jni_class_name(jni_ref): - case "org.kson.KsonValue$KsonNull": + case "org.kson.api.KsonValue$KsonArray": + return _from_kotlin_object(_KsonValue_KsonArray, jni_ref) + + case "org.kson.api.KsonValue$KsonNull": return _from_kotlin_object(_KsonValue_KsonNull, jni_ref) - case "org.kson.KsonValue$KsonArray": - return _from_kotlin_object(_KsonValue_KsonArray, jni_ref) + case "org.kson.api.KsonValue$KsonNumber": + return _from_kotlin_object(_KsonValue_KsonNumber, jni_ref) - case "org.kson.KsonValue$KsonString": - return _from_kotlin_object(_KsonValue_KsonString, jni_ref) + case "org.kson.api.KsonValue$KsonObject": + return _from_kotlin_object(_KsonValue_KsonObject, jni_ref) - case "org.kson.KsonValue$KsonEmbed": - return _from_kotlin_object(_KsonValue_KsonEmbed, jni_ref) + case "org.kson.api.KsonValue$KsonString": + return _from_kotlin_object(_KsonValue_KsonString, jni_ref) - case "org.kson.KsonValue$KsonBoolean": + case "org.kson.api.KsonValue$KsonBoolean": return _from_kotlin_object(_KsonValue_KsonBoolean, jni_ref) - case "org.kson.KsonValue$KsonObject": - return _from_kotlin_object(_KsonValue_KsonObject, jni_ref) - - case "org.kson.KsonValue$KsonNumber": - return _from_kotlin_object(_KsonValue_KsonNumber, jni_ref) + case "org.kson.api.KsonValue$KsonEmbed": + return _from_kotlin_object(_KsonValue_KsonEmbed, jni_ref) - case "org.kson.KsonValue$KsonNumber$Decimal": + case "org.kson.api.KsonValue$KsonNumber$Decimal": return _from_kotlin_object(_KsonValue_KsonNumber_Decimal, jni_ref) - case "org.kson.KsonValue$KsonNumber$Integer": + case "org.kson.api.KsonValue$KsonNumber$Integer": return _from_kotlin_object(_KsonValue_KsonNumber_Integer, jni_ref) class _KsonValue_KsonObject(KsonValue): - """A Kson object with key-value pairs""" _jni_ref: Any + def __init__( + self, + properties: Dict[str, KsonValue], + property_keys: Dict[str, _KsonValue_KsonString], + internal_start: Position, + internal_end: Position, + ): + if properties is None: + raise ValueError("`properties` cannot be None") + if property_keys is None: + raise ValueError("`property_keys` cannot be None") + if internal_start is None: + raise ValueError("`internal_start` cannot be None") + if internal_end is None: + raise ValueError("`internal_end` cannot be None") + self._jni_ref = _construct( + b"org/kson/api/KsonValue$KsonObject", + b"(Ljava/util/Map;Ljava/util/Map;Lorg/kson/api/Position;Lorg/kson/api/Position;)V", + [ + + _to_kotlin_map(properties), + _to_kotlin_map(property_keys), + internal_start._jni_ref, + internal_end._jni_ref, + ] + ) def __eq__(self, other): return _call_method(b"java/lang/Object", self._jni_ref, b"equals", b"(Ljava/lang/Object;)Z", "BooleanMethod", [other._jni_ref]) @@ -702,7 +805,7 @@ def properties( jni_ref = self._jni_ref result = _call_method( - b"org/kson/KsonValue$KsonObject", + b"org/kson/api/KsonValue$KsonObject", jni_ref, b"getProperties", b"()Ljava/util/Map;", @@ -719,7 +822,7 @@ def property_keys( jni_ref = self._jni_ref result = _call_method( - b"org/kson/KsonValue$KsonObject", + b"org/kson/api/KsonValue$KsonObject", jni_ref, b"getPropertyKeys", b"()Ljava/util/Map;", @@ -736,23 +839,61 @@ def type( jni_ref = self._jni_ref result = _call_method( - b"org/kson/KsonValue$KsonObject", + b"org/kson/api/KsonValue$KsonObject", jni_ref, b"getType", - b"()Lorg/kson/KsonValueType;", + b"()Lorg/kson/api/KsonValueType;", "ObjectMethod", [] ) return cast(Any, (lambda x0: KsonValueType._from_kotlin_enum(x0))(result)) + + def to_string( + self, + ) -> str: + + + jni_ref = self._jni_ref + result = _call_method( + b"org/kson/api/KsonValue$KsonObject", + jni_ref, + b"toString", + b"()Ljava/lang/String;", + "ObjectMethod", + [] + ) + + return cast(Any, (_java_string_to_python_str)(result)) KsonValue.KsonObject = _KsonValue_KsonObject class _KsonValue_KsonArray(KsonValue): - """A Kson array with elements""" _jni_ref: Any + def __init__( + self, + elements: List[KsonValue], + internal_start: Position, + internal_end: Position, + ): + if elements is None: + raise ValueError("`elements` cannot be None") + if internal_start is None: + raise ValueError("`internal_start` cannot be None") + if internal_end is None: + raise ValueError("`internal_end` cannot be None") + self._jni_ref = _construct( + b"org/kson/api/KsonValue$KsonArray", + b"(Ljava/util/List;Lorg/kson/api/Position;Lorg/kson/api/Position;)V", + [ + + _to_kotlin_list(elements), + internal_start._jni_ref, + internal_end._jni_ref, + ] + ) def __eq__(self, other): return _call_method(b"java/lang/Object", self._jni_ref, b"equals", b"(Ljava/lang/Object;)Z", "BooleanMethod", [other._jni_ref]) @@ -767,7 +908,7 @@ def elements( jni_ref = self._jni_ref result = _call_method( - b"org/kson/KsonValue$KsonArray", + b"org/kson/api/KsonValue$KsonArray", jni_ref, b"getElements", b"()Ljava/util/List;", @@ -784,10 +925,10 @@ def type( jni_ref = self._jni_ref result = _call_method( - b"org/kson/KsonValue$KsonArray", + b"org/kson/api/KsonValue$KsonArray", jni_ref, b"getType", - b"()Lorg/kson/KsonValueType;", + b"()Lorg/kson/api/KsonValueType;", "ObjectMethod", [] ) @@ -797,10 +938,31 @@ def type( class _KsonValue_KsonString(KsonValue): - """A Kson string value""" _jni_ref: Any + def __init__( + self, + value: str, + internal_start: Position, + internal_end: Position, + ): + if value is None: + raise ValueError("`value` cannot be None") + if internal_start is None: + raise ValueError("`internal_start` cannot be None") + if internal_end is None: + raise ValueError("`internal_end` cannot be None") + self._jni_ref = _construct( + b"org/kson/api/KsonValue$KsonString", + b"(Ljava/lang/String;Lorg/kson/api/Position;Lorg/kson/api/Position;)V", + [ + + _python_str_to_java_string(value), + internal_start._jni_ref, + internal_end._jni_ref, + ] + ) def __eq__(self, other): return _call_method(b"java/lang/Object", self._jni_ref, b"equals", b"(Ljava/lang/Object;)Z", "BooleanMethod", [other._jni_ref]) @@ -815,7 +977,7 @@ def value( jni_ref = self._jni_ref result = _call_method( - b"org/kson/KsonValue$KsonString", + b"org/kson/api/KsonValue$KsonString", jni_ref, b"getValue", b"()Ljava/lang/String;", @@ -832,10 +994,10 @@ def type( jni_ref = self._jni_ref result = _call_method( - b"org/kson/KsonValue$KsonString", + b"org/kson/api/KsonValue$KsonString", jni_ref, b"getType", - b"()Lorg/kson/KsonValueType;", + b"()Lorg/kson/api/KsonValueType;", "ObjectMethod", [] ) @@ -845,30 +1007,11 @@ def type( class _KsonValue_KsonNumber(KsonValue): - """A Kson number value.""" _jni_ref: Any Decimal: TypeAlias Integer: TypeAlias - def __init__( - self, - start: Position, - end: Position, - ): - if start is None: - raise ValueError("`start` cannot be None") - if end is None: - raise ValueError("`end` cannot be None") - self._jni_ref = _construct( - b"org/kson/KsonValue$KsonNumber", - b"(Lorg/kson/Position;Lorg/kson/Position;)V", - [ - - start._jni_ref, - end._jni_ref, - ] - ) def __eq__(self, other): return _call_method(b"java/lang/Object", self._jni_ref, b"equals", b"(Ljava/lang/Object;)Z", "BooleanMethod", [other._jni_ref]) @@ -879,10 +1022,10 @@ def __hash__(self): def _downcast(jni_ref) -> Any: match _jni_class_name(jni_ref): - case "org.kson.KsonValue$KsonNumber$Decimal": + case "org.kson.api.KsonValue$KsonNumber$Decimal": return _from_kotlin_object(_KsonValue_KsonNumber_Decimal, jni_ref) - case "org.kson.KsonValue$KsonNumber$Integer": + case "org.kson.api.KsonValue$KsonNumber$Integer": return _from_kotlin_object(_KsonValue_KsonNumber_Integer, jni_ref) KsonValue.KsonNumber = _KsonValue_KsonNumber @@ -890,6 +1033,28 @@ class _KsonValue_KsonNumber_Integer(KsonValue.KsonNumber): _jni_ref: Any + def __init__( + self, + value: int, + internal_start: Position, + internal_end: Position, + ): + if value is None: + raise ValueError("`value` cannot be None") + if internal_start is None: + raise ValueError("`internal_start` cannot be None") + if internal_end is None: + raise ValueError("`internal_end` cannot be None") + self._jni_ref = _construct( + b"org/kson/api/KsonValue$KsonNumber$Integer", + b"(ILorg/kson/api/Position;Lorg/kson/api/Position;)V", + [ + + ffi.cast('jint', value), + internal_start._jni_ref, + internal_end._jni_ref, + ] + ) def __eq__(self, other): return _call_method(b"java/lang/Object", self._jni_ref, b"equals", b"(Ljava/lang/Object;)Z", "BooleanMethod", [other._jni_ref]) @@ -904,7 +1069,7 @@ def value( jni_ref = self._jni_ref result = _call_method( - b"org/kson/KsonValue$KsonNumber$Integer", + b"org/kson/api/KsonValue$KsonNumber$Integer", jni_ref, b"getValue", b"()I", @@ -921,10 +1086,10 @@ def internal_start( jni_ref = self._jni_ref result = _call_method( - b"org/kson/KsonValue$KsonNumber$Integer", + b"org/kson/api/KsonValue$KsonNumber$Integer", jni_ref, b"getInternalStart", - b"()Lorg/kson/Position;", + b"()Lorg/kson/api/Position;", "ObjectMethod", [] ) @@ -938,10 +1103,10 @@ def internal_end( jni_ref = self._jni_ref result = _call_method( - b"org/kson/KsonValue$KsonNumber$Integer", + b"org/kson/api/KsonValue$KsonNumber$Integer", jni_ref, b"getInternalEnd", - b"()Lorg/kson/Position;", + b"()Lorg/kson/api/Position;", "ObjectMethod", [] ) @@ -955,10 +1120,10 @@ def type( jni_ref = self._jni_ref result = _call_method( - b"org/kson/KsonValue$KsonNumber$Integer", + b"org/kson/api/KsonValue$KsonNumber$Integer", jni_ref, b"getType", - b"()Lorg/kson/KsonValueType;", + b"()Lorg/kson/api/KsonValueType;", "ObjectMethod", [] ) @@ -971,6 +1136,28 @@ class _KsonValue_KsonNumber_Decimal(KsonValue.KsonNumber): _jni_ref: Any + def __init__( + self, + value: float, + internal_start: Position, + internal_end: Position, + ): + if value is None: + raise ValueError("`value` cannot be None") + if internal_start is None: + raise ValueError("`internal_start` cannot be None") + if internal_end is None: + raise ValueError("`internal_end` cannot be None") + self._jni_ref = _construct( + b"org/kson/api/KsonValue$KsonNumber$Decimal", + b"(DLorg/kson/api/Position;Lorg/kson/api/Position;)V", + [ + + ffi.cast('jdouble', value), + internal_start._jni_ref, + internal_end._jni_ref, + ] + ) def __eq__(self, other): return _call_method(b"java/lang/Object", self._jni_ref, b"equals", b"(Ljava/lang/Object;)Z", "BooleanMethod", [other._jni_ref]) @@ -985,7 +1172,7 @@ def value( jni_ref = self._jni_ref result = _call_method( - b"org/kson/KsonValue$KsonNumber$Decimal", + b"org/kson/api/KsonValue$KsonNumber$Decimal", jni_ref, b"getValue", b"()D", @@ -1002,10 +1189,10 @@ def type( jni_ref = self._jni_ref result = _call_method( - b"org/kson/KsonValue$KsonNumber$Decimal", + b"org/kson/api/KsonValue$KsonNumber$Decimal", jni_ref, b"getType", - b"()Lorg/kson/KsonValueType;", + b"()Lorg/kson/api/KsonValueType;", "ObjectMethod", [] ) @@ -1016,10 +1203,31 @@ def type( class _KsonValue_KsonBoolean(KsonValue): - """A Kson boolean value""" _jni_ref: Any + def __init__( + self, + value: bool, + internal_start: Position, + internal_end: Position, + ): + if value is None: + raise ValueError("`value` cannot be None") + if internal_start is None: + raise ValueError("`internal_start` cannot be None") + if internal_end is None: + raise ValueError("`internal_end` cannot be None") + self._jni_ref = _construct( + b"org/kson/api/KsonValue$KsonBoolean", + b"(ZLorg/kson/api/Position;Lorg/kson/api/Position;)V", + [ + + ffi.cast('jboolean', value), + internal_start._jni_ref, + internal_end._jni_ref, + ] + ) def __eq__(self, other): return _call_method(b"java/lang/Object", self._jni_ref, b"equals", b"(Ljava/lang/Object;)Z", "BooleanMethod", [other._jni_ref]) @@ -1034,7 +1242,7 @@ def value( jni_ref = self._jni_ref result = _call_method( - b"org/kson/KsonValue$KsonBoolean", + b"org/kson/api/KsonValue$KsonBoolean", jni_ref, b"getValue", b"()Z", @@ -1051,10 +1259,10 @@ def type( jni_ref = self._jni_ref result = _call_method( - b"org/kson/KsonValue$KsonBoolean", + b"org/kson/api/KsonValue$KsonBoolean", jni_ref, b"getType", - b"()Lorg/kson/KsonValueType;", + b"()Lorg/kson/api/KsonValueType;", "ObjectMethod", [] ) @@ -1064,10 +1272,27 @@ def type( class _KsonValue_KsonNull(KsonValue): - """A Kson null value""" _jni_ref: Any + def __init__( + self, + internal_start: Position, + internal_end: Position, + ): + if internal_start is None: + raise ValueError("`internal_start` cannot be None") + if internal_end is None: + raise ValueError("`internal_end` cannot be None") + self._jni_ref = _construct( + b"org/kson/api/KsonValue$KsonNull", + b"(Lorg/kson/api/Position;Lorg/kson/api/Position;)V", + [ + + internal_start._jni_ref, + internal_end._jni_ref, + ] + ) def __eq__(self, other): return _call_method(b"java/lang/Object", self._jni_ref, b"equals", b"(Ljava/lang/Object;)Z", "BooleanMethod", [other._jni_ref]) @@ -1082,10 +1307,10 @@ def type( jni_ref = self._jni_ref result = _call_method( - b"org/kson/KsonValue$KsonNull", + b"org/kson/api/KsonValue$KsonNull", jni_ref, b"getType", - b"()Lorg/kson/KsonValueType;", + b"()Lorg/kson/api/KsonValueType;", "ObjectMethod", [] ) @@ -1095,10 +1320,33 @@ def type( class _KsonValue_KsonEmbed(KsonValue): - """A Kson embed block""" _jni_ref: Any + def __init__( + self, + tag: Optional[str], + content: str, + internal_start: Position, + internal_end: Position, + ): + if content is None: + raise ValueError("`content` cannot be None") + if internal_start is None: + raise ValueError("`internal_start` cannot be None") + if internal_end is None: + raise ValueError("`internal_end` cannot be None") + self._jni_ref = _construct( + b"org/kson/api/KsonValue$KsonEmbed", + b"(Ljava/lang/String;Ljava/lang/String;Lorg/kson/api/Position;Lorg/kson/api/Position;)V", + [ + + _python_str_to_java_string(tag) if tag is not None else ffi.NULL, + _python_str_to_java_string(content), + internal_start._jni_ref, + internal_end._jni_ref, + ] + ) def __eq__(self, other): return _call_method(b"java/lang/Object", self._jni_ref, b"equals", b"(Ljava/lang/Object;)Z", "BooleanMethod", [other._jni_ref]) @@ -1113,7 +1361,7 @@ def tag( jni_ref = self._jni_ref result = _call_method( - b"org/kson/KsonValue$KsonEmbed", + b"org/kson/api/KsonValue$KsonEmbed", jni_ref, b"getTag", b"()Ljava/lang/String;", @@ -1130,7 +1378,7 @@ def content( jni_ref = self._jni_ref result = _call_method( - b"org/kson/KsonValue$KsonEmbed", + b"org/kson/api/KsonValue$KsonEmbed", jni_ref, b"getContent", b"()Ljava/lang/String;", @@ -1147,10 +1395,10 @@ def type( jni_ref = self._jni_ref result = _call_method( - b"org/kson/KsonValue$KsonEmbed", + b"org/kson/api/KsonValue$KsonEmbed", jni_ref, b"getType", - b"()Lorg/kson/KsonValueType;", + b"()Lorg/kson/api/KsonValueType;", "ObjectMethod", [] ) @@ -1160,8 +1408,7 @@ def type( -class SchemaValidator: - """A validator that can check if Kson source conforms to a schema.""" +class SchemaValidatorService: _jni_ref: Any @@ -1178,18 +1425,12 @@ def validate( filepath: Optional[str], ) -> List[Message]: - """Validates the given Kson source against this validator's schema. - @param kson The Kson source to validate - @param filepath Optional filepath of the document being validated, used by validators to determine which rules to apply - - @return A list of validation error messages, or empty list if valid - """ if kson is None: raise ValueError("`kson` cannot be None") jni_ref = self._jni_ref result = _call_method( - b"org/kson/SchemaValidator", + b"org/kson/api/SchemaValidatorService", jni_ref, b"validate", b"(Ljava/lang/String;Ljava/lang/String;)Ljava/util/List;", @@ -1204,54 +1445,61 @@ def validate( return cast(Any, (lambda x0: _from_kotlin_list(x0, lambda x1: _from_kotlin_object(Message, x1)))(result)) -class EmbedRuleResult: +class TranspileOptions: _jni_ref: Any - Success: TypeAlias - Failure: TypeAlias - def __init__( - self, - - ): - - self._jni_ref = _construct( - b"org/kson/EmbedRuleResult", - b"()V", - [] - ) + Yaml: TypeAlias + Json: TypeAlias def __eq__(self, other): return _call_method(b"java/lang/Object", self._jni_ref, b"equals", b"(Ljava/lang/Object;)Z", "BooleanMethod", [other._jni_ref]) def __hash__(self): return _call_method(b"java/lang/Object", self._jni_ref, b"hashCode", b"()I", "IntMethod", []) + + def retain_embed_tags( + self, + ) -> bool: + + + jni_ref = self._jni_ref + result = _call_method( + b"org/kson/api/TranspileOptions", + jni_ref, + b"getRetainEmbedTags", + b"()Z", + "BooleanMethod", + [] + ) + + return cast(Any, (lambda x0: x0)(result)) @staticmethod def _downcast(jni_ref) -> Any: match _jni_class_name(jni_ref): - case "org.kson.EmbedRuleResult$Success": - return _from_kotlin_object(_EmbedRuleResult_Success, jni_ref) + case "org.kson.api.TranspileOptions$Yaml": + return _from_kotlin_object(_TranspileOptions_Yaml, jni_ref) - case "org.kson.EmbedRuleResult$Failure": - return _from_kotlin_object(_EmbedRuleResult_Failure, jni_ref) + case "org.kson.api.TranspileOptions$Json": + return _from_kotlin_object(_TranspileOptions_Json, jni_ref) -class _EmbedRuleResult_Success(EmbedRuleResult): +class _TranspileOptions_Json(TranspileOptions): _jni_ref: Any def __init__( self, - embed_rule: EmbedRule, + retain_embed_tags: bool, ): - if embed_rule is None: - raise ValueError("`embed_rule` cannot be None") + if retain_embed_tags is None: + raise ValueError("`retain_embed_tags` cannot be None") self._jni_ref = _construct( - b"org/kson/EmbedRuleResult$Success", - b"(Lorg/kson/EmbedRule;)V", + b"org/kson/api/TranspileOptions$Json", + b"(Z)V", [ - embed_rule._jni_ref, + ffi.cast('jboolean', retain_embed_tags), ] ) def __eq__(self, other): @@ -1261,41 +1509,41 @@ def __hash__(self): return _call_method(b"java/lang/Object", self._jni_ref, b"hashCode", b"()I", "IntMethod", []) - def embed_rule( + def retain_embed_tags( self, - ) -> EmbedRule: + ) -> bool: jni_ref = self._jni_ref result = _call_method( - b"org/kson/EmbedRuleResult$Success", + b"org/kson/api/TranspileOptions$Json", jni_ref, - b"getEmbedRule", - b"()Lorg/kson/EmbedRule;", - "ObjectMethod", + b"getRetainEmbedTags", + b"()Z", + "BooleanMethod", [] ) - return cast(Any, (lambda x0: _from_kotlin_object(EmbedRule, x0))(result)) -EmbedRuleResult.Success = _EmbedRuleResult_Success + return cast(Any, (lambda x0: x0)(result)) +TranspileOptions.Json = _TranspileOptions_Json -class _EmbedRuleResult_Failure(EmbedRuleResult): +class _TranspileOptions_Yaml(TranspileOptions): _jni_ref: Any def __init__( self, - message: str, + retain_embed_tags: bool, ): - if message is None: - raise ValueError("`message` cannot be None") + if retain_embed_tags is None: + raise ValueError("`retain_embed_tags` cannot be None") self._jni_ref = _construct( - b"org/kson/EmbedRuleResult$Failure", - b"(Ljava/lang/String;)V", + b"org/kson/api/TranspileOptions$Yaml", + b"(Z)V", [ - _python_str_to_java_string(message), + ffi.cast('jboolean', retain_embed_tags), ] ) def __eq__(self, other): @@ -1305,107 +1553,32 @@ def __hash__(self): return _call_method(b"java/lang/Object", self._jni_ref, b"hashCode", b"()I", "IntMethod", []) - def message( - self, - ) -> str: - - - jni_ref = self._jni_ref - result = _call_method( - b"org/kson/EmbedRuleResult$Failure", - jni_ref, - b"getMessage", - b"()Ljava/lang/String;", - "ObjectMethod", - [] - ) - - return cast(Any, (_java_string_to_python_str)(result)) -EmbedRuleResult.Failure = _EmbedRuleResult_Failure - - - -class Analysis: - """The result of statically analyzing a Kson document""" - - _jni_ref: Any - - def __eq__(self, other): - return _call_method(b"java/lang/Object", self._jni_ref, b"equals", b"(Ljava/lang/Object;)Z", "BooleanMethod", [other._jni_ref]) - - def __hash__(self): - return _call_method(b"java/lang/Object", self._jni_ref, b"hashCode", b"()I", "IntMethod", []) - - - def errors( - self, - ) -> List[Message]: - - - jni_ref = self._jni_ref - result = _call_method( - b"org/kson/Analysis", - jni_ref, - b"getErrors", - b"()Ljava/util/List;", - "ObjectMethod", - [] - ) - - return cast(Any, (lambda x0: _from_kotlin_list(x0, lambda x1: _from_kotlin_object(Message, x1)))(result)) - - def tokens( + def retain_embed_tags( self, - ) -> List[Token]: + ) -> bool: jni_ref = self._jni_ref result = _call_method( - b"org/kson/Analysis", + b"org/kson/api/TranspileOptions$Yaml", jni_ref, - b"getTokens", - b"()Ljava/util/List;", - "ObjectMethod", + b"getRetainEmbedTags", + b"()Z", + "BooleanMethod", [] ) - return cast(Any, (lambda x0: _from_kotlin_list(x0, lambda x1: _from_kotlin_object(Token, x1)))(result)) - - def kson_value( - self, - ) -> Optional[KsonValue]: - - - jni_ref = self._jni_ref - result = _call_method( - b"org/kson/Analysis", - jni_ref, - b"getKsonValue", - b"()Lorg/kson/KsonValue;", - "ObjectMethod", - [] - ) + return cast(Any, (lambda x0: x0)(result)) +TranspileOptions.Yaml = _TranspileOptions_Yaml - return cast(Any, (lambda x0: None if x0 == ffi.NULL else (lambda x0: KsonValue._downcast(x0))(x0))(result)) class Result: - """Result of a Kson conversion operation""" _jni_ref: Any Failure: TypeAlias Success: TypeAlias - def __init__( - self, - - ): - - self._jni_ref = _construct( - b"org/kson/Result", - b"()V", - [] - ) def __eq__(self, other): return _call_method(b"java/lang/Object", self._jni_ref, b"equals", b"(Ljava/lang/Object;)Z", "BooleanMethod", [other._jni_ref]) @@ -1416,10 +1589,10 @@ def __hash__(self): def _downcast(jni_ref) -> Any: match _jni_class_name(jni_ref): - case "org.kson.Result$Failure": + case "org.kson.api.Result$Failure": return _from_kotlin_object(_Result_Failure, jni_ref) - case "org.kson.Result$Success": + case "org.kson.api.Result$Success": return _from_kotlin_object(_Result_Success, jni_ref) class _Result_Success(Result): @@ -1433,7 +1606,7 @@ def __init__( if output is None: raise ValueError("`output` cannot be None") self._jni_ref = _construct( - b"org/kson/Result$Success", + b"org/kson/api/Result$Success", b"(Ljava/lang/String;)V", [ @@ -1454,7 +1627,7 @@ def output( jni_ref = self._jni_ref result = _call_method( - b"org/kson/Result$Success", + b"org/kson/api/Result$Success", jni_ref, b"getOutput", b"()Ljava/lang/String;", @@ -1477,7 +1650,7 @@ def __init__( if errors is None: raise ValueError("`errors` cannot be None") self._jni_ref = _construct( - b"org/kson/Result$Failure", + b"org/kson/api/Result$Failure", b"(Ljava/util/List;)V", [ @@ -1498,7 +1671,7 @@ def errors( jni_ref = self._jni_ref result = _call_method( - b"org/kson/Result$Failure", + b"org/kson/api/Result$Failure", jni_ref, b"getErrors", b"()Ljava/util/List;", @@ -1511,22 +1684,35 @@ def errors( -class SchemaResult: - """A [parseSchema] result""" +class Token: _jni_ref: Any - Failure: TypeAlias - Success: TypeAlias def __init__( self, - + token_type: TokenType, + text: str, + start: Position, + end: Position, ): - + if token_type is None: + raise ValueError("`token_type` cannot be None") + if text is None: + raise ValueError("`text` cannot be None") + if start is None: + raise ValueError("`start` cannot be None") + if end is None: + raise ValueError("`end` cannot be None") self._jni_ref = _construct( - b"org/kson/SchemaResult", - b"()V", - [] + b"org/kson/api/Token", + b"(Lorg/kson/api/TokenType;Ljava/lang/String;Lorg/kson/api/Position;Lorg/kson/api/Position;)V", + [ + + token_type._to_kotlin_enum(), + _python_str_to_java_string(text), + start._jni_ref, + end._jni_ref, + ] ) def __eq__(self, other): return _call_method(b"java/lang/Object", self._jni_ref, b"equals", b"(Ljava/lang/Object;)Z", "BooleanMethod", [other._jni_ref]) @@ -1534,76 +1720,98 @@ def __eq__(self, other): def __hash__(self): return _call_method(b"java/lang/Object", self._jni_ref, b"hashCode", b"()I", "IntMethod", []) - @staticmethod - def _downcast(jni_ref) -> Any: - match _jni_class_name(jni_ref): - case "org.kson.SchemaResult$Failure": - return _from_kotlin_object(_SchemaResult_Failure, jni_ref) + def token_type( + self, + ) -> TokenType: - case "org.kson.SchemaResult$Success": - return _from_kotlin_object(_SchemaResult_Success, jni_ref) -class _SchemaResult_Success(SchemaResult): + jni_ref = self._jni_ref + result = _call_method( + b"org/kson/api/Token", + jni_ref, + b"getTokenType", + b"()Lorg/kson/api/TokenType;", + "ObjectMethod", + [] + ) - _jni_ref: Any + return cast(Any, (lambda x0: TokenType._from_kotlin_enum(x0))(result)) - def __init__( + def text( self, - schema_validator: SchemaValidator, - ): - if schema_validator is None: - raise ValueError("`schema_validator` cannot be None") - self._jni_ref = _construct( - b"org/kson/SchemaResult$Success", - b"(Lorg/kson/SchemaValidator;)V", - [ + ) -> str: - schema_validator._jni_ref, - ] + + jni_ref = self._jni_ref + result = _call_method( + b"org/kson/api/Token", + jni_ref, + b"getText", + b"()Ljava/lang/String;", + "ObjectMethod", + [] ) - def __eq__(self, other): - return _call_method(b"java/lang/Object", self._jni_ref, b"equals", b"(Ljava/lang/Object;)Z", "BooleanMethod", [other._jni_ref]) - def __hash__(self): - return _call_method(b"java/lang/Object", self._jni_ref, b"hashCode", b"()I", "IntMethod", []) + return cast(Any, (_java_string_to_python_str)(result)) + + def start( + self, + ) -> Position: - def schema_validator( + jni_ref = self._jni_ref + result = _call_method( + b"org/kson/api/Token", + jni_ref, + b"getStart", + b"()Lorg/kson/api/Position;", + "ObjectMethod", + [] + ) + + return cast(Any, (lambda x0: _from_kotlin_object(Position, x0))(result)) + + def end( self, - ) -> SchemaValidator: + ) -> Position: jni_ref = self._jni_ref result = _call_method( - b"org/kson/SchemaResult$Success", + b"org/kson/api/Token", jni_ref, - b"getSchemaValidator", - b"()Lorg/kson/SchemaValidator;", + b"getEnd", + b"()Lorg/kson/api/Position;", "ObjectMethod", [] ) - return cast(Any, (lambda x0: _from_kotlin_object(SchemaValidator, x0))(result)) -SchemaResult.Success = _SchemaResult_Success + return cast(Any, (lambda x0: _from_kotlin_object(Position, x0))(result)) -class _SchemaResult_Failure(SchemaResult): +class Analysis: _jni_ref: Any def __init__( self, errors: List[Message], + tokens: List[Token], + kson_value: Optional[KsonValue], ): if errors is None: raise ValueError("`errors` cannot be None") + if tokens is None: + raise ValueError("`tokens` cannot be None") self._jni_ref = _construct( - b"org/kson/SchemaResult$Failure", - b"(Ljava/util/List;)V", + b"org/kson/api/Analysis", + b"(Ljava/util/List;Ljava/util/List;Lorg/kson/api/KsonValue;)V", [ _to_kotlin_list(errors), + _to_kotlin_list(tokens), + kson_value._jni_ref if kson_value is not None else ffi.NULL, ] ) def __eq__(self, other): @@ -1620,7 +1828,7 @@ def errors( jni_ref = self._jni_ref result = _call_method( - b"org/kson/SchemaResult$Failure", + b"org/kson/api/Analysis", jni_ref, b"getErrors", b"()Ljava/util/List;", @@ -1629,77 +1837,60 @@ def errors( ) return cast(Any, (lambda x0: _from_kotlin_list(x0, lambda x1: _from_kotlin_object(Message, x1)))(result)) -SchemaResult.Failure = _SchemaResult_Failure - - - -class TranspileOptions: - """Core interface for transpilation options shared across all output formats.""" - - _jni_ref: Any - Json: TypeAlias - Yaml: TypeAlias - def __init__( + def tokens( self, + ) -> List[Token]: - ): - self._jni_ref = _construct( - b"org/kson/TranspileOptions", - b"()V", + jni_ref = self._jni_ref + result = _call_method( + b"org/kson/api/Analysis", + jni_ref, + b"getTokens", + b"()Ljava/util/List;", + "ObjectMethod", [] ) - def __eq__(self, other): - return _call_method(b"java/lang/Object", self._jni_ref, b"equals", b"(Ljava/lang/Object;)Z", "BooleanMethod", [other._jni_ref]) - - def __hash__(self): - return _call_method(b"java/lang/Object", self._jni_ref, b"hashCode", b"()I", "IntMethod", []) + return cast(Any, (lambda x0: _from_kotlin_list(x0, lambda x1: _from_kotlin_object(Token, x1)))(result)) - def retain_embed_tags( + def kson_value( self, - ) -> bool: + ) -> Optional[KsonValue]: jni_ref = self._jni_ref result = _call_method( - b"org/kson/TranspileOptions", + b"org/kson/api/Analysis", jni_ref, - b"getRetainEmbedTags", - b"()Z", - "BooleanMethod", + b"getKsonValue", + b"()Lorg/kson/api/KsonValue;", + "ObjectMethod", [] ) - return cast(Any, (lambda x0: x0)(result)) - @staticmethod - def _downcast(jni_ref) -> Any: - match _jni_class_name(jni_ref): - - case "org.kson.TranspileOptions$Json": - return _from_kotlin_object(_TranspileOptions_Json, jni_ref) + return cast(Any, (lambda x0: None if x0 == ffi.NULL else (lambda x0: KsonValue._downcast(x0))(x0))(result)) - case "org.kson.TranspileOptions$Yaml": - return _from_kotlin_object(_TranspileOptions_Yaml, jni_ref) -class _TranspileOptions_Json(TranspileOptions): - """Options for transpiling Kson to JSON.""" +class EmbedRule: _jni_ref: Any def __init__( self, - retain_embed_tags: bool, + path_pattern: str, + tag: Optional[str], ): - if retain_embed_tags is None: - raise ValueError("`retain_embed_tags` cannot be None") + if path_pattern is None: + raise ValueError("`path_pattern` cannot be None") self._jni_ref = _construct( - b"org/kson/TranspileOptions$Json", - b"(Z)V", + b"org/kson/api/EmbedRule", + b"(Ljava/lang/String;Ljava/lang/String;)V", [ - ffi.cast('jboolean', retain_embed_tags), + _python_str_to_java_string(path_pattern), + _python_str_to_java_string(tag) if tag is not None else ffi.NULL, ] ) def __eq__(self, other): @@ -1709,42 +1900,65 @@ def __hash__(self): return _call_method(b"java/lang/Object", self._jni_ref, b"hashCode", b"()I", "IntMethod", []) - def retain_embed_tags( + def path_pattern( self, - ) -> bool: + ) -> str: jni_ref = self._jni_ref result = _call_method( - b"org/kson/TranspileOptions$Json", + b"org/kson/api/EmbedRule", jni_ref, - b"getRetainEmbedTags", - b"()Z", - "BooleanMethod", + b"getPathPattern", + b"()Ljava/lang/String;", + "ObjectMethod", [] ) - return cast(Any, (lambda x0: x0)(result)) -TranspileOptions.Json = _TranspileOptions_Json + return cast(Any, (_java_string_to_python_str)(result)) + def tag( + self, + ) -> Optional[str]: -class _TranspileOptions_Yaml(TranspileOptions): - """Options for transpiling Kson to YAML.""" + + jni_ref = self._jni_ref + result = _call_method( + b"org/kson/api/EmbedRule", + jni_ref, + b"getTag", + b"()Ljava/lang/String;", + "ObjectMethod", + [] + ) + + return cast(Any, (lambda x0: None if x0 == ffi.NULL else (_java_string_to_python_str)(x0))(result)) + + +class FormatOptions: _jni_ref: Any def __init__( self, - retain_embed_tags: bool, + indent_type: IndentType, + formatting_style: FormattingStyle, + embed_block_rules: List[EmbedRule], ): - if retain_embed_tags is None: - raise ValueError("`retain_embed_tags` cannot be None") + if indent_type is None: + raise ValueError("`indent_type` cannot be None") + if formatting_style is None: + raise ValueError("`formatting_style` cannot be None") + if embed_block_rules is None: + raise ValueError("`embed_block_rules` cannot be None") self._jni_ref = _construct( - b"org/kson/TranspileOptions$Yaml", - b"(Z)V", + b"org/kson/api/FormatOptions", + b"(Lorg/kson/api/IndentType;Lorg/kson/api/FormattingStyle;Ljava/util/List;)V", [ - ffi.cast('jboolean', retain_embed_tags), + indent_type._jni_ref, + formatting_style._to_kotlin_enum(), + _to_kotlin_list(embed_block_rules), ] ) def __eq__(self, other): @@ -1754,24 +1968,56 @@ def __hash__(self): return _call_method(b"java/lang/Object", self._jni_ref, b"hashCode", b"()I", "IntMethod", []) - def retain_embed_tags( + def indent_type( self, - ) -> bool: + ) -> IndentType: jni_ref = self._jni_ref result = _call_method( - b"org/kson/TranspileOptions$Yaml", + b"org/kson/api/FormatOptions", jni_ref, - b"getRetainEmbedTags", - b"()Z", - "BooleanMethod", + b"getIndentType", + b"()Lorg/kson/api/IndentType;", + "ObjectMethod", [] ) - return cast(Any, (lambda x0: x0)(result)) -TranspileOptions.Yaml = _TranspileOptions_Yaml + return cast(Any, (lambda x0: IndentType._downcast(x0))(result)) + def formatting_style( + self, + ) -> FormattingStyle: + + + jni_ref = self._jni_ref + result = _call_method( + b"org/kson/api/FormatOptions", + jni_ref, + b"getFormattingStyle", + b"()Lorg/kson/api/FormattingStyle;", + "ObjectMethod", + [] + ) + + return cast(Any, (lambda x0: FormattingStyle._from_kotlin_enum(x0))(result)) + + def embed_block_rules( + self, + ) -> List[EmbedRule]: + + + jni_ref = self._jni_ref + result = _call_method( + b"org/kson/api/FormatOptions", + jni_ref, + b"getEmbedBlockRules", + b"()Ljava/util/List;", + "ObjectMethod", + [] + ) + + return cast(Any, (lambda x0: _from_kotlin_list(x0, lambda x1: _from_kotlin_object(EmbedRule, x1)))(result)) class Kson: @@ -1808,7 +2054,7 @@ def format( b"org/kson/Kson", jni_ref, b"format", - b"(Ljava/lang/String;Lorg/kson/FormatOptions;)Ljava/lang/String;", + b"(Ljava/lang/String;Lorg/kson/api/FormatOptions;)Ljava/lang/String;", "ObjectMethod", [ @@ -1841,7 +2087,7 @@ def to_json( b"org/kson/Kson", jni_ref, b"toJson", - b"(Ljava/lang/String;Lorg/kson/TranspileOptions$Json;)Lorg/kson/Result;", + b"(Ljava/lang/String;Lorg/kson/api/TranspileOptions$Json;)Lorg/kson/api/Result;", "ObjectMethod", [ @@ -1874,7 +2120,7 @@ def to_yaml( b"org/kson/Kson", jni_ref, b"toYaml", - b"(Ljava/lang/String;Lorg/kson/TranspileOptions$Yaml;)Lorg/kson/Result;", + b"(Ljava/lang/String;Lorg/kson/api/TranspileOptions$Yaml;)Lorg/kson/api/Result;", "ObjectMethod", [ @@ -1904,7 +2150,7 @@ def analyze( b"org/kson/Kson", jni_ref, b"analyze", - b"(Ljava/lang/String;Ljava/lang/String;)Lorg/kson/Analysis;", + b"(Ljava/lang/String;Ljava/lang/String;)Lorg/kson/api/Analysis;", "ObjectMethod", [ @@ -1933,7 +2179,7 @@ def parse_schema( b"org/kson/Kson", jni_ref, b"parseSchema", - b"(Ljava/lang/String;)Lorg/kson/SchemaResult;", + b"(Ljava/lang/String;)Lorg/kson/api/SchemaResult;", "ObjectMethod", [ @@ -1944,231 +2190,66 @@ def parse_schema( return cast(Any, (lambda x0: SchemaResult._downcast(x0))(result)) -class EmbedRule: - """A rule for formatting string values at specific paths as embed blocks. - - When formatting KSON, strings at paths matching [pathPattern] will be rendered - as embed blocks instead of regular strings. - - **Warning:** JsonPointerGlob syntax is experimental and may change in future versions. - """ - - _jni_ref: Any - - def __eq__(self, other): - return _call_method(b"java/lang/Object", self._jni_ref, b"equals", b"(Ljava/lang/Object;)Z", "BooleanMethod", [other._jni_ref]) - - def __hash__(self): - return _call_method(b"java/lang/Object", self._jni_ref, b"hashCode", b"()I", "IntMethod", []) - - - def path_pattern( - self, - ) -> str: - - - jni_ref = self._jni_ref - result = _call_method( - b"org/kson/EmbedRule", - jni_ref, - b"getPathPattern", - b"()Ljava/lang/String;", - "ObjectMethod", - [] - ) - - return cast(Any, (_java_string_to_python_str)(result)) - - def tag( - self, - ) -> Optional[str]: - - - jni_ref = self._jni_ref - result = _call_method( - b"org/kson/EmbedRule", - jni_ref, - b"getTag", - b"()Ljava/lang/String;", - "ObjectMethod", - [] - ) - - return cast(Any, (lambda x0: None if x0 == ffi.NULL else (_java_string_to_python_str)(x0))(result)) - - @staticmethod - def from_path_pattern( - path_pattern: str, - tag: Optional[str], - - ) -> EmbedRuleResult: - """Builds a new [EmbedRule]. - - @param pathPattern A JsonPointerGlob pattern (e.g., "/scripts/ *", "/queries/ **") - @param tag Optional embed tag to include (e.g., "yaml", "sql", "bash") - @return [EmbedRuleResult.Success] if [pathPattern] is a valid JsonPointerGlob, otherwise [EmbedRuleResult.Failure] - - Example: - ```kotlin - EmbedRule.fromPathPattern("/scripts/ *", tag = "bash") // Match all values under "scripts" - EmbedRule.fromPathPattern("/config/description") // Match exact path, no tag - ``` - """ - - if path_pattern is None: - raise ValueError("`path_pattern` cannot be None") - jni_ref = _access_static_field(b"org/kson/EmbedRule", b"INSTANCE", b"Lorg/kson/EmbedRule;") - result = _call_method( - b"org/kson/EmbedRule", - jni_ref, - b"fromPathPattern", - b"(Ljava/lang/String;Ljava/lang/String;)Lorg/kson/EmbedRuleResult;", - "ObjectMethod", - [ - - _python_str_to_java_string(path_pattern), - _python_str_to_java_string(tag) if tag is not None else ffi.NULL, - ] - ) - - return cast(Any, (lambda x0: EmbedRuleResult._downcast(x0))(result)) - - -class IndentType: - """Options for indenting Kson Output""" - - _jni_ref: Any - - Tabs: TypeAlias - Spaces: TypeAlias - def __init__( - self, - - ): - - self._jni_ref = _construct( - b"org/kson/IndentType", - b"()V", - [] - ) - def __eq__(self, other): - return _call_method(b"java/lang/Object", self._jni_ref, b"equals", b"(Ljava/lang/Object;)Z", "BooleanMethod", [other._jni_ref]) - - def __hash__(self): - return _call_method(b"java/lang/Object", self._jni_ref, b"hashCode", b"()I", "IntMethod", []) - - @staticmethod - def _downcast(jni_ref) -> Any: - match _jni_class_name(jni_ref): - - case "org.kson.IndentType$Tabs": - return _from_kotlin_object(_IndentType_Tabs, jni_ref) - - case "org.kson.IndentType$Spaces": - return _from_kotlin_object(_IndentType_Spaces, jni_ref) - -class _IndentType_Spaces(IndentType): - """Use spaces for indentation with the specified count""" - - _jni_ref: Any - - def __init__( - self, - size: int, - ): - if size is None: - raise ValueError("`size` cannot be None") - self._jni_ref = _construct( - b"org/kson/IndentType$Spaces", - b"(I)V", - [ - - ffi.cast('jint', size), - ] - ) - def __eq__(self, other): - return _call_method(b"java/lang/Object", self._jni_ref, b"equals", b"(Ljava/lang/Object;)Z", "BooleanMethod", [other._jni_ref]) - - def __hash__(self): - return _call_method(b"java/lang/Object", self._jni_ref, b"hashCode", b"()I", "IntMethod", []) - - - def size( - self, - ) -> int: - - - jni_ref = self._jni_ref - result = _call_method( - b"org/kson/IndentType$Spaces", - jni_ref, - b"getSize", - b"()I", - "IntMethod", - [] - ) - - return cast(Any, (lambda x0: x0)(result)) -IndentType.Spaces = _IndentType_Spaces - - -class _IndentType_Tabs(IndentType): - """Use tabs for indentation""" - - _jni_ref: Any - - def __eq__(self, other): - return _call_method(b"java/lang/Object", self._jni_ref, b"equals", b"(Ljava/lang/Object;)Z", "BooleanMethod", [other._jni_ref]) - - def __hash__(self): - return _call_method(b"java/lang/Object", self._jni_ref, b"hashCode", b"()I", "IntMethod", []) - -IndentType.Tabs = _IndentType_Tabs - - - class MessageSeverity(Enum): - """Represents the severity of a [Message]""" - def _to_kotlin_enum(self): match self: case MessageSeverity.ERROR: - return _access_static_field(b"org/kson/MessageSeverity", b"ERROR", b"Lorg/kson/MessageSeverity;") + return _access_static_field(b"org/kson/api/MessageSeverity", b"ERROR", b"Lorg/kson/api/MessageSeverity;") case MessageSeverity.WARNING: - return _access_static_field(b"org/kson/MessageSeverity", b"WARNING", b"Lorg/kson/MessageSeverity;") + return _access_static_field(b"org/kson/api/MessageSeverity", b"WARNING", b"Lorg/kson/api/MessageSeverity;") @staticmethod def _from_kotlin_enum(jni_ref): - index = _call_method(b"org/kson/MessageSeverity", jni_ref, b"ordinal", b"()I", "IntMethod", []) + index = _call_method(b"org/kson/api/MessageSeverity", jni_ref, b"ordinal", b"()I", "IntMethod", []) return MessageSeverity(index) ERROR = 0 WARNING = 1 -class KsonValueType(Enum): - """Type discriminator for KsonValue subclasses""" +class FormattingStyle(Enum): + def _to_kotlin_enum(self): + match self: + case FormattingStyle.PLAIN: + return _access_static_field(b"org/kson/api/FormattingStyle", b"PLAIN", b"Lorg/kson/api/FormattingStyle;") + case FormattingStyle.DELIMITED: + return _access_static_field(b"org/kson/api/FormattingStyle", b"DELIMITED", b"Lorg/kson/api/FormattingStyle;") + case FormattingStyle.COMPACT: + return _access_static_field(b"org/kson/api/FormattingStyle", b"COMPACT", b"Lorg/kson/api/FormattingStyle;") + case FormattingStyle.CLASSIC: + return _access_static_field(b"org/kson/api/FormattingStyle", b"CLASSIC", b"Lorg/kson/api/FormattingStyle;") + @staticmethod + def _from_kotlin_enum(jni_ref): + index = _call_method(b"org/kson/api/FormattingStyle", jni_ref, b"ordinal", b"()I", "IntMethod", []) + return FormattingStyle(index) + + PLAIN = 0 + DELIMITED = 1 + COMPACT = 2 + CLASSIC = 3 + +class KsonValueType(Enum): def _to_kotlin_enum(self): match self: case KsonValueType.OBJECT: - return _access_static_field(b"org/kson/KsonValueType", b"OBJECT", b"Lorg/kson/KsonValueType;") + return _access_static_field(b"org/kson/api/KsonValueType", b"OBJECT", b"Lorg/kson/api/KsonValueType;") case KsonValueType.ARRAY: - return _access_static_field(b"org/kson/KsonValueType", b"ARRAY", b"Lorg/kson/KsonValueType;") + return _access_static_field(b"org/kson/api/KsonValueType", b"ARRAY", b"Lorg/kson/api/KsonValueType;") case KsonValueType.STRING: - return _access_static_field(b"org/kson/KsonValueType", b"STRING", b"Lorg/kson/KsonValueType;") + return _access_static_field(b"org/kson/api/KsonValueType", b"STRING", b"Lorg/kson/api/KsonValueType;") case KsonValueType.INTEGER: - return _access_static_field(b"org/kson/KsonValueType", b"INTEGER", b"Lorg/kson/KsonValueType;") + return _access_static_field(b"org/kson/api/KsonValueType", b"INTEGER", b"Lorg/kson/api/KsonValueType;") case KsonValueType.DECIMAL: - return _access_static_field(b"org/kson/KsonValueType", b"DECIMAL", b"Lorg/kson/KsonValueType;") + return _access_static_field(b"org/kson/api/KsonValueType", b"DECIMAL", b"Lorg/kson/api/KsonValueType;") case KsonValueType.BOOLEAN: - return _access_static_field(b"org/kson/KsonValueType", b"BOOLEAN", b"Lorg/kson/KsonValueType;") + return _access_static_field(b"org/kson/api/KsonValueType", b"BOOLEAN", b"Lorg/kson/api/KsonValueType;") case KsonValueType.NULL: - return _access_static_field(b"org/kson/KsonValueType", b"NULL", b"Lorg/kson/KsonValueType;") + return _access_static_field(b"org/kson/api/KsonValueType", b"NULL", b"Lorg/kson/api/KsonValueType;") case KsonValueType.EMBED: - return _access_static_field(b"org/kson/KsonValueType", b"EMBED", b"Lorg/kson/KsonValueType;") + return _access_static_field(b"org/kson/api/KsonValueType", b"EMBED", b"Lorg/kson/api/KsonValueType;") @staticmethod def _from_kotlin_enum(jni_ref): - index = _call_method(b"org/kson/KsonValueType", jni_ref, b"ordinal", b"()I", "IntMethod", []) + index = _call_method(b"org/kson/api/KsonValueType", jni_ref, b"ordinal", b"()I", "IntMethod", []) return KsonValueType(index) OBJECT = 0 @@ -2181,92 +2262,68 @@ def _from_kotlin_enum(jni_ref): EMBED = 7 -class FormattingStyle(Enum): - """[FormattingStyle] options for Kson Output""" - - def _to_kotlin_enum(self): - match self: - case FormattingStyle.PLAIN: - return _access_static_field(b"org/kson/FormattingStyle", b"PLAIN", b"Lorg/kson/FormattingStyle;") - case FormattingStyle.DELIMITED: - return _access_static_field(b"org/kson/FormattingStyle", b"DELIMITED", b"Lorg/kson/FormattingStyle;") - case FormattingStyle.COMPACT: - return _access_static_field(b"org/kson/FormattingStyle", b"COMPACT", b"Lorg/kson/FormattingStyle;") - case FormattingStyle.CLASSIC: - return _access_static_field(b"org/kson/FormattingStyle", b"CLASSIC", b"Lorg/kson/FormattingStyle;") - @staticmethod - def _from_kotlin_enum(jni_ref): - index = _call_method(b"org/kson/FormattingStyle", jni_ref, b"ordinal", b"()I", "IntMethod", []) - return FormattingStyle(index) - - PLAIN = 0 - DELIMITED = 1 - COMPACT = 2 - CLASSIC = 3 - - class TokenType(Enum): def _to_kotlin_enum(self): match self: case TokenType.CURLY_BRACE_L: - return _access_static_field(b"org/kson/TokenType", b"CURLY_BRACE_L", b"Lorg/kson/TokenType;") + return _access_static_field(b"org/kson/api/TokenType", b"CURLY_BRACE_L", b"Lorg/kson/api/TokenType;") case TokenType.CURLY_BRACE_R: - return _access_static_field(b"org/kson/TokenType", b"CURLY_BRACE_R", b"Lorg/kson/TokenType;") + return _access_static_field(b"org/kson/api/TokenType", b"CURLY_BRACE_R", b"Lorg/kson/api/TokenType;") case TokenType.SQUARE_BRACKET_L: - return _access_static_field(b"org/kson/TokenType", b"SQUARE_BRACKET_L", b"Lorg/kson/TokenType;") + return _access_static_field(b"org/kson/api/TokenType", b"SQUARE_BRACKET_L", b"Lorg/kson/api/TokenType;") case TokenType.SQUARE_BRACKET_R: - return _access_static_field(b"org/kson/TokenType", b"SQUARE_BRACKET_R", b"Lorg/kson/TokenType;") + return _access_static_field(b"org/kson/api/TokenType", b"SQUARE_BRACKET_R", b"Lorg/kson/api/TokenType;") case TokenType.ANGLE_BRACKET_L: - return _access_static_field(b"org/kson/TokenType", b"ANGLE_BRACKET_L", b"Lorg/kson/TokenType;") + return _access_static_field(b"org/kson/api/TokenType", b"ANGLE_BRACKET_L", b"Lorg/kson/api/TokenType;") case TokenType.ANGLE_BRACKET_R: - return _access_static_field(b"org/kson/TokenType", b"ANGLE_BRACKET_R", b"Lorg/kson/TokenType;") + return _access_static_field(b"org/kson/api/TokenType", b"ANGLE_BRACKET_R", b"Lorg/kson/api/TokenType;") case TokenType.COLON: - return _access_static_field(b"org/kson/TokenType", b"COLON", b"Lorg/kson/TokenType;") + return _access_static_field(b"org/kson/api/TokenType", b"COLON", b"Lorg/kson/api/TokenType;") case TokenType.DOT: - return _access_static_field(b"org/kson/TokenType", b"DOT", b"Lorg/kson/TokenType;") + return _access_static_field(b"org/kson/api/TokenType", b"DOT", b"Lorg/kson/api/TokenType;") case TokenType.END_DASH: - return _access_static_field(b"org/kson/TokenType", b"END_DASH", b"Lorg/kson/TokenType;") + return _access_static_field(b"org/kson/api/TokenType", b"END_DASH", b"Lorg/kson/api/TokenType;") case TokenType.COMMA: - return _access_static_field(b"org/kson/TokenType", b"COMMA", b"Lorg/kson/TokenType;") + return _access_static_field(b"org/kson/api/TokenType", b"COMMA", b"Lorg/kson/api/TokenType;") case TokenType.COMMENT: - return _access_static_field(b"org/kson/TokenType", b"COMMENT", b"Lorg/kson/TokenType;") + return _access_static_field(b"org/kson/api/TokenType", b"COMMENT", b"Lorg/kson/api/TokenType;") case TokenType.EMBED_OPEN_DELIM: - return _access_static_field(b"org/kson/TokenType", b"EMBED_OPEN_DELIM", b"Lorg/kson/TokenType;") + return _access_static_field(b"org/kson/api/TokenType", b"EMBED_OPEN_DELIM", b"Lorg/kson/api/TokenType;") case TokenType.EMBED_CLOSE_DELIM: - return _access_static_field(b"org/kson/TokenType", b"EMBED_CLOSE_DELIM", b"Lorg/kson/TokenType;") + return _access_static_field(b"org/kson/api/TokenType", b"EMBED_CLOSE_DELIM", b"Lorg/kson/api/TokenType;") case TokenType.EMBED_TAG: - return _access_static_field(b"org/kson/TokenType", b"EMBED_TAG", b"Lorg/kson/TokenType;") + return _access_static_field(b"org/kson/api/TokenType", b"EMBED_TAG", b"Lorg/kson/api/TokenType;") case TokenType.EMBED_PREAMBLE_NEWLINE: - return _access_static_field(b"org/kson/TokenType", b"EMBED_PREAMBLE_NEWLINE", b"Lorg/kson/TokenType;") + return _access_static_field(b"org/kson/api/TokenType", b"EMBED_PREAMBLE_NEWLINE", b"Lorg/kson/api/TokenType;") case TokenType.EMBED_CONTENT: - return _access_static_field(b"org/kson/TokenType", b"EMBED_CONTENT", b"Lorg/kson/TokenType;") + return _access_static_field(b"org/kson/api/TokenType", b"EMBED_CONTENT", b"Lorg/kson/api/TokenType;") case TokenType.FALSE: - return _access_static_field(b"org/kson/TokenType", b"FALSE", b"Lorg/kson/TokenType;") + return _access_static_field(b"org/kson/api/TokenType", b"FALSE", b"Lorg/kson/api/TokenType;") case TokenType.UNQUOTED_STRING: - return _access_static_field(b"org/kson/TokenType", b"UNQUOTED_STRING", b"Lorg/kson/TokenType;") + return _access_static_field(b"org/kson/api/TokenType", b"UNQUOTED_STRING", b"Lorg/kson/api/TokenType;") case TokenType.ILLEGAL_CHAR: - return _access_static_field(b"org/kson/TokenType", b"ILLEGAL_CHAR", b"Lorg/kson/TokenType;") + return _access_static_field(b"org/kson/api/TokenType", b"ILLEGAL_CHAR", b"Lorg/kson/api/TokenType;") case TokenType.LIST_DASH: - return _access_static_field(b"org/kson/TokenType", b"LIST_DASH", b"Lorg/kson/TokenType;") + return _access_static_field(b"org/kson/api/TokenType", b"LIST_DASH", b"Lorg/kson/api/TokenType;") case TokenType.NULL: - return _access_static_field(b"org/kson/TokenType", b"NULL", b"Lorg/kson/TokenType;") + return _access_static_field(b"org/kson/api/TokenType", b"NULL", b"Lorg/kson/api/TokenType;") case TokenType.NUMBER: - return _access_static_field(b"org/kson/TokenType", b"NUMBER", b"Lorg/kson/TokenType;") + return _access_static_field(b"org/kson/api/TokenType", b"NUMBER", b"Lorg/kson/api/TokenType;") case TokenType.STRING_OPEN_QUOTE: - return _access_static_field(b"org/kson/TokenType", b"STRING_OPEN_QUOTE", b"Lorg/kson/TokenType;") + return _access_static_field(b"org/kson/api/TokenType", b"STRING_OPEN_QUOTE", b"Lorg/kson/api/TokenType;") case TokenType.STRING_CLOSE_QUOTE: - return _access_static_field(b"org/kson/TokenType", b"STRING_CLOSE_QUOTE", b"Lorg/kson/TokenType;") + return _access_static_field(b"org/kson/api/TokenType", b"STRING_CLOSE_QUOTE", b"Lorg/kson/api/TokenType;") case TokenType.STRING_CONTENT: - return _access_static_field(b"org/kson/TokenType", b"STRING_CONTENT", b"Lorg/kson/TokenType;") + return _access_static_field(b"org/kson/api/TokenType", b"STRING_CONTENT", b"Lorg/kson/api/TokenType;") case TokenType.TRUE: - return _access_static_field(b"org/kson/TokenType", b"TRUE", b"Lorg/kson/TokenType;") + return _access_static_field(b"org/kson/api/TokenType", b"TRUE", b"Lorg/kson/api/TokenType;") case TokenType.WHITESPACE: - return _access_static_field(b"org/kson/TokenType", b"WHITESPACE", b"Lorg/kson/TokenType;") + return _access_static_field(b"org/kson/api/TokenType", b"WHITESPACE", b"Lorg/kson/api/TokenType;") case TokenType.EOF: - return _access_static_field(b"org/kson/TokenType", b"EOF", b"Lorg/kson/TokenType;") + return _access_static_field(b"org/kson/api/TokenType", b"EOF", b"Lorg/kson/api/TokenType;") @staticmethod def _from_kotlin_enum(jni_ref): - index = _call_method(b"org/kson/TokenType", jni_ref, b"ordinal", b"()I", "IntMethod", []) + index = _call_method(b"org/kson/api/TokenType", jni_ref, b"ordinal", b"()I", "IntMethod", []) return TokenType(index) CURLY_BRACE_L = 0 diff --git a/lib-python/stubs/cffi/__init__.pyi b/lib-python/stubs/cffi/__init__.pyi new file mode 100644 index 00000000..60dac9ca --- /dev/null +++ b/lib-python/stubs/cffi/__init__.pyi @@ -0,0 +1,8 @@ +# This stub exists because otherwise type checking fails. The root cause is that `pyright` ships +# with a buggy stub for the `cffi` library. A fix is ready, but it will not be available until the +# next release. +# +# This code can be deleted once we upgrade `pyright` to a version higher than `1.1.408`. + +from typing import Any +def __getattr__(name: str) -> Any: ... diff --git a/lib-python/tests/api_server.py b/lib-python/tests/api_server.py new file mode 100644 index 00000000..676ec6c8 --- /dev/null +++ b/lib-python/tests/api_server.py @@ -0,0 +1,270 @@ +"""HTTP server exposing the KSON Python API, conforming to kson-api-schema.json.""" + +import json +import sys +import traceback +from http.server import HTTPServer, BaseHTTPRequestHandler +from kson import ( + Kson, FormatOptions, IndentType, FormattingStyle, EmbedRule, + TranspileOptions, Result, SchemaResult, KsonValue, KsonValueType, + MessageSeverity, +) + + +def _serialize_position(pos): + return {"line": pos.line(), "column": pos.column()} + + +def _serialize_message(msg): + return { + "message": msg.message(), + "severity": msg.severity().name, + "start": _serialize_position(msg.start()), + "end": _serialize_position(msg.end()), + } + + +def _serialize_token(token): + return { + "tokenType": token.token_type().name, + "text": token.text(), + "start": _serialize_position(token.start()), + "end": _serialize_position(token.end()), + } + + +def _serialize_kson_value(value): + if value is None: + return None + + vtype = value.type() + + if vtype == KsonValueType.OBJECT: + props = value.properties() + prop_keys = value.property_keys() + return { + "type": "OBJECT", + "properties": {k: _serialize_kson_value(v) for k, v in props.items()}, + "propertyKeys": {k: _serialize_kson_value(v) for k, v in prop_keys.items()}, + "start": _serialize_position(value.start()), + "end": _serialize_position(value.end()), + } + elif vtype == KsonValueType.ARRAY: + return { + "type": "ARRAY", + "elements": [_serialize_kson_value(e) for e in value.elements()], + "start": _serialize_position(value.start()), + "end": _serialize_position(value.end()), + } + elif vtype == KsonValueType.STRING: + return { + "type": "STRING", + "value": value.value(), + "start": _serialize_position(value.start()), + "end": _serialize_position(value.end()), + } + elif vtype == KsonValueType.INTEGER: + return { + "type": "INTEGER", + "value": value.value(), + "start": _serialize_position(value.start()), + "end": _serialize_position(value.end()), + } + elif vtype == KsonValueType.DECIMAL: + return { + "type": "DECIMAL", + "value": value.value(), + "start": _serialize_position(value.start()), + "end": _serialize_position(value.end()), + } + elif vtype == KsonValueType.BOOLEAN: + return { + "type": "BOOLEAN", + "value": bool(value.value()), + "start": _serialize_position(value.start()), + "end": _serialize_position(value.end()), + } + elif vtype == KsonValueType.NULL: + return { + "type": "NULL", + "start": _serialize_position(value.start()), + "end": _serialize_position(value.end()), + } + elif vtype == KsonValueType.EMBED: + return { + "type": "EMBED", + "tag": value.tag(), + "content": value.content(), + "start": _serialize_position(value.start()), + "end": _serialize_position(value.end()), + } + + +def _parse_indent_type(obj): + if obj is None: + return IndentType.Spaces(2) + if obj["type"] == "tabs": + return IndentType.Tabs() + return IndentType.Spaces(obj.get("size", 2)) + + +def _parse_formatting_style(name): + if name is None: + return FormattingStyle.PLAIN + return FormattingStyle[name] + + +def _parse_embed_rule(obj): + return EmbedRule(obj["pathPattern"], obj.get("tag")) + + +def _parse_format_options(obj): + if obj is None: + return FormatOptions(IndentType.Spaces(2), FormattingStyle.PLAIN, []) + indent = _parse_indent_type(obj.get("indentType")) + style = _parse_formatting_style(obj.get("formattingStyle")) + rules = [_parse_embed_rule(r) for r in obj.get("embedBlockRules", [])] + return FormatOptions(indent, style, rules) + + +def handle_format(req): + options = _parse_format_options(req.get("formatOptions")) + output = Kson.format(req["kson"], options) + return {"command": "format", "success": True, "output": output} + + +def handle_to_json(req): + retain = req.get("retainEmbedTags", True) + result = Kson.to_json(req["kson"], TranspileOptions.Json(retain_embed_tags=retain)) + if isinstance(result, Result.Success): + return {"command": "toJson", "success": True, "output": result.output()} + else: + return { + "command": "toJson", + "success": False, + "errors": [_serialize_message(m) for m in result.errors()], + } + + +def handle_to_yaml(req): + retain = req.get("retainEmbedTags", True) + result = Kson.to_yaml(req["kson"], TranspileOptions.Yaml(retain_embed_tags=retain)) + if isinstance(result, Result.Success): + return {"command": "toYaml", "success": True, "output": result.output()} + else: + return { + "command": "toYaml", + "success": False, + "errors": [_serialize_message(m) for m in result.errors()], + } + + +def handle_analyze(req): + filepath = req.get("filepath") + analysis = Kson.analyze(req["kson"], filepath) + return { + "command": "analyze", + "errors": [_serialize_message(m) for m in analysis.errors()], + "tokens": [_serialize_token(t) for t in analysis.tokens()], + "ksonValue": _serialize_kson_value(analysis.kson_value()), + } + + +def handle_parse_schema(req): + result = Kson.parse_schema(req["schemaKson"]) + if isinstance(result, SchemaResult.Success): + return {"command": "parseSchema", "success": True} + else: + return { + "command": "parseSchema", + "success": False, + "errors": [_serialize_message(m) for m in result.errors()], + } + + +def handle_validate(req): + schema_result = Kson.parse_schema(req["schemaKson"]) + if isinstance(schema_result, SchemaResult.Failure): + return { + "command": "validate", + "success": False, + "errors": [_serialize_message(m) for m in schema_result.errors()], + } + + validator = schema_result.schema_validator() + errors = validator.validate(req["kson"], req.get("filepath")) + return { + "command": "validate", + "success": len(errors) == 0, + "errors": [_serialize_message(m) for m in errors], + } + + +HANDLERS = { + "format": handle_format, + "toJson": handle_to_json, + "toYaml": handle_to_yaml, + "analyze": handle_analyze, + "parseSchema": handle_parse_schema, + "validate": handle_validate, +} + + +class KsonHandler(BaseHTTPRequestHandler): + def do_POST(self): + length = int(self.headers.get("Content-Length", 0)) + body = self.rfile.read(length) + + try: + req = json.loads(body) + except json.JSONDecodeError as e: + self._send_error(400, f"Invalid JSON: {e}") + return + + command = req.get("command") + handler = HANDLERS.get(command) + if handler is None: + self._send_error(400, f"Unknown command: {command}") + return + + try: + response = handler(req) + except Exception as e: + stack_trace = traceback.format_exc() + self._send_error(500, stack_trace) + return + + self._send_json(200, response) + + def _send_json(self, status, obj): + data = json.dumps(obj).encode("utf-8") + self.send_response(status) + self.send_header("Content-Type", "application/json") + self.send_header("Content-Length", str(len(data))) + self.end_headers() + self.wfile.write(data) + + def _send_error(self, status, message): + self._send_json(status, {"internal_error": message}) + + def log_message(self, format, *args): + # Suppress default stderr logging + pass + + +def main(): + port = int(sys.argv[1]) if len(sys.argv) > 1 else 8080 + server = HTTPServer(("127.0.0.1", port), KsonHandler) + print(f"Listening on http://127.0.0.1:{port}", flush=True) + try: + server.serve_forever() + except KeyboardInterrupt: + pass + except BaseException as e: + print(f"Exception! {e}") + print("Shutting down...") + server.server_close() + + +if __name__ == "__main__": + main() diff --git a/lib-rust/.gitignore b/lib-rust/.gitignore deleted file mode 100644 index 6cba219f..00000000 --- a/lib-rust/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -artifacts -kotlin diff --git a/lib-rust/kson-lib-tests/build.gradle.kts b/lib-rust/kson-lib-tests/build.gradle.kts new file mode 100644 index 00000000..71516170 --- /dev/null +++ b/lib-rust/kson-lib-tests/build.gradle.kts @@ -0,0 +1,61 @@ +plugins { + kotlin("multiplatform") +} + +repositories { + mavenCentral() +} + +val releaseBuildDir: String = project(":lib-rust").projectDir.resolve("kson-test-server/target/release").absolutePath + +val buildTestServer by tasks.registering(Exec::class) { + dependsOn(":kson-lib:buildWithGraalVmNativeImage") + + val nativeArtifactsDir = project(":kson-lib").projectDir.resolve("build/kotlin/compileGraalVmNativeImage").absolutePath + + environment( + Pair("KSON_PREBUILT_BIN_DIR", nativeArtifactsDir), + Pair("KSON_COPY_SHARED_LIBRARY_TO_DIR", releaseBuildDir), + ) + + val cargoManifestPath = project(":lib-rust").projectDir.resolve("kson-test-server/Cargo.toml") + + group = "build" + workingDir = project(":lib-rust").projectDir + commandLine = "./pixiw run cargo build --release --manifest-path $cargoManifestPath".split(" ") + standardOutput = System.out + errorOutput = System.err + isIgnoreExitValue = false +} + +tasks.withType { + dependsOn(buildTestServer) + + systemProperty("releaseBuildDir", releaseBuildDir) + + useJUnitPlatform() + jvmArgs("-Djunit.jupiter.extensions.autodetection.enabled=true") +} + +kotlin { + jvm() + + sourceSets { + commonTest { + dependencies { + implementation(project(":kson-service-tests")) + implementation(project(":kson-http")) + } + } + jvmTest { + dependencies { + implementation(kotlin("test-junit5")) + + // Important: this ensures we have a recent-enough version of JUnit, supporting the `AutoCloseable` + // interface (otherwise test runs never finish because the HTTP server doesn't get closed) + implementation(project.dependencies.platform("org.junit:junit-bom:5.14.3")) + } + } + } +} + diff --git a/lib-rust/kson-lib-tests/src/jvmTest/kotlin/ServerExtension.kt b/lib-rust/kson-lib-tests/src/jvmTest/kotlin/ServerExtension.kt new file mode 100644 index 00000000..59133d62 --- /dev/null +++ b/lib-rust/kson-lib-tests/src/jvmTest/kotlin/ServerExtension.kt @@ -0,0 +1,56 @@ +import org.junit.jupiter.api.extension.BeforeAllCallback +import org.junit.jupiter.api.extension.ExtensionContext +import org.kson.Kson +import java.io.File +import java.net.Socket + +/** + * A JUnit extension that makes sure a KSON server is running before all tests and gets + * shut down at the end + */ +class ServerExtension : BeforeAllCallback, AutoCloseable { + + companion object { + private var started = false + private var process: Process? = null + } + + override fun beforeAll(context: ExtensionContext) { + if (!started) { + started = true + + // Register for cleanup when the root context closes (after all tests) + context.root.getStore(ExtensionContext.Namespace.GLOBAL) + .put("serverExtension", this) + + val releaseBuildDir = File(System.getProperty("releaseBuildDir")) + val port = 8081 + Kson.setPort(port) + + val processBuilder = ProcessBuilder(listOf("./kson-test-server", port.toString())) + .directory(releaseBuildDir) + .redirectOutput(ProcessBuilder.Redirect.INHERIT) + .redirectErrorStream(true); + + processBuilder.environment()["LD_LIBRARY_PATH"] = releaseBuildDir.absolutePath + + process = processBuilder.start() + + // Wait for readiness + repeat(30) { + try { + Socket("localhost", port).close() + return + } catch (_: Exception) { + Thread.sleep(1000) + } + } + throw RuntimeException("Server did not start in time") + } + } + + override fun close() { + process?.destroy() + process?.waitFor() + } +} diff --git a/lib-rust/kson-lib-tests/src/jvmTest/kotlin/SmokeTest.kt b/lib-rust/kson-lib-tests/src/jvmTest/kotlin/SmokeTest.kt new file mode 100644 index 00000000..cbd81068 --- /dev/null +++ b/lib-rust/kson-lib-tests/src/jvmTest/kotlin/SmokeTest.kt @@ -0,0 +1,9 @@ +import org.kson.Kson +import org.kson.api.KsonService +import org.kson.api.KsonServiceSmokeTest + +class SmokeTest : KsonServiceSmokeTest() { + override fun createService(): KsonService { + return Kson + } +} \ No newline at end of file diff --git a/lib-rust/kson-lib-tests/src/jvmTest/resources/META-INF/services/org.junit.jupiter.api.extension.Extension b/lib-rust/kson-lib-tests/src/jvmTest/resources/META-INF/services/org.junit.jupiter.api.extension.Extension new file mode 100644 index 00000000..849fdf0a --- /dev/null +++ b/lib-rust/kson-lib-tests/src/jvmTest/resources/META-INF/services/org.junit.jupiter.api.extension.Extension @@ -0,0 +1 @@ +ServerExtension diff --git a/lib-rust/kson-test-server/Cargo.lock b/lib-rust/kson-test-server/Cargo.lock new file mode 100644 index 00000000..dc4827f7 --- /dev/null +++ b/lib-rust/kson-test-server/Cargo.lock @@ -0,0 +1,1103 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "adler2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" + +[[package]] +name = "aho-corasick" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +dependencies = [ + "memchr", +] + +[[package]] +name = "anyhow" +version = "1.0.102" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" + +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + +[[package]] +name = "axum" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b52af3cb4058c895d37317bb27508dccc8e5f2d39454016b297bf4a400597b8" +dependencies = [ + "axum-core", + "bytes", + "form_urlencoded", + "futures-util", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-util", + "itoa", + "matchit", + "memchr", + "mime", + "percent-encoding", + "pin-project-lite", + "serde_core", + "serde_json", + "serde_path_to_error", + "serde_urlencoded", + "sync_wrapper", + "tokio", + "tower", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "axum-core" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08c78f31d7b1291f7ee735c1c6780ccde7785daae9a9206026862dab7d8792d1" +dependencies = [ + "bytes", + "futures-core", + "http", + "http-body", + "http-body-util", + "mime", + "pin-project-lite", + "sync_wrapper", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "bindgen" +version = "0.71.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f58bf3d7db68cfbac37cfc485a8d711e87e064c3d0fe0435b92f7a407f9d6b3" +dependencies = [ + "bitflags", + "cexpr", + "clang-sys", + "itertools", + "log", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "syn", +] + +[[package]] +name = "bitflags" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" + +[[package]] +name = "bytes" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" + +[[package]] +name = "cc" +version = "1.2.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aebf35691d1bfb0ac386a69bac2fde4dd276fb618cf8bf4f5318fe285e821bb2" +dependencies = [ + "find-msvc-tools", + "shlex", +] + +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "clang-sys" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" +dependencies = [ + "glob", + "libc", + "libloading", +] + +[[package]] +name = "crc32fast" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + +[[package]] +name = "errno" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + +[[package]] +name = "filetime" +version = "0.2.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f98844151eee8917efc50bd9e8318cb963ae8b297431495d3f758616ea5c57db" +dependencies = [ + "cfg-if", + "libc", + "libredox", +] + +[[package]] +name = "find-msvc-tools" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" + +[[package]] +name = "flate2" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "form_urlencoded" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "futures-channel" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" +dependencies = [ + "futures-core", +] + +[[package]] +name = "futures-core" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" + +[[package]] +name = "futures-task" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" + +[[package]] +name = "futures-util" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +dependencies = [ + "futures-core", + "futures-task", + "pin-project-lite", + "slab", +] + +[[package]] +name = "getrandom" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "glob" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" + +[[package]] +name = "http" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a" +dependencies = [ + "bytes", + "itoa", +] + +[[package]] +name = "http-body" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +dependencies = [ + "bytes", + "http", +] + +[[package]] +name = "http-body-util" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" +dependencies = [ + "bytes", + "futures-core", + "http", + "http-body", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" + +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + +[[package]] +name = "hyper" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ab2d4f250c3d7b1c9fcdff1cece94ea4e2dfbec68614f7b87cb205f24ca9d11" +dependencies = [ + "atomic-waker", + "bytes", + "futures-channel", + "futures-core", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "pin-utils", + "smallvec", + "tokio", +] + +[[package]] +name = "hyper-util" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0" +dependencies = [ + "bytes", + "http", + "http-body", + "hyper", + "pin-project-lite", + "tokio", + "tower-service", +] + +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" + +[[package]] +name = "kson-rs" +version = "0.3.0-dev" +dependencies = [ + "kson-sys", +] + +[[package]] +name = "kson-sys" +version = "0.3.0-dev" +dependencies = [ + "anyhow", + "bindgen", + "flate2", + "tar", + "ureq", +] + +[[package]] +name = "kson-test-server" +version = "0.1.0" +dependencies = [ + "axum", + "kson-rs", + "serde", + "serde_json", + "tokio", +] + +[[package]] +name = "libc" +version = "0.2.182" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6800badb6cb2082ffd7b6a67e6125bb39f18782f793520caee8cb8846be06112" + +[[package]] +name = "libloading" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55" +dependencies = [ + "cfg-if", + "windows-link", +] + +[[package]] +name = "libredox" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d0b95e02c851351f877147b7deea7b1afb1df71b63aa5f8270716e0c5720616" +dependencies = [ + "bitflags", + "libc", + "redox_syscall", +] + +[[package]] +name = "linux-raw-sys" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" + +[[package]] +name = "log" +version = "0.4.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" + +[[package]] +name = "matchit" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3" + +[[package]] +name = "memchr" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "miniz_oxide" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" +dependencies = [ + "adler2", + "simd-adler32", +] + +[[package]] +name = "mio" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc" +dependencies = [ + "libc", + "wasi", + "windows-sys 0.61.2", +] + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + +[[package]] +name = "percent-encoding" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" + +[[package]] +name = "pin-project-lite" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "prettyplease" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" +dependencies = [ + "proc-macro2", + "syn", +] + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21b2ebcf727b7760c461f091f9f0f539b77b8e87f2fd88131e7f1b433b3cece4" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "redox_syscall" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35985aa610addc02e24fc232012c86fd11f14111180f902b67e2d5331f8ebf2b" +dependencies = [ + "bitflags", +] + +[[package]] +name = "regex" +version = "1.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a96887878f22d7bad8a3b6dc5b7440e0ada9a245242924394987b21cf2210a4c" + +[[package]] +name = "ring" +version = "0.17.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" +dependencies = [ + "cc", + "cfg-if", + "getrandom", + "libc", + "untrusted", + "windows-sys 0.52.0", +] + +[[package]] +name = "rustc-hash" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" + +[[package]] +name = "rustix" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "146c9e247ccc180c1f61615433868c99f3de3ae256a30a43b49f67c2d9171f34" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.61.2", +] + +[[package]] +name = "rustls" +version = "0.23.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c665f33d38cea657d9614f766881e4d510e0eda4239891eea56b4cadcf01801b" +dependencies = [ + "log", + "once_cell", + "ring", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-pki-types" +version = "1.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be040f8b0a225e40375822a563fa9524378b9d63112f53e19ffff34df5d33fdd" +dependencies = [ + "zeroize", +] + +[[package]] +name = "rustls-webpki" +version = "0.103.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7df23109aa6c1567d1c575b9952556388da57401e4ace1d15f79eedad0d8f53" +dependencies = [ + "ring", + "rustls-pki-types", + "untrusted", +] + +[[package]] +name = "ryu" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.149" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "serde_path_to_error" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10a9ff822e371bb5403e391ecd83e182e0e77ba7f6fe0160b795797109d1b457" +dependencies = [ + "itoa", + "serde", + "serde_core", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "simd-adler32" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e320a6c5ad31d271ad523dcf3ad13e2767ad8b1cb8f047f75a8aeaf8da139da2" + +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" + +[[package]] +name = "socket2" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86f4aa3ad99f2088c990dfa82d367e19cb29268ed67c574d10d0a4bfe71f07e0" +dependencies = [ + "libc", + "windows-sys 0.60.2", +] + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "syn" +version = "2.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "sync_wrapper" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" + +[[package]] +name = "tar" +version = "0.4.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d863878d212c87a19c1a610eb53bb01fe12951c0501cf5a0d65f724914a667a" +dependencies = [ + "filetime", + "libc", + "xattr", +] + +[[package]] +name = "tokio" +version = "1.49.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72a2903cd7736441aac9df9d7688bd0ce48edccaadf181c3b90be801e81d3d86" +dependencies = [ + "libc", + "mio", + "pin-project-lite", + "socket2", + "tokio-macros", + "windows-sys 0.61.2", +] + +[[package]] +name = "tokio-macros" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tower" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4" +dependencies = [ + "futures-core", + "futures-util", + "pin-project-lite", + "sync_wrapper", + "tokio", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-layer" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" + +[[package]] +name = "tower-service" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" + +[[package]] +name = "tracing" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" +dependencies = [ + "log", + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" +dependencies = [ + "once_cell", +] + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "ureq" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdc97a28575b85cfedf2a7e7d3cc64b3e11bd8ac766666318003abbacc7a21fc" +dependencies = [ + "base64", + "flate2", + "log", + "percent-encoding", + "rustls", + "rustls-pki-types", + "ureq-proto", + "utf-8", + "webpki-roots", +] + +[[package]] +name = "ureq-proto" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d81f9efa9df032be5934a46a068815a10a042b494b6a58cb0a1a97bb5467ed6f" +dependencies = [ + "base64", + "http", + "httparse", + "log", +] + +[[package]] +name = "utf-8" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "webpki-roots" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22cfaf3c063993ff62e73cb4311efde4db1efb31ab78a3e5c457939ad5cc0bed" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets 0.53.5", +] + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm 0.52.6", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.53.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" +dependencies = [ + "windows-link", + "windows_aarch64_gnullvm 0.53.1", + "windows_aarch64_msvc 0.53.1", + "windows_i686_gnu 0.53.1", + "windows_i686_gnullvm 0.53.1", + "windows_i686_msvc 0.53.1", + "windows_x86_64_gnu 0.53.1", + "windows_x86_64_gnullvm 0.53.1", + "windows_x86_64_msvc 0.53.1", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_i686_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" + +[[package]] +name = "xattr" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32e45ad4206f6d2479085147f02bc2ef834ac85886624a23575ae137c8aa8156" +dependencies = [ + "libc", + "rustix", +] + +[[package]] +name = "zeroize" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" + +[[package]] +name = "zmij" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" diff --git a/lib-rust/kson-test-server/Cargo.toml b/lib-rust/kson-test-server/Cargo.toml new file mode 100644 index 00000000..89db7acc --- /dev/null +++ b/lib-rust/kson-test-server/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "kson-test-server" +version = "0.1.0" +edition = "2024" + +[profile.release] +rpath = true + +[dependencies] +kson = { package = "kson-rs", path = "../kson" } +axum = "0.8" +serde = { version = "1", features = ["derive"] } +serde_json = "1" +tokio = { version = "1", features = ["rt-multi-thread", "macros"] } diff --git a/lib-rust/kson-test-server/src/main.rs b/lib-rust/kson-test-server/src/main.rs new file mode 100644 index 00000000..466ed72e --- /dev/null +++ b/lib-rust/kson-test-server/src/main.rs @@ -0,0 +1,409 @@ +use axum::{Router, routing::post, Json}; +use serde::Deserialize; +use serde_json::Value; + +use kson::{ + EmbedRule, FormatOptions, FormattingStyle, IndentType, Kson, KsonValue, + indent_type, kson_value, transpile_options, +}; + +// --- Request types --- + +#[derive(Deserialize)] +#[serde(tag = "command")] +enum Request { + #[serde(rename = "format")] + Format { + kson: String, + #[serde(rename = "formatOptions")] + format_options: Option, + }, + #[serde(rename = "toJson")] + ToJson { + kson: String, + #[serde(rename = "retainEmbedTags", default = "default_true")] + retain_embed_tags: bool, + }, + #[serde(rename = "toYaml")] + ToYaml { + kson: String, + #[serde(rename = "retainEmbedTags", default = "default_true")] + retain_embed_tags: bool, + }, + #[serde(rename = "analyze")] + Analyze { + kson: String, + filepath: Option, + }, + #[serde(rename = "parseSchema")] + ParseSchema { + #[serde(rename = "schemaKson")] + schema_kson: String, + }, + #[serde(rename = "validate")] + Validate { + #[serde(rename = "schemaKson")] + schema_kson: String, + kson: String, + filepath: Option, + }, +} + +fn default_true() -> bool { + true +} + +#[derive(Deserialize)] +#[serde(rename_all = "camelCase")] +struct FormatOptionsDto { + indent_type: Option, + formatting_style: Option, + embed_block_rules: Option>, +} + +#[derive(Deserialize)] +struct IndentTypeDto { + r#type: String, + size: Option, +} + +#[derive(Deserialize)] +#[serde(rename_all = "camelCase")] +struct EmbedRuleDto { + path_pattern: String, + tag: Option, +} + +// --- Response serialization helpers --- + +fn serialize_position(pos: kson::Position) -> Value { + serde_json::json!({ + "line": pos.line(), + "column": pos.column(), + }) +} + +fn serialize_message(msg: kson::Message) -> Value { + let severity = match msg.severity() { + kson::MessageSeverity::Error => "ERROR", + kson::MessageSeverity::Warning => "WARNING", + }; + serde_json::json!({ + "message": msg.message(), + "severity": severity, + "start": serialize_position(msg.start()), + "end": serialize_position(msg.end()), + }) +} + +fn serialize_token(token: kson::Token) -> Value { + let token_type = match token.token_type() { + kson::TokenType::CurlyBraceL => "CURLY_BRACE_L", + kson::TokenType::CurlyBraceR => "CURLY_BRACE_R", + kson::TokenType::SquareBracketL => "SQUARE_BRACKET_L", + kson::TokenType::SquareBracketR => "SQUARE_BRACKET_R", + kson::TokenType::AngleBracketL => "ANGLE_BRACKET_L", + kson::TokenType::AngleBracketR => "ANGLE_BRACKET_R", + kson::TokenType::Colon => "COLON", + kson::TokenType::Dot => "DOT", + kson::TokenType::EndDash => "END_DASH", + kson::TokenType::Comma => "COMMA", + kson::TokenType::Comment => "COMMENT", + kson::TokenType::EmbedOpenDelim => "EMBED_OPEN_DELIM", + kson::TokenType::EmbedCloseDelim => "EMBED_CLOSE_DELIM", + kson::TokenType::EmbedTag => "EMBED_TAG", + kson::TokenType::EmbedPreambleNewline => "EMBED_PREAMBLE_NEWLINE", + kson::TokenType::EmbedContent => "EMBED_CONTENT", + kson::TokenType::False => "FALSE", + kson::TokenType::UnquotedString => "UNQUOTED_STRING", + kson::TokenType::IllegalChar => "ILLEGAL_CHAR", + kson::TokenType::ListDash => "LIST_DASH", + kson::TokenType::Null => "NULL", + kson::TokenType::Number => "NUMBER", + kson::TokenType::StringOpenQuote => "STRING_OPEN_QUOTE", + kson::TokenType::StringCloseQuote => "STRING_CLOSE_QUOTE", + kson::TokenType::StringContent => "STRING_CONTENT", + kson::TokenType::True => "TRUE", + kson::TokenType::Whitespace => "WHITESPACE", + kson::TokenType::Eof => "EOF", + }; + serde_json::json!({ + "tokenType": token_type, + "text": token.text(), + "start": serialize_position(token.start()), + "end": serialize_position(token.end()), + }) +} + +fn serialize_kson_value(value: &KsonValue) -> Value { + match value { + KsonValue::KsonObject(obj) => { + let props = obj.properties(); + let property_keys = obj.property_keys(); + let mut serialized_props: serde_json::Map = serde_json::Map::new(); + for (key, val) in &props { + serialized_props.insert(key.clone(), serialize_kson_value(val)); + } + let mut serialized_keys: serde_json::Map = serde_json::Map::new(); + for (key, kson_str) in &property_keys { + serialized_keys.insert(key.clone(), serialize_kson_string(kson_str)); + } + serde_json::json!({ + "type": "OBJECT", + "properties": serialized_props, + "propertyKeys": serialized_keys, + "start": serialize_position(obj.start()), + "end": serialize_position(obj.end()), + }) + } + KsonValue::KsonArray(arr) => { + let elements: Vec = arr.elements().iter().map(serialize_kson_value).collect(); + serde_json::json!({ + "type": "ARRAY", + "elements": elements, + "start": serialize_position(arr.start()), + "end": serialize_position(arr.end()), + }) + } + KsonValue::KsonString(s) => serialize_kson_string(s), + KsonValue::KsonNumber(num) => match num { + kson_value::KsonNumber::Integer(i) => { + serde_json::json!({ + "type": "INTEGER", + "value": i.value(), + "start": serialize_position(i.start()), + "end": serialize_position(i.end()), + }) + } + kson_value::KsonNumber::Decimal(d) => { + serde_json::json!({ + "type": "DECIMAL", + "value": d.value(), + "start": serialize_position(d.start()), + "end": serialize_position(d.end()), + }) + } + }, + KsonValue::KsonBoolean(b) => { + serde_json::json!({ + "type": "BOOLEAN", + "value": b.value(), + "start": serialize_position(b.start()), + "end": serialize_position(b.end()), + }) + } + KsonValue::KsonNull(n) => { + serde_json::json!({ + "type": "NULL", + "start": serialize_position(n.start()), + "end": serialize_position(n.end()), + }) + } + KsonValue::KsonEmbed(e) => { + serde_json::json!({ + "type": "EMBED", + "tag": e.tag(), + "content": e.content(), + "start": serialize_position(e.start()), + "end": serialize_position(e.end()), + }) + } + } +} + +fn serialize_kson_string(s: &kson_value::KsonString) -> Value { + serde_json::json!({ + "type": "STRING", + "value": s.value(), + "start": serialize_position(s.start()), + "end": serialize_position(s.end()), + }) +} + +// --- Request handling --- + +fn convert_indent_type(dto: &IndentTypeDto) -> IndentType { + match dto.r#type.as_str() { + "tabs" => IndentType::Tabs(indent_type::Tabs::new()), + _ => IndentType::Spaces(indent_type::Spaces::new(dto.size.unwrap_or(2))), + } +} + +fn convert_formatting_style(s: &str) -> FormattingStyle { + match s { + "DELIMITED" => FormattingStyle::Delimited, + "COMPACT" => FormattingStyle::Compact, + "CLASSIC" => FormattingStyle::Classic, + _ => FormattingStyle::Plain, + } +} + +fn convert_format_options(dto: Option<&FormatOptionsDto>) -> FormatOptions { + let indent_type = dto + .and_then(|d| d.indent_type.as_ref()) + .map(convert_indent_type) + .unwrap_or_else(|| IndentType::Spaces(indent_type::Spaces::new(2))); + + let formatting_style = dto + .and_then(|d| d.formatting_style.as_deref()) + .map(convert_formatting_style) + .unwrap_or(FormattingStyle::Plain); + + let embed_rules: Vec = dto + .and_then(|d| d.embed_block_rules.as_ref()) + .map(|rules| { + rules + .iter() + .map(|r| EmbedRule::new(&r.path_pattern, r.tag.as_deref())) + .collect() + }) + .unwrap_or_default(); + + FormatOptions::new(indent_type, formatting_style, &embed_rules) +} + +fn handle_request(request: Request) -> Value { + match request { + Request::Format { + kson, + format_options, + } => { + let opts = convert_format_options(format_options.as_ref()); + let output = Kson::format(&kson, opts); + serde_json::json!({ + "command": "format", + "success": true, + "output": output, + }) + } + Request::ToJson { + kson, + retain_embed_tags, + } => { + let opts = transpile_options::Json::new(retain_embed_tags); + match Kson::to_json(&kson, opts) { + Ok(success) => serde_json::json!({ + "command": "toJson", + "success": true, + "output": success.output(), + }), + Err(failure) => { + let errors: Vec = + failure.errors().into_iter().map(serialize_message).collect(); + serde_json::json!({ + "command": "toJson", + "success": false, + "errors": errors, + }) + } + } + } + Request::ToYaml { + kson, + retain_embed_tags, + } => { + let opts = transpile_options::Yaml::new(retain_embed_tags); + match Kson::to_yaml(&kson, opts) { + Ok(success) => serde_json::json!({ + "command": "toYaml", + "success": true, + "output": success.output(), + }), + Err(failure) => { + let errors: Vec = + failure.errors().into_iter().map(serialize_message).collect(); + serde_json::json!({ + "command": "toYaml", + "success": false, + "errors": errors, + }) + } + } + } + Request::Analyze { kson, filepath } => { + let analysis = Kson::analyze(&kson, filepath.as_deref()); + let errors: Vec = analysis + .errors() + .into_iter() + .map(serialize_message) + .collect(); + let tokens: Vec = analysis + .tokens() + .into_iter() + .map(serialize_token) + .collect(); + let kson_value = analysis + .kson_value() + .as_ref() + .map(serialize_kson_value); + serde_json::json!({ + "command": "analyze", + "errors": errors, + "tokens": tokens, + "ksonValue": kson_value, + }) + } + Request::ParseSchema { schema_kson } => match Kson::parse_schema(&schema_kson) { + Ok(_) => serde_json::json!({ + "command": "parseSchema", + "success": true, + }), + Err(failure) => { + let errors: Vec = + failure.errors().into_iter().map(serialize_message).collect(); + serde_json::json!({ + "command": "parseSchema", + "success": false, + "errors": errors, + }) + } + }, + Request::Validate { + schema_kson, + kson, + filepath, + } => match Kson::parse_schema(&schema_kson) { + Ok(success) => { + let validator = success.schema_validator(); + let errors: Vec = validator + .validate(&kson, filepath.as_deref()) + .into_iter() + .map(serialize_message) + .collect(); + let is_success = errors.is_empty(); + serde_json::json!({ + "command": "validate", + "success": is_success, + "errors": errors, + }) + } + Err(failure) => { + let errors: Vec = + failure.errors().into_iter().map(serialize_message).collect(); + serde_json::json!({ + "command": "validate", + "success": false, + "errors": errors, + }) + } + } + } +} + +async fn handler(Json(request): Json) -> Json { + Json(handle_request(request)) +} + +#[tokio::main] +async fn main() { + let port: u16 = std::env::args() + .nth(1) + .and_then(|s| s.parse().ok()) + .unwrap_or(3000); + + let app = Router::new().route("/", post(handler)); + let addr = std::net::SocketAddr::from(([127, 0, 0, 1], port)); + println!("Listening on {addr}"); + let listener = tokio::net::TcpListener::bind(addr).await.unwrap(); + axum::serve(listener, app).await.unwrap(); +} diff --git a/lib-rust/kson/src/generated/mod.rs b/lib-rust/kson/src/generated/mod.rs index 50c1b7df..0c89ad11 100644 --- a/lib-rust/kson/src/generated/mod.rs +++ b/lib-rust/kson/src/generated/mod.rs @@ -11,149 +11,250 @@ use self::sys::jobject; use self::util::{AsKotlinObject, FromKotlinObject, KotlinPtr, ToKotlinObject}; -/// Options for formatting Kson output. -/// -/// @param indentType The type of indentation to use (spaces or tabs) -/// @param formattingStyle The formatting style (PLAIN, DELIMITED, COMPACT, CLASSIC) -/// @param embedBlockRules Rules for formatting specific paths as embed blocks #[derive(Clone)] -pub struct FormatOptions { - kotlin_ptr: KotlinPtr, +pub enum SchemaResult { + Failure(schema_result::Failure), + Success(schema_result::Success), } -impl FromKotlinObject for FormatOptions { - fn from_kotlin_object(obj: self::sys::jobject) -> Self { - let (env, _detach_guard) = util::attach_thread_to_java_vm(); - let kotlin_ptr = util::to_gc_global_ref(env, obj); - Self { kotlin_ptr } +pub mod schema_result { + use super::*; + + + + #[derive(Clone)] + pub struct Failure { + kotlin_ptr: KotlinPtr, } -} -impl ToKotlinObject for FormatOptions { - fn to_kotlin_object(&self) -> KotlinPtr { - self.kotlin_ptr.clone() + impl FromKotlinObject for Failure { + fn from_kotlin_object(obj: self::sys::jobject) -> Self { + let (env, _detach_guard) = util::attach_thread_to_java_vm(); + let kotlin_ptr = util::to_gc_global_ref(env, obj); + Self { kotlin_ptr } + } } -} -impl AsKotlinObject for FormatOptions { - fn as_kotlin_object(&self) -> self::sys::jobject { - self.kotlin_ptr.inner.inner + impl ToKotlinObject for Failure { + fn to_kotlin_object(&self) -> KotlinPtr { + self.kotlin_ptr.clone() + } } -} -impl FormatOptions { - pub fn new( - indent_type: IndentType, - formatting_style: FormattingStyle, - embed_block_rules: &[EmbedRule], - ) -> Self { - let (env, _detach_guard) = util::attach_thread_to_java_vm(); - let class = util::get_class(env, c"org/kson/FormatOptions"); - let constructor = util::get_method(env, class.as_kotlin_object(), c"", c"(Lorg/kson/IndentType;Lorg/kson/FormattingStyle;Ljava/util/List;)V"); + impl AsKotlinObject for Failure { + fn as_kotlin_object(&self) -> self::sys::jobject { + self.kotlin_ptr.inner.inner + } + } - let indent_type_ptr = indent_type.to_kotlin_object(); - let indent_type = indent_type_ptr.as_kotlin_object(); - let formatting_style_ptr = formatting_style.to_kotlin_object(); - let formatting_style = formatting_style_ptr.as_kotlin_object(); - let embed_block_rules_ptr = util::to_kotlin_list(embed_block_rules); - let embed_block_rules = embed_block_rules_ptr.as_kotlin_object(); + impl Failure { + pub fn new( + errors: &[Message], + ) -> Self { + let (env, _detach_guard) = util::attach_thread_to_java_vm(); + let class = util::get_class(env, c"org/kson/api/SchemaResult$Failure"); + let constructor = util::get_method(env, class.as_kotlin_object(), c"", c"(Ljava/util/List;)V"); - let jobject = unsafe { (**env).NewObject.unwrap()(env, class.as_kotlin_object(), constructor, - indent_type, - formatting_style, - embed_block_rules, - )}; - util::panic_upon_exception(env); - Self { - kotlin_ptr: util::to_gc_global_ref(env, jobject) + let errors_ptr = util::to_kotlin_list(errors); + let errors = errors_ptr.as_kotlin_object(); + + let jobject = unsafe { (**env).NewObject.unwrap()(env, class.as_kotlin_object(), constructor, + errors, + )}; + util::panic_upon_exception(env); + Self { + kotlin_ptr: util::to_gc_global_ref(env, jobject) + } } } -} -impl FormatOptions { + impl Failure { - pub fn indent_type( - &self, - ) -> IndentType { - let self_ptr = self.to_kotlin_object(); - let self_obj = self_ptr.as_kotlin_object(); + pub fn errors( + &self, + ) -> Vec { + let self_ptr = self.to_kotlin_object(); + let self_obj = self_ptr.as_kotlin_object(); - let (_, _detach_guard) = util::attach_thread_to_java_vm(); - let result = call_jvm_function!( - util, - c"org/kson/FormatOptions", - c"getIndentType", - c"()Lorg/kson/IndentType;", - CallObjectMethod, - self_obj, + let (_, _detach_guard) = util::attach_thread_to_java_vm(); + let result = call_jvm_function!( + util, + c"org/kson/api/SchemaResult$Failure", + c"getErrors", + c"()Ljava/util/List;", + CallObjectMethod, + self_obj, - ); + ); - FromKotlinObject::from_kotlin_object(result) + util::from_kotlin_list(result) + } } + impl std::fmt::Debug for Failure { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let obj = self.to_kotlin_object(); + write!(f, "{}", util::call_to_string(c"org/kson/api/SchemaResult$Failure", &obj)) + } + } - pub fn formatting_style( - &self, - ) -> FormattingStyle { - let self_ptr = self.to_kotlin_object(); - let self_obj = self_ptr.as_kotlin_object(); + impl Eq for Failure {} + impl PartialEq for Failure { + fn eq(&self, other: &Failure) -> bool { + util::equals(self.to_kotlin_object(), other.to_kotlin_object()) + } + } + impl std::hash::Hash for Failure { + fn hash(&self, state: &mut H) + where + H: std::hash::Hasher, + { + util::apply_hash_code(self.to_kotlin_object(), state) + } + } - let (_, _detach_guard) = util::attach_thread_to_java_vm(); - let result = call_jvm_function!( - util, - c"org/kson/FormatOptions", - c"getFormattingStyle", - c"()Lorg/kson/FormattingStyle;", - CallObjectMethod, - self_obj, + #[derive(Clone)] + pub struct Success { + kotlin_ptr: KotlinPtr, + } - ); + impl FromKotlinObject for Success { + fn from_kotlin_object(obj: self::sys::jobject) -> Self { + let (env, _detach_guard) = util::attach_thread_to_java_vm(); + let kotlin_ptr = util::to_gc_global_ref(env, obj); + Self { kotlin_ptr } + } + } - FromKotlinObject::from_kotlin_object(result) + impl ToKotlinObject for Success { + fn to_kotlin_object(&self) -> KotlinPtr { + self.kotlin_ptr.clone() + } } + impl AsKotlinObject for Success { + fn as_kotlin_object(&self) -> self::sys::jobject { + self.kotlin_ptr.inner.inner + } + } - pub fn embed_block_rules( - &self, - ) -> Vec { - let self_ptr = self.to_kotlin_object(); - let self_obj = self_ptr.as_kotlin_object(); + impl Success { + pub fn new( + schema_validator: SchemaValidatorService, + ) -> Self { + let (env, _detach_guard) = util::attach_thread_to_java_vm(); + let class = util::get_class(env, c"org/kson/api/SchemaResult$Success"); + let constructor = util::get_method(env, class.as_kotlin_object(), c"", c"(Lorg/kson/api/SchemaValidatorService;)V"); + let schema_validator_ptr = schema_validator.to_kotlin_object(); + let schema_validator = schema_validator_ptr.as_kotlin_object(); - let (_, _detach_guard) = util::attach_thread_to_java_vm(); - let result = call_jvm_function!( - util, - c"org/kson/FormatOptions", - c"getEmbedBlockRules", - c"()Ljava/util/List;", - CallObjectMethod, - self_obj, + let jobject = unsafe { (**env).NewObject.unwrap()(env, class.as_kotlin_object(), constructor, + schema_validator, + )}; + util::panic_upon_exception(env); + Self { + kotlin_ptr: util::to_gc_global_ref(env, jobject) + } + } + } - ); - util::from_kotlin_list(result) + impl Success { + + + pub fn schema_validator( + &self, + ) -> SchemaValidatorService { + let self_ptr = self.to_kotlin_object(); + let self_obj = self_ptr.as_kotlin_object(); + + + let (_, _detach_guard) = util::attach_thread_to_java_vm(); + let result = call_jvm_function!( + util, + c"org/kson/api/SchemaResult$Success", + c"getSchemaValidator", + c"()Lorg/kson/api/SchemaValidatorService;", + CallObjectMethod, + self_obj, + + ); + + FromKotlinObject::from_kotlin_object(result) + } + } + + impl std::fmt::Debug for Success { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let obj = self.to_kotlin_object(); + write!(f, "{}", util::call_to_string(c"org/kson/api/SchemaResult$Success", &obj)) + } + } + + impl Eq for Success {} + impl PartialEq for Success { + fn eq(&self, other: &Success) -> bool { + util::equals(self.to_kotlin_object(), other.to_kotlin_object()) + } + } + impl std::hash::Hash for Success { + fn hash(&self, state: &mut H) + where + H: std::hash::Hasher, + { + util::apply_hash_code(self.to_kotlin_object(), state) + } + } +} +impl FromKotlinObject for SchemaResult { + fn from_kotlin_object(obj: jobject) -> Self { + match util::class_name(obj).as_str() { + "org.kson.api.SchemaResult$Failure" => SchemaResult::Failure(schema_result::Failure::from_kotlin_object(obj)), + "org.kson.api.SchemaResult$Success" => SchemaResult::Success(schema_result::Success::from_kotlin_object(obj)), + _ => unreachable!(), + } } } -impl std::fmt::Debug for FormatOptions { +impl ToKotlinObject for SchemaResult { + fn to_kotlin_object(&self) -> KotlinPtr { + match self { + Self::Failure(inner) => inner.to_kotlin_object(), + Self::Success(inner) => inner.to_kotlin_object(), + } + } +} + +impl SchemaResult { + pub fn name(self) -> String { + let obj = self.to_kotlin_object(); + util::enum_name(&obj) + } +} + + +impl SchemaResult { +} + +impl std::fmt::Debug for SchemaResult { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let obj = self.to_kotlin_object(); - write!(f, "{}", util::call_to_string(c"org/kson/FormatOptions", &obj)) + write!(f, "{}", util::call_to_string(c"org/kson/api/SchemaResult", &obj)) } } -impl Eq for FormatOptions {} -impl PartialEq for FormatOptions { - fn eq(&self, other: &FormatOptions) -> bool { +impl Eq for SchemaResult {} +impl PartialEq for SchemaResult { + fn eq(&self, other: &SchemaResult) -> bool { util::equals(self.to_kotlin_object(), other.to_kotlin_object()) } } -impl std::hash::Hash for FormatOptions { +impl std::hash::Hash for SchemaResult { fn hash(&self, state: &mut H) where H: std::hash::Hasher, @@ -163,10 +264,7 @@ impl std::hash::Hash for FormatOptions { } -/// A zero-based line/column position in a document -/// -/// @param line The line number where the error occurred (0-based) -/// @param column The column number where the error occurred (0-based) + #[derive(Clone)] pub struct Position { kotlin_ptr: KotlinPtr, @@ -193,6 +291,25 @@ impl AsKotlinObject for Position { } impl Position { + pub fn new( + line: i32, + column: i32, + ) -> Self { + let (env, _detach_guard) = util::attach_thread_to_java_vm(); + let class = util::get_class(env, c"org/kson/api/Position"); + let constructor = util::get_method(env, class.as_kotlin_object(), c"", c"(II)V"); + + + + let jobject = unsafe { (**env).NewObject.unwrap()(env, class.as_kotlin_object(), constructor, + line, + column, + )}; + util::panic_upon_exception(env); + Self { + kotlin_ptr: util::to_gc_global_ref(env, jobject) + } + } } @@ -209,7 +326,7 @@ impl Position { let (_, _detach_guard) = util::attach_thread_to_java_vm(); let result = call_jvm_function!( util, - c"org/kson/Position", + c"org/kson/api/Position", c"getLine", c"()I", CallIntMethod, @@ -231,7 +348,7 @@ impl Position { let (_, _detach_guard) = util::attach_thread_to_java_vm(); let result = call_jvm_function!( util, - c"org/kson/Position", + c"org/kson/api/Position", c"getColumn", c"()I", CallIntMethod, @@ -246,7 +363,7 @@ impl Position { impl std::fmt::Debug for Position { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let obj = self.to_kotlin_object(); - write!(f, "{}", util::call_to_string(c"org/kson/Position", &obj)) + write!(f, "{}", util::call_to_string(c"org/kson/api/Position", &obj)) } } @@ -266,141 +383,234 @@ impl std::hash::Hash for Position { } -/// Represents a message logged during Kson processing #[derive(Clone)] -pub struct Message { - kotlin_ptr: KotlinPtr, +pub enum IndentType { + Spaces(indent_type::Spaces), + Tabs(indent_type::Tabs), } -impl FromKotlinObject for Message { - fn from_kotlin_object(obj: self::sys::jobject) -> Self { - let (env, _detach_guard) = util::attach_thread_to_java_vm(); - let kotlin_ptr = util::to_gc_global_ref(env, obj); - Self { kotlin_ptr } +pub mod indent_type { + use super::*; + + + + #[derive(Clone)] + pub struct Spaces { + kotlin_ptr: KotlinPtr, } -} -impl ToKotlinObject for Message { - fn to_kotlin_object(&self) -> KotlinPtr { - self.kotlin_ptr.clone() + impl FromKotlinObject for Spaces { + fn from_kotlin_object(obj: self::sys::jobject) -> Self { + let (env, _detach_guard) = util::attach_thread_to_java_vm(); + let kotlin_ptr = util::to_gc_global_ref(env, obj); + Self { kotlin_ptr } + } } -} -impl AsKotlinObject for Message { - fn as_kotlin_object(&self) -> self::sys::jobject { - self.kotlin_ptr.inner.inner + impl ToKotlinObject for Spaces { + fn to_kotlin_object(&self) -> KotlinPtr { + self.kotlin_ptr.clone() + } } -} -impl Message { -} + impl AsKotlinObject for Spaces { + fn as_kotlin_object(&self) -> self::sys::jobject { + self.kotlin_ptr.inner.inner + } + } + impl Spaces { + pub fn new( + size: i32, + ) -> Self { + let (env, _detach_guard) = util::attach_thread_to_java_vm(); + let class = util::get_class(env, c"org/kson/api/IndentType$Spaces"); + let constructor = util::get_method(env, class.as_kotlin_object(), c"", c"(I)V"); -impl Message { - pub fn message( - &self, - ) -> String { - let self_ptr = self.to_kotlin_object(); - let self_obj = self_ptr.as_kotlin_object(); + let jobject = unsafe { (**env).NewObject.unwrap()(env, class.as_kotlin_object(), constructor, + size, + )}; + util::panic_upon_exception(env); + Self { + kotlin_ptr: util::to_gc_global_ref(env, jobject) + } + } + } - let (_, _detach_guard) = util::attach_thread_to_java_vm(); - let result = call_jvm_function!( - util, - c"org/kson/Message", - c"getMessage", - c"()Ljava/lang/String;", - CallObjectMethod, - self_obj, + impl Spaces { - ); - FromKotlinObject::from_kotlin_object(result) - } + pub fn size( + &self, + ) -> i32 { + let self_ptr = self.to_kotlin_object(); + let self_obj = self_ptr.as_kotlin_object(); - pub fn severity( - &self, - ) -> MessageSeverity { - let self_ptr = self.to_kotlin_object(); - let self_obj = self_ptr.as_kotlin_object(); + let (_, _detach_guard) = util::attach_thread_to_java_vm(); + let result = call_jvm_function!( + util, + c"org/kson/api/IndentType$Spaces", + c"getSize", + c"()I", + CallIntMethod, + self_obj, + ); - let (_, _detach_guard) = util::attach_thread_to_java_vm(); - let result = call_jvm_function!( - util, - c"org/kson/Message", - c"getSeverity", - c"()Lorg/kson/MessageSeverity;", - CallObjectMethod, - self_obj, + result + } + } - ); + impl std::fmt::Debug for Spaces { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let obj = self.to_kotlin_object(); + write!(f, "{}", util::call_to_string(c"org/kson/api/IndentType$Spaces", &obj)) + } + } - FromKotlinObject::from_kotlin_object(result) + impl Eq for Spaces {} + impl PartialEq for Spaces { + fn eq(&self, other: &Spaces) -> bool { + util::equals(self.to_kotlin_object(), other.to_kotlin_object()) + } + } + impl std::hash::Hash for Spaces { + fn hash(&self, state: &mut H) + where + H: std::hash::Hasher, + { + util::apply_hash_code(self.to_kotlin_object(), state) + } } - pub fn start( - &self, - ) -> Position { - let self_ptr = self.to_kotlin_object(); - let self_obj = self_ptr.as_kotlin_object(); + #[derive(Clone)] + pub struct Tabs { + kotlin_ptr: KotlinPtr, + } + impl FromKotlinObject for Tabs { + fn from_kotlin_object(obj: self::sys::jobject) -> Self { + let (env, _detach_guard) = util::attach_thread_to_java_vm(); + let kotlin_ptr = util::to_gc_global_ref(env, obj); + Self { kotlin_ptr } + } + } - let (_, _detach_guard) = util::attach_thread_to_java_vm(); - let result = call_jvm_function!( - util, - c"org/kson/Message", - c"getStart", - c"()Lorg/kson/Position;", - CallObjectMethod, - self_obj, + impl ToKotlinObject for Tabs { + fn to_kotlin_object(&self) -> KotlinPtr { + self.kotlin_ptr.clone() + } + } - ); + impl AsKotlinObject for Tabs { + fn as_kotlin_object(&self) -> self::sys::jobject { + self.kotlin_ptr.inner.inner + } + } - FromKotlinObject::from_kotlin_object(result) + impl Tabs { + pub fn new() -> Self { + let kotlin_ptr = util::access_static_field(c"org/kson/api/IndentType$Tabs", c"INSTANCE", c"Lorg/kson/api/IndentType$Tabs;"); + Self { kotlin_ptr } + } } - pub fn end( - &self, - ) -> Position { - let self_ptr = self.to_kotlin_object(); - let self_obj = self_ptr.as_kotlin_object(); + impl Tabs { - let (_, _detach_guard) = util::attach_thread_to_java_vm(); - let result = call_jvm_function!( - util, - c"org/kson/Message", - c"getEnd", - c"()Lorg/kson/Position;", - CallObjectMethod, - self_obj, + pub fn to_string( + ) -> String { + let self_ptr = util::access_static_field(c"org/kson/api/IndentType$Tabs", c"INSTANCE", c"Lorg/kson/api/IndentType$Tabs;"); + let self_obj = self_ptr.as_kotlin_object(); - ); - FromKotlinObject::from_kotlin_object(result) + let (_, _detach_guard) = util::attach_thread_to_java_vm(); + let result = call_jvm_function!( + util, + c"org/kson/api/IndentType$Tabs", + c"toString", + c"()Ljava/lang/String;", + CallObjectMethod, + self_obj, + + ); + + FromKotlinObject::from_kotlin_object(result) + } + } + + impl std::fmt::Debug for Tabs { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let obj = self.to_kotlin_object(); + write!(f, "{}", util::call_to_string(c"org/kson/api/IndentType$Tabs", &obj)) + } + } + + impl Eq for Tabs {} + impl PartialEq for Tabs { + fn eq(&self, other: &Tabs) -> bool { + util::equals(self.to_kotlin_object(), other.to_kotlin_object()) + } + } + impl std::hash::Hash for Tabs { + fn hash(&self, state: &mut H) + where + H: std::hash::Hasher, + { + util::apply_hash_code(self.to_kotlin_object(), state) + } + } +} +impl FromKotlinObject for IndentType { + fn from_kotlin_object(obj: jobject) -> Self { + match util::class_name(obj).as_str() { + "org.kson.api.IndentType$Spaces" => IndentType::Spaces(indent_type::Spaces::from_kotlin_object(obj)), + "org.kson.api.IndentType$Tabs" => IndentType::Tabs(indent_type::Tabs::from_kotlin_object(obj)), + _ => unreachable!(), + } + } +} + +impl ToKotlinObject for IndentType { + fn to_kotlin_object(&self) -> KotlinPtr { + match self { + Self::Spaces(inner) => inner.to_kotlin_object(), + Self::Tabs(inner) => inner.to_kotlin_object(), + } + } +} + +impl IndentType { + pub fn name(self) -> String { + let obj = self.to_kotlin_object(); + util::enum_name(&obj) } } -impl std::fmt::Debug for Message { + +impl IndentType { +} + +impl std::fmt::Debug for IndentType { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let obj = self.to_kotlin_object(); - write!(f, "{}", util::call_to_string(c"org/kson/Message", &obj)) + write!(f, "{}", util::call_to_string(c"org/kson/api/IndentType", &obj)) } } -impl Eq for Message {} -impl PartialEq for Message { - fn eq(&self, other: &Message) -> bool { +impl Eq for IndentType {} +impl PartialEq for IndentType { + fn eq(&self, other: &IndentType) -> bool { util::equals(self.to_kotlin_object(), other.to_kotlin_object()) } } -impl std::hash::Hash for Message { +impl std::hash::Hash for IndentType { fn hash(&self, state: &mut H) where H: std::hash::Hasher, @@ -410,13 +620,13 @@ impl std::hash::Hash for Message { } -/// [Token] produced by the lexing phase of a Kson parse + #[derive(Clone)] -pub struct Token { +pub struct Message { kotlin_ptr: KotlinPtr, } -impl FromKotlinObject for Token { +impl FromKotlinObject for Message { fn from_kotlin_object(obj: self::sys::jobject) -> Self { let (env, _detach_guard) = util::attach_thread_to_java_vm(); let kotlin_ptr = util::to_gc_global_ref(env, obj); @@ -424,28 +634,58 @@ impl FromKotlinObject for Token { } } -impl ToKotlinObject for Token { +impl ToKotlinObject for Message { fn to_kotlin_object(&self) -> KotlinPtr { self.kotlin_ptr.clone() } } -impl AsKotlinObject for Token { +impl AsKotlinObject for Message { fn as_kotlin_object(&self) -> self::sys::jobject { self.kotlin_ptr.inner.inner } } -impl Token { +impl Message { + pub fn new( + message: &str, + severity: MessageSeverity, + start: Position, + end: Position, + ) -> Self { + let (env, _detach_guard) = util::attach_thread_to_java_vm(); + let class = util::get_class(env, c"org/kson/api/Message"); + let constructor = util::get_method(env, class.as_kotlin_object(), c"", c"(Ljava/lang/String;Lorg/kson/api/MessageSeverity;Lorg/kson/api/Position;Lorg/kson/api/Position;)V"); + + let message_ptr = message.to_kotlin_object(); + let message = message_ptr.as_kotlin_object(); + let severity_ptr = severity.to_kotlin_object(); + let severity = severity_ptr.as_kotlin_object(); + let start_ptr = start.to_kotlin_object(); + let start = start_ptr.as_kotlin_object(); + let end_ptr = end.to_kotlin_object(); + let end = end_ptr.as_kotlin_object(); + + let jobject = unsafe { (**env).NewObject.unwrap()(env, class.as_kotlin_object(), constructor, + message, + severity, + start, + end, + )}; + util::panic_upon_exception(env); + Self { + kotlin_ptr: util::to_gc_global_ref(env, jobject) + } + } } -impl Token { +impl Message { - pub fn token_type( + pub fn message( &self, - ) -> TokenType { + ) -> String { let self_ptr = self.to_kotlin_object(); let self_obj = self_ptr.as_kotlin_object(); @@ -453,9 +693,9 @@ impl Token { let (_, _detach_guard) = util::attach_thread_to_java_vm(); let result = call_jvm_function!( util, - c"org/kson/Token", - c"getTokenType", - c"()Lorg/kson/TokenType;", + c"org/kson/api/Message", + c"getMessage", + c"()Ljava/lang/String;", CallObjectMethod, self_obj, @@ -465,9 +705,9 @@ impl Token { } - pub fn text( + pub fn severity( &self, - ) -> String { + ) -> MessageSeverity { let self_ptr = self.to_kotlin_object(); let self_obj = self_ptr.as_kotlin_object(); @@ -475,9 +715,9 @@ impl Token { let (_, _detach_guard) = util::attach_thread_to_java_vm(); let result = call_jvm_function!( util, - c"org/kson/Token", - c"getText", - c"()Ljava/lang/String;", + c"org/kson/api/Message", + c"getSeverity", + c"()Lorg/kson/api/MessageSeverity;", CallObjectMethod, self_obj, @@ -497,9 +737,9 @@ impl Token { let (_, _detach_guard) = util::attach_thread_to_java_vm(); let result = call_jvm_function!( util, - c"org/kson/Token", + c"org/kson/api/Message", c"getStart", - c"()Lorg/kson/Position;", + c"()Lorg/kson/api/Position;", CallObjectMethod, self_obj, @@ -519,9 +759,9 @@ impl Token { let (_, _detach_guard) = util::attach_thread_to_java_vm(); let result = call_jvm_function!( util, - c"org/kson/Token", + c"org/kson/api/Message", c"getEnd", - c"()Lorg/kson/Position;", + c"()Lorg/kson/api/Position;", CallObjectMethod, self_obj, @@ -531,20 +771,20 @@ impl Token { } } -impl std::fmt::Debug for Token { +impl std::fmt::Debug for Message { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let obj = self.to_kotlin_object(); - write!(f, "{}", util::call_to_string(c"org/kson/Token", &obj)) + write!(f, "{}", util::call_to_string(c"org/kson/api/Message", &obj)) } } -impl Eq for Token {} -impl PartialEq for Token { - fn eq(&self, other: &Token) -> bool { +impl Eq for Message {} +impl PartialEq for Message { + fn eq(&self, other: &Message) -> bool { util::equals(self.to_kotlin_object(), other.to_kotlin_object()) } } -impl std::hash::Hash for Token { +impl std::hash::Hash for Message { fn hash(&self, state: &mut H) where H: std::hash::Hasher, @@ -553,7 +793,7 @@ impl std::hash::Hash for Token { } } -/// Represents a parsed [InternalKsonValue] in the public API + #[derive(Clone)] pub enum KsonValue { KsonArray(kson_value::KsonArray), @@ -569,7 +809,7 @@ pub mod kson_value { use super::*; - /// A Kson array with elements + #[derive(Clone)] pub struct KsonArray { kotlin_ptr: KotlinPtr, @@ -596,6 +836,32 @@ pub mod kson_value { } impl KsonArray { + pub fn new( + elements: &[KsonValue], + internal_start: Position, + internal_end: Position, + ) -> Self { + let (env, _detach_guard) = util::attach_thread_to_java_vm(); + let class = util::get_class(env, c"org/kson/api/KsonValue$KsonArray"); + let constructor = util::get_method(env, class.as_kotlin_object(), c"", c"(Ljava/util/List;Lorg/kson/api/Position;Lorg/kson/api/Position;)V"); + + let elements_ptr = util::to_kotlin_list(elements); + let elements = elements_ptr.as_kotlin_object(); + let internal_start_ptr = internal_start.to_kotlin_object(); + let internal_start = internal_start_ptr.as_kotlin_object(); + let internal_end_ptr = internal_end.to_kotlin_object(); + let internal_end = internal_end_ptr.as_kotlin_object(); + + let jobject = unsafe { (**env).NewObject.unwrap()(env, class.as_kotlin_object(), constructor, + elements, + internal_start, + internal_end, + )}; + util::panic_upon_exception(env); + Self { + kotlin_ptr: util::to_gc_global_ref(env, jobject) + } + } } @@ -612,7 +878,7 @@ pub mod kson_value { let (_, _detach_guard) = util::attach_thread_to_java_vm(); let result = call_jvm_function!( util, - c"org/kson/KsonValue$KsonArray", + c"org/kson/api/KsonValue$KsonArray", c"getElements", c"()Ljava/util/List;", CallObjectMethod, @@ -634,9 +900,9 @@ pub mod kson_value { let (_, _detach_guard) = util::attach_thread_to_java_vm(); let result = call_jvm_function!( util, - c"org/kson/KsonValue$KsonArray", + c"org/kson/api/KsonValue$KsonArray", c"getType", - c"()Lorg/kson/KsonValueType;", + c"()Lorg/kson/api/KsonValueType;", CallObjectMethod, self_obj, @@ -656,9 +922,9 @@ pub mod kson_value { let (_, _detach_guard) = util::attach_thread_to_java_vm(); let result = call_jvm_function!( util, - c"org/kson/KsonValue", + c"org/kson/api/KsonValue", c"getStart", - c"()Lorg/kson/Position;", + c"()Lorg/kson/api/Position;", CallObjectMethod, self_obj, @@ -678,9 +944,9 @@ pub mod kson_value { let (_, _detach_guard) = util::attach_thread_to_java_vm(); let result = call_jvm_function!( util, - c"org/kson/KsonValue", + c"org/kson/api/KsonValue", c"getEnd", - c"()Lorg/kson/Position;", + c"()Lorg/kson/api/Position;", CallObjectMethod, self_obj, @@ -693,7 +959,7 @@ pub mod kson_value { impl std::fmt::Debug for KsonArray { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let obj = self.to_kotlin_object(); - write!(f, "{}", util::call_to_string(c"org/kson/KsonValue$KsonArray", &obj)) + write!(f, "{}", util::call_to_string(c"org/kson/api/KsonValue$KsonArray", &obj)) } } @@ -712,7 +978,7 @@ pub mod kson_value { } } - /// A Kson boolean value + #[derive(Clone)] pub struct KsonBoolean { kotlin_ptr: KotlinPtr, @@ -739,6 +1005,31 @@ pub mod kson_value { } impl KsonBoolean { + pub fn new( + value: bool, + internal_start: Position, + internal_end: Position, + ) -> Self { + let (env, _detach_guard) = util::attach_thread_to_java_vm(); + let class = util::get_class(env, c"org/kson/api/KsonValue$KsonBoolean"); + let constructor = util::get_method(env, class.as_kotlin_object(), c"", c"(ZLorg/kson/api/Position;Lorg/kson/api/Position;)V"); + + let value = value as c_int; + let internal_start_ptr = internal_start.to_kotlin_object(); + let internal_start = internal_start_ptr.as_kotlin_object(); + let internal_end_ptr = internal_end.to_kotlin_object(); + let internal_end = internal_end_ptr.as_kotlin_object(); + + let jobject = unsafe { (**env).NewObject.unwrap()(env, class.as_kotlin_object(), constructor, + value, + internal_start, + internal_end, + )}; + util::panic_upon_exception(env); + Self { + kotlin_ptr: util::to_gc_global_ref(env, jobject) + } + } } @@ -755,7 +1046,7 @@ pub mod kson_value { let (_, _detach_guard) = util::attach_thread_to_java_vm(); let result = call_jvm_function!( util, - c"org/kson/KsonValue$KsonBoolean", + c"org/kson/api/KsonValue$KsonBoolean", c"getValue", c"()Z", CallBooleanMethod, @@ -777,9 +1068,9 @@ pub mod kson_value { let (_, _detach_guard) = util::attach_thread_to_java_vm(); let result = call_jvm_function!( util, - c"org/kson/KsonValue$KsonBoolean", + c"org/kson/api/KsonValue$KsonBoolean", c"getType", - c"()Lorg/kson/KsonValueType;", + c"()Lorg/kson/api/KsonValueType;", CallObjectMethod, self_obj, @@ -799,9 +1090,9 @@ pub mod kson_value { let (_, _detach_guard) = util::attach_thread_to_java_vm(); let result = call_jvm_function!( util, - c"org/kson/KsonValue", + c"org/kson/api/KsonValue", c"getStart", - c"()Lorg/kson/Position;", + c"()Lorg/kson/api/Position;", CallObjectMethod, self_obj, @@ -821,9 +1112,9 @@ pub mod kson_value { let (_, _detach_guard) = util::attach_thread_to_java_vm(); let result = call_jvm_function!( util, - c"org/kson/KsonValue", + c"org/kson/api/KsonValue", c"getEnd", - c"()Lorg/kson/Position;", + c"()Lorg/kson/api/Position;", CallObjectMethod, self_obj, @@ -836,7 +1127,7 @@ pub mod kson_value { impl std::fmt::Debug for KsonBoolean { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let obj = self.to_kotlin_object(); - write!(f, "{}", util::call_to_string(c"org/kson/KsonValue$KsonBoolean", &obj)) + write!(f, "{}", util::call_to_string(c"org/kson/api/KsonValue$KsonBoolean", &obj)) } } @@ -855,7 +1146,7 @@ pub mod kson_value { } } - /// A Kson embed block + #[derive(Clone)] pub struct KsonEmbed { kotlin_ptr: KotlinPtr, @@ -882,6 +1173,36 @@ pub mod kson_value { } impl KsonEmbed { + pub fn new( + tag: Option<&str>, + content: &str, + internal_start: Position, + internal_end: Position, + ) -> Self { + let (env, _detach_guard) = util::attach_thread_to_java_vm(); + let class = util::get_class(env, c"org/kson/api/KsonValue$KsonEmbed"); + let constructor = util::get_method(env, class.as_kotlin_object(), c"", c"(Ljava/lang/String;Ljava/lang/String;Lorg/kson/api/Position;Lorg/kson/api/Position;)V"); + + let tag_ptr = tag.to_kotlin_object(); + let tag = tag_ptr.as_kotlin_object(); + let content_ptr = content.to_kotlin_object(); + let content = content_ptr.as_kotlin_object(); + let internal_start_ptr = internal_start.to_kotlin_object(); + let internal_start = internal_start_ptr.as_kotlin_object(); + let internal_end_ptr = internal_end.to_kotlin_object(); + let internal_end = internal_end_ptr.as_kotlin_object(); + + let jobject = unsafe { (**env).NewObject.unwrap()(env, class.as_kotlin_object(), constructor, + tag, + content, + internal_start, + internal_end, + )}; + util::panic_upon_exception(env); + Self { + kotlin_ptr: util::to_gc_global_ref(env, jobject) + } + } } @@ -898,7 +1219,7 @@ pub mod kson_value { let (_, _detach_guard) = util::attach_thread_to_java_vm(); let result = call_jvm_function!( util, - c"org/kson/KsonValue$KsonEmbed", + c"org/kson/api/KsonValue$KsonEmbed", c"getTag", c"()Ljava/lang/String;", CallObjectMethod, @@ -920,7 +1241,7 @@ pub mod kson_value { let (_, _detach_guard) = util::attach_thread_to_java_vm(); let result = call_jvm_function!( util, - c"org/kson/KsonValue$KsonEmbed", + c"org/kson/api/KsonValue$KsonEmbed", c"getContent", c"()Ljava/lang/String;", CallObjectMethod, @@ -942,9 +1263,9 @@ pub mod kson_value { let (_, _detach_guard) = util::attach_thread_to_java_vm(); let result = call_jvm_function!( util, - c"org/kson/KsonValue$KsonEmbed", + c"org/kson/api/KsonValue$KsonEmbed", c"getType", - c"()Lorg/kson/KsonValueType;", + c"()Lorg/kson/api/KsonValueType;", CallObjectMethod, self_obj, @@ -964,9 +1285,9 @@ pub mod kson_value { let (_, _detach_guard) = util::attach_thread_to_java_vm(); let result = call_jvm_function!( util, - c"org/kson/KsonValue", + c"org/kson/api/KsonValue", c"getStart", - c"()Lorg/kson/Position;", + c"()Lorg/kson/api/Position;", CallObjectMethod, self_obj, @@ -986,9 +1307,9 @@ pub mod kson_value { let (_, _detach_guard) = util::attach_thread_to_java_vm(); let result = call_jvm_function!( util, - c"org/kson/KsonValue", + c"org/kson/api/KsonValue", c"getEnd", - c"()Lorg/kson/Position;", + c"()Lorg/kson/api/Position;", CallObjectMethod, self_obj, @@ -1001,7 +1322,7 @@ pub mod kson_value { impl std::fmt::Debug for KsonEmbed { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let obj = self.to_kotlin_object(); - write!(f, "{}", util::call_to_string(c"org/kson/KsonValue$KsonEmbed", &obj)) + write!(f, "{}", util::call_to_string(c"org/kson/api/KsonValue$KsonEmbed", &obj)) } } @@ -1020,7 +1341,7 @@ pub mod kson_value { } } - /// A Kson null value + #[derive(Clone)] pub struct KsonNull { kotlin_ptr: KotlinPtr, @@ -1047,6 +1368,28 @@ pub mod kson_value { } impl KsonNull { + pub fn new( + internal_start: Position, + internal_end: Position, + ) -> Self { + let (env, _detach_guard) = util::attach_thread_to_java_vm(); + let class = util::get_class(env, c"org/kson/api/KsonValue$KsonNull"); + let constructor = util::get_method(env, class.as_kotlin_object(), c"", c"(Lorg/kson/api/Position;Lorg/kson/api/Position;)V"); + + let internal_start_ptr = internal_start.to_kotlin_object(); + let internal_start = internal_start_ptr.as_kotlin_object(); + let internal_end_ptr = internal_end.to_kotlin_object(); + let internal_end = internal_end_ptr.as_kotlin_object(); + + let jobject = unsafe { (**env).NewObject.unwrap()(env, class.as_kotlin_object(), constructor, + internal_start, + internal_end, + )}; + util::panic_upon_exception(env); + Self { + kotlin_ptr: util::to_gc_global_ref(env, jobject) + } + } } @@ -1063,9 +1406,9 @@ pub mod kson_value { let (_, _detach_guard) = util::attach_thread_to_java_vm(); let result = call_jvm_function!( util, - c"org/kson/KsonValue$KsonNull", + c"org/kson/api/KsonValue$KsonNull", c"getType", - c"()Lorg/kson/KsonValueType;", + c"()Lorg/kson/api/KsonValueType;", CallObjectMethod, self_obj, @@ -1085,222 +1428,79 @@ pub mod kson_value { let (_, _detach_guard) = util::attach_thread_to_java_vm(); let result = call_jvm_function!( util, - c"org/kson/KsonValue", + c"org/kson/api/KsonValue", c"getStart", - c"()Lorg/kson/Position;", - CallObjectMethod, - self_obj, - - ); - - FromKotlinObject::from_kotlin_object(result) - } - - - pub fn end( - &self, - ) -> Position { - let self_ptr = self.to_kotlin_object(); - let self_obj = self_ptr.as_kotlin_object(); - - - let (_, _detach_guard) = util::attach_thread_to_java_vm(); - let result = call_jvm_function!( - util, - c"org/kson/KsonValue", - c"getEnd", - c"()Lorg/kson/Position;", + c"()Lorg/kson/api/Position;", CallObjectMethod, - self_obj, - - ); - - FromKotlinObject::from_kotlin_object(result) - } - } - - impl std::fmt::Debug for KsonNull { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let obj = self.to_kotlin_object(); - write!(f, "{}", util::call_to_string(c"org/kson/KsonValue$KsonNull", &obj)) - } - } - - impl Eq for KsonNull {} - impl PartialEq for KsonNull { - fn eq(&self, other: &KsonNull) -> bool { - util::equals(self.to_kotlin_object(), other.to_kotlin_object()) - } - } - impl std::hash::Hash for KsonNull { - fn hash(&self, state: &mut H) - where - H: std::hash::Hasher, - { - util::apply_hash_code(self.to_kotlin_object(), state) - } - } - /// A Kson number value. - #[derive(Clone)] - pub enum KsonNumber { - Decimal(kson_value::kson_number::Decimal), - Integer(kson_value::kson_number::Integer), - } - - pub mod kson_number { - use super::*; - - - - #[derive(Clone)] - pub struct Decimal { - kotlin_ptr: KotlinPtr, - } - - impl FromKotlinObject for Decimal { - fn from_kotlin_object(obj: self::sys::jobject) -> Self { - let (env, _detach_guard) = util::attach_thread_to_java_vm(); - let kotlin_ptr = util::to_gc_global_ref(env, obj); - Self { kotlin_ptr } - } - } - - impl ToKotlinObject for Decimal { - fn to_kotlin_object(&self) -> KotlinPtr { - self.kotlin_ptr.clone() - } - } - - impl AsKotlinObject for Decimal { - fn as_kotlin_object(&self) -> self::sys::jobject { - self.kotlin_ptr.inner.inner - } - } - - impl Decimal { - } - - - impl Decimal { - - - pub fn value( - &self, - ) -> f64 { - let self_ptr = self.to_kotlin_object(); - let self_obj = self_ptr.as_kotlin_object(); - - - let (_, _detach_guard) = util::attach_thread_to_java_vm(); - let result = call_jvm_function!( - util, - c"org/kson/KsonValue$KsonNumber$Decimal", - c"getValue", - c"()D", - CallDoubleMethod, - self_obj, - - ); - - result - } - - - pub fn type_( - &self, - ) -> KsonValueType { - let self_ptr = self.to_kotlin_object(); - let self_obj = self_ptr.as_kotlin_object(); - - - let (_, _detach_guard) = util::attach_thread_to_java_vm(); - let result = call_jvm_function!( - util, - c"org/kson/KsonValue$KsonNumber$Decimal", - c"getType", - c"()Lorg/kson/KsonValueType;", - CallObjectMethod, - self_obj, - - ); - - FromKotlinObject::from_kotlin_object(result) - } - - - pub fn start( - &self, - ) -> Position { - let self_ptr = self.to_kotlin_object(); - let self_obj = self_ptr.as_kotlin_object(); - - - let (_, _detach_guard) = util::attach_thread_to_java_vm(); - let result = call_jvm_function!( - util, - c"org/kson/KsonValue", - c"getStart", - c"()Lorg/kson/Position;", - CallObjectMethod, - self_obj, + self_obj, - ); + ); - FromKotlinObject::from_kotlin_object(result) - } + FromKotlinObject::from_kotlin_object(result) + } - pub fn end( - &self, - ) -> Position { - let self_ptr = self.to_kotlin_object(); - let self_obj = self_ptr.as_kotlin_object(); + pub fn end( + &self, + ) -> Position { + let self_ptr = self.to_kotlin_object(); + let self_obj = self_ptr.as_kotlin_object(); - let (_, _detach_guard) = util::attach_thread_to_java_vm(); - let result = call_jvm_function!( - util, - c"org/kson/KsonValue", - c"getEnd", - c"()Lorg/kson/Position;", - CallObjectMethod, - self_obj, + let (_, _detach_guard) = util::attach_thread_to_java_vm(); + let result = call_jvm_function!( + util, + c"org/kson/api/KsonValue", + c"getEnd", + c"()Lorg/kson/api/Position;", + CallObjectMethod, + self_obj, - ); + ); - FromKotlinObject::from_kotlin_object(result) - } + FromKotlinObject::from_kotlin_object(result) } + } - impl std::fmt::Debug for Decimal { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let obj = self.to_kotlin_object(); - write!(f, "{}", util::call_to_string(c"org/kson/KsonValue$KsonNumber$Decimal", &obj)) - } + impl std::fmt::Debug for KsonNull { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let obj = self.to_kotlin_object(); + write!(f, "{}", util::call_to_string(c"org/kson/api/KsonValue$KsonNull", &obj)) } + } - impl Eq for Decimal {} - impl PartialEq for Decimal { - fn eq(&self, other: &Decimal) -> bool { - util::equals(self.to_kotlin_object(), other.to_kotlin_object()) - } + impl Eq for KsonNull {} + impl PartialEq for KsonNull { + fn eq(&self, other: &KsonNull) -> bool { + util::equals(self.to_kotlin_object(), other.to_kotlin_object()) } - impl std::hash::Hash for Decimal { - fn hash(&self, state: &mut H) - where - H: std::hash::Hasher, - { - util::apply_hash_code(self.to_kotlin_object(), state) - } + } + impl std::hash::Hash for KsonNull { + fn hash(&self, state: &mut H) + where + H: std::hash::Hasher, + { + util::apply_hash_code(self.to_kotlin_object(), state) } + } + + #[derive(Clone)] + pub enum KsonNumber { + Decimal(kson_value::kson_number::Decimal), + Integer(kson_value::kson_number::Integer), + } + + pub mod kson_number { + use super::*; + #[derive(Clone)] - pub struct Integer { + pub struct Decimal { kotlin_ptr: KotlinPtr, } - impl FromKotlinObject for Integer { + impl FromKotlinObject for Decimal { fn from_kotlin_object(obj: self::sys::jobject) -> Self { let (env, _detach_guard) = util::attach_thread_to_java_vm(); let kotlin_ptr = util::to_gc_global_ref(env, obj); @@ -1308,28 +1508,53 @@ pub mod kson_value { } } - impl ToKotlinObject for Integer { + impl ToKotlinObject for Decimal { fn to_kotlin_object(&self) -> KotlinPtr { self.kotlin_ptr.clone() } } - impl AsKotlinObject for Integer { + impl AsKotlinObject for Decimal { fn as_kotlin_object(&self) -> self::sys::jobject { self.kotlin_ptr.inner.inner } } - impl Integer { + impl Decimal { + pub fn new( + value: f64, + internal_start: Position, + internal_end: Position, + ) -> Self { + let (env, _detach_guard) = util::attach_thread_to_java_vm(); + let class = util::get_class(env, c"org/kson/api/KsonValue$KsonNumber$Decimal"); + let constructor = util::get_method(env, class.as_kotlin_object(), c"", c"(DLorg/kson/api/Position;Lorg/kson/api/Position;)V"); + + + let internal_start_ptr = internal_start.to_kotlin_object(); + let internal_start = internal_start_ptr.as_kotlin_object(); + let internal_end_ptr = internal_end.to_kotlin_object(); + let internal_end = internal_end_ptr.as_kotlin_object(); + + let jobject = unsafe { (**env).NewObject.unwrap()(env, class.as_kotlin_object(), constructor, + value, + internal_start, + internal_end, + )}; + util::panic_upon_exception(env); + Self { + kotlin_ptr: util::to_gc_global_ref(env, jobject) + } + } } - impl Integer { + impl Decimal { pub fn value( &self, - ) -> i32 { + ) -> f64 { let self_ptr = self.to_kotlin_object(); let self_obj = self_ptr.as_kotlin_object(); @@ -1337,10 +1562,10 @@ pub mod kson_value { let (_, _detach_guard) = util::attach_thread_to_java_vm(); let result = call_jvm_function!( util, - c"org/kson/KsonValue$KsonNumber$Integer", + c"org/kson/api/KsonValue$KsonNumber$Decimal", c"getValue", - c"()I", - CallIntMethod, + c"()D", + CallDoubleMethod, self_obj, ); @@ -1349,50 +1574,6 @@ pub mod kson_value { } - pub fn internal_start( - &self, - ) -> Position { - let self_ptr = self.to_kotlin_object(); - let self_obj = self_ptr.as_kotlin_object(); - - - let (_, _detach_guard) = util::attach_thread_to_java_vm(); - let result = call_jvm_function!( - util, - c"org/kson/KsonValue$KsonNumber$Integer", - c"getInternalStart", - c"()Lorg/kson/Position;", - CallObjectMethod, - self_obj, - - ); - - FromKotlinObject::from_kotlin_object(result) - } - - - pub fn internal_end( - &self, - ) -> Position { - let self_ptr = self.to_kotlin_object(); - let self_obj = self_ptr.as_kotlin_object(); - - - let (_, _detach_guard) = util::attach_thread_to_java_vm(); - let result = call_jvm_function!( - util, - c"org/kson/KsonValue$KsonNumber$Integer", - c"getInternalEnd", - c"()Lorg/kson/Position;", - CallObjectMethod, - self_obj, - - ); - - FromKotlinObject::from_kotlin_object(result) - } - - pub fn type_( &self, ) -> KsonValueType { @@ -1403,9 +1584,9 @@ pub mod kson_value { let (_, _detach_guard) = util::attach_thread_to_java_vm(); let result = call_jvm_function!( util, - c"org/kson/KsonValue$KsonNumber$Integer", + c"org/kson/api/KsonValue$KsonNumber$Decimal", c"getType", - c"()Lorg/kson/KsonValueType;", + c"()Lorg/kson/api/KsonValueType;", CallObjectMethod, self_obj, @@ -1425,9 +1606,9 @@ pub mod kson_value { let (_, _detach_guard) = util::attach_thread_to_java_vm(); let result = call_jvm_function!( util, - c"org/kson/KsonValue", + c"org/kson/api/KsonValue", c"getStart", - c"()Lorg/kson/Position;", + c"()Lorg/kson/api/Position;", CallObjectMethod, self_obj, @@ -1447,9 +1628,9 @@ pub mod kson_value { let (_, _detach_guard) = util::attach_thread_to_java_vm(); let result = call_jvm_function!( util, - c"org/kson/KsonValue", + c"org/kson/api/KsonValue", c"getEnd", - c"()Lorg/kson/Position;", + c"()Lorg/kson/api/Position;", CallObjectMethod, self_obj, @@ -1459,20 +1640,20 @@ pub mod kson_value { } } - impl std::fmt::Debug for Integer { + impl std::fmt::Debug for Decimal { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let obj = self.to_kotlin_object(); - write!(f, "{}", util::call_to_string(c"org/kson/KsonValue$KsonNumber$Integer", &obj)) + write!(f, "{}", util::call_to_string(c"org/kson/api/KsonValue$KsonNumber$Decimal", &obj)) } } - impl Eq for Integer {} - impl PartialEq for Integer { - fn eq(&self, other: &Integer) -> bool { + impl Eq for Decimal {} + impl PartialEq for Decimal { + fn eq(&self, other: &Decimal) -> bool { util::equals(self.to_kotlin_object(), other.to_kotlin_object()) } } - impl std::hash::Hash for Integer { + impl std::hash::Hash for Decimal { fn hash(&self, state: &mut H) where H: std::hash::Hasher, @@ -1480,348 +1661,252 @@ pub mod kson_value { util::apply_hash_code(self.to_kotlin_object(), state) } } - } - impl FromKotlinObject for KsonNumber { - fn from_kotlin_object(obj: jobject) -> Self { - match util::class_name(obj).as_str() { - "org.kson.KsonValue$KsonNumber$Decimal" => kson_value::KsonNumber::Decimal(kson_value::kson_number::Decimal::from_kotlin_object(obj)), - "org.kson.KsonValue$KsonNumber$Integer" => kson_value::KsonNumber::Integer(kson_value::kson_number::Integer::from_kotlin_object(obj)), - _ => unreachable!(), - } - } - } - - impl ToKotlinObject for KsonNumber { - fn to_kotlin_object(&self) -> KotlinPtr { - match self { - Self::Decimal(inner) => inner.to_kotlin_object(), - Self::Integer(inner) => inner.to_kotlin_object(), - } - } - } - - impl KsonNumber { - pub fn name(self) -> String { - let obj = self.to_kotlin_object(); - util::enum_name(&obj) - } - } - - - impl KsonNumber { - - - pub fn start( - &self, - ) -> Position { - let self_ptr = self.to_kotlin_object(); - let self_obj = self_ptr.as_kotlin_object(); - - - let (_, _detach_guard) = util::attach_thread_to_java_vm(); - let result = call_jvm_function!( - util, - c"org/kson/KsonValue", - c"getStart", - c"()Lorg/kson/Position;", - CallObjectMethod, - self_obj, - - ); - - FromKotlinObject::from_kotlin_object(result) - } - - - pub fn end( - &self, - ) -> Position { - let self_ptr = self.to_kotlin_object(); - let self_obj = self_ptr.as_kotlin_object(); - - - let (_, _detach_guard) = util::attach_thread_to_java_vm(); - let result = call_jvm_function!( - util, - c"org/kson/KsonValue", - c"getEnd", - c"()Lorg/kson/Position;", - CallObjectMethod, - self_obj, - - ); - - FromKotlinObject::from_kotlin_object(result) - } - - /// Type discriminator for easier type checking in TypeScript/JavaScript - pub fn type_( - &self, - ) -> KsonValueType { - let self_ptr = self.to_kotlin_object(); - let self_obj = self_ptr.as_kotlin_object(); - - - let (_, _detach_guard) = util::attach_thread_to_java_vm(); - let result = call_jvm_function!( - util, - c"org/kson/KsonValue", - c"getType", - c"()Lorg/kson/KsonValueType;", - CallObjectMethod, - self_obj, - ); - - FromKotlinObject::from_kotlin_object(result) - } - } - impl std::fmt::Debug for KsonNumber { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let obj = self.to_kotlin_object(); - write!(f, "{}", util::call_to_string(c"org/kson/KsonValue$KsonNumber", &obj)) + #[derive(Clone)] + pub struct Integer { + kotlin_ptr: KotlinPtr, } - } - impl Eq for KsonNumber {} - impl PartialEq for KsonNumber { - fn eq(&self, other: &KsonNumber) -> bool { - util::equals(self.to_kotlin_object(), other.to_kotlin_object()) - } - } - impl std::hash::Hash for KsonNumber { - fn hash(&self, state: &mut H) - where - H: std::hash::Hasher, - { - util::apply_hash_code(self.to_kotlin_object(), state) + impl FromKotlinObject for Integer { + fn from_kotlin_object(obj: self::sys::jobject) -> Self { + let (env, _detach_guard) = util::attach_thread_to_java_vm(); + let kotlin_ptr = util::to_gc_global_ref(env, obj); + Self { kotlin_ptr } + } } - } - - /// A Kson object with key-value pairs - #[derive(Clone)] - pub struct KsonObject { - kotlin_ptr: KotlinPtr, - } - impl FromKotlinObject for KsonObject { - fn from_kotlin_object(obj: self::sys::jobject) -> Self { - let (env, _detach_guard) = util::attach_thread_to_java_vm(); - let kotlin_ptr = util::to_gc_global_ref(env, obj); - Self { kotlin_ptr } + impl ToKotlinObject for Integer { + fn to_kotlin_object(&self) -> KotlinPtr { + self.kotlin_ptr.clone() + } } - } - impl ToKotlinObject for KsonObject { - fn to_kotlin_object(&self) -> KotlinPtr { - self.kotlin_ptr.clone() + impl AsKotlinObject for Integer { + fn as_kotlin_object(&self) -> self::sys::jobject { + self.kotlin_ptr.inner.inner + } } - } - impl AsKotlinObject for KsonObject { - fn as_kotlin_object(&self) -> self::sys::jobject { - self.kotlin_ptr.inner.inner + impl Integer { + pub fn new( + value: i32, + internal_start: Position, + internal_end: Position, + ) -> Self { + let (env, _detach_guard) = util::attach_thread_to_java_vm(); + let class = util::get_class(env, c"org/kson/api/KsonValue$KsonNumber$Integer"); + let constructor = util::get_method(env, class.as_kotlin_object(), c"", c"(ILorg/kson/api/Position;Lorg/kson/api/Position;)V"); + + + let internal_start_ptr = internal_start.to_kotlin_object(); + let internal_start = internal_start_ptr.as_kotlin_object(); + let internal_end_ptr = internal_end.to_kotlin_object(); + let internal_end = internal_end_ptr.as_kotlin_object(); + + let jobject = unsafe { (**env).NewObject.unwrap()(env, class.as_kotlin_object(), constructor, + value, + internal_start, + internal_end, + )}; + util::panic_upon_exception(env); + Self { + kotlin_ptr: util::to_gc_global_ref(env, jobject) + } + } } - } - impl KsonObject { - } + impl Integer { - impl KsonObject { + pub fn value( + &self, + ) -> i32 { + let self_ptr = self.to_kotlin_object(); + let self_obj = self_ptr.as_kotlin_object(); - pub fn properties( - &self, - ) -> std::collections::HashMap { - let self_ptr = self.to_kotlin_object(); - let self_obj = self_ptr.as_kotlin_object(); + let (_, _detach_guard) = util::attach_thread_to_java_vm(); + let result = call_jvm_function!( + util, + c"org/kson/api/KsonValue$KsonNumber$Integer", + c"getValue", + c"()I", + CallIntMethod, + self_obj, - let (_, _detach_guard) = util::attach_thread_to_java_vm(); - let result = call_jvm_function!( - util, - c"org/kson/KsonValue$KsonObject", - c"getProperties", - c"()Ljava/util/Map;", - CallObjectMethod, - self_obj, + ); - ); + result + } - util::from_kotlin_value_map(result) - } + pub fn internal_start( + &self, + ) -> Position { + let self_ptr = self.to_kotlin_object(); + let self_obj = self_ptr.as_kotlin_object(); - pub fn property_keys( - &self, - ) -> std::collections::HashMap { - let self_ptr = self.to_kotlin_object(); - let self_obj = self_ptr.as_kotlin_object(); + let (_, _detach_guard) = util::attach_thread_to_java_vm(); + let result = call_jvm_function!( + util, + c"org/kson/api/KsonValue$KsonNumber$Integer", + c"getInternalStart", + c"()Lorg/kson/api/Position;", + CallObjectMethod, + self_obj, - let (_, _detach_guard) = util::attach_thread_to_java_vm(); - let result = call_jvm_function!( - util, - c"org/kson/KsonValue$KsonObject", - c"getPropertyKeys", - c"()Ljava/util/Map;", - CallObjectMethod, - self_obj, + ); - ); + FromKotlinObject::from_kotlin_object(result) + } - util::from_kotlin_value_map(result) - } + pub fn internal_end( + &self, + ) -> Position { + let self_ptr = self.to_kotlin_object(); + let self_obj = self_ptr.as_kotlin_object(); - pub fn type_( - &self, - ) -> KsonValueType { - let self_ptr = self.to_kotlin_object(); - let self_obj = self_ptr.as_kotlin_object(); + let (_, _detach_guard) = util::attach_thread_to_java_vm(); + let result = call_jvm_function!( + util, + c"org/kson/api/KsonValue$KsonNumber$Integer", + c"getInternalEnd", + c"()Lorg/kson/api/Position;", + CallObjectMethod, + self_obj, + + ); - let (_, _detach_guard) = util::attach_thread_to_java_vm(); - let result = call_jvm_function!( - util, - c"org/kson/KsonValue$KsonObject", - c"getType", - c"()Lorg/kson/KsonValueType;", - CallObjectMethod, - self_obj, + FromKotlinObject::from_kotlin_object(result) + } - ); - FromKotlinObject::from_kotlin_object(result) - } + pub fn type_( + &self, + ) -> KsonValueType { + let self_ptr = self.to_kotlin_object(); + let self_obj = self_ptr.as_kotlin_object(); - pub fn start( - &self, - ) -> Position { - let self_ptr = self.to_kotlin_object(); - let self_obj = self_ptr.as_kotlin_object(); + let (_, _detach_guard) = util::attach_thread_to_java_vm(); + let result = call_jvm_function!( + util, + c"org/kson/api/KsonValue$KsonNumber$Integer", + c"getType", + c"()Lorg/kson/api/KsonValueType;", + CallObjectMethod, + self_obj, + ); - let (_, _detach_guard) = util::attach_thread_to_java_vm(); - let result = call_jvm_function!( - util, - c"org/kson/KsonValue", - c"getStart", - c"()Lorg/kson/Position;", - CallObjectMethod, - self_obj, + FromKotlinObject::from_kotlin_object(result) + } - ); - FromKotlinObject::from_kotlin_object(result) - } + pub fn start( + &self, + ) -> Position { + let self_ptr = self.to_kotlin_object(); + let self_obj = self_ptr.as_kotlin_object(); - pub fn end( - &self, - ) -> Position { - let self_ptr = self.to_kotlin_object(); - let self_obj = self_ptr.as_kotlin_object(); + let (_, _detach_guard) = util::attach_thread_to_java_vm(); + let result = call_jvm_function!( + util, + c"org/kson/api/KsonValue", + c"getStart", + c"()Lorg/kson/api/Position;", + CallObjectMethod, + self_obj, + ); - let (_, _detach_guard) = util::attach_thread_to_java_vm(); - let result = call_jvm_function!( - util, - c"org/kson/KsonValue", - c"getEnd", - c"()Lorg/kson/Position;", - CallObjectMethod, - self_obj, + FromKotlinObject::from_kotlin_object(result) + } - ); - FromKotlinObject::from_kotlin_object(result) + pub fn end( + &self, + ) -> Position { + let self_ptr = self.to_kotlin_object(); + let self_obj = self_ptr.as_kotlin_object(); + + + let (_, _detach_guard) = util::attach_thread_to_java_vm(); + let result = call_jvm_function!( + util, + c"org/kson/api/KsonValue", + c"getEnd", + c"()Lorg/kson/api/Position;", + CallObjectMethod, + self_obj, + + ); + + FromKotlinObject::from_kotlin_object(result) + } } - } - impl std::fmt::Debug for KsonObject { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let obj = self.to_kotlin_object(); - write!(f, "{}", util::call_to_string(c"org/kson/KsonValue$KsonObject", &obj)) + impl std::fmt::Debug for Integer { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let obj = self.to_kotlin_object(); + write!(f, "{}", util::call_to_string(c"org/kson/api/KsonValue$KsonNumber$Integer", &obj)) + } } - } - impl Eq for KsonObject {} - impl PartialEq for KsonObject { - fn eq(&self, other: &KsonObject) -> bool { - util::equals(self.to_kotlin_object(), other.to_kotlin_object()) + impl Eq for Integer {} + impl PartialEq for Integer { + fn eq(&self, other: &Integer) -> bool { + util::equals(self.to_kotlin_object(), other.to_kotlin_object()) + } } - } - impl std::hash::Hash for KsonObject { - fn hash(&self, state: &mut H) - where - H: std::hash::Hasher, - { - util::apply_hash_code(self.to_kotlin_object(), state) + impl std::hash::Hash for Integer { + fn hash(&self, state: &mut H) + where + H: std::hash::Hasher, + { + util::apply_hash_code(self.to_kotlin_object(), state) + } } } - - /// A Kson string value - #[derive(Clone)] - pub struct KsonString { - kotlin_ptr: KotlinPtr, - } - - impl FromKotlinObject for KsonString { - fn from_kotlin_object(obj: self::sys::jobject) -> Self { - let (env, _detach_guard) = util::attach_thread_to_java_vm(); - let kotlin_ptr = util::to_gc_global_ref(env, obj); - Self { kotlin_ptr } + impl FromKotlinObject for KsonNumber { + fn from_kotlin_object(obj: jobject) -> Self { + match util::class_name(obj).as_str() { + "org.kson.api.KsonValue$KsonNumber$Decimal" => kson_value::KsonNumber::Decimal(kson_value::kson_number::Decimal::from_kotlin_object(obj)), + "org.kson.api.KsonValue$KsonNumber$Integer" => kson_value::KsonNumber::Integer(kson_value::kson_number::Integer::from_kotlin_object(obj)), + _ => unreachable!(), + } } } - impl ToKotlinObject for KsonString { + impl ToKotlinObject for KsonNumber { fn to_kotlin_object(&self) -> KotlinPtr { - self.kotlin_ptr.clone() - } - } - - impl AsKotlinObject for KsonString { - fn as_kotlin_object(&self) -> self::sys::jobject { - self.kotlin_ptr.inner.inner + match self { + Self::Decimal(inner) => inner.to_kotlin_object(), + Self::Integer(inner) => inner.to_kotlin_object(), + } } } - impl KsonString { + impl KsonNumber { + pub fn name(self) -> String { + let obj = self.to_kotlin_object(); + util::enum_name(&obj) + } } - impl KsonString { - - - pub fn value( - &self, - ) -> String { - let self_ptr = self.to_kotlin_object(); - let self_obj = self_ptr.as_kotlin_object(); - - - let (_, _detach_guard) = util::attach_thread_to_java_vm(); - let result = call_jvm_function!( - util, - c"org/kson/KsonValue$KsonString", - c"getValue", - c"()Ljava/lang/String;", - CallObjectMethod, - self_obj, - - ); - - FromKotlinObject::from_kotlin_object(result) - } + impl KsonNumber { - pub fn type_( + pub fn start( &self, - ) -> KsonValueType { + ) -> Position { let self_ptr = self.to_kotlin_object(); let self_obj = self_ptr.as_kotlin_object(); @@ -1829,9 +1914,9 @@ pub mod kson_value { let (_, _detach_guard) = util::attach_thread_to_java_vm(); let result = call_jvm_function!( util, - c"org/kson/KsonValue$KsonString", - c"getType", - c"()Lorg/kson/KsonValueType;", + c"org/kson/api/KsonValue", + c"getStart", + c"()Lorg/kson/api/Position;", CallObjectMethod, self_obj, @@ -1841,7 +1926,7 @@ pub mod kson_value { } - pub fn start( + pub fn end( &self, ) -> Position { let self_ptr = self.to_kotlin_object(); @@ -1851,9 +1936,9 @@ pub mod kson_value { let (_, _detach_guard) = util::attach_thread_to_java_vm(); let result = call_jvm_function!( util, - c"org/kson/KsonValue", - c"getStart", - c"()Lorg/kson/Position;", + c"org/kson/api/KsonValue", + c"getEnd", + c"()Lorg/kson/api/Position;", CallObjectMethod, self_obj, @@ -1863,9 +1948,9 @@ pub mod kson_value { } - pub fn end( + pub fn type_( &self, - ) -> Position { + ) -> KsonValueType { let self_ptr = self.to_kotlin_object(); let self_obj = self_ptr.as_kotlin_object(); @@ -1873,9 +1958,9 @@ pub mod kson_value { let (_, _detach_guard) = util::attach_thread_to_java_vm(); let result = call_jvm_function!( util, - c"org/kson/KsonValue", - c"getEnd", - c"()Lorg/kson/Position;", + c"org/kson/api/KsonValue", + c"getType", + c"()Lorg/kson/api/KsonValueType;", CallObjectMethod, self_obj, @@ -1885,20 +1970,20 @@ pub mod kson_value { } } - impl std::fmt::Debug for KsonString { + impl std::fmt::Debug for KsonNumber { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let obj = self.to_kotlin_object(); - write!(f, "{}", util::call_to_string(c"org/kson/KsonValue$KsonString", &obj)) + write!(f, "{}", util::call_to_string(c"org/kson/api/KsonValue$KsonNumber", &obj)) } } - impl Eq for KsonString {} - impl PartialEq for KsonString { - fn eq(&self, other: &KsonString) -> bool { + impl Eq for KsonNumber {} + impl PartialEq for KsonNumber { + fn eq(&self, other: &KsonNumber) -> bool { util::equals(self.to_kotlin_object(), other.to_kotlin_object()) } } - impl std::hash::Hash for KsonString { + impl std::hash::Hash for KsonNumber { fn hash(&self, state: &mut H) where H: std::hash::Hasher, @@ -1906,243 +1991,14 @@ pub mod kson_value { util::apply_hash_code(self.to_kotlin_object(), state) } } -} -impl FromKotlinObject for KsonValue { - fn from_kotlin_object(obj: jobject) -> Self { - match util::class_name(obj).as_str() { - "org.kson.KsonValue$KsonArray" => KsonValue::KsonArray(kson_value::KsonArray::from_kotlin_object(obj)), - "org.kson.KsonValue$KsonBoolean" => KsonValue::KsonBoolean(kson_value::KsonBoolean::from_kotlin_object(obj)), - "org.kson.KsonValue$KsonEmbed" => KsonValue::KsonEmbed(kson_value::KsonEmbed::from_kotlin_object(obj)), - "org.kson.KsonValue$KsonNull" => KsonValue::KsonNull(kson_value::KsonNull::from_kotlin_object(obj)), - "org.kson.KsonValue$KsonNumber" => KsonValue::KsonNumber(kson_value::KsonNumber::from_kotlin_object(obj)), - "org.kson.KsonValue$KsonObject" => KsonValue::KsonObject(kson_value::KsonObject::from_kotlin_object(obj)), - "org.kson.KsonValue$KsonString" => KsonValue::KsonString(kson_value::KsonString::from_kotlin_object(obj)), - "org.kson.KsonValue$KsonNumber$Decimal" => KsonValue::KsonNumber(kson_value::KsonNumber::Decimal(kson_value::kson_number::Decimal::from_kotlin_object(obj))), - "org.kson.KsonValue$KsonNumber$Integer" => KsonValue::KsonNumber(kson_value::KsonNumber::Integer(kson_value::kson_number::Integer::from_kotlin_object(obj))), - _ => unreachable!(), - } - } -} - -impl ToKotlinObject for KsonValue { - fn to_kotlin_object(&self) -> KotlinPtr { - match self { - Self::KsonArray(inner) => inner.to_kotlin_object(), - Self::KsonBoolean(inner) => inner.to_kotlin_object(), - Self::KsonEmbed(inner) => inner.to_kotlin_object(), - Self::KsonNull(inner) => inner.to_kotlin_object(), - Self::KsonNumber(inner) => inner.to_kotlin_object(), - Self::KsonObject(inner) => inner.to_kotlin_object(), - Self::KsonString(inner) => inner.to_kotlin_object(), - } - } -} - -impl KsonValue { - pub fn name(self) -> String { - let obj = self.to_kotlin_object(); - util::enum_name(&obj) - } -} - - -impl KsonValue { - - - pub fn start( - &self, - ) -> Position { - let self_ptr = self.to_kotlin_object(); - let self_obj = self_ptr.as_kotlin_object(); - - - let (_, _detach_guard) = util::attach_thread_to_java_vm(); - let result = call_jvm_function!( - util, - c"org/kson/KsonValue", - c"getStart", - c"()Lorg/kson/Position;", - CallObjectMethod, - self_obj, - - ); - - FromKotlinObject::from_kotlin_object(result) - } - - - pub fn end( - &self, - ) -> Position { - let self_ptr = self.to_kotlin_object(); - let self_obj = self_ptr.as_kotlin_object(); - - - let (_, _detach_guard) = util::attach_thread_to_java_vm(); - let result = call_jvm_function!( - util, - c"org/kson/KsonValue", - c"getEnd", - c"()Lorg/kson/Position;", - CallObjectMethod, - self_obj, - - ); - - FromKotlinObject::from_kotlin_object(result) - } - - /// Type discriminator for easier type checking in TypeScript/JavaScript - pub fn type_( - &self, - ) -> KsonValueType { - let self_ptr = self.to_kotlin_object(); - let self_obj = self_ptr.as_kotlin_object(); - - - let (_, _detach_guard) = util::attach_thread_to_java_vm(); - let result = call_jvm_function!( - util, - c"org/kson/KsonValue", - c"getType", - c"()Lorg/kson/KsonValueType;", - CallObjectMethod, - self_obj, - - ); - - FromKotlinObject::from_kotlin_object(result) - } -} - -impl std::fmt::Debug for KsonValue { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let obj = self.to_kotlin_object(); - write!(f, "{}", util::call_to_string(c"org/kson/KsonValue", &obj)) - } -} - -impl Eq for KsonValue {} -impl PartialEq for KsonValue { - fn eq(&self, other: &KsonValue) -> bool { - util::equals(self.to_kotlin_object(), other.to_kotlin_object()) - } -} -impl std::hash::Hash for KsonValue { - fn hash(&self, state: &mut H) - where - H: std::hash::Hasher, - { - util::apply_hash_code(self.to_kotlin_object(), state) - } -} - - -/// A validator that can check if Kson source conforms to a schema. -#[derive(Clone)] -pub struct SchemaValidator { - kotlin_ptr: KotlinPtr, -} - -impl FromKotlinObject for SchemaValidator { - fn from_kotlin_object(obj: self::sys::jobject) -> Self { - let (env, _detach_guard) = util::attach_thread_to_java_vm(); - let kotlin_ptr = util::to_gc_global_ref(env, obj); - Self { kotlin_ptr } - } -} - -impl ToKotlinObject for SchemaValidator { - fn to_kotlin_object(&self) -> KotlinPtr { - self.kotlin_ptr.clone() - } -} - -impl AsKotlinObject for SchemaValidator { - fn as_kotlin_object(&self) -> self::sys::jobject { - self.kotlin_ptr.inner.inner - } -} - -impl SchemaValidator { -} - - -impl SchemaValidator { - - /// Validates the given Kson source against this validator's schema. - /// @param kson The Kson source to validate - /// @param filepath Optional filepath of the document being validated, used by validators to determine which rules to apply - /// - /// @return A list of validation error messages, or empty list if valid - pub fn validate( - &self, - kson: &str, - filepath: Option<&str>, - ) -> Vec { - let self_ptr = self.to_kotlin_object(); - let self_obj = self_ptr.as_kotlin_object(); - let kson_ptr = kson.to_kotlin_object(); - let kson = kson_ptr.as_kotlin_object(); - let filepath_ptr = filepath.to_kotlin_object(); - let filepath = filepath_ptr.as_kotlin_object(); - - let (_, _detach_guard) = util::attach_thread_to_java_vm(); - let result = call_jvm_function!( - util, - c"org/kson/SchemaValidator", - c"validate", - c"(Ljava/lang/String;Ljava/lang/String;)Ljava/util/List;", - CallObjectMethod, - self_obj, - kson, - filepath, - ); - - util::from_kotlin_list(result) - } -} - -impl std::fmt::Debug for SchemaValidator { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let obj = self.to_kotlin_object(); - write!(f, "{}", util::call_to_string(c"org/kson/SchemaValidator", &obj)) - } -} - -impl Eq for SchemaValidator {} -impl PartialEq for SchemaValidator { - fn eq(&self, other: &SchemaValidator) -> bool { - util::equals(self.to_kotlin_object(), other.to_kotlin_object()) - } -} -impl std::hash::Hash for SchemaValidator { - fn hash(&self, state: &mut H) - where - H: std::hash::Hasher, - { - util::apply_hash_code(self.to_kotlin_object(), state) - } -} - - -#[derive(Clone)] -pub enum EmbedRuleResult { - Failure(embed_rule_result::Failure), - Success(embed_rule_result::Success), -} - -pub mod embed_rule_result { - use super::*; - #[derive(Clone)] - pub struct Failure { + pub struct KsonObject { kotlin_ptr: KotlinPtr, } - impl FromKotlinObject for Failure { + impl FromKotlinObject for KsonObject { fn from_kotlin_object(obj: self::sys::jobject) -> Self { let (env, _detach_guard) = util::attach_thread_to_java_vm(); let kotlin_ptr = util::to_gc_global_ref(env, obj); @@ -2150,31 +2006,43 @@ pub mod embed_rule_result { } } - impl ToKotlinObject for Failure { + impl ToKotlinObject for KsonObject { fn to_kotlin_object(&self) -> KotlinPtr { self.kotlin_ptr.clone() } } - impl AsKotlinObject for Failure { + impl AsKotlinObject for KsonObject { fn as_kotlin_object(&self) -> self::sys::jobject { self.kotlin_ptr.inner.inner } } - impl Failure { + impl KsonObject { pub fn new( - message: &str, + properties: &std::collections::HashMap<&str, KsonValue>, + property_keys: &std::collections::HashMap<&str, kson_value::KsonString>, + internal_start: Position, + internal_end: Position, ) -> Self { let (env, _detach_guard) = util::attach_thread_to_java_vm(); - let class = util::get_class(env, c"org/kson/EmbedRuleResult$Failure"); - let constructor = util::get_method(env, class.as_kotlin_object(), c"", c"(Ljava/lang/String;)V"); - - let message_ptr = message.to_kotlin_object(); - let message = message_ptr.as_kotlin_object(); + let class = util::get_class(env, c"org/kson/api/KsonValue$KsonObject"); + let constructor = util::get_method(env, class.as_kotlin_object(), c"", c"(Ljava/util/Map;Ljava/util/Map;Lorg/kson/api/Position;Lorg/kson/api/Position;)V"); + + let properties_ptr = util::to_kotlin_map(properties); + let properties = properties_ptr.as_kotlin_object(); + let property_keys_ptr = util::to_kotlin_map(property_keys); + let property_keys = property_keys_ptr.as_kotlin_object(); + let internal_start_ptr = internal_start.to_kotlin_object(); + let internal_start = internal_start_ptr.as_kotlin_object(); + let internal_end_ptr = internal_end.to_kotlin_object(); + let internal_end = internal_end_ptr.as_kotlin_object(); let jobject = unsafe { (**env).NewObject.unwrap()(env, class.as_kotlin_object(), constructor, - message, + properties, + property_keys, + internal_start, + internal_end, )}; util::panic_upon_exception(env); Self { @@ -2184,10 +2052,76 @@ pub mod embed_rule_result { } - impl Failure { + impl KsonObject { + + + pub fn properties( + &self, + ) -> std::collections::HashMap { + let self_ptr = self.to_kotlin_object(); + let self_obj = self_ptr.as_kotlin_object(); + + + let (_, _detach_guard) = util::attach_thread_to_java_vm(); + let result = call_jvm_function!( + util, + c"org/kson/api/KsonValue$KsonObject", + c"getProperties", + c"()Ljava/util/Map;", + CallObjectMethod, + self_obj, + + ); + + util::from_kotlin_value_map(result) + } + + + pub fn property_keys( + &self, + ) -> std::collections::HashMap { + let self_ptr = self.to_kotlin_object(); + let self_obj = self_ptr.as_kotlin_object(); + + + let (_, _detach_guard) = util::attach_thread_to_java_vm(); + let result = call_jvm_function!( + util, + c"org/kson/api/KsonValue$KsonObject", + c"getPropertyKeys", + c"()Ljava/util/Map;", + CallObjectMethod, + self_obj, + + ); + + util::from_kotlin_value_map(result) + } + + + pub fn type_( + &self, + ) -> KsonValueType { + let self_ptr = self.to_kotlin_object(); + let self_obj = self_ptr.as_kotlin_object(); + + + let (_, _detach_guard) = util::attach_thread_to_java_vm(); + let result = call_jvm_function!( + util, + c"org/kson/api/KsonValue$KsonObject", + c"getType", + c"()Lorg/kson/api/KsonValueType;", + CallObjectMethod, + self_obj, + + ); + + FromKotlinObject::from_kotlin_object(result) + } - pub fn message( + pub fn to_string( &self, ) -> String { let self_ptr = self.to_kotlin_object(); @@ -2197,8 +2131,8 @@ pub mod embed_rule_result { let (_, _detach_guard) = util::attach_thread_to_java_vm(); let result = call_jvm_function!( util, - c"org/kson/EmbedRuleResult$Failure", - c"getMessage", + c"org/kson/api/KsonValue$KsonObject", + c"toString", c"()Ljava/lang/String;", CallObjectMethod, self_obj, @@ -2207,22 +2141,66 @@ pub mod embed_rule_result { FromKotlinObject::from_kotlin_object(result) } + + + pub fn start( + &self, + ) -> Position { + let self_ptr = self.to_kotlin_object(); + let self_obj = self_ptr.as_kotlin_object(); + + + let (_, _detach_guard) = util::attach_thread_to_java_vm(); + let result = call_jvm_function!( + util, + c"org/kson/api/KsonValue", + c"getStart", + c"()Lorg/kson/api/Position;", + CallObjectMethod, + self_obj, + + ); + + FromKotlinObject::from_kotlin_object(result) + } + + + pub fn end( + &self, + ) -> Position { + let self_ptr = self.to_kotlin_object(); + let self_obj = self_ptr.as_kotlin_object(); + + + let (_, _detach_guard) = util::attach_thread_to_java_vm(); + let result = call_jvm_function!( + util, + c"org/kson/api/KsonValue", + c"getEnd", + c"()Lorg/kson/api/Position;", + CallObjectMethod, + self_obj, + + ); + + FromKotlinObject::from_kotlin_object(result) + } } - impl std::fmt::Debug for Failure { + impl std::fmt::Debug for KsonObject { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let obj = self.to_kotlin_object(); - write!(f, "{}", util::call_to_string(c"org/kson/EmbedRuleResult$Failure", &obj)) + write!(f, "{}", util::call_to_string(c"org/kson/api/KsonValue$KsonObject", &obj)) } } - impl Eq for Failure {} - impl PartialEq for Failure { - fn eq(&self, other: &Failure) -> bool { + impl Eq for KsonObject {} + impl PartialEq for KsonObject { + fn eq(&self, other: &KsonObject) -> bool { util::equals(self.to_kotlin_object(), other.to_kotlin_object()) } } - impl std::hash::Hash for Failure { + impl std::hash::Hash for KsonObject { fn hash(&self, state: &mut H) where H: std::hash::Hasher, @@ -2233,11 +2211,11 @@ pub mod embed_rule_result { #[derive(Clone)] - pub struct Success { + pub struct KsonString { kotlin_ptr: KotlinPtr, } - impl FromKotlinObject for Success { + impl FromKotlinObject for KsonString { fn from_kotlin_object(obj: self::sys::jobject) -> Self { let (env, _detach_guard) = util::attach_thread_to_java_vm(); let kotlin_ptr = util::to_gc_global_ref(env, obj); @@ -2245,31 +2223,39 @@ pub mod embed_rule_result { } } - impl ToKotlinObject for Success { + impl ToKotlinObject for KsonString { fn to_kotlin_object(&self) -> KotlinPtr { self.kotlin_ptr.clone() } } - impl AsKotlinObject for Success { + impl AsKotlinObject for KsonString { fn as_kotlin_object(&self) -> self::sys::jobject { self.kotlin_ptr.inner.inner } } - impl Success { + impl KsonString { pub fn new( - embed_rule: EmbedRule, + value: &str, + internal_start: Position, + internal_end: Position, ) -> Self { let (env, _detach_guard) = util::attach_thread_to_java_vm(); - let class = util::get_class(env, c"org/kson/EmbedRuleResult$Success"); - let constructor = util::get_method(env, class.as_kotlin_object(), c"", c"(Lorg/kson/EmbedRule;)V"); + let class = util::get_class(env, c"org/kson/api/KsonValue$KsonString"); + let constructor = util::get_method(env, class.as_kotlin_object(), c"", c"(Ljava/lang/String;Lorg/kson/api/Position;Lorg/kson/api/Position;)V"); - let embed_rule_ptr = embed_rule.to_kotlin_object(); - let embed_rule = embed_rule_ptr.as_kotlin_object(); + let value_ptr = value.to_kotlin_object(); + let value = value_ptr.as_kotlin_object(); + let internal_start_ptr = internal_start.to_kotlin_object(); + let internal_start = internal_start_ptr.as_kotlin_object(); + let internal_end_ptr = internal_end.to_kotlin_object(); + let internal_end = internal_end_ptr.as_kotlin_object(); let jobject = unsafe { (**env).NewObject.unwrap()(env, class.as_kotlin_object(), constructor, - embed_rule, + value, + internal_start, + internal_end, )}; util::panic_upon_exception(env); Self { @@ -2279,12 +2265,78 @@ pub mod embed_rule_result { } - impl Success { + impl KsonString { + + + pub fn value( + &self, + ) -> String { + let self_ptr = self.to_kotlin_object(); + let self_obj = self_ptr.as_kotlin_object(); + + + let (_, _detach_guard) = util::attach_thread_to_java_vm(); + let result = call_jvm_function!( + util, + c"org/kson/api/KsonValue$KsonString", + c"getValue", + c"()Ljava/lang/String;", + CallObjectMethod, + self_obj, + + ); + + FromKotlinObject::from_kotlin_object(result) + } + + + pub fn type_( + &self, + ) -> KsonValueType { + let self_ptr = self.to_kotlin_object(); + let self_obj = self_ptr.as_kotlin_object(); + + + let (_, _detach_guard) = util::attach_thread_to_java_vm(); + let result = call_jvm_function!( + util, + c"org/kson/api/KsonValue$KsonString", + c"getType", + c"()Lorg/kson/api/KsonValueType;", + CallObjectMethod, + self_obj, + + ); + + FromKotlinObject::from_kotlin_object(result) + } + + + pub fn start( + &self, + ) -> Position { + let self_ptr = self.to_kotlin_object(); + let self_obj = self_ptr.as_kotlin_object(); + + + let (_, _detach_guard) = util::attach_thread_to_java_vm(); + let result = call_jvm_function!( + util, + c"org/kson/api/KsonValue", + c"getStart", + c"()Lorg/kson/api/Position;", + CallObjectMethod, + self_obj, + + ); + FromKotlinObject::from_kotlin_object(result) + } - pub fn embed_rule( + + pub fn end( &self, - ) -> EmbedRule { + ) -> Position { let self_ptr = self.to_kotlin_object(); let self_obj = self_ptr.as_kotlin_object(); @@ -2292,9 +2344,9 @@ pub mod embed_rule_result { let (_, _detach_guard) = util::attach_thread_to_java_vm(); let result = call_jvm_function!( util, - c"org/kson/EmbedRuleResult$Success", - c"getEmbedRule", - c"()Lorg/kson/EmbedRule;", + c"org/kson/api/KsonValue", + c"getEnd", + c"()Lorg/kson/api/Position;", CallObjectMethod, self_obj, @@ -2304,20 +2356,20 @@ pub mod embed_rule_result { } } - impl std::fmt::Debug for Success { + impl std::fmt::Debug for KsonString { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let obj = self.to_kotlin_object(); - write!(f, "{}", util::call_to_string(c"org/kson/EmbedRuleResult$Success", &obj)) + write!(f, "{}", util::call_to_string(c"org/kson/api/KsonValue$KsonString", &obj)) } } - impl Eq for Success {} - impl PartialEq for Success { - fn eq(&self, other: &Success) -> bool { + impl Eq for KsonString {} + impl PartialEq for KsonString { + fn eq(&self, other: &KsonString) -> bool { util::equals(self.to_kotlin_object(), other.to_kotlin_object()) } } - impl std::hash::Hash for Success { + impl std::hash::Hash for KsonString { fn hash(&self, state: &mut H) where H: std::hash::Hasher, @@ -2326,50 +2378,128 @@ pub mod embed_rule_result { } } } -impl FromKotlinObject for EmbedRuleResult { +impl FromKotlinObject for KsonValue { fn from_kotlin_object(obj: jobject) -> Self { match util::class_name(obj).as_str() { - "org.kson.EmbedRuleResult$Failure" => EmbedRuleResult::Failure(embed_rule_result::Failure::from_kotlin_object(obj)), - "org.kson.EmbedRuleResult$Success" => EmbedRuleResult::Success(embed_rule_result::Success::from_kotlin_object(obj)), + "org.kson.api.KsonValue$KsonArray" => KsonValue::KsonArray(kson_value::KsonArray::from_kotlin_object(obj)), + "org.kson.api.KsonValue$KsonBoolean" => KsonValue::KsonBoolean(kson_value::KsonBoolean::from_kotlin_object(obj)), + "org.kson.api.KsonValue$KsonEmbed" => KsonValue::KsonEmbed(kson_value::KsonEmbed::from_kotlin_object(obj)), + "org.kson.api.KsonValue$KsonNull" => KsonValue::KsonNull(kson_value::KsonNull::from_kotlin_object(obj)), + "org.kson.api.KsonValue$KsonNumber" => KsonValue::KsonNumber(kson_value::KsonNumber::from_kotlin_object(obj)), + "org.kson.api.KsonValue$KsonObject" => KsonValue::KsonObject(kson_value::KsonObject::from_kotlin_object(obj)), + "org.kson.api.KsonValue$KsonString" => KsonValue::KsonString(kson_value::KsonString::from_kotlin_object(obj)), + "org.kson.api.KsonValue$KsonNumber$Decimal" => KsonValue::KsonNumber(kson_value::KsonNumber::Decimal(kson_value::kson_number::Decimal::from_kotlin_object(obj))), + "org.kson.api.KsonValue$KsonNumber$Integer" => KsonValue::KsonNumber(kson_value::KsonNumber::Integer(kson_value::kson_number::Integer::from_kotlin_object(obj))), _ => unreachable!(), } } } -impl ToKotlinObject for EmbedRuleResult { - fn to_kotlin_object(&self) -> KotlinPtr { - match self { - Self::Failure(inner) => inner.to_kotlin_object(), - Self::Success(inner) => inner.to_kotlin_object(), - } - } -} +impl ToKotlinObject for KsonValue { + fn to_kotlin_object(&self) -> KotlinPtr { + match self { + Self::KsonArray(inner) => inner.to_kotlin_object(), + Self::KsonBoolean(inner) => inner.to_kotlin_object(), + Self::KsonEmbed(inner) => inner.to_kotlin_object(), + Self::KsonNull(inner) => inner.to_kotlin_object(), + Self::KsonNumber(inner) => inner.to_kotlin_object(), + Self::KsonObject(inner) => inner.to_kotlin_object(), + Self::KsonString(inner) => inner.to_kotlin_object(), + } + } +} + +impl KsonValue { + pub fn name(self) -> String { + let obj = self.to_kotlin_object(); + util::enum_name(&obj) + } +} + + +impl KsonValue { + + + pub fn start( + &self, + ) -> Position { + let self_ptr = self.to_kotlin_object(); + let self_obj = self_ptr.as_kotlin_object(); + + + let (_, _detach_guard) = util::attach_thread_to_java_vm(); + let result = call_jvm_function!( + util, + c"org/kson/api/KsonValue", + c"getStart", + c"()Lorg/kson/api/Position;", + CallObjectMethod, + self_obj, + + ); + + FromKotlinObject::from_kotlin_object(result) + } + + + pub fn end( + &self, + ) -> Position { + let self_ptr = self.to_kotlin_object(); + let self_obj = self_ptr.as_kotlin_object(); + -impl EmbedRuleResult { - pub fn name(self) -> String { - let obj = self.to_kotlin_object(); - util::enum_name(&obj) + let (_, _detach_guard) = util::attach_thread_to_java_vm(); + let result = call_jvm_function!( + util, + c"org/kson/api/KsonValue", + c"getEnd", + c"()Lorg/kson/api/Position;", + CallObjectMethod, + self_obj, + + ); + + FromKotlinObject::from_kotlin_object(result) } -} -impl EmbedRuleResult { + pub fn type_( + &self, + ) -> KsonValueType { + let self_ptr = self.to_kotlin_object(); + let self_obj = self_ptr.as_kotlin_object(); + + + let (_, _detach_guard) = util::attach_thread_to_java_vm(); + let result = call_jvm_function!( + util, + c"org/kson/api/KsonValue", + c"getType", + c"()Lorg/kson/api/KsonValueType;", + CallObjectMethod, + self_obj, + + ); + + FromKotlinObject::from_kotlin_object(result) + } } -impl std::fmt::Debug for EmbedRuleResult { +impl std::fmt::Debug for KsonValue { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let obj = self.to_kotlin_object(); - write!(f, "{}", util::call_to_string(c"org/kson/EmbedRuleResult", &obj)) + write!(f, "{}", util::call_to_string(c"org/kson/api/KsonValue", &obj)) } } -impl Eq for EmbedRuleResult {} -impl PartialEq for EmbedRuleResult { - fn eq(&self, other: &EmbedRuleResult) -> bool { +impl Eq for KsonValue {} +impl PartialEq for KsonValue { + fn eq(&self, other: &KsonValue) -> bool { util::equals(self.to_kotlin_object(), other.to_kotlin_object()) } } -impl std::hash::Hash for EmbedRuleResult { +impl std::hash::Hash for KsonValue { fn hash(&self, state: &mut H) where H: std::hash::Hasher, @@ -2379,13 +2509,13 @@ impl std::hash::Hash for EmbedRuleResult { } -/// The result of statically analyzing a Kson document + #[derive(Clone)] -pub struct Analysis { +pub struct SchemaValidatorService { kotlin_ptr: KotlinPtr, } -impl FromKotlinObject for Analysis { +impl FromKotlinObject for SchemaValidatorService { fn from_kotlin_object(obj: self::sys::jobject) -> Self { let (env, _detach_guard) = util::attach_thread_to_java_vm(); let kotlin_ptr = util::to_gc_global_ref(env, obj); @@ -2393,105 +2523,67 @@ impl FromKotlinObject for Analysis { } } -impl ToKotlinObject for Analysis { +impl ToKotlinObject for SchemaValidatorService { fn to_kotlin_object(&self) -> KotlinPtr { self.kotlin_ptr.clone() } } -impl AsKotlinObject for Analysis { +impl AsKotlinObject for SchemaValidatorService { fn as_kotlin_object(&self) -> self::sys::jobject { self.kotlin_ptr.inner.inner } } -impl Analysis { +impl SchemaValidatorService { } -impl Analysis { +impl SchemaValidatorService { - pub fn errors( + pub fn validate( &self, + kson: &str, + filepath: Option<&str>, ) -> Vec { let self_ptr = self.to_kotlin_object(); let self_obj = self_ptr.as_kotlin_object(); - - - let (_, _detach_guard) = util::attach_thread_to_java_vm(); - let result = call_jvm_function!( - util, - c"org/kson/Analysis", - c"getErrors", - c"()Ljava/util/List;", - CallObjectMethod, - self_obj, - - ); - - util::from_kotlin_list(result) - } - - - pub fn tokens( - &self, - ) -> Vec { - let self_ptr = self.to_kotlin_object(); - let self_obj = self_ptr.as_kotlin_object(); - + let kson_ptr = kson.to_kotlin_object(); + let kson = kson_ptr.as_kotlin_object(); + let filepath_ptr = filepath.to_kotlin_object(); + let filepath = filepath_ptr.as_kotlin_object(); let (_, _detach_guard) = util::attach_thread_to_java_vm(); let result = call_jvm_function!( util, - c"org/kson/Analysis", - c"getTokens", - c"()Ljava/util/List;", + c"org/kson/api/SchemaValidatorService", + c"validate", + c"(Ljava/lang/String;Ljava/lang/String;)Ljava/util/List;", CallObjectMethod, self_obj, - + kson, + filepath, ); util::from_kotlin_list(result) } - - - pub fn kson_value( - &self, - ) -> Option { - let self_ptr = self.to_kotlin_object(); - let self_obj = self_ptr.as_kotlin_object(); - - - let (_, _detach_guard) = util::attach_thread_to_java_vm(); - let result = call_jvm_function!( - util, - c"org/kson/Analysis", - c"getKsonValue", - c"()Lorg/kson/KsonValue;", - CallObjectMethod, - self_obj, - - ); - - FromKotlinObject::from_kotlin_object(result) - } } -impl std::fmt::Debug for Analysis { +impl std::fmt::Debug for SchemaValidatorService { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let obj = self.to_kotlin_object(); - write!(f, "{}", util::call_to_string(c"org/kson/Analysis", &obj)) + write!(f, "{}", util::call_to_string(c"org/kson/api/SchemaValidatorService", &obj)) } } -impl Eq for Analysis {} -impl PartialEq for Analysis { - fn eq(&self, other: &Analysis) -> bool { +impl Eq for SchemaValidatorService {} +impl PartialEq for SchemaValidatorService { + fn eq(&self, other: &SchemaValidatorService) -> bool { util::equals(self.to_kotlin_object(), other.to_kotlin_object()) } } -impl std::hash::Hash for Analysis { +impl std::hash::Hash for SchemaValidatorService { fn hash(&self, state: &mut H) where H: std::hash::Hasher, @@ -2500,24 +2592,24 @@ impl std::hash::Hash for Analysis { } } -/// Result of a Kson conversion operation + #[derive(Clone)] -pub enum Result { - Failure(result::Failure), - Success(result::Success), +pub enum TranspileOptions { + Json(transpile_options::Json), + Yaml(transpile_options::Yaml), } -pub mod result { +pub mod transpile_options { use super::*; #[derive(Clone)] - pub struct Failure { + pub struct Json { kotlin_ptr: KotlinPtr, } - impl FromKotlinObject for Failure { + impl FromKotlinObject for Json { fn from_kotlin_object(obj: self::sys::jobject) -> Self { let (env, _detach_guard) = util::attach_thread_to_java_vm(); let kotlin_ptr = util::to_gc_global_ref(env, obj); @@ -2525,31 +2617,30 @@ pub mod result { } } - impl ToKotlinObject for Failure { + impl ToKotlinObject for Json { fn to_kotlin_object(&self) -> KotlinPtr { self.kotlin_ptr.clone() } } - impl AsKotlinObject for Failure { + impl AsKotlinObject for Json { fn as_kotlin_object(&self) -> self::sys::jobject { self.kotlin_ptr.inner.inner } } - impl Failure { + impl Json { pub fn new( - errors: &[Message], + retain_embed_tags: bool, ) -> Self { let (env, _detach_guard) = util::attach_thread_to_java_vm(); - let class = util::get_class(env, c"org/kson/Result$Failure"); - let constructor = util::get_method(env, class.as_kotlin_object(), c"", c"(Ljava/util/List;)V"); + let class = util::get_class(env, c"org/kson/api/TranspileOptions$Json"); + let constructor = util::get_method(env, class.as_kotlin_object(), c"", c"(Z)V"); - let errors_ptr = util::to_kotlin_list(errors); - let errors = errors_ptr.as_kotlin_object(); + let retain_embed_tags = retain_embed_tags as c_int; let jobject = unsafe { (**env).NewObject.unwrap()(env, class.as_kotlin_object(), constructor, - errors, + retain_embed_tags, )}; util::panic_upon_exception(env); Self { @@ -2559,12 +2650,12 @@ pub mod result { } - impl Failure { + impl Json { - pub fn errors( + pub fn retain_embed_tags( &self, - ) -> Vec { + ) -> bool { let self_ptr = self.to_kotlin_object(); let self_obj = self_ptr.as_kotlin_object(); @@ -2572,32 +2663,32 @@ pub mod result { let (_, _detach_guard) = util::attach_thread_to_java_vm(); let result = call_jvm_function!( util, - c"org/kson/Result$Failure", - c"getErrors", - c"()Ljava/util/List;", - CallObjectMethod, + c"org/kson/api/TranspileOptions$Json", + c"getRetainEmbedTags", + c"()Z", + CallBooleanMethod, self_obj, ); - util::from_kotlin_list(result) + result != 0 } } - impl std::fmt::Debug for Failure { + impl std::fmt::Debug for Json { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let obj = self.to_kotlin_object(); - write!(f, "{}", util::call_to_string(c"org/kson/Result$Failure", &obj)) + write!(f, "{}", util::call_to_string(c"org/kson/api/TranspileOptions$Json", &obj)) } } - impl Eq for Failure {} - impl PartialEq for Failure { - fn eq(&self, other: &Failure) -> bool { + impl Eq for Json {} + impl PartialEq for Json { + fn eq(&self, other: &Json) -> bool { util::equals(self.to_kotlin_object(), other.to_kotlin_object()) } } - impl std::hash::Hash for Failure { + impl std::hash::Hash for Json { fn hash(&self, state: &mut H) where H: std::hash::Hasher, @@ -2608,11 +2699,11 @@ pub mod result { #[derive(Clone)] - pub struct Success { + pub struct Yaml { kotlin_ptr: KotlinPtr, } - impl FromKotlinObject for Success { + impl FromKotlinObject for Yaml { fn from_kotlin_object(obj: self::sys::jobject) -> Self { let (env, _detach_guard) = util::attach_thread_to_java_vm(); let kotlin_ptr = util::to_gc_global_ref(env, obj); @@ -2620,31 +2711,30 @@ pub mod result { } } - impl ToKotlinObject for Success { + impl ToKotlinObject for Yaml { fn to_kotlin_object(&self) -> KotlinPtr { self.kotlin_ptr.clone() } } - impl AsKotlinObject for Success { + impl AsKotlinObject for Yaml { fn as_kotlin_object(&self) -> self::sys::jobject { self.kotlin_ptr.inner.inner } } - impl Success { + impl Yaml { pub fn new( - output: &str, + retain_embed_tags: bool, ) -> Self { let (env, _detach_guard) = util::attach_thread_to_java_vm(); - let class = util::get_class(env, c"org/kson/Result$Success"); - let constructor = util::get_method(env, class.as_kotlin_object(), c"", c"(Ljava/lang/String;)V"); + let class = util::get_class(env, c"org/kson/api/TranspileOptions$Yaml"); + let constructor = util::get_method(env, class.as_kotlin_object(), c"", c"(Z)V"); - let output_ptr = output.to_kotlin_object(); - let output = output_ptr.as_kotlin_object(); + let retain_embed_tags = retain_embed_tags as c_int; let jobject = unsafe { (**env).NewObject.unwrap()(env, class.as_kotlin_object(), constructor, - output, + retain_embed_tags, )}; util::panic_upon_exception(env); Self { @@ -2654,12 +2744,12 @@ pub mod result { } - impl Success { + impl Yaml { - pub fn output( + pub fn retain_embed_tags( &self, - ) -> String { + ) -> bool { let self_ptr = self.to_kotlin_object(); let self_obj = self_ptr.as_kotlin_object(); @@ -2667,32 +2757,32 @@ pub mod result { let (_, _detach_guard) = util::attach_thread_to_java_vm(); let result = call_jvm_function!( util, - c"org/kson/Result$Success", - c"getOutput", - c"()Ljava/lang/String;", - CallObjectMethod, + c"org/kson/api/TranspileOptions$Yaml", + c"getRetainEmbedTags", + c"()Z", + CallBooleanMethod, self_obj, ); - FromKotlinObject::from_kotlin_object(result) + result != 0 } } - impl std::fmt::Debug for Success { + impl std::fmt::Debug for Yaml { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let obj = self.to_kotlin_object(); - write!(f, "{}", util::call_to_string(c"org/kson/Result$Success", &obj)) + write!(f, "{}", util::call_to_string(c"org/kson/api/TranspileOptions$Yaml", &obj)) } } - impl Eq for Success {} - impl PartialEq for Success { - fn eq(&self, other: &Success) -> bool { + impl Eq for Yaml {} + impl PartialEq for Yaml { + fn eq(&self, other: &Yaml) -> bool { util::equals(self.to_kotlin_object(), other.to_kotlin_object()) } } - impl std::hash::Hash for Success { + impl std::hash::Hash for Yaml { fn hash(&self, state: &mut H) where H: std::hash::Hasher, @@ -2701,26 +2791,26 @@ pub mod result { } } } -impl FromKotlinObject for Result { +impl FromKotlinObject for TranspileOptions { fn from_kotlin_object(obj: jobject) -> Self { match util::class_name(obj).as_str() { - "org.kson.Result$Failure" => Result::Failure(result::Failure::from_kotlin_object(obj)), - "org.kson.Result$Success" => Result::Success(result::Success::from_kotlin_object(obj)), + "org.kson.api.TranspileOptions$Json" => TranspileOptions::Json(transpile_options::Json::from_kotlin_object(obj)), + "org.kson.api.TranspileOptions$Yaml" => TranspileOptions::Yaml(transpile_options::Yaml::from_kotlin_object(obj)), _ => unreachable!(), } } } -impl ToKotlinObject for Result { +impl ToKotlinObject for TranspileOptions { fn to_kotlin_object(&self) -> KotlinPtr { match self { - Self::Failure(inner) => inner.to_kotlin_object(), - Self::Success(inner) => inner.to_kotlin_object(), + Self::Json(inner) => inner.to_kotlin_object(), + Self::Yaml(inner) => inner.to_kotlin_object(), } } } -impl Result { +impl TranspileOptions { pub fn name(self) -> String { let obj = self.to_kotlin_object(); util::enum_name(&obj) @@ -2728,23 +2818,45 @@ impl Result { } -impl Result { +impl TranspileOptions { + + + pub fn retain_embed_tags( + &self, + ) -> bool { + let self_ptr = self.to_kotlin_object(); + let self_obj = self_ptr.as_kotlin_object(); + + + let (_, _detach_guard) = util::attach_thread_to_java_vm(); + let result = call_jvm_function!( + util, + c"org/kson/api/TranspileOptions", + c"getRetainEmbedTags", + c"()Z", + CallBooleanMethod, + self_obj, + + ); + + result != 0 + } } -impl std::fmt::Debug for Result { +impl std::fmt::Debug for TranspileOptions { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let obj = self.to_kotlin_object(); - write!(f, "{}", util::call_to_string(c"org/kson/Result", &obj)) + write!(f, "{}", util::call_to_string(c"org/kson/api/TranspileOptions", &obj)) } } -impl Eq for Result {} -impl PartialEq for Result { - fn eq(&self, other: &Result) -> bool { +impl Eq for TranspileOptions {} +impl PartialEq for TranspileOptions { + fn eq(&self, other: &TranspileOptions) -> bool { util::equals(self.to_kotlin_object(), other.to_kotlin_object()) } } -impl std::hash::Hash for Result { +impl std::hash::Hash for TranspileOptions { fn hash(&self, state: &mut H) where H: std::hash::Hasher, @@ -2753,14 +2865,14 @@ impl std::hash::Hash for Result { } } -/// A [parseSchema] result + #[derive(Clone)] -pub enum SchemaResult { - Failure(schema_result::Failure), - Success(schema_result::Success), +pub enum Result { + Failure(result::Failure), + Success(result::Success), } -pub mod schema_result { +pub mod result { use super::*; @@ -2795,7 +2907,7 @@ pub mod schema_result { errors: &[Message], ) -> Self { let (env, _detach_guard) = util::attach_thread_to_java_vm(); - let class = util::get_class(env, c"org/kson/SchemaResult$Failure"); + let class = util::get_class(env, c"org/kson/api/Result$Failure"); let constructor = util::get_method(env, class.as_kotlin_object(), c"", c"(Ljava/util/List;)V"); let errors_ptr = util::to_kotlin_list(errors); @@ -2825,7 +2937,7 @@ pub mod schema_result { let (_, _detach_guard) = util::attach_thread_to_java_vm(); let result = call_jvm_function!( util, - c"org/kson/SchemaResult$Failure", + c"org/kson/api/Result$Failure", c"getErrors", c"()Ljava/util/List;", CallObjectMethod, @@ -2840,7 +2952,7 @@ pub mod schema_result { impl std::fmt::Debug for Failure { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let obj = self.to_kotlin_object(); - write!(f, "{}", util::call_to_string(c"org/kson/SchemaResult$Failure", &obj)) + write!(f, "{}", util::call_to_string(c"org/kson/api/Result$Failure", &obj)) } } @@ -2887,17 +2999,17 @@ pub mod schema_result { impl Success { pub fn new( - schema_validator: SchemaValidator, + output: &str, ) -> Self { let (env, _detach_guard) = util::attach_thread_to_java_vm(); - let class = util::get_class(env, c"org/kson/SchemaResult$Success"); - let constructor = util::get_method(env, class.as_kotlin_object(), c"", c"(Lorg/kson/SchemaValidator;)V"); + let class = util::get_class(env, c"org/kson/api/Result$Success"); + let constructor = util::get_method(env, class.as_kotlin_object(), c"", c"(Ljava/lang/String;)V"); - let schema_validator_ptr = schema_validator.to_kotlin_object(); - let schema_validator = schema_validator_ptr.as_kotlin_object(); + let output_ptr = output.to_kotlin_object(); + let output = output_ptr.as_kotlin_object(); let jobject = unsafe { (**env).NewObject.unwrap()(env, class.as_kotlin_object(), constructor, - schema_validator, + output, )}; util::panic_upon_exception(env); Self { @@ -2910,9 +3022,9 @@ pub mod schema_result { impl Success { - pub fn schema_validator( + pub fn output( &self, - ) -> SchemaValidator { + ) -> String { let self_ptr = self.to_kotlin_object(); let self_obj = self_ptr.as_kotlin_object(); @@ -2920,9 +3032,9 @@ pub mod schema_result { let (_, _detach_guard) = util::attach_thread_to_java_vm(); let result = call_jvm_function!( util, - c"org/kson/SchemaResult$Success", - c"getSchemaValidator", - c"()Lorg/kson/SchemaValidator;", + c"org/kson/api/Result$Success", + c"getOutput", + c"()Ljava/lang/String;", CallObjectMethod, self_obj, @@ -2935,7 +3047,7 @@ pub mod schema_result { impl std::fmt::Debug for Success { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let obj = self.to_kotlin_object(); - write!(f, "{}", util::call_to_string(c"org/kson/SchemaResult$Success", &obj)) + write!(f, "{}", util::call_to_string(c"org/kson/api/Result$Success", &obj)) } } @@ -2954,17 +3066,17 @@ pub mod schema_result { } } } -impl FromKotlinObject for SchemaResult { +impl FromKotlinObject for Result { fn from_kotlin_object(obj: jobject) -> Self { match util::class_name(obj).as_str() { - "org.kson.SchemaResult$Failure" => SchemaResult::Failure(schema_result::Failure::from_kotlin_object(obj)), - "org.kson.SchemaResult$Success" => SchemaResult::Success(schema_result::Success::from_kotlin_object(obj)), + "org.kson.api.Result$Failure" => Result::Failure(result::Failure::from_kotlin_object(obj)), + "org.kson.api.Result$Success" => Result::Success(result::Success::from_kotlin_object(obj)), _ => unreachable!(), } } } -impl ToKotlinObject for SchemaResult { +impl ToKotlinObject for Result { fn to_kotlin_object(&self) -> KotlinPtr { match self { Self::Failure(inner) => inner.to_kotlin_object(), @@ -2973,7 +3085,7 @@ impl ToKotlinObject for SchemaResult { } } -impl SchemaResult { +impl Result { pub fn name(self) -> String { let obj = self.to_kotlin_object(); util::enum_name(&obj) @@ -2981,23 +3093,23 @@ impl SchemaResult { } -impl SchemaResult { +impl Result { } -impl std::fmt::Debug for SchemaResult { +impl std::fmt::Debug for Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let obj = self.to_kotlin_object(); - write!(f, "{}", util::call_to_string(c"org/kson/SchemaResult", &obj)) + write!(f, "{}", util::call_to_string(c"org/kson/api/Result", &obj)) } } -impl Eq for SchemaResult {} -impl PartialEq for SchemaResult { - fn eq(&self, other: &SchemaResult) -> bool { +impl Eq for Result {} +impl PartialEq for Result { + fn eq(&self, other: &Result) -> bool { util::equals(self.to_kotlin_object(), other.to_kotlin_object()) } } -impl std::hash::Hash for SchemaResult { +impl std::hash::Hash for Result { fn hash(&self, state: &mut H) where H: std::hash::Hasher, @@ -3006,238 +3118,139 @@ impl std::hash::Hash for SchemaResult { } } -/// Core interface for transpilation options shared across all output formats. -#[derive(Clone)] -pub enum TranspileOptions { - Json(transpile_options::Json), - Yaml(transpile_options::Yaml), -} - -pub mod transpile_options { - use super::*; - - /// Options for transpiling Kson to JSON. - #[derive(Clone)] - pub struct Json { - kotlin_ptr: KotlinPtr, - } - impl FromKotlinObject for Json { - fn from_kotlin_object(obj: self::sys::jobject) -> Self { - let (env, _detach_guard) = util::attach_thread_to_java_vm(); - let kotlin_ptr = util::to_gc_global_ref(env, obj); - Self { kotlin_ptr } - } - } +#[derive(Clone)] +pub struct Token { + kotlin_ptr: KotlinPtr, +} - impl ToKotlinObject for Json { - fn to_kotlin_object(&self) -> KotlinPtr { - self.kotlin_ptr.clone() - } +impl FromKotlinObject for Token { + fn from_kotlin_object(obj: self::sys::jobject) -> Self { + let (env, _detach_guard) = util::attach_thread_to_java_vm(); + let kotlin_ptr = util::to_gc_global_ref(env, obj); + Self { kotlin_ptr } } +} - impl AsKotlinObject for Json { - fn as_kotlin_object(&self) -> self::sys::jobject { - self.kotlin_ptr.inner.inner - } +impl ToKotlinObject for Token { + fn to_kotlin_object(&self) -> KotlinPtr { + self.kotlin_ptr.clone() } +} - impl Json { - pub fn new( - retain_embed_tags: bool, - ) -> Self { - let (env, _detach_guard) = util::attach_thread_to_java_vm(); - let class = util::get_class(env, c"org/kson/TranspileOptions$Json"); - let constructor = util::get_method(env, class.as_kotlin_object(), c"", c"(Z)V"); - - let retain_embed_tags = retain_embed_tags as c_int; - - let jobject = unsafe { (**env).NewObject.unwrap()(env, class.as_kotlin_object(), constructor, - retain_embed_tags, - )}; - util::panic_upon_exception(env); - Self { - kotlin_ptr: util::to_gc_global_ref(env, jobject) - } - } +impl AsKotlinObject for Token { + fn as_kotlin_object(&self) -> self::sys::jobject { + self.kotlin_ptr.inner.inner } +} +impl Token { + pub fn new( + token_type: TokenType, + text: &str, + start: Position, + end: Position, + ) -> Self { + let (env, _detach_guard) = util::attach_thread_to_java_vm(); + let class = util::get_class(env, c"org/kson/api/Token"); + let constructor = util::get_method(env, class.as_kotlin_object(), c"", c"(Lorg/kson/api/TokenType;Ljava/lang/String;Lorg/kson/api/Position;Lorg/kson/api/Position;)V"); + + let token_type_ptr = token_type.to_kotlin_object(); + let token_type = token_type_ptr.as_kotlin_object(); + let text_ptr = text.to_kotlin_object(); + let text = text_ptr.as_kotlin_object(); + let start_ptr = start.to_kotlin_object(); + let start = start_ptr.as_kotlin_object(); + let end_ptr = end.to_kotlin_object(); + let end = end_ptr.as_kotlin_object(); - impl Json { - - - pub fn retain_embed_tags( - &self, - ) -> bool { - let self_ptr = self.to_kotlin_object(); - let self_obj = self_ptr.as_kotlin_object(); - - - let (_, _detach_guard) = util::attach_thread_to_java_vm(); - let result = call_jvm_function!( - util, - c"org/kson/TranspileOptions$Json", - c"getRetainEmbedTags", - c"()Z", - CallBooleanMethod, - self_obj, - - ); - - result != 0 - } - } - - impl std::fmt::Debug for Json { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let obj = self.to_kotlin_object(); - write!(f, "{}", util::call_to_string(c"org/kson/TranspileOptions$Json", &obj)) + let jobject = unsafe { (**env).NewObject.unwrap()(env, class.as_kotlin_object(), constructor, + token_type, + text, + start, + end, + )}; + util::panic_upon_exception(env); + Self { + kotlin_ptr: util::to_gc_global_ref(env, jobject) } } +} - impl Eq for Json {} - impl PartialEq for Json { - fn eq(&self, other: &Json) -> bool { - util::equals(self.to_kotlin_object(), other.to_kotlin_object()) - } - } - impl std::hash::Hash for Json { - fn hash(&self, state: &mut H) - where - H: std::hash::Hasher, - { - util::apply_hash_code(self.to_kotlin_object(), state) - } - } - /// Options for transpiling Kson to YAML. - #[derive(Clone)] - pub struct Yaml { - kotlin_ptr: KotlinPtr, - } +impl Token { - impl FromKotlinObject for Yaml { - fn from_kotlin_object(obj: self::sys::jobject) -> Self { - let (env, _detach_guard) = util::attach_thread_to_java_vm(); - let kotlin_ptr = util::to_gc_global_ref(env, obj); - Self { kotlin_ptr } - } - } - impl ToKotlinObject for Yaml { - fn to_kotlin_object(&self) -> KotlinPtr { - self.kotlin_ptr.clone() - } - } + pub fn token_type( + &self, + ) -> TokenType { + let self_ptr = self.to_kotlin_object(); + let self_obj = self_ptr.as_kotlin_object(); - impl AsKotlinObject for Yaml { - fn as_kotlin_object(&self) -> self::sys::jobject { - self.kotlin_ptr.inner.inner - } - } - impl Yaml { - pub fn new( - retain_embed_tags: bool, - ) -> Self { - let (env, _detach_guard) = util::attach_thread_to_java_vm(); - let class = util::get_class(env, c"org/kson/TranspileOptions$Yaml"); - let constructor = util::get_method(env, class.as_kotlin_object(), c"", c"(Z)V"); + let (_, _detach_guard) = util::attach_thread_to_java_vm(); + let result = call_jvm_function!( + util, + c"org/kson/api/Token", + c"getTokenType", + c"()Lorg/kson/api/TokenType;", + CallObjectMethod, + self_obj, - let retain_embed_tags = retain_embed_tags as c_int; + ); - let jobject = unsafe { (**env).NewObject.unwrap()(env, class.as_kotlin_object(), constructor, - retain_embed_tags, - )}; - util::panic_upon_exception(env); - Self { - kotlin_ptr: util::to_gc_global_ref(env, jobject) - } - } + FromKotlinObject::from_kotlin_object(result) } - impl Yaml { - - - pub fn retain_embed_tags( - &self, - ) -> bool { - let self_ptr = self.to_kotlin_object(); - let self_obj = self_ptr.as_kotlin_object(); - - - let (_, _detach_guard) = util::attach_thread_to_java_vm(); - let result = call_jvm_function!( - util, - c"org/kson/TranspileOptions$Yaml", - c"getRetainEmbedTags", - c"()Z", - CallBooleanMethod, - self_obj, - - ); - - result != 0 - } - } - - impl std::fmt::Debug for Yaml { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let obj = self.to_kotlin_object(); - write!(f, "{}", util::call_to_string(c"org/kson/TranspileOptions$Yaml", &obj)) - } - } + pub fn text( + &self, + ) -> String { + let self_ptr = self.to_kotlin_object(); + let self_obj = self_ptr.as_kotlin_object(); - impl Eq for Yaml {} - impl PartialEq for Yaml { - fn eq(&self, other: &Yaml) -> bool { - util::equals(self.to_kotlin_object(), other.to_kotlin_object()) - } - } - impl std::hash::Hash for Yaml { - fn hash(&self, state: &mut H) - where - H: std::hash::Hasher, - { - util::apply_hash_code(self.to_kotlin_object(), state) - } - } -} -impl FromKotlinObject for TranspileOptions { - fn from_kotlin_object(obj: jobject) -> Self { - match util::class_name(obj).as_str() { - "org.kson.TranspileOptions$Json" => TranspileOptions::Json(transpile_options::Json::from_kotlin_object(obj)), - "org.kson.TranspileOptions$Yaml" => TranspileOptions::Yaml(transpile_options::Yaml::from_kotlin_object(obj)), - _ => unreachable!(), - } - } -} -impl ToKotlinObject for TranspileOptions { - fn to_kotlin_object(&self) -> KotlinPtr { - match self { - Self::Json(inner) => inner.to_kotlin_object(), - Self::Yaml(inner) => inner.to_kotlin_object(), - } - } -} + let (_, _detach_guard) = util::attach_thread_to_java_vm(); + let result = call_jvm_function!( + util, + c"org/kson/api/Token", + c"getText", + c"()Ljava/lang/String;", + CallObjectMethod, + self_obj, -impl TranspileOptions { - pub fn name(self) -> String { - let obj = self.to_kotlin_object(); - util::enum_name(&obj) + ); + + FromKotlinObject::from_kotlin_object(result) } -} -impl TranspileOptions { + pub fn start( + &self, + ) -> Position { + let self_ptr = self.to_kotlin_object(); + let self_obj = self_ptr.as_kotlin_object(); - pub fn retain_embed_tags( + let (_, _detach_guard) = util::attach_thread_to_java_vm(); + let result = call_jvm_function!( + util, + c"org/kson/api/Token", + c"getStart", + c"()Lorg/kson/api/Position;", + CallObjectMethod, + self_obj, + + ); + + FromKotlinObject::from_kotlin_object(result) + } + + + pub fn end( &self, - ) -> bool { + ) -> Position { let self_ptr = self.to_kotlin_object(); let self_obj = self_ptr.as_kotlin_object(); @@ -3245,32 +3258,32 @@ impl TranspileOptions { let (_, _detach_guard) = util::attach_thread_to_java_vm(); let result = call_jvm_function!( util, - c"org/kson/TranspileOptions", - c"getRetainEmbedTags", - c"()Z", - CallBooleanMethod, + c"org/kson/api/Token", + c"getEnd", + c"()Lorg/kson/api/Position;", + CallObjectMethod, self_obj, ); - result != 0 + FromKotlinObject::from_kotlin_object(result) } } -impl std::fmt::Debug for TranspileOptions { +impl std::fmt::Debug for Token { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let obj = self.to_kotlin_object(); - write!(f, "{}", util::call_to_string(c"org/kson/TranspileOptions", &obj)) + write!(f, "{}", util::call_to_string(c"org/kson/api/Token", &obj)) } } -impl Eq for TranspileOptions {} -impl PartialEq for TranspileOptions { - fn eq(&self, other: &TranspileOptions) -> bool { +impl Eq for Token {} +impl PartialEq for Token { + fn eq(&self, other: &Token) -> bool { util::equals(self.to_kotlin_object(), other.to_kotlin_object()) } } -impl std::hash::Hash for TranspileOptions { +impl std::hash::Hash for Token { fn hash(&self, state: &mut H) where H: std::hash::Hasher, @@ -3280,13 +3293,13 @@ impl std::hash::Hash for TranspileOptions { } -/// The [Kson](https://kson.org) language + #[derive(Clone)] -pub struct Kson { +pub struct Analysis { kotlin_ptr: KotlinPtr, } -impl FromKotlinObject for Kson { +impl FromKotlinObject for Analysis { fn from_kotlin_object(obj: self::sys::jobject) -> Self { let (env, _detach_guard) = util::attach_thread_to_java_vm(); let kotlin_ptr = util::to_gc_global_ref(env, obj); @@ -3294,188 +3307,253 @@ impl FromKotlinObject for Kson { } } -impl ToKotlinObject for Kson { +impl ToKotlinObject for Analysis { fn to_kotlin_object(&self) -> KotlinPtr { self.kotlin_ptr.clone() } } -impl AsKotlinObject for Kson { +impl AsKotlinObject for Analysis { fn as_kotlin_object(&self) -> self::sys::jobject { self.kotlin_ptr.inner.inner } } -impl Kson { +impl Analysis { + pub fn new( + errors: &[Message], + tokens: &[Token], + kson_value: Option, + ) -> Self { + let (env, _detach_guard) = util::attach_thread_to_java_vm(); + let class = util::get_class(env, c"org/kson/api/Analysis"); + let constructor = util::get_method(env, class.as_kotlin_object(), c"", c"(Ljava/util/List;Ljava/util/List;Lorg/kson/api/KsonValue;)V"); + + let errors_ptr = util::to_kotlin_list(errors); + let errors = errors_ptr.as_kotlin_object(); + let tokens_ptr = util::to_kotlin_list(tokens); + let tokens = tokens_ptr.as_kotlin_object(); + let kson_value_ptr = kson_value.to_kotlin_object(); + let kson_value = kson_value_ptr.as_kotlin_object(); + + let jobject = unsafe { (**env).NewObject.unwrap()(env, class.as_kotlin_object(), constructor, + errors, + tokens, + kson_value, + )}; + util::panic_upon_exception(env); + Self { + kotlin_ptr: util::to_gc_global_ref(env, jobject) + } + } } -impl Kson { +impl Analysis { - /// Formats Kson source with the specified formatting options. - /// - /// @param kson The Kson source to format - /// @param formatOptions The formatting options to apply - /// @return The formatted Kson source - pub fn format( - kson: &str, - format_options: FormatOptions, - ) -> String { - let self_ptr = util::access_static_field(c"org/kson/Kson", c"INSTANCE", c"Lorg/kson/Kson;"); + + pub fn errors( + &self, + ) -> Vec { + let self_ptr = self.to_kotlin_object(); let self_obj = self_ptr.as_kotlin_object(); - let kson_ptr = kson.to_kotlin_object(); - let kson = kson_ptr.as_kotlin_object(); - let format_options_ptr = format_options.to_kotlin_object(); - let format_options = format_options_ptr.as_kotlin_object(); + let (_, _detach_guard) = util::attach_thread_to_java_vm(); let result = call_jvm_function!( util, - c"org/kson/Kson", - c"format", - c"(Ljava/lang/String;Lorg/kson/FormatOptions;)Ljava/lang/String;", + c"org/kson/api/Analysis", + c"getErrors", + c"()Ljava/util/List;", CallObjectMethod, self_obj, - kson, - format_options, + ); - FromKotlinObject::from_kotlin_object(result) + util::from_kotlin_list(result) } - /// Converts Kson to Json. - /// - /// @param kson The Kson source to convert - /// @param options Options for the JSON transpilation - /// @return A Result containing either the Json output or error messages - pub fn to_json( - kson: &str, - options: transpile_options::Json, - ) -> std::result::Result { - let self_ptr = util::access_static_field(c"org/kson/Kson", c"INSTANCE", c"Lorg/kson/Kson;"); + + pub fn tokens( + &self, + ) -> Vec { + let self_ptr = self.to_kotlin_object(); let self_obj = self_ptr.as_kotlin_object(); - let kson_ptr = kson.to_kotlin_object(); - let kson = kson_ptr.as_kotlin_object(); - let options_ptr = options.to_kotlin_object(); - let options = options_ptr.as_kotlin_object(); + let (_, _detach_guard) = util::attach_thread_to_java_vm(); let result = call_jvm_function!( util, - c"org/kson/Kson", - c"toJson", - c"(Ljava/lang/String;Lorg/kson/TranspileOptions$Json;)Lorg/kson/Result;", + c"org/kson/api/Analysis", + c"getTokens", + c"()Ljava/util/List;", CallObjectMethod, self_obj, - kson, - options, + ); - crate::kson_result_into_rust_result(FromKotlinObject::from_kotlin_object(result)) + util::from_kotlin_list(result) } - /// Converts Kson to Yaml, preserving comments - /// - /// @param kson The Kson source to convert - /// @param options Options for the YAML transpilation - /// @return A Result containing either the Yaml output or error messages - pub fn to_yaml( - kson: &str, - options: transpile_options::Yaml, - ) -> std::result::Result { - let self_ptr = util::access_static_field(c"org/kson/Kson", c"INSTANCE", c"Lorg/kson/Kson;"); + + pub fn kson_value( + &self, + ) -> Option { + let self_ptr = self.to_kotlin_object(); let self_obj = self_ptr.as_kotlin_object(); - let kson_ptr = kson.to_kotlin_object(); - let kson = kson_ptr.as_kotlin_object(); - let options_ptr = options.to_kotlin_object(); - let options = options_ptr.as_kotlin_object(); + let (_, _detach_guard) = util::attach_thread_to_java_vm(); let result = call_jvm_function!( util, - c"org/kson/Kson", - c"toYaml", - c"(Ljava/lang/String;Lorg/kson/TranspileOptions$Yaml;)Lorg/kson/Result;", + c"org/kson/api/Analysis", + c"getKsonValue", + c"()Lorg/kson/api/KsonValue;", CallObjectMethod, self_obj, - kson, - options, + ); - crate::kson_result_into_rust_result(FromKotlinObject::from_kotlin_object(result)) + FromKotlinObject::from_kotlin_object(result) + } +} + +impl std::fmt::Debug for Analysis { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let obj = self.to_kotlin_object(); + write!(f, "{}", util::call_to_string(c"org/kson/api/Analysis", &obj)) } +} - /// Statically analyze the given Kson and return an [Analysis] object containing any messages generated along with a - /// tokenized version of the source. Useful for tooling/editor support. - /// @param kson The Kson source to analyze - /// @param filepath Filepath of the document being analyzed - pub fn analyze( - kson: &str, - filepath: Option<&str>, - ) -> Analysis { - let self_ptr = util::access_static_field(c"org/kson/Kson", c"INSTANCE", c"Lorg/kson/Kson;"); +impl Eq for Analysis {} +impl PartialEq for Analysis { + fn eq(&self, other: &Analysis) -> bool { + util::equals(self.to_kotlin_object(), other.to_kotlin_object()) + } +} +impl std::hash::Hash for Analysis { + fn hash(&self, state: &mut H) + where + H: std::hash::Hasher, + { + util::apply_hash_code(self.to_kotlin_object(), state) + } +} + + + +#[derive(Clone)] +pub struct EmbedRule { + kotlin_ptr: KotlinPtr, +} + +impl FromKotlinObject for EmbedRule { + fn from_kotlin_object(obj: self::sys::jobject) -> Self { + let (env, _detach_guard) = util::attach_thread_to_java_vm(); + let kotlin_ptr = util::to_gc_global_ref(env, obj); + Self { kotlin_ptr } + } +} + +impl ToKotlinObject for EmbedRule { + fn to_kotlin_object(&self) -> KotlinPtr { + self.kotlin_ptr.clone() + } +} + +impl AsKotlinObject for EmbedRule { + fn as_kotlin_object(&self) -> self::sys::jobject { + self.kotlin_ptr.inner.inner + } +} + +impl EmbedRule { + pub fn new( + path_pattern: &str, + tag: Option<&str>, + ) -> Self { + let (env, _detach_guard) = util::attach_thread_to_java_vm(); + let class = util::get_class(env, c"org/kson/api/EmbedRule"); + let constructor = util::get_method(env, class.as_kotlin_object(), c"", c"(Ljava/lang/String;Ljava/lang/String;)V"); + + let path_pattern_ptr = path_pattern.to_kotlin_object(); + let path_pattern = path_pattern_ptr.as_kotlin_object(); + let tag_ptr = tag.to_kotlin_object(); + let tag = tag_ptr.as_kotlin_object(); + + let jobject = unsafe { (**env).NewObject.unwrap()(env, class.as_kotlin_object(), constructor, + path_pattern, + tag, + )}; + util::panic_upon_exception(env); + Self { + kotlin_ptr: util::to_gc_global_ref(env, jobject) + } + } +} + + +impl EmbedRule { + + + pub fn path_pattern( + &self, + ) -> String { + let self_ptr = self.to_kotlin_object(); let self_obj = self_ptr.as_kotlin_object(); - let kson_ptr = kson.to_kotlin_object(); - let kson = kson_ptr.as_kotlin_object(); - let filepath_ptr = filepath.to_kotlin_object(); - let filepath = filepath_ptr.as_kotlin_object(); + let (_, _detach_guard) = util::attach_thread_to_java_vm(); let result = call_jvm_function!( util, - c"org/kson/Kson", - c"analyze", - c"(Ljava/lang/String;Ljava/lang/String;)Lorg/kson/Analysis;", + c"org/kson/api/EmbedRule", + c"getPathPattern", + c"()Ljava/lang/String;", CallObjectMethod, self_obj, - kson, - filepath, + ); FromKotlinObject::from_kotlin_object(result) } - /// Parses a Kson schema definition and returns a validator for that schema. - /// - /// @param schemaKson The Kson source defining a Json Schema - /// @return A SchemaValidator that can validate Kson documents against the schema - pub fn parse_schema( - schema_kson: &str, - ) -> std::result::Result { - let self_ptr = util::access_static_field(c"org/kson/Kson", c"INSTANCE", c"Lorg/kson/Kson;"); + + pub fn tag( + &self, + ) -> Option { + let self_ptr = self.to_kotlin_object(); let self_obj = self_ptr.as_kotlin_object(); - let schema_kson_ptr = schema_kson.to_kotlin_object(); - let schema_kson = schema_kson_ptr.as_kotlin_object(); + let (_, _detach_guard) = util::attach_thread_to_java_vm(); let result = call_jvm_function!( util, - c"org/kson/Kson", - c"parseSchema", - c"(Ljava/lang/String;)Lorg/kson/SchemaResult;", + c"org/kson/api/EmbedRule", + c"getTag", + c"()Ljava/lang/String;", CallObjectMethod, self_obj, - schema_kson, + ); - crate::kson_schema_result_into_rust_result(FromKotlinObject::from_kotlin_object(result)) + FromKotlinObject::from_kotlin_object(result) } } -impl std::fmt::Debug for Kson { +impl std::fmt::Debug for EmbedRule { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let obj = self.to_kotlin_object(); - write!(f, "{}", util::call_to_string(c"org/kson/Kson", &obj)) + write!(f, "{}", util::call_to_string(c"org/kson/api/EmbedRule", &obj)) } } -impl Eq for Kson {} -impl PartialEq for Kson { - fn eq(&self, other: &Kson) -> bool { +impl Eq for EmbedRule {} +impl PartialEq for EmbedRule { + fn eq(&self, other: &EmbedRule) -> bool { util::equals(self.to_kotlin_object(), other.to_kotlin_object()) } } -impl std::hash::Hash for Kson { +impl std::hash::Hash for EmbedRule { fn hash(&self, state: &mut H) where H: std::hash::Hasher, @@ -3485,18 +3563,13 @@ impl std::hash::Hash for Kson { } -/// A rule for formatting string values at specific paths as embed blocks. -/// -/// When formatting KSON, strings at paths matching [pathPattern] will be rendered -/// as embed blocks instead of regular strings. -/// -/// **Warning:** JsonPointerGlob syntax is experimental and may change in future versions. + #[derive(Clone)] -pub struct EmbedRule { +pub struct FormatOptions { kotlin_ptr: KotlinPtr, } -impl FromKotlinObject for EmbedRule { +impl FromKotlinObject for FormatOptions { fn from_kotlin_object(obj: self::sys::jobject) -> Self { let (env, _detach_guard) = util::attach_thread_to_java_vm(); let kotlin_ptr = util::to_gc_global_ref(env, obj); @@ -3504,28 +3577,54 @@ impl FromKotlinObject for EmbedRule { } } -impl ToKotlinObject for EmbedRule { +impl ToKotlinObject for FormatOptions { fn to_kotlin_object(&self) -> KotlinPtr { self.kotlin_ptr.clone() } } -impl AsKotlinObject for EmbedRule { +impl AsKotlinObject for FormatOptions { fn as_kotlin_object(&self) -> self::sys::jobject { self.kotlin_ptr.inner.inner } } -impl EmbedRule { +impl FormatOptions { + pub fn new( + indent_type: IndentType, + formatting_style: FormattingStyle, + embed_block_rules: &[EmbedRule], + ) -> Self { + let (env, _detach_guard) = util::attach_thread_to_java_vm(); + let class = util::get_class(env, c"org/kson/api/FormatOptions"); + let constructor = util::get_method(env, class.as_kotlin_object(), c"", c"(Lorg/kson/api/IndentType;Lorg/kson/api/FormattingStyle;Ljava/util/List;)V"); + + let indent_type_ptr = indent_type.to_kotlin_object(); + let indent_type = indent_type_ptr.as_kotlin_object(); + let formatting_style_ptr = formatting_style.to_kotlin_object(); + let formatting_style = formatting_style_ptr.as_kotlin_object(); + let embed_block_rules_ptr = util::to_kotlin_list(embed_block_rules); + let embed_block_rules = embed_block_rules_ptr.as_kotlin_object(); + + let jobject = unsafe { (**env).NewObject.unwrap()(env, class.as_kotlin_object(), constructor, + indent_type, + formatting_style, + embed_block_rules, + )}; + util::panic_upon_exception(env); + Self { + kotlin_ptr: util::to_gc_global_ref(env, jobject) + } + } } -impl EmbedRule { +impl FormatOptions { - pub fn path_pattern( + pub fn indent_type( &self, - ) -> String { + ) -> IndentType { let self_ptr = self.to_kotlin_object(); let self_obj = self_ptr.as_kotlin_object(); @@ -3533,9 +3632,9 @@ impl EmbedRule { let (_, _detach_guard) = util::attach_thread_to_java_vm(); let result = call_jvm_function!( util, - c"org/kson/EmbedRule", - c"getPathPattern", - c"()Ljava/lang/String;", + c"org/kson/api/FormatOptions", + c"getIndentType", + c"()Lorg/kson/api/IndentType;", CallObjectMethod, self_obj, @@ -3545,9 +3644,9 @@ impl EmbedRule { } - pub fn tag( + pub fn formatting_style( &self, - ) -> Option { + ) -> FormattingStyle { let self_ptr = self.to_kotlin_object(); let self_obj = self_ptr.as_kotlin_object(); @@ -3555,9 +3654,9 @@ impl EmbedRule { let (_, _detach_guard) = util::attach_thread_to_java_vm(); let result = call_jvm_function!( util, - c"org/kson/EmbedRule", - c"getTag", - c"()Ljava/lang/String;", + c"org/kson/api/FormatOptions", + c"getFormattingStyle", + c"()Lorg/kson/api/FormattingStyle;", CallObjectMethod, self_obj, @@ -3566,58 +3665,43 @@ impl EmbedRule { FromKotlinObject::from_kotlin_object(result) } - /// Builds a new [EmbedRule]. - /// - /// @param pathPattern A JsonPointerGlob pattern (e.g., "/scripts/ *", "/queries/ **") - /// @param tag Optional embed tag to include (e.g., "yaml", "sql", "bash") - /// @return [EmbedRuleResult.Success] if [pathPattern] is a valid JsonPointerGlob, otherwise [EmbedRuleResult.Failure] - /// - /// Example: - /// ```kotlin - /// EmbedRule.fromPathPattern("/scripts/ *", tag = "bash") // Match all values under "scripts" - /// EmbedRule.fromPathPattern("/config/description") // Match exact path, no tag - /// ``` - pub fn from_path_pattern( - path_pattern: &str, - tag: Option<&str>, - ) -> EmbedRuleResult { - let self_ptr = util::access_static_field(c"org/kson/EmbedRule", c"INSTANCE", c"Lorg/kson/EmbedRule;"); + + pub fn embed_block_rules( + &self, + ) -> Vec { + let self_ptr = self.to_kotlin_object(); let self_obj = self_ptr.as_kotlin_object(); - let path_pattern_ptr = path_pattern.to_kotlin_object(); - let path_pattern = path_pattern_ptr.as_kotlin_object(); - let tag_ptr = tag.to_kotlin_object(); - let tag = tag_ptr.as_kotlin_object(); + let (_, _detach_guard) = util::attach_thread_to_java_vm(); let result = call_jvm_function!( util, - c"org/kson/EmbedRule", - c"fromPathPattern", - c"(Ljava/lang/String;Ljava/lang/String;)Lorg/kson/EmbedRuleResult;", + c"org/kson/api/FormatOptions", + c"getEmbedBlockRules", + c"()Ljava/util/List;", CallObjectMethod, self_obj, - path_pattern, - tag, + ); - FromKotlinObject::from_kotlin_object(result) + util::from_kotlin_list(result) } } -impl std::fmt::Debug for EmbedRule { +impl std::fmt::Debug for FormatOptions { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let obj = self.to_kotlin_object(); - write!(f, "{}", util::call_to_string(c"org/kson/EmbedRule", &obj)) + write!(f, "{}", util::call_to_string(c"org/kson/api/FormatOptions", &obj)) } } -impl Eq for EmbedRule {} -impl PartialEq for EmbedRule { - fn eq(&self, other: &EmbedRule) -> bool { +impl Eq for FormatOptions {} +impl PartialEq for FormatOptions { + fn eq(&self, other: &FormatOptions) -> bool { util::equals(self.to_kotlin_object(), other.to_kotlin_object()) } } -impl std::hash::Hash for EmbedRule { +impl std::hash::Hash for FormatOptions { fn hash(&self, state: &mut H) where H: std::hash::Hasher, @@ -3626,210 +3710,203 @@ impl std::hash::Hash for EmbedRule { } } -/// Options for indenting Kson Output + +/// The [Kson](https://kson.org) language #[derive(Clone)] -pub enum IndentType { - Spaces(indent_type::Spaces), - Tabs(indent_type::Tabs), +pub struct Kson { + kotlin_ptr: KotlinPtr, } -pub mod indent_type { - use super::*; - - - /// Use spaces for indentation with the specified count - #[derive(Clone)] - pub struct Spaces { - kotlin_ptr: KotlinPtr, - } - - impl FromKotlinObject for Spaces { - fn from_kotlin_object(obj: self::sys::jobject) -> Self { - let (env, _detach_guard) = util::attach_thread_to_java_vm(); - let kotlin_ptr = util::to_gc_global_ref(env, obj); - Self { kotlin_ptr } - } - } - - impl ToKotlinObject for Spaces { - fn to_kotlin_object(&self) -> KotlinPtr { - self.kotlin_ptr.clone() - } +impl FromKotlinObject for Kson { + fn from_kotlin_object(obj: self::sys::jobject) -> Self { + let (env, _detach_guard) = util::attach_thread_to_java_vm(); + let kotlin_ptr = util::to_gc_global_ref(env, obj); + Self { kotlin_ptr } } +} - impl AsKotlinObject for Spaces { - fn as_kotlin_object(&self) -> self::sys::jobject { - self.kotlin_ptr.inner.inner - } +impl ToKotlinObject for Kson { + fn to_kotlin_object(&self) -> KotlinPtr { + self.kotlin_ptr.clone() } +} - impl Spaces { - pub fn new( - size: i32, - ) -> Self { - let (env, _detach_guard) = util::attach_thread_to_java_vm(); - let class = util::get_class(env, c"org/kson/IndentType$Spaces"); - let constructor = util::get_method(env, class.as_kotlin_object(), c"", c"(I)V"); - - - - let jobject = unsafe { (**env).NewObject.unwrap()(env, class.as_kotlin_object(), constructor, - size, - )}; - util::panic_upon_exception(env); - Self { - kotlin_ptr: util::to_gc_global_ref(env, jobject) - } - } +impl AsKotlinObject for Kson { + fn as_kotlin_object(&self) -> self::sys::jobject { + self.kotlin_ptr.inner.inner } +} +impl Kson { +} - impl Spaces { - - - pub fn size( - &self, - ) -> i32 { - let self_ptr = self.to_kotlin_object(); - let self_obj = self_ptr.as_kotlin_object(); - - - let (_, _detach_guard) = util::attach_thread_to_java_vm(); - let result = call_jvm_function!( - util, - c"org/kson/IndentType$Spaces", - c"getSize", - c"()I", - CallIntMethod, - self_obj, - - ); - - result - } - } - impl std::fmt::Debug for Spaces { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let obj = self.to_kotlin_object(); - write!(f, "{}", util::call_to_string(c"org/kson/IndentType$Spaces", &obj)) - } - } +impl Kson { - impl Eq for Spaces {} - impl PartialEq for Spaces { - fn eq(&self, other: &Spaces) -> bool { - util::equals(self.to_kotlin_object(), other.to_kotlin_object()) - } - } - impl std::hash::Hash for Spaces { - fn hash(&self, state: &mut H) - where - H: std::hash::Hasher, - { - util::apply_hash_code(self.to_kotlin_object(), state) - } - } + /// Formats Kson source with the specified formatting options. + /// + /// @param kson The Kson source to format + /// @param formatOptions The formatting options to apply + /// @return The formatted Kson source + pub fn format( + kson: &str, + format_options: FormatOptions, + ) -> String { + let self_ptr = util::access_static_field(c"org/kson/Kson", c"INSTANCE", c"Lorg/kson/Kson;"); + let self_obj = self_ptr.as_kotlin_object(); + let kson_ptr = kson.to_kotlin_object(); + let kson = kson_ptr.as_kotlin_object(); + let format_options_ptr = format_options.to_kotlin_object(); + let format_options = format_options_ptr.as_kotlin_object(); - /// Use tabs for indentation - #[derive(Clone)] - pub struct Tabs { - kotlin_ptr: KotlinPtr, - } + let (_, _detach_guard) = util::attach_thread_to_java_vm(); + let result = call_jvm_function!( + util, + c"org/kson/Kson", + c"format", + c"(Ljava/lang/String;Lorg/kson/api/FormatOptions;)Ljava/lang/String;", + CallObjectMethod, + self_obj, + kson, + format_options, + ); - impl FromKotlinObject for Tabs { - fn from_kotlin_object(obj: self::sys::jobject) -> Self { - let (env, _detach_guard) = util::attach_thread_to_java_vm(); - let kotlin_ptr = util::to_gc_global_ref(env, obj); - Self { kotlin_ptr } - } + FromKotlinObject::from_kotlin_object(result) } - impl ToKotlinObject for Tabs { - fn to_kotlin_object(&self) -> KotlinPtr { - self.kotlin_ptr.clone() - } - } + /// Converts Kson to Json. + /// + /// @param kson The Kson source to convert + /// @param options Options for the JSON transpilation + /// @return A Result containing either the Json output or error messages + pub fn to_json( + kson: &str, + options: transpile_options::Json, + ) -> std::result::Result { + let self_ptr = util::access_static_field(c"org/kson/Kson", c"INSTANCE", c"Lorg/kson/Kson;"); + let self_obj = self_ptr.as_kotlin_object(); + let kson_ptr = kson.to_kotlin_object(); + let kson = kson_ptr.as_kotlin_object(); + let options_ptr = options.to_kotlin_object(); + let options = options_ptr.as_kotlin_object(); - impl AsKotlinObject for Tabs { - fn as_kotlin_object(&self) -> self::sys::jobject { - self.kotlin_ptr.inner.inner - } - } + let (_, _detach_guard) = util::attach_thread_to_java_vm(); + let result = call_jvm_function!( + util, + c"org/kson/Kson", + c"toJson", + c"(Ljava/lang/String;Lorg/kson/api/TranspileOptions$Json;)Lorg/kson/api/Result;", + CallObjectMethod, + self_obj, + kson, + options, + ); - impl Tabs { + crate::kson_result_into_rust_result(FromKotlinObject::from_kotlin_object(result)) } + /// Converts Kson to Yaml, preserving comments + /// + /// @param kson The Kson source to convert + /// @param options Options for the YAML transpilation + /// @return A Result containing either the Yaml output or error messages + pub fn to_yaml( + kson: &str, + options: transpile_options::Yaml, + ) -> std::result::Result { + let self_ptr = util::access_static_field(c"org/kson/Kson", c"INSTANCE", c"Lorg/kson/Kson;"); + let self_obj = self_ptr.as_kotlin_object(); + let kson_ptr = kson.to_kotlin_object(); + let kson = kson_ptr.as_kotlin_object(); + let options_ptr = options.to_kotlin_object(); + let options = options_ptr.as_kotlin_object(); - impl Tabs { - } + let (_, _detach_guard) = util::attach_thread_to_java_vm(); + let result = call_jvm_function!( + util, + c"org/kson/Kson", + c"toYaml", + c"(Ljava/lang/String;Lorg/kson/api/TranspileOptions$Yaml;)Lorg/kson/api/Result;", + CallObjectMethod, + self_obj, + kson, + options, + ); - impl std::fmt::Debug for Tabs { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let obj = self.to_kotlin_object(); - write!(f, "{}", util::call_to_string(c"org/kson/IndentType$Tabs", &obj)) - } + crate::kson_result_into_rust_result(FromKotlinObject::from_kotlin_object(result)) } - impl Eq for Tabs {} - impl PartialEq for Tabs { - fn eq(&self, other: &Tabs) -> bool { - util::equals(self.to_kotlin_object(), other.to_kotlin_object()) - } - } - impl std::hash::Hash for Tabs { - fn hash(&self, state: &mut H) - where - H: std::hash::Hasher, - { - util::apply_hash_code(self.to_kotlin_object(), state) - } - } -} -impl FromKotlinObject for IndentType { - fn from_kotlin_object(obj: jobject) -> Self { - match util::class_name(obj).as_str() { - "org.kson.IndentType$Spaces" => IndentType::Spaces(indent_type::Spaces::from_kotlin_object(obj)), - "org.kson.IndentType$Tabs" => IndentType::Tabs(indent_type::Tabs::from_kotlin_object(obj)), - _ => unreachable!(), - } - } -} + /// Statically analyze the given Kson and return an [Analysis] object containing any messages generated along with a + /// tokenized version of the source. Useful for tooling/editor support. + /// @param kson The Kson source to analyze + /// @param filepath Filepath of the document being analyzed + pub fn analyze( + kson: &str, + filepath: Option<&str>, + ) -> Analysis { + let self_ptr = util::access_static_field(c"org/kson/Kson", c"INSTANCE", c"Lorg/kson/Kson;"); + let self_obj = self_ptr.as_kotlin_object(); + let kson_ptr = kson.to_kotlin_object(); + let kson = kson_ptr.as_kotlin_object(); + let filepath_ptr = filepath.to_kotlin_object(); + let filepath = filepath_ptr.as_kotlin_object(); -impl ToKotlinObject for IndentType { - fn to_kotlin_object(&self) -> KotlinPtr { - match self { - Self::Spaces(inner) => inner.to_kotlin_object(), - Self::Tabs(inner) => inner.to_kotlin_object(), - } - } -} + let (_, _detach_guard) = util::attach_thread_to_java_vm(); + let result = call_jvm_function!( + util, + c"org/kson/Kson", + c"analyze", + c"(Ljava/lang/String;Ljava/lang/String;)Lorg/kson/api/Analysis;", + CallObjectMethod, + self_obj, + kson, + filepath, + ); -impl IndentType { - pub fn name(self) -> String { - let obj = self.to_kotlin_object(); - util::enum_name(&obj) + FromKotlinObject::from_kotlin_object(result) } -} + /// Parses a Kson schema definition and returns a validator for that schema. + /// + /// @param schemaKson The Kson source defining a Json Schema + /// @return A SchemaValidator that can validate Kson documents against the schema + pub fn parse_schema( + schema_kson: &str, + ) -> std::result::Result { + let self_ptr = util::access_static_field(c"org/kson/Kson", c"INSTANCE", c"Lorg/kson/Kson;"); + let self_obj = self_ptr.as_kotlin_object(); + let schema_kson_ptr = schema_kson.to_kotlin_object(); + let schema_kson = schema_kson_ptr.as_kotlin_object(); + + let (_, _detach_guard) = util::attach_thread_to_java_vm(); + let result = call_jvm_function!( + util, + c"org/kson/Kson", + c"parseSchema", + c"(Ljava/lang/String;)Lorg/kson/api/SchemaResult;", + CallObjectMethod, + self_obj, + schema_kson, + ); -impl IndentType { + crate::kson_schema_result_into_rust_result(FromKotlinObject::from_kotlin_object(result)) + } } -impl std::fmt::Debug for IndentType { +impl std::fmt::Debug for Kson { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let obj = self.to_kotlin_object(); - write!(f, "{}", util::call_to_string(c"org/kson/IndentType", &obj)) + write!(f, "{}", util::call_to_string(c"org/kson/Kson", &obj)) } } -impl Eq for IndentType {} -impl PartialEq for IndentType { - fn eq(&self, other: &IndentType) -> bool { +impl Eq for Kson {} +impl PartialEq for Kson { + fn eq(&self, other: &Kson) -> bool { util::equals(self.to_kotlin_object(), other.to_kotlin_object()) } } -impl std::hash::Hash for IndentType { +impl std::hash::Hash for Kson { fn hash(&self, state: &mut H) where H: std::hash::Hasher, @@ -3838,7 +3915,7 @@ impl std::hash::Hash for IndentType { } } -/// Represents the severity of a [Message] + #[derive(Copy, Clone)] pub enum MessageSeverity { Error, @@ -3847,7 +3924,7 @@ pub enum MessageSeverity { impl FromKotlinObject for MessageSeverity { fn from_kotlin_object(obj: jobject) -> Self { - match util::enum_ordinal(c"org/kson/MessageSeverity", obj) { + match util::enum_ordinal(c"org/kson/api/MessageSeverity", obj) { 0 => MessageSeverity::Error, 1 => MessageSeverity::Warning, _ => unreachable!(), @@ -3858,8 +3935,8 @@ impl FromKotlinObject for MessageSeverity { impl ToKotlinObject for MessageSeverity { fn to_kotlin_object(&self) -> KotlinPtr { match self { - MessageSeverity::Error => util::access_static_field(c"org/kson/MessageSeverity", c"ERROR", c"Lorg/kson/MessageSeverity;"), - MessageSeverity::Warning => util::access_static_field(c"org/kson/MessageSeverity", c"WARNING", c"Lorg/kson/MessageSeverity;"), + MessageSeverity::Error => util::access_static_field(c"org/kson/api/MessageSeverity", c"ERROR", c"Lorg/kson/api/MessageSeverity;"), + MessageSeverity::Warning => util::access_static_field(c"org/kson/api/MessageSeverity", c"WARNING", c"Lorg/kson/api/MessageSeverity;"), } } } @@ -3870,89 +3947,89 @@ impl MessageSeverity { util::enum_name(&obj) } } -/// Type discriminator for KsonValue subclasses + #[derive(Copy, Clone)] -pub enum KsonValueType { - Object, - Array, - String, - Integer, - Decimal, - Boolean, - Null, - Embed, +pub enum FormattingStyle { + Plain, + Delimited, + Compact, + Classic, } -impl FromKotlinObject for KsonValueType { +impl FromKotlinObject for FormattingStyle { fn from_kotlin_object(obj: jobject) -> Self { - match util::enum_ordinal(c"org/kson/KsonValueType", obj) { - 0 => KsonValueType::Object, - 1 => KsonValueType::Array, - 2 => KsonValueType::String, - 3 => KsonValueType::Integer, - 4 => KsonValueType::Decimal, - 5 => KsonValueType::Boolean, - 6 => KsonValueType::Null, - 7 => KsonValueType::Embed, + match util::enum_ordinal(c"org/kson/api/FormattingStyle", obj) { + 0 => FormattingStyle::Plain, + 1 => FormattingStyle::Delimited, + 2 => FormattingStyle::Compact, + 3 => FormattingStyle::Classic, _ => unreachable!(), } } } -impl ToKotlinObject for KsonValueType { +impl ToKotlinObject for FormattingStyle { fn to_kotlin_object(&self) -> KotlinPtr { match self { - KsonValueType::Object => util::access_static_field(c"org/kson/KsonValueType", c"OBJECT", c"Lorg/kson/KsonValueType;"), - KsonValueType::Array => util::access_static_field(c"org/kson/KsonValueType", c"ARRAY", c"Lorg/kson/KsonValueType;"), - KsonValueType::String => util::access_static_field(c"org/kson/KsonValueType", c"STRING", c"Lorg/kson/KsonValueType;"), - KsonValueType::Integer => util::access_static_field(c"org/kson/KsonValueType", c"INTEGER", c"Lorg/kson/KsonValueType;"), - KsonValueType::Decimal => util::access_static_field(c"org/kson/KsonValueType", c"DECIMAL", c"Lorg/kson/KsonValueType;"), - KsonValueType::Boolean => util::access_static_field(c"org/kson/KsonValueType", c"BOOLEAN", c"Lorg/kson/KsonValueType;"), - KsonValueType::Null => util::access_static_field(c"org/kson/KsonValueType", c"NULL", c"Lorg/kson/KsonValueType;"), - KsonValueType::Embed => util::access_static_field(c"org/kson/KsonValueType", c"EMBED", c"Lorg/kson/KsonValueType;"), + FormattingStyle::Plain => util::access_static_field(c"org/kson/api/FormattingStyle", c"PLAIN", c"Lorg/kson/api/FormattingStyle;"), + FormattingStyle::Delimited => util::access_static_field(c"org/kson/api/FormattingStyle", c"DELIMITED", c"Lorg/kson/api/FormattingStyle;"), + FormattingStyle::Compact => util::access_static_field(c"org/kson/api/FormattingStyle", c"COMPACT", c"Lorg/kson/api/FormattingStyle;"), + FormattingStyle::Classic => util::access_static_field(c"org/kson/api/FormattingStyle", c"CLASSIC", c"Lorg/kson/api/FormattingStyle;"), } } } -impl KsonValueType { +impl FormattingStyle { pub fn name(self) -> String { let obj = self.to_kotlin_object(); util::enum_name(&obj) } } -/// [FormattingStyle] options for Kson Output + #[derive(Copy, Clone)] -pub enum FormattingStyle { - Plain, - Delimited, - Compact, - Classic, +pub enum KsonValueType { + Object, + Array, + String, + Integer, + Decimal, + Boolean, + Null, + Embed, } -impl FromKotlinObject for FormattingStyle { +impl FromKotlinObject for KsonValueType { fn from_kotlin_object(obj: jobject) -> Self { - match util::enum_ordinal(c"org/kson/FormattingStyle", obj) { - 0 => FormattingStyle::Plain, - 1 => FormattingStyle::Delimited, - 2 => FormattingStyle::Compact, - 3 => FormattingStyle::Classic, + match util::enum_ordinal(c"org/kson/api/KsonValueType", obj) { + 0 => KsonValueType::Object, + 1 => KsonValueType::Array, + 2 => KsonValueType::String, + 3 => KsonValueType::Integer, + 4 => KsonValueType::Decimal, + 5 => KsonValueType::Boolean, + 6 => KsonValueType::Null, + 7 => KsonValueType::Embed, _ => unreachable!(), } } } -impl ToKotlinObject for FormattingStyle { +impl ToKotlinObject for KsonValueType { fn to_kotlin_object(&self) -> KotlinPtr { match self { - FormattingStyle::Plain => util::access_static_field(c"org/kson/FormattingStyle", c"PLAIN", c"Lorg/kson/FormattingStyle;"), - FormattingStyle::Delimited => util::access_static_field(c"org/kson/FormattingStyle", c"DELIMITED", c"Lorg/kson/FormattingStyle;"), - FormattingStyle::Compact => util::access_static_field(c"org/kson/FormattingStyle", c"COMPACT", c"Lorg/kson/FormattingStyle;"), - FormattingStyle::Classic => util::access_static_field(c"org/kson/FormattingStyle", c"CLASSIC", c"Lorg/kson/FormattingStyle;"), + KsonValueType::Object => util::access_static_field(c"org/kson/api/KsonValueType", c"OBJECT", c"Lorg/kson/api/KsonValueType;"), + KsonValueType::Array => util::access_static_field(c"org/kson/api/KsonValueType", c"ARRAY", c"Lorg/kson/api/KsonValueType;"), + KsonValueType::String => util::access_static_field(c"org/kson/api/KsonValueType", c"STRING", c"Lorg/kson/api/KsonValueType;"), + KsonValueType::Integer => util::access_static_field(c"org/kson/api/KsonValueType", c"INTEGER", c"Lorg/kson/api/KsonValueType;"), + KsonValueType::Decimal => util::access_static_field(c"org/kson/api/KsonValueType", c"DECIMAL", c"Lorg/kson/api/KsonValueType;"), + KsonValueType::Boolean => util::access_static_field(c"org/kson/api/KsonValueType", c"BOOLEAN", c"Lorg/kson/api/KsonValueType;"), + KsonValueType::Null => util::access_static_field(c"org/kson/api/KsonValueType", c"NULL", c"Lorg/kson/api/KsonValueType;"), + KsonValueType::Embed => util::access_static_field(c"org/kson/api/KsonValueType", c"EMBED", c"Lorg/kson/api/KsonValueType;"), } } } -impl FormattingStyle { +impl KsonValueType { pub fn name(self) -> String { let obj = self.to_kotlin_object(); util::enum_name(&obj) @@ -3993,7 +4070,7 @@ pub enum TokenType { impl FromKotlinObject for TokenType { fn from_kotlin_object(obj: jobject) -> Self { - match util::enum_ordinal(c"org/kson/TokenType", obj) { + match util::enum_ordinal(c"org/kson/api/TokenType", obj) { 0 => TokenType::CurlyBraceL, 1 => TokenType::CurlyBraceR, 2 => TokenType::SquareBracketL, @@ -4030,34 +4107,34 @@ impl FromKotlinObject for TokenType { impl ToKotlinObject for TokenType { fn to_kotlin_object(&self) -> KotlinPtr { match self { - TokenType::CurlyBraceL => util::access_static_field(c"org/kson/TokenType", c"CURLY_BRACE_L", c"Lorg/kson/TokenType;"), - TokenType::CurlyBraceR => util::access_static_field(c"org/kson/TokenType", c"CURLY_BRACE_R", c"Lorg/kson/TokenType;"), - TokenType::SquareBracketL => util::access_static_field(c"org/kson/TokenType", c"SQUARE_BRACKET_L", c"Lorg/kson/TokenType;"), - TokenType::SquareBracketR => util::access_static_field(c"org/kson/TokenType", c"SQUARE_BRACKET_R", c"Lorg/kson/TokenType;"), - TokenType::AngleBracketL => util::access_static_field(c"org/kson/TokenType", c"ANGLE_BRACKET_L", c"Lorg/kson/TokenType;"), - TokenType::AngleBracketR => util::access_static_field(c"org/kson/TokenType", c"ANGLE_BRACKET_R", c"Lorg/kson/TokenType;"), - TokenType::Colon => util::access_static_field(c"org/kson/TokenType", c"COLON", c"Lorg/kson/TokenType;"), - TokenType::Dot => util::access_static_field(c"org/kson/TokenType", c"DOT", c"Lorg/kson/TokenType;"), - TokenType::EndDash => util::access_static_field(c"org/kson/TokenType", c"END_DASH", c"Lorg/kson/TokenType;"), - TokenType::Comma => util::access_static_field(c"org/kson/TokenType", c"COMMA", c"Lorg/kson/TokenType;"), - TokenType::Comment => util::access_static_field(c"org/kson/TokenType", c"COMMENT", c"Lorg/kson/TokenType;"), - TokenType::EmbedOpenDelim => util::access_static_field(c"org/kson/TokenType", c"EMBED_OPEN_DELIM", c"Lorg/kson/TokenType;"), - TokenType::EmbedCloseDelim => util::access_static_field(c"org/kson/TokenType", c"EMBED_CLOSE_DELIM", c"Lorg/kson/TokenType;"), - TokenType::EmbedTag => util::access_static_field(c"org/kson/TokenType", c"EMBED_TAG", c"Lorg/kson/TokenType;"), - TokenType::EmbedPreambleNewline => util::access_static_field(c"org/kson/TokenType", c"EMBED_PREAMBLE_NEWLINE", c"Lorg/kson/TokenType;"), - TokenType::EmbedContent => util::access_static_field(c"org/kson/TokenType", c"EMBED_CONTENT", c"Lorg/kson/TokenType;"), - TokenType::False => util::access_static_field(c"org/kson/TokenType", c"FALSE", c"Lorg/kson/TokenType;"), - TokenType::UnquotedString => util::access_static_field(c"org/kson/TokenType", c"UNQUOTED_STRING", c"Lorg/kson/TokenType;"), - TokenType::IllegalChar => util::access_static_field(c"org/kson/TokenType", c"ILLEGAL_CHAR", c"Lorg/kson/TokenType;"), - TokenType::ListDash => util::access_static_field(c"org/kson/TokenType", c"LIST_DASH", c"Lorg/kson/TokenType;"), - TokenType::Null => util::access_static_field(c"org/kson/TokenType", c"NULL", c"Lorg/kson/TokenType;"), - TokenType::Number => util::access_static_field(c"org/kson/TokenType", c"NUMBER", c"Lorg/kson/TokenType;"), - TokenType::StringOpenQuote => util::access_static_field(c"org/kson/TokenType", c"STRING_OPEN_QUOTE", c"Lorg/kson/TokenType;"), - TokenType::StringCloseQuote => util::access_static_field(c"org/kson/TokenType", c"STRING_CLOSE_QUOTE", c"Lorg/kson/TokenType;"), - TokenType::StringContent => util::access_static_field(c"org/kson/TokenType", c"STRING_CONTENT", c"Lorg/kson/TokenType;"), - TokenType::True => util::access_static_field(c"org/kson/TokenType", c"TRUE", c"Lorg/kson/TokenType;"), - TokenType::Whitespace => util::access_static_field(c"org/kson/TokenType", c"WHITESPACE", c"Lorg/kson/TokenType;"), - TokenType::Eof => util::access_static_field(c"org/kson/TokenType", c"EOF", c"Lorg/kson/TokenType;"), + TokenType::CurlyBraceL => util::access_static_field(c"org/kson/api/TokenType", c"CURLY_BRACE_L", c"Lorg/kson/api/TokenType;"), + TokenType::CurlyBraceR => util::access_static_field(c"org/kson/api/TokenType", c"CURLY_BRACE_R", c"Lorg/kson/api/TokenType;"), + TokenType::SquareBracketL => util::access_static_field(c"org/kson/api/TokenType", c"SQUARE_BRACKET_L", c"Lorg/kson/api/TokenType;"), + TokenType::SquareBracketR => util::access_static_field(c"org/kson/api/TokenType", c"SQUARE_BRACKET_R", c"Lorg/kson/api/TokenType;"), + TokenType::AngleBracketL => util::access_static_field(c"org/kson/api/TokenType", c"ANGLE_BRACKET_L", c"Lorg/kson/api/TokenType;"), + TokenType::AngleBracketR => util::access_static_field(c"org/kson/api/TokenType", c"ANGLE_BRACKET_R", c"Lorg/kson/api/TokenType;"), + TokenType::Colon => util::access_static_field(c"org/kson/api/TokenType", c"COLON", c"Lorg/kson/api/TokenType;"), + TokenType::Dot => util::access_static_field(c"org/kson/api/TokenType", c"DOT", c"Lorg/kson/api/TokenType;"), + TokenType::EndDash => util::access_static_field(c"org/kson/api/TokenType", c"END_DASH", c"Lorg/kson/api/TokenType;"), + TokenType::Comma => util::access_static_field(c"org/kson/api/TokenType", c"COMMA", c"Lorg/kson/api/TokenType;"), + TokenType::Comment => util::access_static_field(c"org/kson/api/TokenType", c"COMMENT", c"Lorg/kson/api/TokenType;"), + TokenType::EmbedOpenDelim => util::access_static_field(c"org/kson/api/TokenType", c"EMBED_OPEN_DELIM", c"Lorg/kson/api/TokenType;"), + TokenType::EmbedCloseDelim => util::access_static_field(c"org/kson/api/TokenType", c"EMBED_CLOSE_DELIM", c"Lorg/kson/api/TokenType;"), + TokenType::EmbedTag => util::access_static_field(c"org/kson/api/TokenType", c"EMBED_TAG", c"Lorg/kson/api/TokenType;"), + TokenType::EmbedPreambleNewline => util::access_static_field(c"org/kson/api/TokenType", c"EMBED_PREAMBLE_NEWLINE", c"Lorg/kson/api/TokenType;"), + TokenType::EmbedContent => util::access_static_field(c"org/kson/api/TokenType", c"EMBED_CONTENT", c"Lorg/kson/api/TokenType;"), + TokenType::False => util::access_static_field(c"org/kson/api/TokenType", c"FALSE", c"Lorg/kson/api/TokenType;"), + TokenType::UnquotedString => util::access_static_field(c"org/kson/api/TokenType", c"UNQUOTED_STRING", c"Lorg/kson/api/TokenType;"), + TokenType::IllegalChar => util::access_static_field(c"org/kson/api/TokenType", c"ILLEGAL_CHAR", c"Lorg/kson/api/TokenType;"), + TokenType::ListDash => util::access_static_field(c"org/kson/api/TokenType", c"LIST_DASH", c"Lorg/kson/api/TokenType;"), + TokenType::Null => util::access_static_field(c"org/kson/api/TokenType", c"NULL", c"Lorg/kson/api/TokenType;"), + TokenType::Number => util::access_static_field(c"org/kson/api/TokenType", c"NUMBER", c"Lorg/kson/api/TokenType;"), + TokenType::StringOpenQuote => util::access_static_field(c"org/kson/api/TokenType", c"STRING_OPEN_QUOTE", c"Lorg/kson/api/TokenType;"), + TokenType::StringCloseQuote => util::access_static_field(c"org/kson/api/TokenType", c"STRING_CLOSE_QUOTE", c"Lorg/kson/api/TokenType;"), + TokenType::StringContent => util::access_static_field(c"org/kson/api/TokenType", c"STRING_CONTENT", c"Lorg/kson/api/TokenType;"), + TokenType::True => util::access_static_field(c"org/kson/api/TokenType", c"TRUE", c"Lorg/kson/api/TokenType;"), + TokenType::Whitespace => util::access_static_field(c"org/kson/api/TokenType", c"WHITESPACE", c"Lorg/kson/api/TokenType;"), + TokenType::Eof => util::access_static_field(c"org/kson/api/TokenType", c"EOF", c"Lorg/kson/api/TokenType;"), } } } diff --git a/lib-rust/kson/src/generated/util.rs b/lib-rust/kson/src/generated/util.rs index e21b2d22..d57f51e6 100644 --- a/lib-rust/kson/src/generated/util.rs +++ b/lib-rust/kson/src/generated/util.rs @@ -401,6 +401,40 @@ pub(super) fn from_kotlin_list(list: jobject) -> Vec { elements } +pub(super) fn to_kotlin_map< + K: ToKotlinObject + Eq + std::hash::Hash, + V: ToKotlinObject, +>( + items: &std::collections::HashMap +) -> KotlinPtr { + let (env, _detach_guard) = attach_thread_to_java_vm(); + + // Create a new map + let map_class = get_class(env, c"java/util/HashMap"); + let constructor = get_method(env, map_class.as_kotlin_object(), c"", c"()V"); + let map = unsafe { + let new_object = (**env).NewObjectA.unwrap(); + new_object(env, map_class.as_kotlin_object(), constructor, std::ptr::null()) + }; + panic_upon_exception(env); + let map = to_gc_global_ref(env, map); + + // Add entries to it + let put_method = get_method(env, map_class.as_kotlin_object(), c"put", c"(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;"); + for (key, value) in items { + let key = key.to_kotlin_object(); + let value = value.to_kotlin_object(); + let args = &[jvalue { l: key.as_kotlin_object() }, jvalue { l: value.as_kotlin_object() }]; + unsafe { + let call_object = (**env).CallObjectMethodA.unwrap(); + call_object(env, map.as_kotlin_object(), put_method, args.as_ptr()); + } + panic_upon_exception(env); + } + + map +} + pub(super) fn from_kotlin_value_map< K: FromKotlinObject + Eq + std::hash::Hash, V: FromKotlinObject, diff --git a/settings.gradle.kts b/settings.gradle.kts index ed22c46e..ef7b68b5 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -13,10 +13,15 @@ pluginManagement { rootProject.name = "kson" include("native-tests") +include("kson-service-api") +include("kson-service-tests") include("kson-lib") +include("kson-http") include("kson-tooling-lib") include("lib-python") +include("lib-python:kson-lib-tests") include("lib-rust") +include("lib-rust:kson-lib-tests") include("tooling:jetbrains") include("tooling:language-server-protocol") include("tooling:lsp-clients")