From 7b55d0b598cb13287d0c3e0fa5c179ea1def8636 Mon Sep 17 00:00:00 2001 From: Janne Mareike Koschinski <janne@kuschku.de> Date: Sun, 21 Feb 2021 16:52:20 +0100 Subject: [PATCH] Cleanup annotation/symbol processing --- .../libquassel/generator/InvokerProcessor.kt | 127 ++------------- .../annotation/RpcFunctionAnnotation.kt | 37 +++++ .../annotation/RpcObjectAnnotation.kt | 31 ++++ .../generator/kotlinmodel/KotlinModel.kt | 36 +++++ .../kotlinmodel/KotlinModelVisitor.kt | 16 ++ .../libquassel/generator/rpcmodel/RpcModel.kt | 52 ++++++ .../generator/rpcmodel/RpcModelVisitor.kt | 17 ++ .../generator/util/kotlinpoet/ArgString.kt | 38 +++++ .../util/kotlinpoet/WhenBlockBuilder.kt | 44 +++++ .../kotlinpoet/buildWhen.kt} | 13 +- .../kotlinpoet/withIndent.kt} | 13 +- .../generator/util/ksp/asClassName.kt | 34 ++++ .../generator/util/{ => ksp}/asType.kt | 0 .../generator/util/ksp/asTypeName.kt | 49 ++++++ .../util/{ => ksp}/findAnnotationWithType.kt | 0 .../{ => ksp}/getClassDeclarationByName.kt | 0 .../generator/util/{ => ksp}/getMember.kt | 2 +- .../generator/util/{ => ksp}/hasAnnotation.kt | 0 .../generator/util/{ => ksp}/toEnum.kt | 5 +- .../libquassel/generator/util/toClassName.kt | 42 ----- .../generator/visitors/KSDeclarationParser.kt | 97 +++++++++++ .../generator/visitors/KotlinSaver.kt | 33 ++++ .../generator/visitors/RpcModelProcessor.kt | 151 ++++++++++++++++++ .../libquassel/protocol/variant/QVariant.kt | 13 ++ .../variant/WrongVariantTypeException.kt | 12 +- .../exceptions/UnknownMethodException.kt | 11 +- .../exceptions/WrongObjectTypeException.kt | 9 +- .../libquassel/state/invoker/Invoker.kt | 21 +++ 28 files changed, 711 insertions(+), 192 deletions(-) create mode 100644 libquassel-generator/src/main/kotlin/de/justjanne/libquassel/generator/annotation/RpcFunctionAnnotation.kt create mode 100644 libquassel-generator/src/main/kotlin/de/justjanne/libquassel/generator/annotation/RpcObjectAnnotation.kt create mode 100644 libquassel-generator/src/main/kotlin/de/justjanne/libquassel/generator/kotlinmodel/KotlinModel.kt create mode 100644 libquassel-generator/src/main/kotlin/de/justjanne/libquassel/generator/kotlinmodel/KotlinModelVisitor.kt create mode 100644 libquassel-generator/src/main/kotlin/de/justjanne/libquassel/generator/rpcmodel/RpcModel.kt create mode 100644 libquassel-generator/src/main/kotlin/de/justjanne/libquassel/generator/rpcmodel/RpcModelVisitor.kt create mode 100644 libquassel-generator/src/main/kotlin/de/justjanne/libquassel/generator/util/kotlinpoet/ArgString.kt create mode 100644 libquassel-generator/src/main/kotlin/de/justjanne/libquassel/generator/util/kotlinpoet/WhenBlockBuilder.kt rename libquassel-generator/src/main/kotlin/de/justjanne/libquassel/generator/{model/SyncedCallModel.kt => util/kotlinpoet/buildWhen.kt} (54%) rename libquassel-generator/src/main/kotlin/de/justjanne/libquassel/generator/{annotation/SyncedCallAnnotationModel.kt => util/kotlinpoet/withIndent.kt} (61%) create mode 100644 libquassel-generator/src/main/kotlin/de/justjanne/libquassel/generator/util/ksp/asClassName.kt rename libquassel-generator/src/main/kotlin/de/justjanne/libquassel/generator/util/{ => ksp}/asType.kt (100%) create mode 100644 libquassel-generator/src/main/kotlin/de/justjanne/libquassel/generator/util/ksp/asTypeName.kt rename libquassel-generator/src/main/kotlin/de/justjanne/libquassel/generator/util/{ => ksp}/findAnnotationWithType.kt (100%) rename libquassel-generator/src/main/kotlin/de/justjanne/libquassel/generator/util/{ => ksp}/getClassDeclarationByName.kt (100%) rename libquassel-generator/src/main/kotlin/de/justjanne/libquassel/generator/util/{ => ksp}/getMember.kt (96%) rename libquassel-generator/src/main/kotlin/de/justjanne/libquassel/generator/util/{ => ksp}/hasAnnotation.kt (100%) rename libquassel-generator/src/main/kotlin/de/justjanne/libquassel/generator/util/{ => ksp}/toEnum.kt (86%) delete mode 100644 libquassel-generator/src/main/kotlin/de/justjanne/libquassel/generator/util/toClassName.kt create mode 100644 libquassel-generator/src/main/kotlin/de/justjanne/libquassel/generator/visitors/KSDeclarationParser.kt create mode 100644 libquassel-generator/src/main/kotlin/de/justjanne/libquassel/generator/visitors/KotlinSaver.kt create mode 100644 libquassel-generator/src/main/kotlin/de/justjanne/libquassel/generator/visitors/RpcModelProcessor.kt rename libquassel-generator/src/main/kotlin/de/justjanne/libquassel/generator/model/SyncedParameterModel.kt => libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/variant/WrongVariantTypeException.kt (60%) rename libquassel-generator/src/main/kotlin/de/justjanne/libquassel/generator/model/SyncedObjectModel.kt => libquassel-state/src/main/kotlin/de/justjanne/libquassel/state/exceptions/UnknownMethodException.kt (66%) rename libquassel-generator/src/main/kotlin/de/justjanne/libquassel/generator/annotation/SyncedObjectAnnotationModel.kt => libquassel-state/src/main/kotlin/de/justjanne/libquassel/state/exceptions/WrongObjectTypeException.kt (69%) create mode 100644 libquassel-state/src/main/kotlin/de/justjanne/libquassel/state/invoker/Invoker.kt diff --git a/libquassel-generator/src/main/kotlin/de/justjanne/libquassel/generator/InvokerProcessor.kt b/libquassel-generator/src/main/kotlin/de/justjanne/libquassel/generator/InvokerProcessor.kt index 9e3c1a2..9fa3bf9 100644 --- a/libquassel-generator/src/main/kotlin/de/justjanne/libquassel/generator/InvokerProcessor.kt +++ b/libquassel-generator/src/main/kotlin/de/justjanne/libquassel/generator/InvokerProcessor.kt @@ -10,28 +10,16 @@ package de.justjanne.libquassel.generator -import com.google.devtools.ksp.getDeclaredFunctions import com.google.devtools.ksp.processing.CodeGenerator import com.google.devtools.ksp.processing.KSPLogger import com.google.devtools.ksp.processing.Resolver import com.google.devtools.ksp.processing.SymbolProcessor import com.google.devtools.ksp.symbol.KSAnnotated -import com.google.devtools.ksp.symbol.KSClassDeclaration -import com.google.devtools.ksp.symbol.KSFunctionDeclaration -import com.google.devtools.ksp.symbol.KSType -import com.google.devtools.ksp.symbol.KSValueParameter -import com.google.devtools.ksp.symbol.KSVisitorVoid import de.justjanne.libquassel.annotations.ProtocolSide -import de.justjanne.libquassel.annotations.SyncedCall import de.justjanne.libquassel.annotations.SyncedObject -import de.justjanne.libquassel.generator.annotation.SyncedCallAnnotationModel -import de.justjanne.libquassel.generator.annotation.SyncedObjectAnnotationModel -import de.justjanne.libquassel.generator.model.SyncedCallModel -import de.justjanne.libquassel.generator.model.SyncedObjectModel -import de.justjanne.libquassel.generator.model.SyncedParameterModel -import de.justjanne.libquassel.generator.util.findAnnotationWithType -import de.justjanne.libquassel.generator.util.getMember -import de.justjanne.libquassel.generator.util.toEnum +import de.justjanne.libquassel.generator.visitors.KSDeclarationParser +import de.justjanne.libquassel.generator.visitors.KotlinSaver +import de.justjanne.libquassel.generator.visitors.RpcModelProcessor class InvokerProcessor : SymbolProcessor { lateinit var codeGenerator: CodeGenerator @@ -48,111 +36,18 @@ class InvokerProcessor : SymbolProcessor { } override fun process(resolver: Resolver): List<KSAnnotated> { - val classes = resolver.getSymbolsWithAnnotation(SyncedObject::class.java.canonicalName) + resolver.getSymbolsWithAnnotation(SyncedObject::class.java.canonicalName) + .mapNotNull { it.accept(KSDeclarationParser(resolver, logger), Unit) } .flatMap { - val visitor = QuasselRpcVisitor(resolver) - it.accept(visitor, Unit) - visitor.classes + listOfNotNull( + it.accept(RpcModelProcessor(), ProtocolSide.CLIENT), + it.accept(RpcModelProcessor(), ProtocolSide.CORE), + ) } - - val message = classes.flatMap { - listOf( - "@SyncedObject(name=${it.rpcName})", - it.name, - ) + it.methods.flatMap { - listOf( - "@SyncedCall(name=${it.rpcName}, target=${it.side})", - it.name, - ) + it.parameters.flatMap { - listOf( - "${it.name}: ${it.type}" - ) - }.map { " $it" } - }.map { " $it" } - }.joinToString("\n") - logger.logging("Data: \n\n$message\n\n") + .map { it.accept(KotlinSaver(), codeGenerator) } return emptyList() } - override fun finish() { - // TODO - } - - inner class QuasselRpcVisitor( - private val resolver: Resolver - ) : KSVisitorVoid() { - val classes = mutableListOf<SyncedObjectModel>() - var functions = mutableListOf<SyncedCallModel>() - var parameters = mutableListOf<SyncedParameterModel>() - - private fun collectFunctions(): List<SyncedCallModel> { - val result = functions - functions = mutableListOf() - return result - } - - private fun collectParameters(): List<SyncedParameterModel> { - val result = parameters - parameters = mutableListOf() - return result - } - - override fun visitClassDeclaration(classDeclaration: KSClassDeclaration, data: Unit) { - try { - val annotation = classDeclaration.findAnnotationWithType<SyncedObject>(resolver) ?: return - val syncedObjectModel = SyncedObjectAnnotationModel( - name = annotation.getMember("name"), - ) - classDeclaration.getDeclaredFunctions().map { it.accept(this, Unit) } - classes.add( - SyncedObjectModel( - classDeclaration.qualifiedName?.asString() ?: return, - syncedObjectModel.name, - collectFunctions() - ) - ) - } catch (t: Throwable) { - logger.error("Error processing class ${classDeclaration.qualifiedName?.asString()}", classDeclaration) - logger.exception(t) - } - } - - override fun visitFunctionDeclaration(function: KSFunctionDeclaration, data: Unit) { - val annotation = function.findAnnotationWithType<SyncedCall>(resolver) ?: return - try { - val syncedCallModel = SyncedCallAnnotationModel( - name = annotation.getMember("name"), - target = annotation.getMember<KSType>("target")?.toEnum<ProtocolSide>(), - ) - function.parameters.map { it.accept(this, Unit) } - functions.add( - SyncedCallModel( - function.simpleName.asString(), - syncedCallModel.name, - syncedCallModel.target, - collectParameters() - ) - ) - } catch (t: Throwable) { - logger.error("Error processing function ${function.qualifiedName?.asString()}", function) - logger.exception(t) - } - } - - override fun visitValueParameter(valueParameter: KSValueParameter, data: Unit) { - try { - super.visitValueParameter(valueParameter, data) - parameters.add( - SyncedParameterModel( - valueParameter.name?.asString(), - valueParameter.type.resolve() - ) - ) - } catch (t: Throwable) { - logger.error("Error processing parameter ${valueParameter.name?.asString()}", valueParameter) - logger.exception(t) - } - } - } + override fun finish() = Unit } diff --git a/libquassel-generator/src/main/kotlin/de/justjanne/libquassel/generator/annotation/RpcFunctionAnnotation.kt b/libquassel-generator/src/main/kotlin/de/justjanne/libquassel/generator/annotation/RpcFunctionAnnotation.kt new file mode 100644 index 0000000..1f1f64f --- /dev/null +++ b/libquassel-generator/src/main/kotlin/de/justjanne/libquassel/generator/annotation/RpcFunctionAnnotation.kt @@ -0,0 +1,37 @@ +/* + * libquassel + * Copyright (c) 2021 Janne Mareike Koschinski + * Copyright (c) 2021 The Quassel Project + * + * This Source Code Form is subject to the terms of the Mozilla Public License, + * v. 2.0. If a copy of the MPL was not distributed with this file, You can + * obtain one at https://mozilla.org/MPL/2.0/. + */ + +package de.justjanne.libquassel.generator.annotation + +import com.google.devtools.ksp.processing.Resolver +import com.google.devtools.ksp.symbol.KSAnnotated +import com.google.devtools.ksp.symbol.KSType +import de.justjanne.libquassel.annotations.ProtocolSide +import de.justjanne.libquassel.annotations.SyncedCall +import de.justjanne.libquassel.generator.util.findAnnotationWithType +import de.justjanne.libquassel.generator.util.getMember +import de.justjanne.libquassel.generator.util.toEnum + +data class RpcFunctionAnnotation( + val name: String?, + val target: ProtocolSide? +) { + companion object { + fun of(it: KSAnnotated, resolver: Resolver): RpcFunctionAnnotation? { + val annotation = it.findAnnotationWithType<SyncedCall>(resolver) + ?: return null + return RpcFunctionAnnotation( + name = annotation.getMember("name"), + target = annotation.getMember<KSType>("target") + ?.toEnum<ProtocolSide>(), + ) + } + } +} diff --git a/libquassel-generator/src/main/kotlin/de/justjanne/libquassel/generator/annotation/RpcObjectAnnotation.kt b/libquassel-generator/src/main/kotlin/de/justjanne/libquassel/generator/annotation/RpcObjectAnnotation.kt new file mode 100644 index 0000000..8a03ba5 --- /dev/null +++ b/libquassel-generator/src/main/kotlin/de/justjanne/libquassel/generator/annotation/RpcObjectAnnotation.kt @@ -0,0 +1,31 @@ +/* + * libquassel + * Copyright (c) 2021 Janne Mareike Koschinski + * Copyright (c) 2021 The Quassel Project + * + * This Source Code Form is subject to the terms of the Mozilla Public License, + * v. 2.0. If a copy of the MPL was not distributed with this file, You can + * obtain one at https://mozilla.org/MPL/2.0/. + */ + +package de.justjanne.libquassel.generator.annotation + +import com.google.devtools.ksp.processing.Resolver +import com.google.devtools.ksp.symbol.KSAnnotated +import de.justjanne.libquassel.annotations.SyncedObject +import de.justjanne.libquassel.generator.util.findAnnotationWithType +import de.justjanne.libquassel.generator.util.getMember + +data class RpcObjectAnnotation( + val name: String? +) { + companion object { + fun of(it: KSAnnotated, resolver: Resolver): RpcObjectAnnotation? { + val annotation = it.findAnnotationWithType<SyncedObject>(resolver) + ?: return null + return RpcObjectAnnotation( + name = annotation.getMember("name"), + ) + } + } +} diff --git a/libquassel-generator/src/main/kotlin/de/justjanne/libquassel/generator/kotlinmodel/KotlinModel.kt b/libquassel-generator/src/main/kotlin/de/justjanne/libquassel/generator/kotlinmodel/KotlinModel.kt new file mode 100644 index 0000000..56ab968 --- /dev/null +++ b/libquassel-generator/src/main/kotlin/de/justjanne/libquassel/generator/kotlinmodel/KotlinModel.kt @@ -0,0 +1,36 @@ +/* + * libquassel + * Copyright (c) 2021 Janne Mareike Koschinski + * Copyright (c) 2021 The Quassel Project + * + * This Source Code Form is subject to the terms of the Mozilla Public License, + * v. 2.0. If a copy of the MPL was not distributed with this file, You can + * obtain one at https://mozilla.org/MPL/2.0/. + */ + +package de.justjanne.libquassel.generator.kotlinmodel + +import com.google.devtools.ksp.symbol.KSClassDeclaration +import com.google.devtools.ksp.symbol.KSFunctionDeclaration +import com.squareup.kotlinpoet.CodeBlock +import com.squareup.kotlinpoet.FileSpec + +sealed class KotlinModel { + data class FileModel( + val source: KSClassDeclaration, + val data: FileSpec + ) : KotlinModel() { + override fun <D, R> accept(visitor: KotlinModelVisitor<D, R>, data: D) = + visitor.visitFileModel(this, data) + } + + data class FunctionModel( + val source: KSFunctionDeclaration, + val data: CodeBlock + ) : KotlinModel() { + override fun <D, R> accept(visitor: KotlinModelVisitor<D, R>, data: D) = + visitor.visitFunctionModel(this, data) + } + + abstract fun <D, R> accept(visitor: KotlinModelVisitor<D, R>, data: D): R +} diff --git a/libquassel-generator/src/main/kotlin/de/justjanne/libquassel/generator/kotlinmodel/KotlinModelVisitor.kt b/libquassel-generator/src/main/kotlin/de/justjanne/libquassel/generator/kotlinmodel/KotlinModelVisitor.kt new file mode 100644 index 0000000..3c95092 --- /dev/null +++ b/libquassel-generator/src/main/kotlin/de/justjanne/libquassel/generator/kotlinmodel/KotlinModelVisitor.kt @@ -0,0 +1,16 @@ +/* + * libquassel + * Copyright (c) 2021 Janne Mareike Koschinski + * Copyright (c) 2021 The Quassel Project + * + * This Source Code Form is subject to the terms of the Mozilla Public License, + * v. 2.0. If a copy of the MPL was not distributed with this file, You can + * obtain one at https://mozilla.org/MPL/2.0/. + */ + +package de.justjanne.libquassel.generator.kotlinmodel + +interface KotlinModelVisitor<D, R> { + fun visitFileModel(model: KotlinModel.FileModel, data: D): R + fun visitFunctionModel(model: KotlinModel.FunctionModel, data: D): R +} diff --git a/libquassel-generator/src/main/kotlin/de/justjanne/libquassel/generator/rpcmodel/RpcModel.kt b/libquassel-generator/src/main/kotlin/de/justjanne/libquassel/generator/rpcmodel/RpcModel.kt new file mode 100644 index 0000000..51ee73a --- /dev/null +++ b/libquassel-generator/src/main/kotlin/de/justjanne/libquassel/generator/rpcmodel/RpcModel.kt @@ -0,0 +1,52 @@ +/* + * libquassel + * Copyright (c) 2021 Janne Mareike Koschinski + * Copyright (c) 2021 The Quassel Project + * + * This Source Code Form is subject to the terms of the Mozilla Public License, + * v. 2.0. If a copy of the MPL was not distributed with this file, You can + * obtain one at https://mozilla.org/MPL/2.0/. + */ + +package de.justjanne.libquassel.generator.rpcmodel + +import com.google.devtools.ksp.symbol.KSClassDeclaration +import com.google.devtools.ksp.symbol.KSFunctionDeclaration +import com.google.devtools.ksp.symbol.KSValueParameter +import com.squareup.kotlinpoet.ClassName +import com.squareup.kotlinpoet.TypeName +import de.justjanne.libquassel.annotations.ProtocolSide + +sealed class RpcModel { + data class ObjectModel( + val source: KSClassDeclaration, + val name: ClassName, + val rpcName: String?, + val methods: List<FunctionModel> + ) : RpcModel() { + override fun <D, R> accept(visitor: RpcModelVisitor<D, R>, data: D) = + visitor.visitObjectModel(this, data) + } + + data class FunctionModel( + val source: KSFunctionDeclaration, + val name: String, + val rpcName: String?, + val side: ProtocolSide?, + val parameters: List<ParameterModel> + ) : RpcModel() { + override fun <D, R> accept(visitor: RpcModelVisitor<D, R>, data: D) = + visitor.visitFunctionModel(this, data) + } + + data class ParameterModel( + val source: KSValueParameter, + val name: String?, + val type: TypeName + ) : RpcModel() { + override fun <D, R> accept(visitor: RpcModelVisitor<D, R>, data: D) = + visitor.visitParameterModel(this, data) + } + + abstract fun <D, R> accept(visitor: RpcModelVisitor<D, R>, data: D): R +} diff --git a/libquassel-generator/src/main/kotlin/de/justjanne/libquassel/generator/rpcmodel/RpcModelVisitor.kt b/libquassel-generator/src/main/kotlin/de/justjanne/libquassel/generator/rpcmodel/RpcModelVisitor.kt new file mode 100644 index 0000000..f4b06f2 --- /dev/null +++ b/libquassel-generator/src/main/kotlin/de/justjanne/libquassel/generator/rpcmodel/RpcModelVisitor.kt @@ -0,0 +1,17 @@ +/* + * libquassel + * Copyright (c) 2021 Janne Mareike Koschinski + * Copyright (c) 2021 The Quassel Project + * + * This Source Code Form is subject to the terms of the Mozilla Public License, + * v. 2.0. If a copy of the MPL was not distributed with this file, You can + * obtain one at https://mozilla.org/MPL/2.0/. + */ + +package de.justjanne.libquassel.generator.rpcmodel + +interface RpcModelVisitor<D, R> { + fun visitObjectModel(model: RpcModel.ObjectModel, data: D): R + fun visitFunctionModel(model: RpcModel.FunctionModel, data: D): R + fun visitParameterModel(model: RpcModel.ParameterModel, data: D): R +} diff --git a/libquassel-generator/src/main/kotlin/de/justjanne/libquassel/generator/util/kotlinpoet/ArgString.kt b/libquassel-generator/src/main/kotlin/de/justjanne/libquassel/generator/util/kotlinpoet/ArgString.kt new file mode 100644 index 0000000..639582b --- /dev/null +++ b/libquassel-generator/src/main/kotlin/de/justjanne/libquassel/generator/util/kotlinpoet/ArgString.kt @@ -0,0 +1,38 @@ +/* + * libquassel + * Copyright (c) 2021 Janne Mareike Koschinski + * Copyright (c) 2021 The Quassel Project + * + * This Source Code Form is subject to the terms of the Mozilla Public License, + * v. 2.0. If a copy of the MPL was not distributed with this file, You can + * obtain one at https://mozilla.org/MPL/2.0/. + */ + +package de.justjanne.libquassel.generator.util.kotlinpoet + +class ArgString constructor( + val name: String, + vararg val args: Any? +) { + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as ArgString + + if (name != other.name) return false + if (!args.contentEquals(other.args)) return false + + return true + } + + override fun hashCode(): Int { + var result = name.hashCode() + result = 31 * result + args.contentHashCode() + return result + } + + override fun toString(): String { + return "ArgString(name='$name', args=${args.contentToString()})" + } +} diff --git a/libquassel-generator/src/main/kotlin/de/justjanne/libquassel/generator/util/kotlinpoet/WhenBlockBuilder.kt b/libquassel-generator/src/main/kotlin/de/justjanne/libquassel/generator/util/kotlinpoet/WhenBlockBuilder.kt new file mode 100644 index 0000000..a889e2e --- /dev/null +++ b/libquassel-generator/src/main/kotlin/de/justjanne/libquassel/generator/util/kotlinpoet/WhenBlockBuilder.kt @@ -0,0 +1,44 @@ +/* + * libquassel + * Copyright (c) 2021 Janne Mareike Koschinski + * Copyright (c) 2021 The Quassel Project + * + * This Source Code Form is subject to the terms of the Mozilla Public License, + * v. 2.0. If a copy of the MPL was not distributed with this file, You can + * obtain one at https://mozilla.org/MPL/2.0/. + */ + +package de.justjanne.libquassel.generator.util.kotlinpoet + +import com.squareup.kotlinpoet.CodeBlock +import com.squareup.kotlinpoet.buildCodeBlock + +class WhenBlockBuilder constructor( + private val over: ArgString +) { + private val cases = mutableListOf<Pair<ArgString, CodeBlock>>() + + constructor(name: String, vararg args: Any?) : this(ArgString(name, args)) + + fun addCase(condition: ArgString, block: CodeBlock) { + cases.add(Pair(condition, block)) + } + + fun build(): CodeBlock = buildCodeBlock { + beginControlFlow("when (${over.name})", over.args) + for ((condition, code) in cases) { + beginControlFlow("${condition.name} ->", *condition.args) + add(code) + endControlFlow() + } + endControlFlow() + } + + inline fun addCase(name: String, vararg args: Any?, f: CodeBlock.Builder.() -> Unit) { + addCase(ArgString(name, args), buildCodeBlock(f)) + } + + inline fun buildElse(f: CodeBlock.Builder.() -> Unit) { + addCase(ArgString("else"), buildCodeBlock(f)) + } +} diff --git a/libquassel-generator/src/main/kotlin/de/justjanne/libquassel/generator/model/SyncedCallModel.kt b/libquassel-generator/src/main/kotlin/de/justjanne/libquassel/generator/util/kotlinpoet/buildWhen.kt similarity index 54% rename from libquassel-generator/src/main/kotlin/de/justjanne/libquassel/generator/model/SyncedCallModel.kt rename to libquassel-generator/src/main/kotlin/de/justjanne/libquassel/generator/util/kotlinpoet/buildWhen.kt index e55a86e..888c06c 100644 --- a/libquassel-generator/src/main/kotlin/de/justjanne/libquassel/generator/model/SyncedCallModel.kt +++ b/libquassel-generator/src/main/kotlin/de/justjanne/libquassel/generator/util/kotlinpoet/buildWhen.kt @@ -8,13 +8,10 @@ * obtain one at https://mozilla.org/MPL/2.0/. */ -package de.justjanne.libquassel.generator.model +package de.justjanne.libquassel.generator.util.kotlinpoet -import de.justjanne.libquassel.annotations.ProtocolSide +import com.squareup.kotlinpoet.CodeBlock -data class SyncedCallModel( - val name: String, - val rpcName: String?, - val side: ProtocolSide?, - val parameters: List<SyncedParameterModel> -) +inline fun CodeBlock.Builder.buildWhen(name: String, vararg args: Any?, f: WhenBlockBuilder.() -> Unit) { + this.add(WhenBlockBuilder(name, args).apply(f).build()) +} diff --git a/libquassel-generator/src/main/kotlin/de/justjanne/libquassel/generator/annotation/SyncedCallAnnotationModel.kt b/libquassel-generator/src/main/kotlin/de/justjanne/libquassel/generator/util/kotlinpoet/withIndent.kt similarity index 61% rename from libquassel-generator/src/main/kotlin/de/justjanne/libquassel/generator/annotation/SyncedCallAnnotationModel.kt rename to libquassel-generator/src/main/kotlin/de/justjanne/libquassel/generator/util/kotlinpoet/withIndent.kt index 536fb79..b5e3d67 100644 --- a/libquassel-generator/src/main/kotlin/de/justjanne/libquassel/generator/annotation/SyncedCallAnnotationModel.kt +++ b/libquassel-generator/src/main/kotlin/de/justjanne/libquassel/generator/util/kotlinpoet/withIndent.kt @@ -8,11 +8,12 @@ * obtain one at https://mozilla.org/MPL/2.0/. */ -package de.justjanne.libquassel.generator.annotation +package de.justjanne.libquassel.generator.util.kotlinpoet -import de.justjanne.libquassel.annotations.ProtocolSide +import com.squareup.kotlinpoet.CodeBlock -data class SyncedCallAnnotationModel( - val name: String?, - val target: ProtocolSide? -) +fun CodeBlock.Builder.withIndent(f: CodeBlock.Builder.() -> Unit) { + indent() + f() + unindent() +} diff --git a/libquassel-generator/src/main/kotlin/de/justjanne/libquassel/generator/util/ksp/asClassName.kt b/libquassel-generator/src/main/kotlin/de/justjanne/libquassel/generator/util/ksp/asClassName.kt new file mode 100644 index 0000000..edae35d --- /dev/null +++ b/libquassel-generator/src/main/kotlin/de/justjanne/libquassel/generator/util/ksp/asClassName.kt @@ -0,0 +1,34 @@ +/* + * libquassel + * Copyright (c) 2021 Janne Mareike Koschinski + * Copyright (c) 2021 The Quassel Project + * + * This Source Code Form is subject to the terms of the Mozilla Public License, + * v. 2.0. If a copy of the MPL was not distributed with this file, You can + * obtain one at https://mozilla.org/MPL/2.0/. + */ + +package de.justjanne.libquassel.generator.util + +import com.google.devtools.ksp.symbol.KSDeclaration +import com.google.devtools.ksp.symbol.KSType +import com.squareup.kotlinpoet.ClassName + +private fun KSDeclaration.parents(): List<KSDeclaration> { + val declarations = mutableListOf(this) + var parent = this.parentDeclaration + while (parent != null) { + declarations.add(parent) + parent = parent.parentDeclaration + } + return declarations.reversed() +} + +fun KSDeclaration.asClassName(): ClassName { + return ClassName( + packageName.asString(), + parents().map { it.simpleName.asString() } + ) +} + +fun KSType.asClassName(): ClassName = declaration.asClassName() diff --git a/libquassel-generator/src/main/kotlin/de/justjanne/libquassel/generator/util/asType.kt b/libquassel-generator/src/main/kotlin/de/justjanne/libquassel/generator/util/ksp/asType.kt similarity index 100% rename from libquassel-generator/src/main/kotlin/de/justjanne/libquassel/generator/util/asType.kt rename to libquassel-generator/src/main/kotlin/de/justjanne/libquassel/generator/util/ksp/asType.kt diff --git a/libquassel-generator/src/main/kotlin/de/justjanne/libquassel/generator/util/ksp/asTypeName.kt b/libquassel-generator/src/main/kotlin/de/justjanne/libquassel/generator/util/ksp/asTypeName.kt new file mode 100644 index 0000000..450c83e --- /dev/null +++ b/libquassel-generator/src/main/kotlin/de/justjanne/libquassel/generator/util/ksp/asTypeName.kt @@ -0,0 +1,49 @@ +/* + * libquassel + * Copyright (c) 2021 Janne Mareike Koschinski + * Copyright (c) 2021 The Quassel Project + * + * This Source Code Form is subject to the terms of the Mozilla Public License, + * v. 2.0. If a copy of the MPL was not distributed with this file, You can + * obtain one at https://mozilla.org/MPL/2.0/. + */ + +package de.justjanne.libquassel.generator.util + +import com.google.devtools.ksp.symbol.KSDeclaration +import com.google.devtools.ksp.symbol.KSType +import com.google.devtools.ksp.symbol.Variance +import com.squareup.kotlinpoet.ClassName +import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy +import com.squareup.kotlinpoet.TypeName +import com.squareup.kotlinpoet.WildcardTypeName + +fun KSDeclaration.asTypeName(): TypeName = + ClassName(packageName.asString(), simpleName.asString()) + +fun KSType.asTypeName(): TypeName { + val baseType = asClassName() + if (arguments.isEmpty()) { + return baseType + } + + val parameters = arguments.map { + val type = it.type?.resolve() + when (it.variance) { + Variance.STAR -> + WildcardTypeName.producerOf(Any::class) + .copy(nullable = true) + Variance.INVARIANT -> + type!!.asTypeName() + .copy(nullable = type.isMarkedNullable) + Variance.COVARIANT -> + WildcardTypeName.producerOf(type!!.asTypeName()) + .copy(nullable = type.isMarkedNullable) + Variance.CONTRAVARIANT -> + WildcardTypeName.consumerOf(type!!.asTypeName()) + .copy(nullable = type.isMarkedNullable) + } + } + + return baseType.parameterizedBy(parameters) +} diff --git a/libquassel-generator/src/main/kotlin/de/justjanne/libquassel/generator/util/findAnnotationWithType.kt b/libquassel-generator/src/main/kotlin/de/justjanne/libquassel/generator/util/ksp/findAnnotationWithType.kt similarity index 100% rename from libquassel-generator/src/main/kotlin/de/justjanne/libquassel/generator/util/findAnnotationWithType.kt rename to libquassel-generator/src/main/kotlin/de/justjanne/libquassel/generator/util/ksp/findAnnotationWithType.kt diff --git a/libquassel-generator/src/main/kotlin/de/justjanne/libquassel/generator/util/getClassDeclarationByName.kt b/libquassel-generator/src/main/kotlin/de/justjanne/libquassel/generator/util/ksp/getClassDeclarationByName.kt similarity index 100% rename from libquassel-generator/src/main/kotlin/de/justjanne/libquassel/generator/util/getClassDeclarationByName.kt rename to libquassel-generator/src/main/kotlin/de/justjanne/libquassel/generator/util/ksp/getClassDeclarationByName.kt diff --git a/libquassel-generator/src/main/kotlin/de/justjanne/libquassel/generator/util/getMember.kt b/libquassel-generator/src/main/kotlin/de/justjanne/libquassel/generator/util/ksp/getMember.kt similarity index 96% rename from libquassel-generator/src/main/kotlin/de/justjanne/libquassel/generator/util/getMember.kt rename to libquassel-generator/src/main/kotlin/de/justjanne/libquassel/generator/util/ksp/getMember.kt index 520994a..953a710 100644 --- a/libquassel-generator/src/main/kotlin/de/justjanne/libquassel/generator/util/getMember.kt +++ b/libquassel-generator/src/main/kotlin/de/justjanne/libquassel/generator/util/ksp/getMember.kt @@ -30,7 +30,7 @@ internal inline fun <reified T> KSAnnotation.getMember(name: String): T? { is KSType -> when { T::class.java != ClassName::class.java -> null - else -> argValue.toClassName() as T + else -> argValue.asTypeName() as T } else -> null } diff --git a/libquassel-generator/src/main/kotlin/de/justjanne/libquassel/generator/util/hasAnnotation.kt b/libquassel-generator/src/main/kotlin/de/justjanne/libquassel/generator/util/ksp/hasAnnotation.kt similarity index 100% rename from libquassel-generator/src/main/kotlin/de/justjanne/libquassel/generator/util/hasAnnotation.kt rename to libquassel-generator/src/main/kotlin/de/justjanne/libquassel/generator/util/ksp/hasAnnotation.kt diff --git a/libquassel-generator/src/main/kotlin/de/justjanne/libquassel/generator/util/toEnum.kt b/libquassel-generator/src/main/kotlin/de/justjanne/libquassel/generator/util/ksp/toEnum.kt similarity index 86% rename from libquassel-generator/src/main/kotlin/de/justjanne/libquassel/generator/util/toEnum.kt rename to libquassel-generator/src/main/kotlin/de/justjanne/libquassel/generator/util/ksp/toEnum.kt index 115a1f7..dea5dea 100644 --- a/libquassel-generator/src/main/kotlin/de/justjanne/libquassel/generator/util/toEnum.kt +++ b/libquassel-generator/src/main/kotlin/de/justjanne/libquassel/generator/util/ksp/toEnum.kt @@ -12,9 +12,10 @@ package de.justjanne.libquassel.generator.util import com.google.devtools.ksp.symbol.KSType import com.squareup.kotlinpoet.ClassName +import com.squareup.kotlinpoet.asClassName internal inline fun <reified T : Enum<T>> KSType.toEnum(): T? { - return toClassName().toEnum(T::class.java) + return asClassName().toEnum(T::class.java) } internal inline fun <reified T : Enum<T>> ClassName.toEnum(): T? { @@ -22,7 +23,7 @@ internal inline fun <reified T : Enum<T>> ClassName.toEnum(): T? { } internal fun <T : Enum<T>> ClassName.toEnum(clazz: Class<T>): T? { - val enumClassName = clazz.toClassName() + val enumClassName = clazz.asClassName() return clazz.enumConstants.find { this.canonicalName == enumClassName.nestedClass(it.name).canonicalName } diff --git a/libquassel-generator/src/main/kotlin/de/justjanne/libquassel/generator/util/toClassName.kt b/libquassel-generator/src/main/kotlin/de/justjanne/libquassel/generator/util/toClassName.kt deleted file mode 100644 index 4542f82..0000000 --- a/libquassel-generator/src/main/kotlin/de/justjanne/libquassel/generator/util/toClassName.kt +++ /dev/null @@ -1,42 +0,0 @@ -/* - * libquassel - * Copyright (c) 2021 Janne Mareike Koschinski - * Copyright (c) 2021 The Quassel Project - * - * This Source Code Form is subject to the terms of the Mozilla Public License, - * v. 2.0. If a copy of the MPL was not distributed with this file, You can - * obtain one at https://mozilla.org/MPL/2.0/. - */ - -package de.justjanne.libquassel.generator.util - -import com.google.devtools.ksp.isLocal -import com.google.devtools.ksp.symbol.KSClassDeclaration -import com.google.devtools.ksp.symbol.KSType -import com.squareup.kotlinpoet.ClassName - -internal fun KSType.toClassName(): ClassName { - val decl = declaration - check(decl is KSClassDeclaration) - return decl.toClassName() -} - -internal fun KSClassDeclaration.toClassName(): ClassName { - require(!isLocal()) { - "Local/anonymous classes are not supported!" - } - val pkgName = packageName.asString() - val typesString = qualifiedName!!.asString().removePrefix("$pkgName.") - - val simpleNames = typesString - .split(".") - return ClassName(pkgName, simpleNames) -} - -fun Class<*>.toClassName(): ClassName { - val packageName = `package`.name - val names = canonicalName.substring(packageName.length) - .trimStart('.') - .split('.') - return ClassName(packageName, names) -} diff --git a/libquassel-generator/src/main/kotlin/de/justjanne/libquassel/generator/visitors/KSDeclarationParser.kt b/libquassel-generator/src/main/kotlin/de/justjanne/libquassel/generator/visitors/KSDeclarationParser.kt new file mode 100644 index 0000000..8eb0326 --- /dev/null +++ b/libquassel-generator/src/main/kotlin/de/justjanne/libquassel/generator/visitors/KSDeclarationParser.kt @@ -0,0 +1,97 @@ +/* + * libquassel + * Copyright (c) 2021 Janne Mareike Koschinski + * Copyright (c) 2021 The Quassel Project + * + * This Source Code Form is subject to the terms of the Mozilla Public License, + * v. 2.0. If a copy of the MPL was not distributed with this file, You can + * obtain one at https://mozilla.org/MPL/2.0/. + */ + +package de.justjanne.libquassel.generator.visitors + +import com.google.devtools.ksp.getDeclaredFunctions +import com.google.devtools.ksp.processing.KSPLogger +import com.google.devtools.ksp.processing.Resolver +import com.google.devtools.ksp.symbol.KSClassDeclaration +import com.google.devtools.ksp.symbol.KSFunctionDeclaration +import com.google.devtools.ksp.symbol.KSNode +import com.google.devtools.ksp.symbol.KSValueParameter +import com.google.devtools.ksp.visitor.KSEmptyVisitor +import com.squareup.kotlinpoet.ClassName +import de.justjanne.libquassel.generator.annotation.RpcFunctionAnnotation +import de.justjanne.libquassel.generator.annotation.RpcObjectAnnotation +import de.justjanne.libquassel.generator.rpcmodel.RpcModel +import de.justjanne.libquassel.generator.util.asTypeName + +class KSDeclarationParser( + private val resolver: Resolver, + private val logger: KSPLogger +) : KSEmptyVisitor<Unit, RpcModel?>() { + override fun visitClassDeclaration( + classDeclaration: KSClassDeclaration, + data: Unit + ): RpcModel.ObjectModel? { + val annotation = RpcObjectAnnotation.of(classDeclaration, resolver) + ?: return null + try { + return RpcModel.ObjectModel( + classDeclaration, + ClassName( + classDeclaration.packageName.asString(), + classDeclaration.simpleName.asString() + ), + annotation.name, + classDeclaration.getDeclaredFunctions() + .mapNotNull { it.accept(this, Unit) } + .mapNotNull { it as? RpcModel.FunctionModel } + ) + } catch (t: Throwable) { + logger.error("Error processing ${annotation.name}", classDeclaration) + logger.exception(t) + throw t + } + } + + override fun visitFunctionDeclaration( + function: KSFunctionDeclaration, + data: Unit + ): RpcModel.FunctionModel? { + val annotation = RpcFunctionAnnotation.of(function, resolver) + ?: return null + try { + return RpcModel.FunctionModel( + function, + function.simpleName.asString(), + annotation.name, + annotation.target, + function.parameters + .mapNotNull { it.accept(this, Unit) } + .mapNotNull { it as? RpcModel.ParameterModel } + ) + } catch (t: Throwable) { + logger.error("Error processing ${annotation.name ?: function.simpleName.asString()}", function) + logger.exception(t) + throw t + } + } + + override fun visitValueParameter( + valueParameter: KSValueParameter, + data: Unit + ): RpcModel.ParameterModel { + try { + return RpcModel.ParameterModel( + valueParameter, + valueParameter.name?.asString(), + valueParameter.type.resolve().asTypeName() + ) + } catch (t: Throwable) { + logger.error("Error processing ${valueParameter.name?.asString()}", valueParameter) + logger.exception(t) + throw t + } + } + + override fun defaultHandler(node: KSNode, data: Unit): RpcModel? = null +} diff --git a/libquassel-generator/src/main/kotlin/de/justjanne/libquassel/generator/visitors/KotlinSaver.kt b/libquassel-generator/src/main/kotlin/de/justjanne/libquassel/generator/visitors/KotlinSaver.kt new file mode 100644 index 0000000..29bfc2c --- /dev/null +++ b/libquassel-generator/src/main/kotlin/de/justjanne/libquassel/generator/visitors/KotlinSaver.kt @@ -0,0 +1,33 @@ +/* + * libquassel + * Copyright (c) 2021 Janne Mareike Koschinski + * Copyright (c) 2021 The Quassel Project + * + * This Source Code Form is subject to the terms of the Mozilla Public License, + * v. 2.0. If a copy of the MPL was not distributed with this file, You can + * obtain one at https://mozilla.org/MPL/2.0/. + */ + +package de.justjanne.libquassel.generator.visitors + +import com.google.devtools.ksp.processing.CodeGenerator +import com.google.devtools.ksp.processing.Dependencies +import de.justjanne.libquassel.generator.kotlinmodel.KotlinModel +import de.justjanne.libquassel.generator.kotlinmodel.KotlinModelVisitor + +class KotlinSaver : KotlinModelVisitor<CodeGenerator, Unit> { + override fun visitFileModel(model: KotlinModel.FileModel, data: CodeGenerator) { + data.createNewFile( + Dependencies(false, model.source.containingFile!!), + model.data.packageName, + model.data.name + ).bufferedWriter(Charsets.UTF_8).use { + model.data.writeTo(it) + } + } + + override fun visitFunctionModel( + model: KotlinModel.FunctionModel, + data: CodeGenerator + ) = Unit +} diff --git a/libquassel-generator/src/main/kotlin/de/justjanne/libquassel/generator/visitors/RpcModelProcessor.kt b/libquassel-generator/src/main/kotlin/de/justjanne/libquassel/generator/visitors/RpcModelProcessor.kt new file mode 100644 index 0000000..04d9807 --- /dev/null +++ b/libquassel-generator/src/main/kotlin/de/justjanne/libquassel/generator/visitors/RpcModelProcessor.kt @@ -0,0 +1,151 @@ +/* + * libquassel + * Copyright (c) 2021 Janne Mareike Koschinski + * Copyright (c) 2021 The Quassel Project + * + * This Source Code Form is subject to the terms of the Mozilla Public License, + * v. 2.0. If a copy of the MPL was not distributed with this file, You can + * obtain one at https://mozilla.org/MPL/2.0/. + */ + +package de.justjanne.libquassel.generator.visitors + +import com.squareup.kotlinpoet.ANY +import com.squareup.kotlinpoet.ClassName +import com.squareup.kotlinpoet.FileSpec +import com.squareup.kotlinpoet.FunSpec +import com.squareup.kotlinpoet.KModifier +import com.squareup.kotlinpoet.ParameterSpec +import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy +import com.squareup.kotlinpoet.PropertySpec +import com.squareup.kotlinpoet.TypeSpec +import com.squareup.kotlinpoet.asTypeName +import com.squareup.kotlinpoet.buildCodeBlock +import de.justjanne.libquassel.annotations.ProtocolSide +import de.justjanne.libquassel.generator.kotlinmodel.KotlinModel +import de.justjanne.libquassel.generator.rpcmodel.RpcModel +import de.justjanne.libquassel.generator.rpcmodel.RpcModelVisitor +import de.justjanne.libquassel.generator.util.kotlinpoet.ArgString +import de.justjanne.libquassel.generator.util.kotlinpoet.buildWhen +import de.justjanne.libquassel.generator.util.kotlinpoet.withIndent + +class RpcModelProcessor : RpcModelVisitor<ProtocolSide, KotlinModel?> { + override fun visitObjectModel(model: RpcModel.ObjectModel, data: ProtocolSide): KotlinModel { + val name = ClassName( + TYPENAME_INVOKER.packageName, + "${model.rpcName}${data.name.toLowerCase().capitalize()}Invoker" + ) + return KotlinModel.FileModel( + model.source, + FileSpec.builder(name.packageName, name.simpleName) + .addImport( + TYPENAME_QVARIANT_INTOORTHROW.packageName, + TYPENAME_QVARIANT_INTOORTHROW.simpleName + ) + .addType( + TypeSpec.objectBuilder(name.simpleName) + .addSuperinterface(TYPENAME_INVOKER.parameterizedBy(model.name)) + .addProperty( + PropertySpec.builder( + "className", + String::class.asTypeName(), + KModifier.OVERRIDE + ).initializer("\"${model.rpcName}\"").build() + ) + .addFunction( + FunSpec.builder("invoke") + .addModifiers(KModifier.OVERRIDE, KModifier.OPERATOR) + .addParameter( + ParameterSpec.builder( + "on", + TYPENAME_ANY + ).build() + ).addParameter( + ParameterSpec.builder( + "method", + String::class.asTypeName() + ).build() + ).addParameter( + ParameterSpec.builder( + "params", + TYPENAME_QVARIANTLIST + ).build() + ) + .addCode( + buildCodeBlock { + beginControlFlow("if (on is %T)", model.name) + buildWhen("method") { + for (method in model.methods) { + val block = method.accept(this@RpcModelProcessor, data) + as? KotlinModel.FunctionModel + ?: continue + addCase(ArgString("%S", method.rpcName ?: method.name), block.data) + } + buildElse { + addStatement("throw %T(className, method)", TYPENAME_UNKNOWN_METHOD_EXCEPTION) + } + } + nextControlFlow("else") + addStatement("throw %T(on, className)", TYPENAME_WRONG_OBJECT_TYPE_EXCEPTION) + endControlFlow() + } + ) + .build() + ) + .build() + ).build() + ) + } + + override fun visitFunctionModel(model: RpcModel.FunctionModel, data: ProtocolSide) = KotlinModel.FunctionModel( + model.source, + buildCodeBlock { + if (model.parameters.isEmpty()) { + addStatement("on.${model.name}()") + } else { + addStatement("on.${model.name}(") + withIndent { + val lastIndex = model.parameters.size - 1 + for ((i, parameter) in model.parameters.withIndex()) { + val suffix = if (i != lastIndex) "," else "" + addStatement( + "${parameter.name} = params[$i].intoOrThrow<%T>()$suffix", + parameter.type + ) + } + } + addStatement(")") + } + } + ) + + override fun visitParameterModel(model: RpcModel.ParameterModel, data: ProtocolSide): KotlinModel? = null + + companion object { + private val TYPENAME_INVOKER = ClassName( + "de.justjanne.libquassel.state.invoker", + "Invoker" + ) + private val TYPENAME_UNKNOWN_METHOD_EXCEPTION = ClassName( + "de.justjanne.libquassel.state.exceptions", + "UnknownMethodException" + ) + private val TYPENAME_WRONG_OBJECT_TYPE_EXCEPTION = ClassName( + "de.justjanne.libquassel.state.exceptions", + "WrongObjectTypeException" + ) + private val TYPENAME_QVARIANTLIST = ClassName( + "de.justjanne.libquassel.protocol.variant", + "QVariantList" + ) + private val TYPENAME_QVARIANT_INTOORTHROW = ClassName( + "de.justjanne.libquassel.protocol.variant", + "intoOrThrow" + ) + private val TYPENAME_ANY = ANY.copy(nullable = true) + + init { + System.setProperty("idea.io.use.nio2", "true") + } + } +} diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/variant/QVariant.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/variant/QVariant.kt index d25c661..ce654bf 100644 --- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/variant/QVariant.kt +++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/variant/QVariant.kt @@ -107,6 +107,8 @@ sealed class QVariant<T> { internal inline fun <reified U> withType(): QVariant<U>? = withType(U::class.java) + fun type(): Class<*>? = data?.let { it::class.java } + internal fun serialize(buffer: ChainedByteBuffer, featureSet: FeatureSet) = serializer.serialize(buffer, data, featureSet) } @@ -130,6 +132,17 @@ inline fun <reified T> qVariant(data: T, type: QuasselType): QVariant<T> = inline fun <reified T> QVariant_?.into(): T? = this?.withType<T>()?.data +/** + * Extract the content of a QVariant in a type-safe manner + * @return value of the QVariant + * @throws if the value could not be coerced into the given type + */ +inline fun <reified T> QVariant_?.intoOrThrow(): T = + this?.withType<T>()?.data ?: throw WrongVariantTypeException( + T::class.java.canonicalName, + this?.type()?.canonicalName ?: "null" + ) + /** * Extract the content of a QVariant in a type-safe manner * @param defValue default value if the type does not match or no value is found diff --git a/libquassel-generator/src/main/kotlin/de/justjanne/libquassel/generator/model/SyncedParameterModel.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/variant/WrongVariantTypeException.kt similarity index 60% rename from libquassel-generator/src/main/kotlin/de/justjanne/libquassel/generator/model/SyncedParameterModel.kt rename to libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/variant/WrongVariantTypeException.kt index e7242ab..83c2084 100644 --- a/libquassel-generator/src/main/kotlin/de/justjanne/libquassel/generator/model/SyncedParameterModel.kt +++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/variant/WrongVariantTypeException.kt @@ -8,11 +8,9 @@ * obtain one at https://mozilla.org/MPL/2.0/. */ -package de.justjanne.libquassel.generator.model +package de.justjanne.libquassel.protocol.variant -import com.google.devtools.ksp.symbol.KSType - -data class SyncedParameterModel( - val name: String?, - val type: KSType -) +data class WrongVariantTypeException( + val expected: String, + val actual: String? +) : Exception("Could not coerce QVariant of type $actual into $expected") diff --git a/libquassel-generator/src/main/kotlin/de/justjanne/libquassel/generator/model/SyncedObjectModel.kt b/libquassel-state/src/main/kotlin/de/justjanne/libquassel/state/exceptions/UnknownMethodException.kt similarity index 66% rename from libquassel-generator/src/main/kotlin/de/justjanne/libquassel/generator/model/SyncedObjectModel.kt rename to libquassel-state/src/main/kotlin/de/justjanne/libquassel/state/exceptions/UnknownMethodException.kt index 04f52dc..592355e 100644 --- a/libquassel-generator/src/main/kotlin/de/justjanne/libquassel/generator/model/SyncedObjectModel.kt +++ b/libquassel-state/src/main/kotlin/de/justjanne/libquassel/state/exceptions/UnknownMethodException.kt @@ -8,10 +8,9 @@ * obtain one at https://mozilla.org/MPL/2.0/. */ -package de.justjanne.libquassel.generator.model +package de.justjanne.libquassel.state.exceptions -data class SyncedObjectModel( - val name: String, - val rpcName: String?, - val methods: List<SyncedCallModel> -) +data class UnknownMethodException( + val className: String, + val methodName: String +) : Exception() diff --git a/libquassel-generator/src/main/kotlin/de/justjanne/libquassel/generator/annotation/SyncedObjectAnnotationModel.kt b/libquassel-state/src/main/kotlin/de/justjanne/libquassel/state/exceptions/WrongObjectTypeException.kt similarity index 69% rename from libquassel-generator/src/main/kotlin/de/justjanne/libquassel/generator/annotation/SyncedObjectAnnotationModel.kt rename to libquassel-state/src/main/kotlin/de/justjanne/libquassel/state/exceptions/WrongObjectTypeException.kt index d2ce55a..61a32ab 100644 --- a/libquassel-generator/src/main/kotlin/de/justjanne/libquassel/generator/annotation/SyncedObjectAnnotationModel.kt +++ b/libquassel-state/src/main/kotlin/de/justjanne/libquassel/state/exceptions/WrongObjectTypeException.kt @@ -8,8 +8,9 @@ * obtain one at https://mozilla.org/MPL/2.0/. */ -package de.justjanne.libquassel.generator.annotation +package de.justjanne.libquassel.state.exceptions -data class SyncedObjectAnnotationModel( - val name: String? -) +data class WrongObjectTypeException( + val obj: Any?, + val type: String +) : Exception() diff --git a/libquassel-state/src/main/kotlin/de/justjanne/libquassel/state/invoker/Invoker.kt b/libquassel-state/src/main/kotlin/de/justjanne/libquassel/state/invoker/Invoker.kt new file mode 100644 index 0000000..89a26df --- /dev/null +++ b/libquassel-state/src/main/kotlin/de/justjanne/libquassel/state/invoker/Invoker.kt @@ -0,0 +1,21 @@ +/* + * libquassel + * Copyright (c) 2021 Janne Mareike Koschinski + * Copyright (c) 2021 The Quassel Project + * + * This Source Code Form is subject to the terms of the Mozilla Public License, + * v. 2.0. If a copy of the MPL was not distributed with this file, You can + * obtain one at https://mozilla.org/MPL/2.0/. + */ + +package de.justjanne.libquassel.state.invoker + +import de.justjanne.libquassel.protocol.variant.QVariantList +import de.justjanne.libquassel.state.exceptions.UnknownMethodException +import de.justjanne.libquassel.state.exceptions.WrongObjectTypeException + +interface Invoker<out T> { + val className: String + @Throws(WrongObjectTypeException::class, UnknownMethodException::class) + fun invoke(on: Any?, method: String, params: QVariantList) +} -- GitLab