mirror of
https://github.com/jellyfin/jellyfin-sdk-kotlin.git
synced 2025-02-26 02:55:58 +00:00
Add compare command to openapi-generator
This commit is contained in:
parent
4aee90b7e0
commit
61de6651f5
@ -1,5 +1,6 @@
|
||||
plugins {
|
||||
alias(libs.plugins.download)
|
||||
alias(libs.plugins.kotlin.serialization)
|
||||
kotlin("jvm")
|
||||
id("application")
|
||||
}
|
||||
@ -23,6 +24,9 @@ dependencies {
|
||||
// Kotlin code generation
|
||||
implementation(libs.kotlinpoet)
|
||||
|
||||
// Compare reporters
|
||||
implementation(libs.kotlinx.serialization.json)
|
||||
|
||||
// Dependency Injection
|
||||
implementation(libs.koin)
|
||||
|
||||
|
@ -8,22 +8,30 @@ import org.jellyfin.openapi.builder.model.ModelsBuilder
|
||||
import org.jellyfin.openapi.builder.openapi.OpenApiApiServicesBuilder
|
||||
import org.jellyfin.openapi.builder.openapi.OpenApiConstantsBuilder
|
||||
import org.jellyfin.openapi.builder.openapi.OpenApiModelsBuilder
|
||||
import org.jellyfin.openapi.compare.InfoComparator
|
||||
import org.jellyfin.openapi.compare.ModelComparator
|
||||
import org.jellyfin.openapi.compare.OperationComparator
|
||||
import org.jellyfin.openapi.compare.model.CompareResult
|
||||
import org.jellyfin.openapi.constants.Packages
|
||||
import org.jellyfin.openapi.model.GeneratorContext
|
||||
import org.jellyfin.openapi.model.GeneratorResult
|
||||
import org.koin.core.component.KoinComponent
|
||||
import org.koin.core.component.inject
|
||||
import java.io.File
|
||||
|
||||
private val logger = KotlinLogging.logger { }
|
||||
|
||||
class Generator(
|
||||
private val openApiApiServicesBuilder: OpenApiApiServicesBuilder,
|
||||
private val apisBuilder: ApisBuilder,
|
||||
private val openApiModelsBuilder: OpenApiModelsBuilder,
|
||||
private val modelsBuilder: ModelsBuilder,
|
||||
private val apiClientExtensionsBuilder: ApiClientExtensionsBuilder,
|
||||
private val openApiConstantsBuilder: OpenApiConstantsBuilder,
|
||||
) {
|
||||
private fun generateInternal(openApiJson: String): GeneratorResult {
|
||||
class Generator : KoinComponent {
|
||||
private val openApiApiServicesBuilder by inject<OpenApiApiServicesBuilder>()
|
||||
private val apisBuilder by inject<ApisBuilder>()
|
||||
private val openApiModelsBuilder by inject<OpenApiModelsBuilder>()
|
||||
private val modelsBuilder by inject<ModelsBuilder>()
|
||||
private val apiClientExtensionsBuilder by inject<ApiClientExtensionsBuilder>()
|
||||
private val openApiConstantsBuilder by inject<OpenApiConstantsBuilder>()
|
||||
private val infoComparator by inject<InfoComparator>()
|
||||
private val operationComparator by inject<OperationComparator>()
|
||||
private val modelComparator by inject<ModelComparator>()
|
||||
|
||||
private fun generateInternal(openApiJson: String): GeneratorContext {
|
||||
val openApiSpecification = OpenAPIV3Parser().readContents(openApiJson)
|
||||
openApiSpecification.messages.forEach { message -> logger.warn { message } }
|
||||
|
||||
@ -41,7 +49,7 @@ class Generator(
|
||||
apiClientExtensionsBuilder.build(context, context.apiServices)
|
||||
openApiConstantsBuilder.build(context, context.info)
|
||||
|
||||
return context.toGeneratorResult()
|
||||
return context
|
||||
}
|
||||
|
||||
fun verify(
|
||||
@ -52,7 +60,7 @@ class Generator(
|
||||
val verification = Verification(apiOutputDir, modelsOutputDir)
|
||||
val result = generateInternal(openApiJson)
|
||||
|
||||
return verification.verify(result)
|
||||
return verification.verify(result.toGeneratorResult())
|
||||
}
|
||||
|
||||
fun generate(
|
||||
@ -60,7 +68,7 @@ class Generator(
|
||||
apiOutputDir: File,
|
||||
modelsOutputDir: File,
|
||||
) {
|
||||
val result = generateInternal(openApiJson)
|
||||
val result = generateInternal(openApiJson).toGeneratorResult()
|
||||
|
||||
// Clear output directories
|
||||
modelsOutputDir.deleteRecursively()
|
||||
@ -78,4 +86,21 @@ class Generator(
|
||||
file.writeTo(directory)
|
||||
}
|
||||
}
|
||||
|
||||
fun compare(
|
||||
oldOpenApiJson: String,
|
||||
newOpenApiJson: String,
|
||||
): CompareResult {
|
||||
// Create generator contexts for both schemas
|
||||
val oldSchema = generateInternal(oldOpenApiJson)
|
||||
val newSchema = generateInternal(newOpenApiJson)
|
||||
|
||||
// Construct and return compare result
|
||||
return CompareResult(
|
||||
binaryDifference = oldOpenApiJson != newOpenApiJson,
|
||||
info = infoComparator.compare(oldSchema, newSchema),
|
||||
api = operationComparator.compare(oldSchema, newSchema),
|
||||
model = modelComparator.compare(oldSchema, newSchema),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -25,14 +25,28 @@ import org.jellyfin.openapi.builder.openapi.OpenApiModelBuilder
|
||||
import org.jellyfin.openapi.builder.openapi.OpenApiModelsBuilder
|
||||
import org.jellyfin.openapi.builder.openapi.OpenApiReturnTypeBuilder
|
||||
import org.jellyfin.openapi.builder.openapi.OpenApiTypeBuilder
|
||||
import org.jellyfin.openapi.cli.CompareCommand
|
||||
import org.jellyfin.openapi.cli.GenerateCommand
|
||||
import org.jellyfin.openapi.cli.MainCommand
|
||||
import org.jellyfin.openapi.cli.VerifyCommand
|
||||
import org.jellyfin.openapi.compare.InfoComparator
|
||||
import org.jellyfin.openapi.compare.ModelComparator
|
||||
import org.jellyfin.openapi.compare.OperationComparator
|
||||
import org.jellyfin.openapi.compare.reporter.CompareReporter
|
||||
import org.jellyfin.openapi.compare.reporter.JsonCompareReporter
|
||||
import org.koin.dsl.bind
|
||||
import org.koin.dsl.module
|
||||
|
||||
val mainModule = module {
|
||||
single { Generator(get(), get(), get(), get(), get(), get()) }
|
||||
single { Generator() }
|
||||
|
||||
// Comparators
|
||||
single { InfoComparator() }
|
||||
single { OperationComparator() }
|
||||
single { ModelComparator() }
|
||||
|
||||
// Compare reporters
|
||||
single { JsonCompareReporter() } bind CompareReporter::class
|
||||
|
||||
// OpenAPI
|
||||
single { OpenApiTypeBuilder(getAll()) }
|
||||
@ -72,4 +86,5 @@ val mainModule = module {
|
||||
single { MainCommand() }
|
||||
single { GenerateCommand() } bind CliktCommand::class
|
||||
single { VerifyCommand() } bind CliktCommand::class
|
||||
single { CompareCommand() } bind CliktCommand::class
|
||||
}
|
||||
|
@ -0,0 +1,68 @@
|
||||
package org.jellyfin.openapi.cli
|
||||
|
||||
import com.github.ajalt.clikt.parameters.arguments.argument
|
||||
import com.github.ajalt.clikt.parameters.options.default
|
||||
import com.github.ajalt.clikt.parameters.options.option
|
||||
import com.github.ajalt.clikt.parameters.types.choice
|
||||
import com.github.ajalt.clikt.parameters.types.file
|
||||
import org.jellyfin.openapi.Generator
|
||||
import org.jellyfin.openapi.compare.reporter.CompareReporter
|
||||
import org.koin.core.component.inject
|
||||
|
||||
class CompareCommand : BaseCommand() {
|
||||
private val generator by inject<Generator>()
|
||||
private val reporters by lazy { getKoin().getAll<CompareReporter>() }
|
||||
|
||||
private val oldOpenApiFile by argument(
|
||||
help = "The old OpenAPI JSON file"
|
||||
).file(
|
||||
mustExist = true,
|
||||
canBeFile = true,
|
||||
canBeDir = false,
|
||||
mustBeReadable = true,
|
||||
)
|
||||
|
||||
private val newOpenApiFile by argument(
|
||||
help = "The new OpenAPI JSON file"
|
||||
).file(
|
||||
mustExist = true,
|
||||
canBeFile = true,
|
||||
canBeDir = false,
|
||||
mustBeReadable = true,
|
||||
)
|
||||
|
||||
private val reporter by option(
|
||||
names = arrayOf("--format", "-f"),
|
||||
help = "The format to use"
|
||||
).choice(
|
||||
choices = reporters.associateBy { it.name }
|
||||
).default(
|
||||
value = reporters.first()
|
||||
)
|
||||
|
||||
private val output by option(
|
||||
names = arrayOf("--output", "-o"),
|
||||
help = "The output"
|
||||
).file(
|
||||
canBeFile = true,
|
||||
canBeDir = false,
|
||||
)
|
||||
|
||||
override fun run() {
|
||||
// Read OpenAPI json
|
||||
val oldOpenApiJson = oldOpenApiFile.readText()
|
||||
val newOpenApiJson = newOpenApiFile.readText()
|
||||
|
||||
// Compare specifications
|
||||
val result = generator.compare(oldOpenApiJson, newOpenApiJson)
|
||||
val formatted = reporter.format(result)
|
||||
|
||||
// Write output
|
||||
if (output != null) {
|
||||
output!!.writeText(formatted)
|
||||
println("Output written to ${output!!.absolutePath}")
|
||||
} else {
|
||||
println(formatted)
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
package org.jellyfin.openapi.compare
|
||||
|
||||
import org.jellyfin.openapi.compare.model.CompareValueDiff
|
||||
import org.jellyfin.openapi.compare.model.buildCompareValueDiffCollection
|
||||
import org.jellyfin.openapi.model.GeneratorContext
|
||||
|
||||
class InfoComparator {
|
||||
fun compare(
|
||||
oldSchema: GeneratorContext,
|
||||
newSchema: GeneratorContext,
|
||||
): Collection<CompareValueDiff> = buildCompareValueDiffCollection(oldSchema.info, newSchema.info) {
|
||||
detect({ title }, "Title")
|
||||
detect({ description }, "Description")
|
||||
detect({ version }, "API version")
|
||||
detect({ extensions["x-jellyfin-version"] }, "Jellyfin version")
|
||||
}
|
||||
}
|
@ -0,0 +1,91 @@
|
||||
package org.jellyfin.openapi.compare
|
||||
|
||||
import org.jellyfin.openapi.compare.model.CompareModel
|
||||
import org.jellyfin.openapi.compare.model.CompareModelConstant
|
||||
import org.jellyfin.openapi.compare.model.CompareModelProperty
|
||||
import org.jellyfin.openapi.compare.model.buildCompareCollectionDiff
|
||||
import org.jellyfin.openapi.compare.model.buildCompareValueDiffCollection
|
||||
import org.jellyfin.openapi.compare.model.emptyCompareCollectionDiff
|
||||
import org.jellyfin.openapi.model.ApiModel
|
||||
import org.jellyfin.openapi.model.EnumApiModel
|
||||
import org.jellyfin.openapi.model.GeneratorContext
|
||||
import org.jellyfin.openapi.model.ObjectApiModel
|
||||
|
||||
class ModelComparator {
|
||||
private fun compareObjectModelProperties(
|
||||
oldModel: ObjectApiModel?,
|
||||
newModel: ObjectApiModel,
|
||||
) = buildCompareCollectionDiff(
|
||||
first = oldModel?.properties ?: newModel.properties,
|
||||
second = newModel.properties,
|
||||
keySelector = { name },
|
||||
comparator = { oldProperty, newProperty -> oldProperty == newProperty },
|
||||
createModel = { oldProperty, newProperty ->
|
||||
CompareModelProperty(
|
||||
newProperty.name,
|
||||
buildCompareValueDiffCollection(oldProperty, newProperty) {
|
||||
detect({ name }, "Name")
|
||||
detect({ description }, "Description")
|
||||
detect({ deprecated }, "Deprecated")
|
||||
detect({ defaultValue }, "Default value")
|
||||
// Check type and nullability separately
|
||||
detect({ type.copy(nullable = false) }, "Type")
|
||||
detect({ type.isNullable }, "Nullable")
|
||||
}
|
||||
)
|
||||
},
|
||||
)
|
||||
|
||||
private fun compareEnumModelConstants(
|
||||
oldModel: EnumApiModel?,
|
||||
newModel: EnumApiModel,
|
||||
) = buildCompareCollectionDiff(
|
||||
first = oldModel?.constants ?: newModel.constants,
|
||||
second = newModel.constants,
|
||||
keySelector = { this },
|
||||
comparator = { oldConstant, newConstant -> oldConstant == newConstant },
|
||||
createModel = { _, newConstant ->
|
||||
CompareModelConstant(
|
||||
name = newConstant,
|
||||
changes = buildCompareValueDiffCollection(oldModel, newModel) {
|
||||
detect({ name }, "Name")
|
||||
}
|
||||
)
|
||||
},
|
||||
)
|
||||
|
||||
private fun compareModel(
|
||||
oldModel: ApiModel?,
|
||||
newModel: ApiModel,
|
||||
) = CompareModel(
|
||||
name = newModel.name,
|
||||
description = newModel.description,
|
||||
properties = when {
|
||||
newModel is ObjectApiModel -> compareObjectModelProperties(oldModel as? ObjectApiModel?, newModel)
|
||||
else -> emptyCompareCollectionDiff()
|
||||
},
|
||||
constants = when {
|
||||
newModel is EnumApiModel -> compareEnumModelConstants(oldModel as? EnumApiModel?, newModel)
|
||||
else -> emptyCompareCollectionDiff()
|
||||
},
|
||||
changes = buildCompareValueDiffCollection(oldModel, newModel) {
|
||||
detect({ name }, "Name")
|
||||
detect({ description }, "Description")
|
||||
detect({ deprecated }, "Deprecated")
|
||||
|
||||
if (oldModel != null && oldModel::class != newModel::class) {
|
||||
add("ObjectType", oldModel::class, newModel::class)
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
fun compare(
|
||||
oldSchema: GeneratorContext,
|
||||
newSchema: GeneratorContext,
|
||||
) = buildCompareCollectionDiff(
|
||||
first = oldSchema.models,
|
||||
second = newSchema.models,
|
||||
keySelector = { name },
|
||||
createModel = ::compareModel
|
||||
)
|
||||
}
|
@ -0,0 +1,68 @@
|
||||
package org.jellyfin.openapi.compare
|
||||
|
||||
import org.jellyfin.openapi.compare.model.CompareOperation
|
||||
import org.jellyfin.openapi.compare.model.CompareOperationParameter
|
||||
import org.jellyfin.openapi.compare.model.buildCompareCollectionDiff
|
||||
import org.jellyfin.openapi.compare.model.buildCompareValueDiffCollection
|
||||
import org.jellyfin.openapi.model.ApiServiceOperation
|
||||
import org.jellyfin.openapi.model.ApiServiceOperationParameter
|
||||
import org.jellyfin.openapi.model.GeneratorContext
|
||||
|
||||
class OperationComparator {
|
||||
private fun compareParameter(
|
||||
oldParameter: ApiServiceOperationParameter?,
|
||||
newParameter: ApiServiceOperationParameter,
|
||||
): CompareOperationParameter = CompareOperationParameter(
|
||||
name = newParameter.name,
|
||||
changes = buildCompareValueDiffCollection(oldParameter, newParameter) {
|
||||
detect({ name }, "Name")
|
||||
detect({ originalName }, "Original name")
|
||||
detect({ description }, "Description")
|
||||
detect({ deprecated }, "Deprecated")
|
||||
detect({ defaultValue }, "Default value")
|
||||
// Check type and nullability separately
|
||||
detect({ type.copy(nullable = false) }, "Type")
|
||||
detect({ type.isNullable }, "Nullable")
|
||||
detect({ validation }, "Validation")
|
||||
},
|
||||
)
|
||||
|
||||
private fun compareParameters(
|
||||
oldParameters: Collection<ApiServiceOperationParameter>?,
|
||||
newParameters: Collection<ApiServiceOperationParameter>,
|
||||
) = buildCompareCollectionDiff(
|
||||
first = oldParameters ?: newParameters,
|
||||
second = newParameters,
|
||||
keySelector = { name },
|
||||
createModel = ::compareParameter,
|
||||
)
|
||||
|
||||
private fun compareOperation(
|
||||
oldOperation: ApiServiceOperation?,
|
||||
newOperation: ApiServiceOperation,
|
||||
) = CompareOperation(
|
||||
name = newOperation.name,
|
||||
pathParameters = compareParameters(oldOperation?.pathParameters, newOperation.pathParameters),
|
||||
queryParameters = compareParameters(oldOperation?.queryParameters, newOperation.queryParameters),
|
||||
changes = buildCompareValueDiffCollection(oldOperation, newOperation) {
|
||||
detect({ name }, "Name")
|
||||
detect({ description }, "Description")
|
||||
detect({ deprecated }, "Deprecated")
|
||||
detect({ pathTemplate }, "URL template")
|
||||
detect({ method }, "Method")
|
||||
detect({ requireAuthentication }, "Authentication")
|
||||
detect({ returnType }, "Return type")
|
||||
detect({ bodyType }, "Body type")
|
||||
},
|
||||
)
|
||||
|
||||
fun compare(
|
||||
oldSchema: GeneratorContext,
|
||||
newSchema: GeneratorContext,
|
||||
) = buildCompareCollectionDiff(
|
||||
first = oldSchema.apiServices.flatMap { it.operations },
|
||||
second = newSchema.apiServices.flatMap { it.operations },
|
||||
keySelector = { method.toString() + pathTemplate },
|
||||
createModel = ::compareOperation,
|
||||
)
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
package org.jellyfin.openapi.compare
|
||||
|
||||
fun <T, K> Iterable<T>.compare(
|
||||
other: Iterable<T>,
|
||||
keySelector: (T) -> K,
|
||||
comparator: (T, T) -> Boolean = { a, b -> a == b },
|
||||
): Iterable<CompareEntry<T>> {
|
||||
val references = map(keySelector).toSet()
|
||||
val otherReferences = other.associateBy(keySelector)
|
||||
|
||||
val entries = mutableListOf<CompareEntry<T>>()
|
||||
|
||||
other.forEach { otherItem ->
|
||||
val otherKey = keySelector(otherItem)
|
||||
if (otherKey !in references) entries.add(CompareEntry.Added(otherItem))
|
||||
}
|
||||
|
||||
forEach { item ->
|
||||
val otherItem = otherReferences[keySelector(item)]
|
||||
|
||||
if (otherItem == null) entries.add(CompareEntry.Removed(item))
|
||||
else if (!comparator(item, otherItem)) entries.add(CompareEntry.Modified(item, otherItem))
|
||||
else entries.add(CompareEntry.Unchanged(item))
|
||||
}
|
||||
|
||||
return entries
|
||||
}
|
||||
|
||||
sealed interface CompareEntry<T> {
|
||||
data class Added<T>(val item: T) : CompareEntry<T>
|
||||
data class Modified<T>(val before: T, val after: T) : CompareEntry<T>
|
||||
data class Unchanged<T>(val item: T) : CompareEntry<T>
|
||||
data class Removed<T>(val item: T) : CompareEntry<T>
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
package org.jellyfin.openapi.compare.model
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
import org.jellyfin.openapi.compare.CompareEntry
|
||||
import org.jellyfin.openapi.compare.compare
|
||||
|
||||
@Serializable
|
||||
data class CompareCollectionDiff<T>(
|
||||
val added: Collection<T> = emptyList(),
|
||||
val removed: Collection<T> = emptyList(),
|
||||
val modified: Collection<T> = emptyList(),
|
||||
val unchanged: Collection<T> = emptyList(),
|
||||
) {
|
||||
fun isEmpty() = added.isEmpty() && removed.isEmpty() && modified.isEmpty() && unchanged.isEmpty()
|
||||
fun isNotEmpty() = added.isNotEmpty() || removed.isNotEmpty() || modified.isNotEmpty() || unchanged.isNotEmpty()
|
||||
|
||||
fun isChanged() = added.isNotEmpty() || removed.isNotEmpty() || modified.isNotEmpty()
|
||||
fun isNotChanged() = added.isEmpty() && removed.isEmpty() && modified.isEmpty()
|
||||
}
|
||||
|
||||
fun <T> emptyCompareCollectionDiff() = CompareCollectionDiff<T>()
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
fun <T, TN, K> buildCompareCollectionDiff(
|
||||
first: Iterable<T>,
|
||||
second: Iterable<T>,
|
||||
keySelector: T.() -> K,
|
||||
comparator: (T, T) -> Boolean = { a, b -> a == b },
|
||||
createModel: (T?, T) -> TN,
|
||||
): CompareCollectionDiff<TN> {
|
||||
val result = first.compare(second, keySelector, comparator)
|
||||
|
||||
return CompareCollectionDiff(
|
||||
added = result.filterIsInstance<CompareEntry.Added<T>>().map { createModel(null, it.item) },
|
||||
removed = result.filterIsInstance<CompareEntry.Removed<T>>().map { createModel(null, it.item) },
|
||||
modified = result.filterIsInstance<CompareEntry.Modified<T>>().map { createModel(it.before, it.after) },
|
||||
unchanged = result.filterIsInstance<CompareEntry.Unchanged<T>>().map { createModel(null, it.item) },
|
||||
)
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
package org.jellyfin.openapi.compare.model
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class CompareModel(
|
||||
val name: String,
|
||||
val description: String?,
|
||||
val properties: CompareCollectionDiff<CompareModelProperty>,
|
||||
val constants: CompareCollectionDiff<CompareModelConstant>,
|
||||
|
||||
val changes: Collection<CompareValueDiff>,
|
||||
)
|
@ -0,0 +1,10 @@
|
||||
package org.jellyfin.openapi.compare.model
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class CompareModelConstant(
|
||||
val name: String,
|
||||
|
||||
val changes: Collection<CompareValueDiff> = emptyList(),
|
||||
)
|
@ -0,0 +1,10 @@
|
||||
package org.jellyfin.openapi.compare.model
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class CompareModelProperty(
|
||||
val name: String,
|
||||
|
||||
val changes: Collection<CompareValueDiff> = emptyList(),
|
||||
)
|
@ -0,0 +1,12 @@
|
||||
package org.jellyfin.openapi.compare.model
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class CompareOperation(
|
||||
val name: String,
|
||||
val pathParameters: CompareCollectionDiff<CompareOperationParameter>,
|
||||
val queryParameters: CompareCollectionDiff<CompareOperationParameter>,
|
||||
|
||||
val changes: Collection<CompareValueDiff>,
|
||||
)
|
@ -0,0 +1,10 @@
|
||||
package org.jellyfin.openapi.compare.model
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class CompareOperationParameter(
|
||||
val name: String,
|
||||
|
||||
val changes: Collection<CompareValueDiff> = emptyList(),
|
||||
)
|
@ -0,0 +1,11 @@
|
||||
package org.jellyfin.openapi.compare.model
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class CompareResult(
|
||||
val binaryDifference: Boolean,
|
||||
val info: Collection<CompareValueDiff>,
|
||||
val api: CompareCollectionDiff<CompareOperation>,
|
||||
val model: CompareCollectionDiff<CompareModel>,
|
||||
)
|
@ -0,0 +1,37 @@
|
||||
package org.jellyfin.openapi.compare.model
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class CompareValueDiff(val name: String, val from: String, val to: String)
|
||||
|
||||
fun <T> buildCompareValueDiffCollection(
|
||||
a: T?,
|
||||
b: T,
|
||||
body: BuildCompareValueDiffCollectionContext<T>.() -> Unit,
|
||||
): Collection<CompareValueDiff> {
|
||||
if (a == null) return emptyList()
|
||||
|
||||
val context = BuildCompareValueDiffCollectionContext(a, b)
|
||||
context.body()
|
||||
return context.values
|
||||
}
|
||||
|
||||
class BuildCompareValueDiffCollectionContext<T>(
|
||||
private val a: T,
|
||||
private val b: T,
|
||||
) {
|
||||
private val _values = mutableListOf<CompareValueDiff>()
|
||||
val values get() = _values.toList()
|
||||
|
||||
fun <K> detect(selector: T.() -> K, name: String) {
|
||||
val valueA = a.selector()
|
||||
val valueB = b.selector()
|
||||
|
||||
if (valueA != valueB) add(name, valueA, valueB)
|
||||
}
|
||||
|
||||
fun <K> add(name: String, from: K, to: K) {
|
||||
_values.add(CompareValueDiff(name, from.toString(), to.toString()))
|
||||
}
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
package org.jellyfin.openapi.compare.reporter
|
||||
|
||||
import org.jellyfin.openapi.compare.model.CompareResult
|
||||
|
||||
interface CompareReporter {
|
||||
val name: String
|
||||
|
||||
fun format(result: CompareResult): String
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
package org.jellyfin.openapi.compare.reporter
|
||||
|
||||
import kotlinx.serialization.encodeToString
|
||||
import kotlinx.serialization.json.Json
|
||||
import org.jellyfin.openapi.compare.model.CompareResult
|
||||
|
||||
class JsonCompareReporter : CompareReporter {
|
||||
override val name = "json"
|
||||
|
||||
override fun format(result: CompareResult): String = Json.encodeToString(result)
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user