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

arrow.meta.processor.MetaProcessor.kt Maven / Gradle / Ivy

package arrow.meta.processor

import arrow.common.utils.AbstractProcessor
import arrow.common.utils.ClassOrPackageDataWrapper
import arrow.common.utils.ProcessorUtils
import arrow.common.utils.knownError
import arrow.meta.ast.Type
import arrow.meta.ast.TypeName
import arrow.meta.encoder.jvm.JvmMetaApi
import arrow.meta.internal.memoize
import arrow.meta.processor.MetaProcessor.AnnotatedElement
import com.squareup.kotlinpoet.FileSpec
import me.eugeniomarletti.kotlin.metadata.KotlinMetadataUtils
import java.io.File
import javax.annotation.processing.RoundEnvironment
import javax.lang.model.SourceVersion
import javax.lang.model.element.ElementKind
import javax.lang.model.element.TypeElement
import kotlin.reflect.KClass

/**
 * The Meta Processor provides access to the Meta Api and is meant to be extended by concrete processors.
 * It performs processing automatically provided the concrete processor implements:
 *
 * override fun transform(annotatedElement: AnnotatedElement): FileSpec.Builder
 *
 * [AnnotatedElement] provides already reified `Type` instances from the Arrow meta AST
 * that attempts to unify as much info as possible from the annotated Kotlin code.
 *
 * The Current [JvmMetaApi] impl includes support for extracting information with a blend
 * of Kotlin Poet, TypeElement java api's and the Kotlin Metadata Library.
 */
abstract class MetaProcessor(private val annotation: KClass) : AbstractProcessor(), JvmMetaApi {

  override fun processorUtils(): ProcessorUtils = this

  override fun kotlinMetadataUtils(): KotlinMetadataUtils = this

  override fun getSupportedSourceVersion(): SourceVersion = SourceVersion.latestSupported()

  override fun getSupportedAnnotationTypes(): Set = setOf(annotation.java.canonicalName)

  override val typeNameDownKind: (typeName: TypeName) -> TypeName =
    ::typeNameDownKindImpl.memoize()

  override val typeElementToMeta: (classElement: TypeElement) -> ClassOrPackageDataWrapper =
    ::getClassOrPackageDataWrapper.memoize()

  override val typeNameToMeta: (typeName: com.squareup.kotlinpoet.TypeName) -> TypeName =
    ::typeNameToMetaImpl.memoize()

  sealed class AnnotatedElement {
    data class Interface(val typeElement: TypeElement, val type: Type) : AnnotatedElement()
    data class Class(val typeElement: TypeElement, val type: Type) : AnnotatedElement()
  }

  abstract fun transform(annotatedElement: AnnotatedElement): List

  private val transformList = mutableListOf()

  /**
   * Processor entry point
   */
  @Suppress("UNCHECKED_CAST")
  override fun onProcess(annotations: Set, roundEnv: RoundEnvironment) {
      transformList += roundEnv
        .getElementsAnnotatedWith(annotation.java)
        .flatMap { element ->
          //          try {
          val result = when (element.kind) {
            ElementKind.INTERFACE, ElementKind.CLASS -> {
              val typeElement = element as TypeElement
              val encodingResult = typeElement.type()
              encodingResult.fold({ knownError(it.toString()) }, {
                transform(
                  if (element.kind.isInterface)
                    AnnotatedElement.Interface(
                      typeElement = typeElement,
                      type = it
                    )
                  else AnnotatedElement.Class(
                    typeElement = typeElement,
                    type = it
                  ))
              })
            }
            else -> knownError("Unsupported meta annotation: $annotation over ${element.kind.name} ")
          }
          result.map { it.build() }
        }
      if (roundEnv.processingOver()) {
        val generatedDir = File(this.generatedDir!!, annotation.java.simpleName).also { it.mkdirs() }
        transformList.forEach { it.writeTo(generatedDir) }
      }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy