diff --git a/src/main/kotlin/net/theevilreaper/dartpoet/DartFile.kt b/src/main/kotlin/net/theevilreaper/dartpoet/DartFile.kt index 10e825c1..657c769e 100644 --- a/src/main/kotlin/net/theevilreaper/dartpoet/DartFile.kt +++ b/src/main/kotlin/net/theevilreaper/dartpoet/DartFile.kt @@ -38,6 +38,10 @@ class DartFile internal constructor( internal val libImport = DirectiveOrdering.sortDirectives(LibraryDirective::class, directives) internal val exportDirectives = DirectiveOrdering.sortDirectives(ExportDirective::class, directives) internal val relativeImports = DirectiveOrdering.sortDirectives(RelativeDirective::class, directives) + + internal val typeDefs = builder.typeDefs.toImmutableList() + internal val hasTypeDefs = typeDefs.isNotEmpty() + init { check(name.trim().isNotEmpty()) { "The name of a class can't be empty (ONLY SPACES ARE NOT ALLOWED" } if (libImport.isNotEmpty()) { diff --git a/src/main/kotlin/net/theevilreaper/dartpoet/DartFileBuilder.kt b/src/main/kotlin/net/theevilreaper/dartpoet/DartFileBuilder.kt index 315ece1a..59b616c9 100644 --- a/src/main/kotlin/net/theevilreaper/dartpoet/DartFileBuilder.kt +++ b/src/main/kotlin/net/theevilreaper/dartpoet/DartFileBuilder.kt @@ -6,6 +6,7 @@ import net.theevilreaper.dartpoet.clazz.ClassSpec import net.theevilreaper.dartpoet.code.CodeBlock import net.theevilreaper.dartpoet.extension.ExtensionSpec import net.theevilreaper.dartpoet.directive.Directive +import net.theevilreaper.dartpoet.function.typedef.TypeDefSpec import net.theevilreaper.dartpoet.property.consts.ConstantPropertySpec import net.theevilreaper.dartpoet.property.PropertySpec import net.theevilreaper.dartpoet.util.DEFAULT_INDENT @@ -20,6 +21,7 @@ class DartFileBuilder( internal val annotations: MutableList = mutableListOf() internal val extensionStack: MutableList = mutableListOf() internal val constants: MutableSet = mutableSetOf() + internal val typeDefs: MutableList = mutableListOf() internal var indent = DEFAULT_INDENT /** @@ -71,6 +73,24 @@ class DartFileBuilder( this.extensionStack += extensions } + /** + * Add a type definition to the file builder. + * @param typeDef the type definition to add + * @return the current instance of [DartFileBuilder] + */ + fun typeDef(typeDef: TypeDefSpec) = apply { + this.typeDefs += typeDef + } + + /** + * Add an array of type definitions to the file builder. + * @param typeDef the type definitions to add + * @return the current instance of [DartFileBuilder] + */ + fun typeDef(vararg typeDef: TypeDefSpec) = apply { + this.typeDefs += typeDef + } + fun type(dartFileSpec: ClassSpec) = apply { this.specTypes += dartFileSpec } diff --git a/src/main/kotlin/net/theevilreaper/dartpoet/clazz/ClassBuilder.kt b/src/main/kotlin/net/theevilreaper/dartpoet/clazz/ClassBuilder.kt index 82a2bdbf..4b150169 100644 --- a/src/main/kotlin/net/theevilreaper/dartpoet/clazz/ClassBuilder.kt +++ b/src/main/kotlin/net/theevilreaper/dartpoet/clazz/ClassBuilder.kt @@ -8,6 +8,7 @@ import net.theevilreaper.dartpoet.function.FunctionSpec import net.theevilreaper.dartpoet.meta.SpecData import net.theevilreaper.dartpoet.meta.SpecMethods import net.theevilreaper.dartpoet.function.constructor.ConstructorSpec +import net.theevilreaper.dartpoet.function.typedef.TypeDefSpec import net.theevilreaper.dartpoet.property.PropertySpec import net.theevilreaper.dartpoet.property.consts.ConstantPropertySpec import net.theevilreaper.dartpoet.type.TypeName @@ -37,6 +38,7 @@ class ClassBuilder internal constructor( internal val functionStack: MutableList = mutableListOf() internal val enumPropertyStack: MutableList = mutableListOf() internal val constantStack: MutableSet = mutableSetOf() + internal val typedefs: MutableList = mutableListOf() internal var superClass: TypeName? = null internal var inheritKeyWord: InheritKeyword? = null internal var endWithNewLine = false @@ -57,6 +59,14 @@ class ClassBuilder internal constructor( this.constantStack += constants } + fun typedef(typeDefSpec: TypeDefSpec) = apply { + this.typedefs += typeDefSpec + } + + fun typedef(vararg typeDefSpec: TypeDefSpec) = apply { + this.typedefs += typeDefSpec + } + /** * Add a [EnumPropertySpec] to the spec. * @param enumPropertySpec the property to add diff --git a/src/main/kotlin/net/theevilreaper/dartpoet/clazz/ClassSpec.kt b/src/main/kotlin/net/theevilreaper/dartpoet/clazz/ClassSpec.kt index 4094c39b..251eb022 100644 --- a/src/main/kotlin/net/theevilreaper/dartpoet/clazz/ClassSpec.kt +++ b/src/main/kotlin/net/theevilreaper/dartpoet/clazz/ClassSpec.kt @@ -31,10 +31,10 @@ class ClassSpec internal constructor( internal val superClass = builder.superClass internal val inheritKeyWord = builder.inheritKeyWord internal val classModifiers = modifiers.filter { it != WITH }.toImmutableSet() - internal val functions = builder.functionStack.filter { !it.isTypeDef }.toImmutableSet() + internal val typeDefs = builder.typedefs.toImmutableList() + internal val functions = builder.functionStack.toImmutableSet() internal val properties = builder.propertyStack.toImmutableSet() internal val constructors = builder.constructorStack.toImmutableSet() - internal val typeDefStack = builder.functionStack.filter { it.isTypeDef }.toImmutableSet() internal val enumPropertyStack = builder.enumPropertyStack.toImmutableList() internal var constantStack = builder.constantStack.toImmutableSet() diff --git a/src/main/kotlin/net/theevilreaper/dartpoet/code/CodeWriterExtension.kt b/src/main/kotlin/net/theevilreaper/dartpoet/code/CodeWriterExtension.kt index 23b7fe7c..2cc70a8e 100644 --- a/src/main/kotlin/net/theevilreaper/dartpoet/code/CodeWriterExtension.kt +++ b/src/main/kotlin/net/theevilreaper/dartpoet/code/CodeWriterExtension.kt @@ -5,6 +5,7 @@ import net.theevilreaper.dartpoet.extension.ExtensionSpec import net.theevilreaper.dartpoet.function.FunctionSpec import net.theevilreaper.dartpoet.function.constructor.ConstructorSpec import net.theevilreaper.dartpoet.directive.Directive +import net.theevilreaper.dartpoet.function.typedef.TypeDefSpec import net.theevilreaper.dartpoet.property.consts.ConstantPropertySpec import net.theevilreaper.dartpoet.parameter.ParameterSpec import net.theevilreaper.dartpoet.property.PropertySpec @@ -199,6 +200,20 @@ fun Set.emitConstants( } } +fun List.emitTypeDefs( + codeWriter: CodeWriter, + emitBlock: (TypeDefSpec) -> Unit = { it.write(codeWriter) } +) = with(codeWriter) { + val emitNewLines = size > 1 + forEachIndexed { index, typeDefSpec -> + if (index > 0 && emitNewLines) { + emit(NEW_LINE) + } + emitBlock(typeDefSpec) + } + emit(NEW_LINE) +} + fun Set.emitProperties( codeWriter: CodeWriter, forceNewLines: Boolean = false, diff --git a/src/main/kotlin/net/theevilreaper/dartpoet/code/writer/ClassWriter.kt b/src/main/kotlin/net/theevilreaper/dartpoet/code/writer/ClassWriter.kt index e450789d..81dac761 100644 --- a/src/main/kotlin/net/theevilreaper/dartpoet/code/writer/ClassWriter.kt +++ b/src/main/kotlin/net/theevilreaper/dartpoet/code/writer/ClassWriter.kt @@ -27,11 +27,6 @@ internal class ClassWriter : Writeable { writeAnonymousClass(spec, writer) return } - spec.typeDefStack.emitFunctions(writer) - - if (spec.typeDefStack.isNotEmpty()) { - writer.emit(NEW_LINE) - } spec.annotations.emitAnnotations(writer, inLineAnnotations = false) writeClassHeader(spec, writer) @@ -100,13 +95,8 @@ internal class ClassWriter : Writeable { } private fun writeAnonymousClass(spec: ClassSpec, writer: CodeWriter) { - spec.typeDefStack.emitFunctions(writer) { - it.write(writer) - } - - spec.functions.emitFunctions(writer) { - it.write(writer) - } + spec.typeDefs.emitTypeDefs(writer) + spec.functions.emitFunctions(writer) if (spec.endsWithNewLine) { writer.emit(NEW_LINE) diff --git a/src/main/kotlin/net/theevilreaper/dartpoet/code/writer/DartFileWriter.kt b/src/main/kotlin/net/theevilreaper/dartpoet/code/writer/DartFileWriter.kt index 9c9aa13d..e73d6211 100644 --- a/src/main/kotlin/net/theevilreaper/dartpoet/code/writer/DartFileWriter.kt +++ b/src/main/kotlin/net/theevilreaper/dartpoet/code/writer/DartFileWriter.kt @@ -25,6 +25,10 @@ internal class DartFileWriter : Writeable, DocumentationAppender { writer.emit(NEW_LINE) } + if (spec.hasTypeDefs) { + spec.typeDefs.emitTypeDefs(writer) + } + if (spec.types.isNotEmpty()) { spec.types.forEach { classWriter.write(it, writer) diff --git a/src/main/kotlin/net/theevilreaper/dartpoet/code/writer/FunctionWriter.kt b/src/main/kotlin/net/theevilreaper/dartpoet/code/writer/FunctionWriter.kt index 22273079..2e53c121 100644 --- a/src/main/kotlin/net/theevilreaper/dartpoet/code/writer/FunctionWriter.kt +++ b/src/main/kotlin/net/theevilreaper/dartpoet/code/writer/FunctionWriter.kt @@ -18,12 +18,8 @@ internal class FunctionWriter : Writeable { if (spec.hasDocs) { spec.docs.forEach { writer.emitDoc(it) } } - if (spec.isTypeDef) { - writeTypeDef(spec, writer) - return - } - if (!spec.isTypeDef && spec.annotation.isNotEmpty()) { + if (spec.annotation.isNotEmpty()) { spec.annotation.forEach { it.write(writer) } writer.emit(NEW_LINE) } diff --git a/src/main/kotlin/net/theevilreaper/dartpoet/code/writer/TypeDefWriter.kt b/src/main/kotlin/net/theevilreaper/dartpoet/code/writer/TypeDefWriter.kt new file mode 100644 index 00000000..4ea76f0c --- /dev/null +++ b/src/main/kotlin/net/theevilreaper/dartpoet/code/writer/TypeDefWriter.kt @@ -0,0 +1,31 @@ +package net.theevilreaper.dartpoet.code.writer + +import net.theevilreaper.dartpoet.DartModifier +import net.theevilreaper.dartpoet.code.CodeWriter +import net.theevilreaper.dartpoet.code.Writeable +import net.theevilreaper.dartpoet.code.emitParameters +import net.theevilreaper.dartpoet.function.typedef.TypeDefSpec +import net.theevilreaper.dartpoet.util.SEMICOLON + +class TypeDefWriter : Writeable { + override fun write(spec: TypeDefSpec, writer: CodeWriter) { + writer.emit("${DartModifier.TYPEDEF.identifier}·${spec.typeDefName}") + if (spec.typeCasts.isNotEmpty()) { + val typesAsString = spec.typeCasts.joinToString(separator = ",·") { it.toString() } + writer.emitCode("<%L>", typesAsString) + } + writer.emit("·=·") + writer.emitCode("%T", spec.returnType) + + if (spec.name != null) { + writer.emitCode("·%L", spec.name) + } + + if (spec.hasParameters) { + writer.emit("(") + spec.parameters.emitParameters(writer, forceNewLines = false, emitBrackets = false, emitSpace = spec.parameters.size > 1) + writer.emit(")") + } + writer.emit(SEMICOLON) + } +} diff --git a/src/main/kotlin/net/theevilreaper/dartpoet/function/FunctionBuilder.kt b/src/main/kotlin/net/theevilreaper/dartpoet/function/FunctionBuilder.kt index ef7ace5f..6ad8e96a 100644 --- a/src/main/kotlin/net/theevilreaper/dartpoet/function/FunctionBuilder.kt +++ b/src/main/kotlin/net/theevilreaper/dartpoet/function/FunctionBuilder.kt @@ -26,7 +26,6 @@ class FunctionBuilder internal constructor( internal var async: Boolean = false internal var returnType: TypeName? = null internal val body: CodeBlock.Builder = CodeBlock.builder() - internal var typedef: Boolean = false internal var typeCast: TypeName? = null internal var setter: Boolean = false internal var getter: Boolean = false @@ -94,14 +93,6 @@ class FunctionBuilder internal constructor( */ fun typeCast(cast: KClass<*>) = apply { this.typeCast = cast.asTypeName() } - /** - * If the function should be generated as typedef definition. - * @param typeDef true for a typedef - */ - fun typedef(typeDef: Boolean) = apply { - this.typedef = typeDef - } - fun addCode(format: String, vararg args: Any?) = apply { body.add(format, *args) } @@ -180,12 +171,6 @@ class FunctionBuilder internal constructor( * @return the created instance */ fun build(): FunctionSpec { - // Remove typedef keyword from the list to prevent problems - if (specData.modifiers.contains(DartModifier.TYPEDEF)) { - typedef(true) - specData.modifiers.remove(DartModifier.TYPEDEF) - } - return FunctionSpec(this) } } diff --git a/src/main/kotlin/net/theevilreaper/dartpoet/function/FunctionSpec.kt b/src/main/kotlin/net/theevilreaper/dartpoet/function/FunctionSpec.kt index abc78e98..c75bd59f 100644 --- a/src/main/kotlin/net/theevilreaper/dartpoet/function/FunctionSpec.kt +++ b/src/main/kotlin/net/theevilreaper/dartpoet/function/FunctionSpec.kt @@ -23,7 +23,6 @@ import net.theevilreaper.dartpoet.util.toImmutableSet class FunctionSpec( builder: FunctionBuilder ) { - internal val name = builder.name internal val returnType: TypeName? = builder.returnType internal val body: CodeBlock = builder.body.build() @@ -33,8 +32,7 @@ class FunctionSpec( internal var modifiers: Set = builder.specData.modifiers.also { hasAllowedModifiers(it, ALLOWED_FUNCTION_MODIFIERS, "function") }.filter { it != DartModifier.PRIVATE && it != DartModifier.PUBLIC }.toImmutableSet() - internal val isPrivate = builder.specData.modifiers.contains(DartModifier.PRIVATE) - internal val isTypeDef = builder.typedef + internal val isPrivate = builder.specData.modifiers.remove(DartModifier.PRIVATE) internal val typeCast = builder.typeCast internal val asSetter = builder.setter internal val isGetter = builder.getter @@ -49,7 +47,6 @@ class FunctionSpec( } init { - //check(!isTypeDef && annotation.isNotEmpty()) { "A typedef can't have annotations" } require(name.trim().isNotEmpty()) { "The name of a function can't be empty" } require(body.isEmpty() || !modifiers.contains(DartModifier.ABSTRACT)) { "An abstract method can't have a body" } @@ -89,7 +86,6 @@ class FunctionSpec( builder.modifiers(*this.modifiers.toTypedArray()) builder.parameters.addAll(this.parameters) builder.async = this.isAsync - builder.typedef = this.isTypeDef builder.typeCast = this.typeCast builder.setter = this.asSetter builder.getter = this.isGetter diff --git a/src/main/kotlin/net/theevilreaper/dartpoet/function/typedef/TypeDefBuilder.kt b/src/main/kotlin/net/theevilreaper/dartpoet/function/typedef/TypeDefBuilder.kt new file mode 100644 index 00000000..1ef14fda --- /dev/null +++ b/src/main/kotlin/net/theevilreaper/dartpoet/function/typedef/TypeDefBuilder.kt @@ -0,0 +1,103 @@ +package net.theevilreaper.dartpoet.function.typedef + +import net.theevilreaper.dartpoet.parameter.ParameterSpec +import net.theevilreaper.dartpoet.type.ClassName +import net.theevilreaper.dartpoet.type.TypeName +import net.theevilreaper.dartpoet.type.asTypeName +import kotlin.reflect.KClass + +/** + * The builder is used to create a type definition with a specific name and optional type cast. + * After the construction the builder maps the data into a [TypeDefSpec] object. + * + * @property typeDefName the name of the type definition. + * @property typeCasts optional array of type-cast for the type definition. + */ +class TypeDefBuilder internal constructor( + val typeDefName: String, + vararg val typeCasts: TypeName? = emptyArray() +) { + /** + * The name of the type definition. + */ + var name: String? = null + + /** + * The return type of the type definition. + */ + var returnType: TypeName? = null + + /** + * List of parameters associated with the type definition. + */ + val parameters: MutableList = mutableListOf() + + /** + * Sets the name of the type definition. + * + * @param name the name of the type definition. + * @return the current instance of [TypeDefBuilder]. + */ + fun name(name: String) = apply { + this.name = name + } + + /** + * Adds a parameter to the list of parameters. + * + * @param parameterSpec the parameter specification. + * @return the current instance of [TypeDefBuilder]. + */ + fun parameter(parameterSpec: ParameterSpec) = apply { + this.parameters += parameterSpec + } + + /** + * Adds multiple parameters to the list of parameters. + * + * @param parameterSpecs the parameter specifications. + * @return the current instance of [TypeDefBuilder]. + */ + fun parameters(vararg parameterSpecs: ParameterSpec) = apply { + this.parameters += parameterSpecs + } + + /** + * Sets the return type of the type definition. + * + * @param typeName the return type as a [TypeName]. + * @return the current instance of [TypeDefBuilder]. + */ + fun returns(typeName: TypeName) = apply { + this.returnType = typeName + } + + /** + * Sets the return type of the type definition. + * + * @param typeName the return type as a [ClassName]. + * @return the current instance of [TypeDefBuilder]. + */ + fun returns(typeName: ClassName) = apply { + this.returnType = typeName + } + + /** + * Sets the return type of the type definition using a [KClass]. + * + * @param typeName the return type as a [KClass]. + * @return the current instance of [TypeDefBuilder]. + */ + fun returns(typeName: KClass<*>) = apply { + this.returnType = typeName.asTypeName() + } + + /** + * Builds and returns an instance of [TypeDefSpec] based on the configuration. + * + * @return an instance of [TypeDefSpec]. + */ + fun build(): TypeDefSpec { + return TypeDefSpec(this) + } +} diff --git a/src/main/kotlin/net/theevilreaper/dartpoet/function/typedef/TypeDefSpec.kt b/src/main/kotlin/net/theevilreaper/dartpoet/function/typedef/TypeDefSpec.kt new file mode 100644 index 00000000..880f4d84 --- /dev/null +++ b/src/main/kotlin/net/theevilreaper/dartpoet/function/typedef/TypeDefSpec.kt @@ -0,0 +1,115 @@ +package net.theevilreaper.dartpoet.function.typedef + +import net.theevilreaper.dartpoet.code.CodeWriter +import net.theevilreaper.dartpoet.code.buildCodeString +import net.theevilreaper.dartpoet.code.writer.FunctionWriter +import net.theevilreaper.dartpoet.code.writer.TypeDefWriter +import net.theevilreaper.dartpoet.type.ClassName +import net.theevilreaper.dartpoet.type.TypeName +import net.theevilreaper.dartpoet.type.asTypeName +import net.theevilreaper.dartpoet.util.toImmutableList +import kotlin.reflect.KClass + +/** + * The class models a typedef from dart into a structure which can be used to generate and organize such methods. + * For more details visit the documentation from dart + * @see Dart Typedefs. + */ +class TypeDefSpec( + val builder: TypeDefBuilder +) { + internal val typeDefName = builder.typeDefName + internal val name = builder.name + internal val typeCasts = builder.typeCasts + internal val returnType = builder.returnType ?: Void::class.asTypeName() + internal val parameters = builder.parameters.toImmutableList() + internal val hasParameters = parameters.isNotEmpty() + + /** + * Performs some checks to avoid invalid data. + */ + init { + require(typeDefName.trim().isNotEmpty()) { "The name of a typedef can't be empty" } + if (name != null) { + require(name.trim().isNotEmpty()) { "The function name of a typedef can't be empty" } + } + } + + /** + * Calls a [FunctionWriter] to append the content from a spec object to a [CodeWriter]. + * @param codeWriter the writer instance + */ + internal fun write(codeWriter: CodeWriter) { + TypeDefWriter().write(this, codeWriter) + } + + /** + * Creates a textual representation from the spec object. + * @return the spec object as string + */ + override fun toString() = buildCodeString { write(this) } + + /** + * Converts a given instance of a [TypeDefSpec] into a [TypeDefBuilder]. + * This is useful if you want to modify an existing spec object. + * @return the created builder + */ + fun toBuilder(): TypeDefBuilder { + return builder + } + + /** + * The companion object contains some static methods to create a new instance of a [TypeDefSpec]. + * + */ + companion object { + + /** + * Static method to create a new instance from the [TypeDefBuilder]. + * @param typeDefName the name of the typedef + * @return the created instance + */ + @JvmStatic + fun builder(typeDefName: String): TypeDefBuilder = TypeDefBuilder(typeDefName) + + /** + * Static method to create a new instance from the [TypeDefBuilder]. + * @param typeDefName the name of the typedef + * @param typeCasts the type cast for the typedef as [TypeName] + * @return the created instance + */ + @JvmStatic + fun builder(typeDefName: String, vararg typeCasts: TypeName): TypeDefBuilder = + TypeDefBuilder(typeDefName, *typeCasts) + + /** + * Static method to create a new instance from the [TypeDefBuilder]. + * @param typeDefName the name of the typedef + * @param typeCast the type cast for the typedef as [Class] + * @return the created instance + */ + @JvmStatic + fun builder(typeDefName: String, vararg typeCasts: ClassName): TypeDefBuilder = + TypeDefBuilder(typeDefName, *typeCasts) + + /** + * Static method to create a new instance from the [TypeDefBuilder]. + * @param typeDefName the name of the typedef + * @param typeCasts the type cast for the typedef as [Class] + * @return the created instance + */ + @JvmStatic + fun builder(typeDefName: String, vararg typeCasts: Class<*>): TypeDefBuilder = + TypeDefBuilder(typeDefName, *typeCasts.map { it.asTypeName() }.toTypedArray()) + + /** + * Static method to create a new instance from the [TypeDefBuilder]. + * @param typeDefName the name of the typedef + * @param typeCasts the type cast for the typedef as [KClass] + * @return the created instance + */ + @JvmStatic + fun builder(typeDefName: String, vararg typeCasts: KClass<*>): TypeDefBuilder = + TypeDefBuilder(typeDefName, *typeCasts.map { it.asTypeName() }.toTypedArray()) + } +} \ No newline at end of file diff --git a/src/main/kotlin/net/theevilreaper/dartpoet/util/Constants.kt b/src/main/kotlin/net/theevilreaper/dartpoet/util/Constants.kt index dd78cd61..641c885b 100644 --- a/src/main/kotlin/net/theevilreaper/dartpoet/util/Constants.kt +++ b/src/main/kotlin/net/theevilreaper/dartpoet/util/Constants.kt @@ -26,8 +26,7 @@ internal const val CURLY_CLOSE = '}' internal const val ROUND_OPEN = "(" internal const val ROUND_CLOSE = ")" -internal val ALLOWED_FUNCTION_MODIFIERS = - setOf(DartModifier.PUBLIC, DartModifier.PRIVATE, DartModifier.STATIC, DartModifier.TYPEDEF) +internal val ALLOWED_FUNCTION_MODIFIERS = setOf(DartModifier.PUBLIC, DartModifier.PRIVATE, DartModifier.STATIC) internal val ALLOWED_PROPERTY_MODIFIERS = setOf(DartModifier.PRIVATE, DartModifier.FINAL, DartModifier.LATE, DartModifier.STATIC, DartModifier.CONST) internal val ALLOWED_CLASS_CONST_MODIFIERS = setOf(DartModifier.CONST) diff --git a/src/test/kotlin/net/theevilreaper/dartpoet/DartFileTest.kt b/src/test/kotlin/net/theevilreaper/dartpoet/DartFileTest.kt index b37abbe1..1aab39ea 100644 --- a/src/test/kotlin/net/theevilreaper/dartpoet/DartFileTest.kt +++ b/src/test/kotlin/net/theevilreaper/dartpoet/DartFileTest.kt @@ -11,6 +11,7 @@ import net.theevilreaper.dartpoet.directive.DartDirective import net.theevilreaper.dartpoet.directive.CastType import net.theevilreaper.dartpoet.directive.LibraryDirective import net.theevilreaper.dartpoet.directive.PartDirective +import net.theevilreaper.dartpoet.function.typedef.TypeDefSpec import net.theevilreaper.dartpoet.property.consts.ConstantPropertySpec import net.theevilreaper.dartpoet.parameter.ParameterSpec import net.theevilreaper.dartpoet.property.PropertySpec @@ -123,10 +124,8 @@ class DartFileTest { val libClass = DartFile.builder("testLib") .type( ClassSpec.anonymousClassBuilder() - .endWithNewLine(true) - .function( - FunctionSpec.builder("JsonMap") - .typedef(true) + .typedef( + TypeDefSpec.builder("JsonMap") .returns(Map::class.parameterizedBy(String::class.asTypeName(), DYNAMIC)) .build() @@ -429,4 +428,4 @@ class DartFileTest { """.trimIndent() ) } -} +} \ No newline at end of file diff --git a/src/test/kotlin/net/theevilreaper/dartpoet/code/writer/FunctionWriterTest.kt b/src/test/kotlin/net/theevilreaper/dartpoet/code/writer/FunctionWriterTest.kt index 137f1200..1ef9c5c8 100644 --- a/src/test/kotlin/net/theevilreaper/dartpoet/code/writer/FunctionWriterTest.kt +++ b/src/test/kotlin/net/theevilreaper/dartpoet/code/writer/FunctionWriterTest.kt @@ -154,20 +154,6 @@ class FunctionWriterTest { ) } - @Test - fun `test typedef write`() { - val function = FunctionSpec.builder("ValueUpdate") - .typedef(true) - .parameter( - ParameterSpec.builder("value", ClassName("E")) - .nullable(true) - .build() - ) - .returns(ClassName("void Function")) - .build() - assertThat(function.toString()).isEqualTo("typedef ValueUpdate = void Function(E? value);") - } - @Test fun `test other getter variant write`() { val function = FunctionSpec.builder("value") diff --git a/src/test/kotlin/net/theevilreaper/dartpoet/code/writer/TypeDefWriterTest.kt b/src/test/kotlin/net/theevilreaper/dartpoet/code/writer/TypeDefWriterTest.kt new file mode 100644 index 00000000..50b78544 --- /dev/null +++ b/src/test/kotlin/net/theevilreaper/dartpoet/code/writer/TypeDefWriterTest.kt @@ -0,0 +1,89 @@ +package net.theevilreaper.dartpoet.code.writer + +import com.google.common.truth.Truth +import net.theevilreaper.dartpoet.function.typedef.TypeDefSpec +import net.theevilreaper.dartpoet.parameter.ParameterSpec +import net.theevilreaper.dartpoet.type.ClassName +import net.theevilreaper.dartpoet.type.DYNAMIC +import net.theevilreaper.dartpoet.type.ParameterizedTypeName.Companion.parameterizedBy +import net.theevilreaper.dartpoet.type.asTypeName +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.Arguments +import org.junit.jupiter.params.provider.MethodSource +import java.util.stream.Stream + +class TypeDefWriterTest { + + companion object { + + private val genericClassName = ClassName("E") + private val secondGenericClassName = ClassName("T") + + @JvmStatic + private fun typeDefs(): Stream = Stream.of( + Arguments.of( + TypeDefSpec.builder("ValueUpdate", genericClassName) + .parameter( + ParameterSpec.builder("value", genericClassName) + .nullable(true) + .build() + ) + .name("Function") + .build(), + "typedef ValueUpdate = void Function(E? value);" + ), + Arguments.of( + TypeDefSpec.builder("json") + .returns(Map::class.parameterizedBy(String::class.asTypeName(), DYNAMIC)) + .build(), + "typedef json = Map;" + ), + ) + + @JvmStatic + private fun multipleCastArguments(): Stream = Stream.of( + Arguments.of( + TypeDefSpec.builder( + "DoubleValueUpdate", + genericClassName, secondGenericClassName + ) + .name("Function") + .parameters( + ParameterSpec.builder("first", genericClassName) + .nullable(true) + .build(), + ParameterSpec.builder("second", secondGenericClassName) + .nullable(true) + .build() + ) + .build(), + "typedef DoubleValueUpdate = void Function(E? first, T? second);" + ), + Arguments.of( + TypeDefSpec.builder("Compare", genericClassName, secondGenericClassName) + .returns(Int::class) + .name("Function") + .parameters( + ParameterSpec.builder("a", genericClassName) + .build(), + ParameterSpec.builder("b", genericClassName) + .build() + ) + .build(), + "typedef Compare = int Function(E a, E b);" + ) + ) + } + + @ParameterizedTest + @MethodSource("typeDefs") + fun `test typedef write`(typeDef: TypeDefSpec, expected: String) { + Truth.assertThat(typeDef.toString()).isEqualTo(expected) + } + + @ParameterizedTest + @MethodSource("multipleCastArguments") + fun `test typedef write with multiple casts`(typeDef: TypeDefSpec, expected: String) { + Truth.assertThat(typeDef.toString()).isEqualTo(expected) + } +} diff --git a/src/test/kotlin/net/theevilreaper/dartpoet/function/typedef/TypeDefSpecTest.kt b/src/test/kotlin/net/theevilreaper/dartpoet/function/typedef/TypeDefSpecTest.kt new file mode 100644 index 00000000..74483b32 --- /dev/null +++ b/src/test/kotlin/net/theevilreaper/dartpoet/function/typedef/TypeDefSpecTest.kt @@ -0,0 +1,53 @@ +package net.theevilreaper.dartpoet.function.typedef + +import net.theevilreaper.dartpoet.parameter.ParameterSpec +import org.junit.jupiter.api.Assertions.* +import org.junit.jupiter.api.Test +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.Arguments +import org.junit.jupiter.params.provider.MethodSource +import java.util.stream.Stream + +class TypeDefSpecTest { + + companion object { + + @JvmStatic + private fun invalidTypeDefs(): Stream = Stream.of( + Arguments.of( + IllegalArgumentException::class.java, + { TypeDefSpec.builder("").build() }, + "The name of a typedef can't be empty" + ), + Arguments.of( + IllegalArgumentException::class.java, + { TypeDefSpec.builder("Test", Int::class) + .name("") + .returns(String::class).build() + }, + "The function name of a typedef can't be empty" + ) + ) + } + + @ParameterizedTest + @MethodSource("invalidTypeDefs") + fun `test invalid typedef creation`(exception: Class, function: () -> Unit, message: String) { + val exceptionMessage = assertThrows(exception, function, message).message + assertEquals(message, exceptionMessage) + } + + @Test + fun `test to builder method`() { + val typeSpec = TypeDefSpec.builder("Test", Int::class) + .name("Function") + .returns(String::class).build() + assertNotEquals(Void::class.java, typeSpec.returnType) + + val newBuilder = typeSpec.toBuilder() + newBuilder.parameter(ParameterSpec.builder("test", String::class).build()) + + val newTypeSpec = newBuilder.build() + assertNotEquals(typeSpec.parameters, newTypeSpec.parameters) + } +} \ No newline at end of file