Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Generating wrong type for extension function type in builder #60

Closed
redgarb opened this issue Aug 22, 2024 · 2 comments
Closed

Generating wrong type for extension function type in builder #60

redgarb opened this issue Aug 22, 2024 · 2 comments

Comments

@redgarb
Copy link

redgarb commented Aug 22, 2024

If i define a class with a property with an extension function type like htmlDescription:

import com.faendir.kotlin.autodsl.AutoDsl
import kotlinx.html.FlowContent
import java.util.Date

@AutoDsl
data class Project(
    val begin: Date,
    val end: Date,
    val htmlDescription: (FlowContent.() -> Unit)
)

Then this builder will be generated.

import com.faendir.kotlin.autodsl.DslInspect
import com.faendir.kotlin.autodsl.DslMandatory
import java.util.Date
import kotlin.Function1
import kotlin.Unit
import kotlinx.html.FlowContent

@DslInspect
public class ProjectBuilder {
  /**
   * @see Project.begin
   */
  @set:DslMandatory(group = "begin0")
  public var begin: Date? = null

  /**
   * @see Project.end
   */
  @set:DslMandatory(group = "end1")
  public var end: Date? = null

  /**
   * @see Project.htmlDescription
   */
  @set:DslMandatory(group = "htmlDescription2")
  public var htmlDescription: Function1<FlowContent, Unit>? = null

  /**
   * @see Project.begin
   */
  public fun withBegin(begin: Date): ProjectBuilder {
    this.begin = begin
    return this
  }

  /**
   * @see Project.end
   */
  public fun withEnd(end: Date): ProjectBuilder {
    this.end = end
    return this
  }

  /**
   * @see Project.htmlDescription
   */
  public fun withHtmlDescription(htmlDescription: Function1<FlowContent, Unit>): ProjectBuilder {
    this.htmlDescription = htmlDescription
    return this
  }

  public fun build(): Project {
    check(begin != null) { "begin must be assigned." }
    check(end != null) { "end must be assigned." }
    check(htmlDescription != null) { "htmlDescription must be assigned." }
    return Project(begin!!, end!!, htmlDescription!!)
  }
}

public fun project(initializer: ProjectBuilder.() -> Unit): Project =
    ProjectBuilder().apply(initializer).build()

Then i have to write this unnecessary ugly code to get this working.

import kotlinx.html.div
import kotlinx.html.p
import kotlinx.html.stream.createHTML
import java.util.Date

fun main() {
    val myProject = project {
        begin = Date()
        end = Date()
        htmlDescription = {
            with (it) {
                div {
                    +"This is a project description."
                }
                p {
                    +"This project runs from start to end."
                }
            }
        }
    }

    val htmlContent = createHTML().div {
        myProject.htmlDescription.invoke(this)
    }

    println(htmlContent)
}

I want to write it like this.

import kotlinx.html.div
import kotlinx.html.p
import kotlinx.html.stream.createHTML
import java.util.Date

fun main() {
    val myProject = project {
        begin = Date()
        end = Date()
        htmlDescription = {
            div {
                +"This is a project description."
            }
            p {
                +"This project runs from start to end."
            }
        }
    }

    val htmlContent = createHTML().div {
        myProject.htmlDescription.invoke(this)
    }

    println(htmlContent)
}

This were possible when the builder will be generated correctly with
FlowContent.() -> Unit instead of Function1<FlowContent, Unit>

Could you please fix this?

Here is a complete running example:


import com.faendir.kotlin.autodsl.AutoDsl
import kotlinx.html.FlowContent
import kotlinx.html.div
import kotlinx.html.p
import kotlinx.html.stream.createHTML
import java.util.Date
import com.faendir.kotlin.autodsl.DslInspect
import com.faendir.kotlin.autodsl.DslMandatory

@AutoDsl
data class Project(
    val begin: Date,
    val end: Date,
    val htmlDescription: (FlowContent.() -> Unit)
)

@DslInspect
class MyProjectBuilder {
    /**
     * @see Project.begin
     */
    @set:DslMandatory(group = "begin0")
    public var begin: Date? = null

    /**
     * @see Project.end
     */
    @set:DslMandatory(group = "end1")
    public var end: Date? = null

    /**
     * @see Project.htmlDescription
     */
    @set:DslMandatory(group = "htmlDescription2")
    public var htmlDescription: (FlowContent.() -> Unit)? = null

    /**
     * @see Project.begin
     */
    public fun withBegin(begin: Date): MyProjectBuilder {
        this.begin = begin
        return this
    }

    /**
     * @see Project.end
     */
    public fun withEnd(end: Date): MyProjectBuilder {
        this.end = end
        return this
    }

    /**
     * @see Project.htmlDescription
     */
    public fun withHtmlDescription(htmlDescription: FlowContent.() -> Unit): MyProjectBuilder {
        this.htmlDescription = htmlDescription
        return this
    }

    public fun build(): Project {
        check(begin != null) { "begin must be assigned." }
        check(end != null) { "end must be assigned." }
        check(htmlDescription != null) { "htmlDescription must be assigned." }
        return Project(begin!!, end!!, htmlDescription!!)
    }
}

public fun myProject(initializer: MyProjectBuilder.() -> Unit): Project =
    MyProjectBuilder().apply(initializer).build()


fun main() {
    var myProject = myProject {
        begin = Date()
        end = Date()
        htmlDescription = {
            div {
                +"This is a project description."
            }
            p {
                +"This project runs from start to end."
            }
        }
    }

    var htmlContent = createHTML().div {
        myProject.htmlDescription.invoke(this)
    }

    println(htmlContent)

    myProject = project {
        begin = Date()
        end = Date()
        htmlDescription = {
            with (it) {
                div {
                    +"This is a project description."
                }
                p {
                    +"This project runs from start to end."
                }
            }
        }
    }

    htmlContent = createHTML().div {
        myProject.htmlDescription.invoke(this)
    }

    println(htmlContent)
}
@F43nd1r
Copy link
Owner

F43nd1r commented Aug 23, 2024

Fixed in v2.4.0

@redgarb
Copy link
Author

redgarb commented Aug 26, 2024

Works now as expected. Thank you! 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants