0

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(),
Roberto Leinardi
  • 10,641
  • 6
  • 65
  • 69

1 Answers1

0

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.

Egor
  • 39,695
  • 10
  • 113
  • 130
  • Hi @Egor, thank you for your answer, I learned a couple of things from it. But my question was more focused on the DSL parameter itself: I wanted to know if there was a better way than just doing this -> `beginControlFlow("%M(${queryParam})", MEMBER_NAME_NAV_ARGUMENT)` – Roberto Leinardi Jul 25 '22 at 08:23
  • 1
    Hey Roberto! Not really, `beginControlFlow` is the generic API to use for both control flows such as `if-else` or `while`, and constructs that take a lambda as an argument. – Egor Jul 25 '22 at 19:56