All Downloads are FREE. Search and download functionalities are using the official Maven repository.

pl.wrzasq.cform.macro.template.CompiledFragment.kt Maven / Gradle / Ivy

There is a newer version: 1.4.0
Show newest version
/**
 * This file is part of the pl.wrzasq.cform.
 *
 * @license http://mit-license.org/ The MIT license
 * @copyright 2022 © by Rafał Wrzeszcz - Wrzasq.pl.
 */

package pl.wrzasq.cform.macro.template

import pl.wrzasq.commons.json.ObjectMapperFactory

private val OBJECT_MAPPER = ObjectMapperFactory.createObjectMapper()

/**
 * Template processing which replaces nested calls to intrinsic functions to produce plain JSON representation.
 *
 * @param input Initial template state.
 */
class CompiledFragment(input: Map) {
    private var plainText = true
    private var index = 0
    private val params = mutableMapOf()
    private val json: String

    /**
     * Raw template serialization.
     *
     * @return JSON representation.
     */
    val raw: Any
        get() = when {
            plainText -> json
            params.isEmpty() -> Fn.sub(json)
            else -> Fn.sub(listOf(json, params))
        }

    init {
        val final = traverseMap(input)
        json = OBJECT_MAPPER.writeValueAsString(final)
    }

    private fun traverse(input: Any): Any = when (input) {
        is Map<*, *> -> traverseMap(asMap(input))
        is List<*> -> input.filterNotNull().map(::traverse)
        else -> input
    }

    private fun traverseMap(input: Map) = input.mapValuesOnly {
        if (it is Map<*, *> && it.size == 1 && isIntrinsicFunction(it.keys.first().toString())) {
            plainText = false
            val (key, value) = asMap(it).entries.first()

            when {
                key == CALL_REF || key == CALL_GET_ATT && value is String -> "\${${value}}"
                key == CALL_GET_ATT && value is List<*> -> "\${${value[0]}.${value[1]}}"
                // this is the simples one - just elevate encapsulation
                key == CALL_SUB && value is String -> value
                key == CALL_SUB && value is List<*> -> {
                    params.putAll(asMapAlways(value[1]))
                    value[0] ?: ""
                }
                // this is our notation - it will be handled afterwards
                key == CALL_IMPORT_VALUE && value is String -> "\${Import:${value}}"
                // this is specific to CodePipeline+CloudFormation but still - intrinsic function
                key == CALL_GET_PARAM -> it
                else -> generateParamPlaceholder(++index, it, params)
            }
        } else {
            traverse(it)
        }
    }

    private fun isIntrinsicFunction(key: String) = key == "Ref" || key.startsWith("Fn::")
}

private fun generateParamPlaceholder(index: Int, value: Any, params: MutableMap): String {
    val param = "param${index}"
    params[param] = value
    return "\${${param}}"
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy