Currently, we have no @DslMarker annotation in place so, as we start to nest these function calls, we have access to functions for multiple different receivers. For example, in the following code snippet, we can call the soda function within the init block for Pizza:
+HawaiianPizza {
+Pepperoni
soda(Coke) // want to avoid this
}
This behavior is not ideal because it doesn't make sense to add a Soda to Pizza. So, by default, we want to limit this behavior. To do that, we can create a new @DslMarker annotation:
@DslMarker
annotation class ItemTagMarker
We can then add that to the base class for our Items:
@ItemTagMarker
abstract class Item(val name: String)
With that annotation in place, the compiler will mark it as an error if you try to access a property of the method of an implicit receiver:
+HawaiianPizza {
+Pepperoni
soda(Coke) // now an error
}
This also means that in cases where we do want to reference an outer receiver, then we must call it explicitly. One example of this is in our implementation of Topping.unaryPlus():
operator fun Topping.unaryPlus() = this@Pizza.toppings.add(this)
Now, if we want to see how our order looks, we can add some logging functionality. To start, we'll add a print method to our Item base class:
@ItemTagMarker
abstract class Item(val name: String) {
open fun log(indent: String = "") {
println("$indent$name")
}
}
The we'll override that behavior for Pizza, as follows:
sealed class Pizza(name: String) : Item(name) {
val toppings: MutableList<Topping> = mutableListOf()
operator fun Topping.unaryPlus() = this@Pizza.toppings.add(this)
override fun log(indent: String) {
super.log(indent)
toppings.forEach {
println("$indent ${it.name}")
}
}
}
Finally, we'll add a print() method to the order class, which will allow us to print the entire order using a log() method:
fun log() {
println("Order: $id")
println("Items")
items.forEach {
print("${it.value} x ")
it.key.log(" ")
}
}
Now, we can print out the following order:
fun main() {
val order = order {
soda(Dr_Pepper)
soda(Coke)
Sprite quantity 1
+Coke
+Dr_Pepper
Sprite quantity 2
+HawaiianPizza {
+Pepperoni
}
pizza {
+Pepperoni
+Olive
}
}
order.log()
}
With this order, we get the following output:
Order: 9de6134e-23f3-44e5-88f1-2af373c399a8
Items
2 x Dr Pepper
2 x Coke
3 x Sprite
1 x Hawaiian Pizza
Pineapple
Pepperoni
1 x Build Your Own Pizza
Pepperoni
Olive
With that, we've built a very simple DSL for building orders for a pizza restaurant. For a production DSL, we'd likely want to build more configurability and expand the functionality, but the building blocks are the same.