Skip to content

KotlinPoet extensions to provide a fluent kotlin DSL for code generation.

License

Notifications You must be signed in to change notification settings

F43nd1r/kotlinbard

 
 

Repository files navigation

Rehost

The original KotlinBard was hosted on bintray, which shut down on May 1st, 2021.

This fork republishes it to maven central.

KotlinBard

KotlinBard is a collection of extension functions for KotlinPoet to provide a fluent kotlin DSL for kotlin code generation.

Example

KotlinPoet's first example is generated with the following:

val file = buildFile("", "HelloWorld") {
    val greeterClass = ClassName("", "Greeter")
    addClass(greeterClass) {
        primaryConstructor {
            addParameter("name", String::class)
        }
        addProperty("name", String::class) {
            initializer("name")
        }
        addFunction("greet") {
            addStatement("println(%P)", "Hello, \$name")
        }
    }

    addFunction("main") {
        addParameter("args", String::class, VARARG)
        addStatement("%T(args[0]).greet()", greeterClass)
    }
}
file.writeTo(System.out)

Fun fact: to cover all of KotlinPoet's functions, KotlinBard uses code generation to generate many extension functions -- it uses a previous version of itself to generate itself!

Features

buildXXX {} and addXXX {} functions for every builder

val file = buildFile {
    addClass(...){ ... }
    addInterface(...){ ... } //Each variant of TypeSpec and FunSpec have separate functions
    addTypeAlias(...){ ... }
}
val annotation = buildAnnotation(...){ ... } //AnnotationSpec
val annotationType = buildAnnotationClass(...){ ... } //TypeSpec that is an annotation class

//Each type of Spec has its own function
val enum = buildEnum(...){ ... }
val intf = buildInterface(...){ ... }
val constructor = buildContructor {}
val getter = buildGetter {}

modify extensions for every Spec, corresponding to converting to a builder then applying actions

val newClass = klass.modify(name = newName) { ... }

CodeBlock creation functions

val aBlock = codeBlock("println(%S)", "Hello, weird world")
//or, for a more inline syntax:
val aBlock = "println(%S)".codeFmt("Hello, weird world")

Builders are marked with DslMarker

val file = buildFile("", "File") {
    addImport(String::class)
    addClass("Foo") {
        addImport(Int::class) //compile error
    }
}

Extensions and DSLs for control flow

val function = buildFunction("analyzeTaco") {
    addCode { //functions are defined in the CodeBlockBuilder scope
        controlFlow("taco.let") {
            addStatement("println(it)")
        }
        `if`("taco.isSpicy()") {
            addStatement("println(%S)", "spicy!!")
        }.`else if`("me.isHungry") {
            addStatement("eat(taco)")
        } `else` {
            addStatement("saveForLater(taco)")
        }

        `do` {
            addStatement("makeTaco()")
        }.`while`("tacos < 5")

        `for`("taco in tacos") {
            addStatement("println(%P)", "taco information: \$taco")
        }

        `when`("taco") {
            "is SpicyTaco" - {
                addStatement("println(%S)", "Spicy!!")
            }
            "else" - "eat(%L)".codeFmt("taco")
        }
    }
}

This generates the following:

fun analyzeTaco() {
    taco.let {
        println(it)
    }
    if (taco.isSpicy()) {
        println("spicy!!")
    } else if (me.isHungry) {
        eat(taco)
    } else {
        saveForLater(taco)
    }
    do {
        makeTaco()
    } while (tacos < 5)
    for (taco in tacos) {
        println("""taco information: $taco""")
    }
    when (taco) {
        is SpicyTaco -> {
            println("Spicy!!")
        }
        else -> eat(taco)
    }
}

Quick property setter with parameter

val mySetter = buildSetter("value") {
    addStatement("field = value")
}

val prop = buildProperty("prop", String::class) {
    mutable()
    setter("value") {
        addStatement("println(%S)", "setting!")
        addStatement("field = value")
    }
}

Extensions for TypeNames

val myTypeVarName = TypeVariableName("T")
    .reified
    .plusTag(ATag::class, myTag)
val myTypeDecName = Int::class.asTypeName()
    .plusAnnotations(annotationSpec)

Usage

With gradle:

repositories {
    mavenCentral()
}

dependencies {
    implementation("com.faendir:kotlinbard:0.4.0")
}

About

KotlinPoet extensions to provide a fluent kotlin DSL for code generation.

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages

  • Kotlin 100.0%