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

com.spotify.scio.MagnoliaMacros.scala Maven / Gradle / Ivy

There is a newer version: 0.14.17
Show newest version
/*
 * Copyright 2019 Spotify AB.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

package com.spotify.scio

import scala.reflect.macros._

private[scio] object MagnoliaMacros {
  import magnolia1._

  // Add a level of indirection to prevent the macro from capturing
  // $outer which would make the Coder serialization fail
  def genWithoutAnnotations[T: c.WeakTypeTag](c: whitebox.Context): c.Tree = {
    import c.universe._
    val wtt = weakTypeOf[T]

    if (wtt <:< typeOf[Iterable[_]]) {
      c.abort(
        c.enclosingPosition,
        s"Automatic coder derivation can't derive a Coder for $wtt <: Seq"
      )
    }

    val magnoliaTree = Magnolia.gen[T](c)

    // format: off
    // Remove annotations from magnolia since they are
    // not serializable and we don't use them anyway
    val empty = q"_root_.scala.Array.empty[_root_.scala.Any]"
    val removeAnnotations: PartialFunction[Tree, Tree] = {
      case q"$caseClass($typeName, $isObject, $isValueClass, $parametersArray, $_, $_, $_)" if caseClass.symbol.name == TypeName("CaseClass") =>
        q"$caseClass($typeName, $isObject, $isValueClass, $parametersArray, $empty, $empty, $empty)"
      case q"Param.apply[$tpTC, $tpT, $tpP]($name, $typeNameParam, $idx, $isRepeated, $typeclassParam, $defaultVal, $_, $_, $_)" =>
        q"_root_.magnolia1.Param[$tpTC, $tpT, $tpP]($name, $typeNameParam, $idx, $isRepeated, $typeclassParam, $defaultVal, $empty, $empty, $empty)"
      case q"new SealedTrait($typeName, $subtypesArray, $_, $_, $_)" =>
        q"new _root_.magnolia1.SealedTrait($typeName, $subtypesArray, $empty, $empty, $empty)"
      case q"Subtype[$tpTC, $tpT, $tpS]($name, $idx, $_, $_, $_, $tc, $isType, $asType)" =>
        q"_root_.magnolia1.Subtype[$tpTC, $tpT, $tpS]($name, $idx, $empty, $empty, $empty, $tc, $isType, $asType)"
    }

    // remove all outer references used in rawConstruct
    // so method serialization do not take any closure
    val inlineRawConstruct: PartialFunction[Tree, Tree] = {
      case q"def rawConstruct(..$params): $tpT = {..$statements}" =>
        val closureCleansed = if (statements.size > 1) statements.tail else statements
        q"def rawConstruct(..$params): $tpT = {..$closureCleansed}"
    }
    // format: on

    val scioTransformer = new Transformer {
      override def transform(tree: Tree): Tree =
        super.transform(
          removeAnnotations.orElse(inlineRawConstruct).applyOrElse[Tree, Tree](tree, t => t)
        )
    }

    scioTransformer.transform(magnoliaTree)
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy