KotlinPoet: how to generate a DSL with parameters

320 views Asked by At

I'm trying to generate this Kotlin code that contains a DSL with a parameter:

listOf(
    navArgument(QUERY_PARAM) {
        type = NavType.StringType
        nullable = true
        defaultValue = null
    },
)

Is there a better way to provide the parameters to the DSL than just build the string manually?

CodeBlock.builder()
    .addStatement("listOf(")
    .indent()
    .beginControlFlow("%M(${queryParam})", MEMBER_NAME_NAV_ARGUMENT)
    .addStatement([...])
    .endControlFlow()
    .unindent()
    .add(")")
    .build(),
1

There are 1 answers

2
Egor On

KotlinPoet's API mostly models language constructs - types, functions, properties, there's not a lot of special API to model bodies of functions, constructors, etc. That said, there are a few methods inside CodeBlock that can help reduce the amount of manually-built strings, in addition to format modifiers. Here's what I came up with, hopefully some bits of it are helpful:

@Test fun dsl() {
  val queryParam = "QUERY_PARAM"
  val navArgument = MemberName(packageName = "", simpleName = "navArgument")
  val stringType = ClassName(packageName = "", simpleNames = listOf("NavType", "StringType"))
  val navArgumentConfiguration = listOf(
    CodeBlock.of("type = %T", stringType),
    CodeBlock.of("nullable = %L", true),
    Companion.of("defaultValue = %L", null),
  )
  val navArgumentCall = CodeBlock.builder() 
    .beginControlFlow("%M(%L)", navArgument, queryParam)
    .add(navArgumentConfiguration.joinToCode(separator = "\n", suffix = "\n"))
    .endControlFlow()
    .build() 
    .trim()
  val navArgumentCalls = listOf(navArgumentCall)
    .joinToCode(prefix = "listOf(⇥\n", separator = ",\n", suffix = ",⇤\n)")
  assertThat(navArgumentCalls.toString()).isEqualTo(
    """
    listOf(
      navArgument(QUERY_PARAM) {
        type = NavType.StringType
        nullable = true
        defaultValue = null
      }
      ,
    )
    """.trimIndent()
  )
}

Note the dangling , - this seems to be a bug which I just filed.