Kotlin type safe builder DSLs, security for the most external function

I am going to use the official official example from the documentation that implements DSL for some HTML creation.

Since Kotlin 1.1, the @DslMarker annotation allows us to limit the scope of functions in our classes, as in the example with the @HtmlTagMarker annotation. This gives us an error when trying to write improperly structured code as follows:

 html { body { body { // this in an error, as it a function call on the outside Html element } } } 

However, this does not prevent the nesting of the outermost function, which is the entry point into the DSL. For example, with an example as of now, this can be written without problems:

 html { html { } } 

Is there a way to make DSL safer in this regard?

+5
source share
1 answer

This can probably be done in a more elegant way somehow, but I can suggest using the @Deprecated annotation with DeprecationLevel.ERROR for a function with the appropriate signature defined for the receiver type, for example:

 @Deprecated("Cannot be used in a html block.", level = DeprecationLevel.ERROR) fun HtmlReceiver.html(action: HtmlReceiver.() -> Unit): Nothing = error("...") 

Or it could be a member function. By the way, the completion of the IDE behaves differently based on whether it is an extension or a member.

This will make calls like internal unacceptable:

 html { html { // Error: Cannot be used in a html block. } } 

(demo of this code)

The top-level function can still be called inside the DSL block via FQN, for example. com.example.html { } , so this trick only protects users from incorrectly calling top-level functions.

+6
source

Source: https://habr.com/ru/post/1266843/


All Articles