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

scalapb.compiler.ProtobufGenerator.scala Maven / Gradle / Ivy

The newest version!
package scalapb.compiler

import com.google.protobuf.Descriptors._
import com.google.protobuf.{CodedOutputStream, DescriptorProtos, ByteString => GoogleByteString}
import com.google.protobuf.Descriptors.FieldDescriptor.Type
import scalapb.compiler.FunctionalPrinter.PrinterEndo
import scalapb.options.Scalapb
import scalapb.options.Scalapb.FieldOptions

import scala.jdk.CollectionConverters._
import com.google.protobuf.compiler.PluginProtos.CodeGeneratorResponse
import protocgen.CodeGenRequest
import protocgen.CodeGenResponse

// Exceptions that are caught and passed upstreams as errors.
case class GeneratorException(message: String) extends Exception(message)

class ProtobufGenerator(
    params: GeneratorParams,
    implicits: DescriptorImplicits
) {

  import implicits._
  import DescriptorImplicits.AsSymbolExtension
  import ProtobufGenerator._

  def printEnum(printer: FunctionalPrinter, e: EnumDescriptor): FunctionalPrinter = {
    val name           = e.scalaType.nameSymbol
    val valuesByNumber = e.getValues().asScala.groupBy(_.getNumber())
    printer
      .when(e.getOptions.getDeprecated) {
        _.add(ProtobufGenerator.deprecatedAnnotation)
      }
      .call(generateScalaDoc(e))
      .seq(e.baseAnnotationList)
      .add(
        s"sealed abstract class $name(val value: _root_.scala.Int) extends ${e.baseTraitExtends.mkString(" with ")} {"
      )
      .indent
      .add(s"type EnumType = ${e.scalaType.fullName}")
      .add(s"type RecognizedType = ${e.recognizedEnum.fullName}")
      .print(e.getValues.asScala) { case (p, v) =>
        p.add(s"def ${v.isName}: _root_.scala.Boolean = false")
      }
      .add(s"def companion: _root_.scalapb.GeneratedEnumCompanion[$name] = ${e.scalaType.fullName}")
      .add(
        s"final def asRecognized: _root_.scala.Option[${e.recognizedEnum.fullName}] = if (isUnrecognized) _root_.scala.None else _root_.scala.Some(this.asInstanceOf[${e.recognizedEnum.fullName}])"
      )
      .outdent
      .add("}")
      .add("")
      .when(e.getOptions.getDeprecated) {
        _.add(ProtobufGenerator.deprecatedAnnotation)
      }
      .add(s"object $name extends ${e.companionExtends.mkString(" with ")} {")
      .indent
      .seq(e.recognizedAnnotationList)
      .add(s"sealed trait ${e.recognizedEnum.nameSymbol} extends $name")
      .add(s"implicit def enumCompanion: _root_.scalapb.GeneratedEnumCompanion[$name] = this")
      .newline
      .print(e.getValues().asScala) { case (p, v) =>
        val firstVal = valuesByNumber(v.getNumber()).head
        if (firstVal == v)
          p.call(generateScalaDoc(v))
            .add("@SerialVersionUID(0L)")
            .seq(v.annotationList)
            .add(s"""case object ${v.scalaName.asSymbol} extends ${v.valueExtends.mkString(
                " with "
              )} {""")
            .indented(
              _.add(s"val index = ${v.getIndex}")
                .add(s"""val name = "${v.getName}"""")
                .print(valuesByNumber(v.getNumber)) { case (p, u) =>
                  p.add(s"override def ${u.isName}: _root_.scala.Boolean = true")
                }
            )
            .add("}")
            .add("")
        else
          p.call(generateScalaDoc(v))
            .seq(v.annotationList)
            .add(s"@transient val ${v.scalaName.asSymbol} = ${firstVal.scalaName.asSymbol}")
            .add("")
      }
      .add("@SerialVersionUID(0L)")
      .seq(e.unrecognizedAnnotationList)
      .add(
        s"""final case class Unrecognized(unrecognizedValue: _root_.scala.Int) extends $name(unrecognizedValue) with _root_.scalapb.UnrecognizedEnum
           |lazy val values: scala.collection.immutable.Seq[ValueType] = scala.collection.immutable.Seq(${e.getValues.asScala
            .map(_.scalaName.asSymbol)
            .mkString(", ")})
           |def fromValue(__value: _root_.scala.Int): $name = __value match {""".stripMargin
      )
      .print(e.valuesWithNoDuplicates) { case (p, v) =>
        p.add(s"  case ${v.getNumber} => ${v.scalaName.asSymbol}")
      }
      .add(
        s"""  case __other => Unrecognized(__other)
           |}
           |def javaDescriptor: _root_.com.google.protobuf.Descriptors.EnumDescriptor = ${e.javaDescriptorSource}
           |def scalaDescriptor: _root_.scalapb.descriptors.EnumDescriptor = ${e.scalaDescriptorSource}""".stripMargin
      )
      .when(e.javaConversions) {
        _.add(
          s"""|def fromJavaValue(pbJavaSource: ${e.javaTypeName}): $name = fromValue(pbJavaSource.getNumber)
              |def toJavaValue(pbScalaSource: $name): ${e.javaTypeName} = {
              |  _root_.scala.Predef.require(!pbScalaSource.isUnrecognized, "Unrecognized enum values can not be converted to Java")
              |  ${e.javaTypeName}.forNumber(pbScalaSource.value)
              |}""".stripMargin
        )
      }
      .outdent
      .add("}")
  }

  def printOneof(printer: FunctionalPrinter, e: OneofDescriptor): FunctionalPrinter = {
    printer
      .add(
        s"sealed abstract class ${e.scalaType.nameSymbol} extends ${e.baseClasses.mkString(" with ")} {"
      )
      .indent
      .add(s"def isEmpty: _root_.scala.Boolean = false")
      .add(s"def isDefined: _root_.scala.Boolean = true")
      .print(e.fields) { case (p, v) =>
        p.add(s"def is${v.upperScalaName}: _root_.scala.Boolean = false")
      }
      .print(e.fields) { case (p, v) =>
        p.add(s"def ${v.scalaName.asSymbol}: _root_.scala.Option[${v.scalaTypeName}] = ${C.None}")
      }
      .outdent
      .add(s"""}
              |object ${e.scalaType.nameSymbol} {
              |  @SerialVersionUID(0L)
              |  case object Empty extends ${e.scalaType.fullName} {
              |    type ValueType = _root_.scala.Nothing
              |    override def isEmpty: _root_.scala.Boolean = true
              |    override def isDefined: _root_.scala.Boolean = false
              |    override def number: _root_.scala.Int = 0
              |    override def value: _root_.scala.Nothing = throw new java.util.NoSuchElementException("Empty.value")
              |  }
              |""".stripMargin)
      .indent
      .print(e.fields) { case (p, v) =>
        p.add(
          s"""@SerialVersionUID(0L)${if (v.getOptions.getDeprecated) {
              " " + ProtobufGenerator.deprecatedAnnotation
            } else ""}
             |final case class ${v.upperScalaName}(value: ${v.scalaTypeName}) extends ${e.scalaType.fullName} {
             |  type ValueType = ${v.scalaTypeName}
             |  override def is${v.upperScalaName}: _root_.scala.Boolean = true
             |  override def ${v.scalaName.asSymbol}: _root_.scala.Option[${v.scalaTypeName}] = Some(value)
             |  override def number: _root_.scala.Int = ${v.getNumber}
             |}""".stripMargin
        )
      }
      .outdent
      .add("}")
  }

  def defaultValueForGet(field: FieldDescriptor, uncustomized: Boolean = false) = {
    // Needs to be 'def' and not val since for some of the cases it's invalid to call it.
    def defaultValue = field.getDefaultValue

    val baseDefaultValue: String = field.getJavaType match {
      case FieldDescriptor.JavaType.INT  => defaultValue.toString
      case FieldDescriptor.JavaType.LONG => defaultValue.toString + "L"
      case FieldDescriptor.JavaType.FLOAT =>
        val f = defaultValue.asInstanceOf[Float]
        if (f.isPosInfinity) "Float.PositiveInfinity"
        else if (f.isNegInfinity) "Float.NegativeInfinity"
        else if (f.isNaN) "Float.NaN"
        else f.toString + "f"
      case FieldDescriptor.JavaType.DOUBLE =>
        val d = defaultValue.asInstanceOf[Double]
        if (d.isPosInfinity) "Double.PositiveInfinity"
        else if (d.isNegInfinity) "Double.NegativeInfinity"
        else if (d.isNaN) "Double.NaN"
        else d.toString
      case FieldDescriptor.JavaType.BOOLEAN =>
        Boolean.unbox(defaultValue.asInstanceOf[java.lang.Boolean]).toString
      case FieldDescriptor.JavaType.BYTE_STRING =>
        val d = defaultValue.asInstanceOf[GoogleByteString]
        if (d.isEmpty)
          "_root_.com.google.protobuf.ByteString.EMPTY"
        else
          d.asScala
            .map(_.toString)
            .mkString("_root_.com.google.protobuf.ByteString.copyFrom(Array[Byte](", ", ", "))")
      case FieldDescriptor.JavaType.STRING => escapeScalaString(defaultValue.asInstanceOf[String])
      case FieldDescriptor.JavaType.MESSAGE =>
        field.getMessageType.scalaType
          .fullNameWithMaybeRoot(field.getContainingType) + ".defaultInstance"
      case FieldDescriptor.JavaType.ENUM =>
        field.getEnumType.scalaType
          .fullNameWithMaybeRoot(field.getContainingType) + "." + defaultValue
          .asInstanceOf[EnumValueDescriptor]
          .scalaName
          .asSymbol
    }
    if (!uncustomized && field.customSingleScalaTypeName.isDefined)
      s"${field.typeMapper.fullName}.toCustom($baseDefaultValue)"
    else baseDefaultValue
  }

  def defaultValueForDefaultInstance(field: FieldDescriptor) =
    if (field.supportsPresence) C.None
    else if (field.isRepeated) field.collection.empty
    else defaultValueForGet(field)

  def javaToScalaConversion(field: FieldDescriptor) = {
    val baseValueConversion = field.getJavaType match {
      case FieldDescriptor.JavaType.INT         => MethodApplication("intValue")
      case FieldDescriptor.JavaType.LONG        => MethodApplication("longValue")
      case FieldDescriptor.JavaType.FLOAT       => MethodApplication("floatValue")
      case FieldDescriptor.JavaType.DOUBLE      => MethodApplication("doubleValue")
      case FieldDescriptor.JavaType.BOOLEAN     => MethodApplication("booleanValue")
      case FieldDescriptor.JavaType.BYTE_STRING => Identity
      case FieldDescriptor.JavaType.STRING      => Identity
      case FieldDescriptor.JavaType.MESSAGE =>
        FunctionApplication(field.getMessageType.scalaType.fullName + ".fromJavaProto")
      case FieldDescriptor.JavaType.ENUM =>
        if (!field.legacyEnumFieldTreatedAsClosed())
          MethodApplication("intValue") andThen FunctionApplication(
            field.getEnumType.scalaType.fullName + ".fromValue"
          )
        else FunctionApplication(field.getEnumType.scalaType.fullName + ".fromJavaValue")
    }
    baseValueConversion andThen toCustomTypeExpr(field)
  }

  def javaFieldToScala(javaHazzer: String, javaGetter: String, field: FieldDescriptor): String = {
    val valueConversion: Expression = javaToScalaConversion(field)

    if (field.supportsPresence)
      s"if ($javaHazzer) Some(${valueConversion.apply(javaGetter, EnclosingType.None)}) else ${C.None}"
    else if (field.isRepeated)
      valueConversion(
        javaGetter + ".asScala",
        sourceType = EnclosingType.Collection(DescriptorImplicits.ScalaSeq, None),
        targetType = field.enclosingType,
        mustCopy = true
      )
    else valueConversion(javaGetter, EnclosingType.None)
  }

  def javaFieldToScala(container: String, field: FieldDescriptor): String = {
    val javaHazzer = container + ".has" + field.upperJavaName
    val upperJavaName =
      if (field.isEnum && !field.legacyEnumFieldTreatedAsClosed()) (field.upperJavaName + "Value")
      else field.upperJavaName
    val javaGetter =
      if (field.isRepeated)
        container + ".get" + upperJavaName + "List"
      else
        container + ".get" + upperJavaName

    javaFieldToScala(javaHazzer, javaGetter, field)
  }

  def javaMapFieldToScala(container: String, field: FieldDescriptor): String = {
    // TODO(thesamet): if both unit conversions are NoOp, we can omit the map call.
    def unitConversion(n: String, field: FieldDescriptor) =
      javaToScalaConversion(field).apply(n, EnclosingType.None)

    val upperJavaName =
      if (
        field.mapType.valueField.isEnum && !field.mapType.valueField.legacyEnumFieldTreatedAsClosed
      )
        (field.upperJavaName + "Value")
      else field.upperJavaName
    ExpressionBuilder.convertCollection(
      s"${container}.get${upperJavaName}Map.asScala.iterator.map(__pv => (${unitConversion(
          "__pv._1",
          field.mapType.keyField
        )}, ${unitConversion("__pv._2", field.mapType.valueField)}))",
      field.enclosingType
    )
  }

  def scalaToJava(field: FieldDescriptor, boxPrimitives: Boolean): Expression = {
    def maybeBox(name: String) = if (boxPrimitives) FunctionApplication(name) else Identity

    field.getJavaType match {
      case FieldDescriptor.JavaType.INT         => maybeBox("_root_.scala.Int.box")
      case FieldDescriptor.JavaType.LONG        => maybeBox("_root_.scala.Long.box")
      case FieldDescriptor.JavaType.FLOAT       => maybeBox("_root_.scala.Float.box")
      case FieldDescriptor.JavaType.DOUBLE      => maybeBox("_root_.scala.Double.box")
      case FieldDescriptor.JavaType.BOOLEAN     => maybeBox("_root_.scala.Boolean.box")
      case FieldDescriptor.JavaType.BYTE_STRING => Identity
      case FieldDescriptor.JavaType.STRING      => Identity
      case FieldDescriptor.JavaType.MESSAGE =>
        FunctionApplication(field.getMessageType.scalaType.fullName + ".toJavaProto")
      case FieldDescriptor.JavaType.ENUM =>
        if (!field.legacyEnumFieldTreatedAsClosed())
          (MethodApplication("value") andThen maybeBox("_root_.scala.Int.box"))
        else
          FunctionApplication(field.getEnumType.scalaType.fullName + ".toJavaValue")
    }
  }

  def assignScalaMapToJava(
      scalaObject: String,
      javaObject: String,
      field: FieldDescriptor
  ): String = {
    def valueConvert(v: String, field: FieldDescriptor) =
      (toBaseTypeExpr(field) andThen scalaToJava(field, boxPrimitives = true))
        .apply(v, EnclosingType.None)

    val putAll =
      s"putAll${field.upperScalaName}" + (if (
                                            field.mapType.valueField.isEnum && !field.mapType.valueField
                                              .legacyEnumFieldTreatedAsClosed()
                                          )
                                            "Value"
                                          else "")

    s"""$javaObject
       |  .$putAll(${field.collection.iterator(
        scalaObject + "." + field.scalaName.asSymbol,
        EnclosingType.None
      )}.map {
       |    __kv => (${valueConvert("__kv._1", field.mapType.keyField)}, ${valueConvert(
        "__kv._2",
        field.mapType.valueField
      )})
       |  }.toMap.asJava)""".stripMargin
  }

  def assignScalaFieldToJava(
      scalaObject: String,
      javaObject: String,
      field: FieldDescriptor
  ): String =
    if (field.isMapField) assignScalaMapToJava(scalaObject, javaObject, field)
    else {
      val javaSetter = javaObject +
        (if (field.isRepeated) ".addAll"
         else
           ".set") + field.upperJavaName + (if (
                                              field.isEnum && !field
                                                .legacyEnumFieldTreatedAsClosed()
                                            ) "Value"
                                            else "")
      val scalaGetter = scalaObject + "." + fieldAccessorSymbol(field)

      val scalaExpr =
        (toBaseTypeExpr(field) andThen scalaToJava(field, boxPrimitives = field.isRepeated))
          .apply(
            scalaGetter,
            sourceType = field.enclosingType,
            targetType = field.enclosingType match {
              case _: EnclosingType.Collection =>
                EnclosingType.Collection(DescriptorImplicits.ScalaIterable, None)
              case o => o
            }
          )
      if (field.supportsPresence || field.isInOneof)
        s"$scalaExpr.foreach($javaSetter)"
      else if (field.isRepeated)
        s"$javaSetter($scalaExpr.asJava)"
      else
        s"$javaSetter($scalaExpr)"
    }

  def generateGetField(message: Descriptor)(fp: FunctionalPrinter) = {
    val signature = "def getFieldByNumber(__fieldNumber: _root_.scala.Int): _root_.scala.Any = "
    if (message.fields.nonEmpty)
      fp.add(signature + "{")
        .indent
        .add("(__fieldNumber: @_root_.scala.unchecked) match {")
        .indent
        .print(message.fields) { case (fp, f) =>
          val e = toBaseFieldType(f)
            .apply(
              fieldAccessorSymbol(f),
              sourceType = f.enclosingType,
              targetType = f.fieldMapEnclosingType
            )
          if (f.supportsPresence || f.isInOneof)
            fp.add(s"case ${f.getNumber} => $e.orNull")
          else if (f.isOptional && !f.noBoxRequired) {
            // In proto3, drop default value
            fp.add(s"case ${f.getNumber} => {")
              .indent
              .add(s"val __t = $e")
              .add({
                val cond =
                  if (!f.isEnum)
                    s"__t != ${defaultValueForGet(f, uncustomized = true)}"
                  else
                    s"__t.getNumber() != 0"
                s"if ($cond) __t else null"
              })
              .outdent
              .add("}")
          } else fp.add(s"case ${f.getNumber} => $e")
        }
        .outdent
        .add("}")
        .outdent
        .add("}")
    else fp.add(signature + "throw new MatchError(__fieldNumber)")
  }

  def singleFieldAsPvalue(fd: FieldDescriptor): LiteralExpression = {
    val d = "_root_.scalapb.descriptors"
    fd.getJavaType match {
      case FieldDescriptor.JavaType.INT         => FunctionApplication(s"$d.PInt")
      case FieldDescriptor.JavaType.LONG        => FunctionApplication(s"$d.PLong")
      case FieldDescriptor.JavaType.FLOAT       => FunctionApplication(s"$d.PFloat")
      case FieldDescriptor.JavaType.DOUBLE      => FunctionApplication(s"$d.PDouble")
      case FieldDescriptor.JavaType.BOOLEAN     => FunctionApplication(s"$d.PBoolean")
      case FieldDescriptor.JavaType.BYTE_STRING => FunctionApplication(s"$d.PByteString")
      case FieldDescriptor.JavaType.STRING      => FunctionApplication(s"$d.PString")
      case FieldDescriptor.JavaType.ENUM        => FunctionApplication(s"$d.PEnum")
      case FieldDescriptor.JavaType.MESSAGE     => MethodApplication("toPMessage")
    }
  }

  def generateGetFieldPValue(message: Descriptor)(fp: FunctionalPrinter) = {
    val signature =
      "def getField(__field: _root_.scalapb.descriptors.FieldDescriptor): _root_.scalapb.descriptors.PValue = "
    if (message.fields.nonEmpty)
      fp.add(signature + "{")
        .indent
        .add("_root_.scala.Predef.require(__field.containingMessage eq companion.scalaDescriptor)")
        .add("(__field.number: @_root_.scala.unchecked) match {")
        .indent
        .print(message.fields) { case (fp, f) =>
          val e = toBaseFieldTypeWithScalaDescriptors(f)
            .andThen(singleFieldAsPvalue(f))
            .apply(
              fieldAccessorSymbol(f),
              sourceType = f.enclosingType,
              targetType = f.enclosingType match {
                case _: EnclosingType.Collection =>
                  EnclosingType.Collection(DescriptorImplicits.ScalaVector, None)
                case other => other
              }
            )
          if (f.supportsPresence || f.isInOneof) {
            fp.add(s"case ${f.getNumber} => $e.getOrElse(_root_.scalapb.descriptors.PEmpty)")
          } else if (f.isRepeated) {
            fp.add(s"case ${f.getNumber} => _root_.scalapb.descriptors.PRepeated($e)")
          } else {
            fp.add(s"case ${f.getNumber} => $e")
          }
        }
        .outdent
        .add("}")
        .outdent
        .add("}")
    else fp.add(signature + "throw new MatchError(__field)")
  }

  def generateWriteSingleValue(field: FieldDescriptor, valueExpr: String)(
      fp: FunctionalPrinter
  ): FunctionalPrinter = {
    if (field.isMessage) {
      fp.add(s"""_output__.writeTag(${field.getNumber}, 2)
                |_output__.writeUInt32NoTag($valueExpr.serializedSize)
                |$valueExpr.writeTo(_output__)""".stripMargin)
    } else {
      val capTypeName = Types.capitalizedType(field.getType)
      fp.add(s"_output__.write$capTypeName(${field.getNumber}, $valueExpr)")
    }
  }

  def sizeExpressionForSingleField(field: FieldDescriptor, expr: String): String =
    if (field.isMessage) {
      val size = s"$expr.serializedSize"
      CodedOutputStream
        .computeTagSize(field.getNumber)
        .toString + s" + _root_.com.google.protobuf.CodedOutputStream.computeUInt32SizeNoTag($size) + $size"
    } else {
      val capTypeName = Types.capitalizedType(field.getType)
      s"_root_.com.google.protobuf.CodedOutputStream.compute${capTypeName}Size(${field.getNumber}, ${expr})"
    }

  def fieldAccessorSymbol(field: FieldDescriptor) =
    if (field.isInOneof)
      (field.getContainingOneof.scalaName.nameSymbol + "." + field.scalaName.asSymbol)
    else
      field.scalaName.asSymbol

  def toBaseTypeExpr(field: FieldDescriptor) =
    if (field.customSingleScalaTypeName.isDefined)
      FunctionApplication(field.typeMapper.fullName + ".toBase")
    else Identity

  def toBaseFieldType(field: FieldDescriptor) =
    if (field.isEnum)
      (toBaseTypeExpr(field) andThen MethodApplication("javaValueDescriptor"))
    else toBaseTypeExpr(field)

  def toBaseFieldTypeWithScalaDescriptors(field: FieldDescriptor) =
    if (field.isEnum)
      (toBaseTypeExpr(field) andThen MethodApplication("scalaValueDescriptor"))
    else toBaseTypeExpr(field)

  def toBaseType(field: FieldDescriptor)(expr: String) = {
    val suffix = if (field.isEnum) ".value" else ""
    toBaseTypeExpr(field).apply(expr, EnclosingType.None) + suffix
  }

  def toCustomTypeExpr(field: FieldDescriptor) =
    if (field.customSingleScalaTypeName.isEmpty) Identity
    else FunctionApplication(s"${field.typeMapper.fullName}.toCustom")

  def toCustomType(field: FieldDescriptor)(expr: String) =
    toCustomTypeExpr(field).apply(expr, EnclosingType.None)

  def generateSerializedSizeForField(
      fp: FunctionalPrinter,
      field: FieldDescriptor
  ): FunctionalPrinter = {
    val fieldNameSymbol = fieldAccessorSymbol(field)

    if (field.isRequired || field.noBoxRequired) {
      fp.add(s"""
                |{
                |  val __value = ${toBaseType(field)(fieldNameSymbol)}
                |  __size += ${sizeExpressionForSingleField(field, "__value")}
                |};""".stripMargin)
    } else if (field.isSingular) {
      fp.add(
        s"""
           |{
           |  val __value = ${toBaseType(field)(fieldNameSymbol)}
           |  if (${isNonEmpty("__value", field)}) {
           |    __size += ${sizeExpressionForSingleField(field, "__value")}
           |  }
           |};""".stripMargin
      )
    } else if (field.isOptional) {
      fp.add(
        s"""if ($fieldNameSymbol.isDefined) {
           |  val __value = ${toBaseType(field)(fieldNameSymbol + ".get")}
           |  __size += ${sizeExpressionForSingleField(field, "__value")}
           |};""".stripMargin
      )
    } else if (field.isRepeated) {
      val tagSize = CodedOutputStream.computeTagSize(field.getNumber)
      if (!field.isPacked) {
        Types.fixedSize(field.getType) match {
          case Some(size) =>
            fp.add(
              s"__size += ${size + tagSize} * ${field.collection.size(field.scalaName.asSymbol, EnclosingType.None)}"
            )
          case None =>
            fp.add(s"""${field.collection.foreach} { __item =>
                      |  val __value = ${toBaseType(field)("__item")}
                      |  __size += ${sizeExpressionForSingleField(field, "__value")}
                      |}""".stripMargin)
        }
      } else {
        val fieldName = field.scalaName
        fp.add(
          s"""if (${field.collection.nonEmptyCheck(fieldNameSymbol)}) {
             |  val __localsize = ${fieldName}SerializedSize
             |  __size += $tagSize + _root_.com.google.protobuf.CodedOutputStream.computeUInt32SizeNoTag(__localsize) + __localsize
             |}""".stripMargin
        )
      }
    } else throw new RuntimeException("Should not reach here.")
  }

  def generateSerializedSize(message: Descriptor)(fp: FunctionalPrinter) = {
    if (message.fields.nonEmpty || message.preservesUnknownFields) {
      fp.when(!message.isValueClass) {
        _.add(
          s"""@transient
             |${message.V.PrivateThis} var __serializedSizeMemoized: _root_.scala.Int = 0""".stripMargin
        )
      }.add(s"${message.V.PrivateThis} def __computeSerializedSize(): _root_.scala.Int = {")
        .indent
        .add("var __size = 0")
        .print(message.fields)(generateSerializedSizeForField)
        .when(message.preservesUnknownFields)(_.add("__size += unknownFields.serializedSize"))
        .add("__size")
        .outdent
        .add("}")
        .add("override def serializedSize: _root_.scala.Int = {")
        .indent
        .when(message.isValueClass) {
          _.add("__computeSerializedSize()")
        }
        .when(!message.isValueClass) {
          // Since zero is a valid value, we actually store the serialized
          // size plus one. Zero means that the memoized value was not initialized.
          _.add("""var __size = __serializedSizeMemoized
                  |if (__size == 0) {
                  |  __size = __computeSerializedSize() + 1
                  |  __serializedSizeMemoized = __size
                  |}
                  |__size - 1
                  |""".stripMargin)
        }
        .outdent
        .add("}")
    } else {
      fp.add("final override def serializedSize: _root_.scala.Int = 0")
    }
  }

  def generateSerializedSizeForPackedFields(message: Descriptor)(fp: FunctionalPrinter) =
    fp.print(message.fields.filter(_.isPacked)) { case (printer, field) =>
      val methodName = s"${field.scalaName}SerializedSize"
      printer
        .add(s"${message.V.PrivateThis} def $methodName = {") // closing brace is in each case
        .call({ fp =>
          Types.fixedSize(field.getType) match {
            case Some(size) =>
              fp.add(
                s"  $size * ${field.collection.size(field.scalaName.asSymbol, EnclosingType.None)}"
              ).add("}")
            case None =>
              val capTypeName = Types.capitalizedType(field.getType)
              val sizeFunc = FunctionApplication(
                s"_root_.com.google.protobuf.CodedOutputStream.compute${capTypeName}SizeNoTag"
              )
              val fromEnum = if (field.isEnum) MethodApplication("value") else Identity
              val fromCustom =
                if (field.customSingleScalaTypeName.isDefined)
                  FunctionApplication(s"${field.typeMapper.fullName}.toBase")
                else Identity
              val funcs    = List(sizeFunc, fromEnum, fromCustom)
              val sizeExpr = ExpressionBuilder.runSingleton(funcs)("__i")
              fp.indent
                .add(s"if (__${methodName}Field == 0) __${methodName}Field = {")
                .add(s"  var __s: _root_.scala.Int = 0")
                .add(s"  ${field.collection.foreach}(__i => __s += $sizeExpr)")
                .add(s"  __s")
                .add(s"}")
                .add(s"__${methodName}Field")
                .outdent
                .add("}") // closing brace for the method
                .add(
                  s"@transient ${message.V.PrivateThis} var __${methodName}Field: _root_.scala.Int = 0"
                )
          }
        })
    }

  private def composeGen(funcs: Seq[String], emitScala3Sources: Boolean) = {
    if (funcs.length == 1) {
      funcs(0)
    } else {
      if (emitScala3Sources) {
        s"(${funcs(0)})" + funcs.tail.map(func => s".compose($func)").mkString
      } else {
        s"(${funcs(0)} _)" + funcs.tail.map(func => s".compose($func)").mkString
      }
    }
  }

  private def isNonEmpty(expr: String, field: FieldDescriptor): String = {
    if (field.getType == Type.BYTES | field.getType == Type.STRING) s"!${expr}.isEmpty"
    else if (field.getType == Type.ENUM) s"${expr} != 0"
    else if (field.getType == Type.MESSAGE) s"${expr}.serializedSize != 0"
    else s"${expr} != ${defaultValueForGet(field, uncustomized = true)}"
  }

  def generateWriteTo(message: Descriptor)(fp: FunctionalPrinter) =
    fp.add(
      s"def writeTo(`_output__`: _root_.com.google.protobuf.CodedOutputStream): _root_.scala.Unit = {"
    ).indent
      .print(message.fields.sortBy(_.getNumber)) { case (printer, field) =>
        val fieldNameSymbol = fieldAccessorSymbol(field)
        val capTypeName     = Types.capitalizedType(field.getType)
        if (field.isPacked) {
          val writeFunc = composeGen(
            Seq(s"_output__.write${capTypeName}NoTag") ++ (
              if (field.isEnum) Seq(s"(_: ${field.baseSingleScalaTypeName}).value") else Nil
            ) ++ (
              if (field.customSingleScalaTypeName.isDefined)
                Seq(s"${field.typeMapper.fullName}.toBase")
              else Nil
            ),
            message.getFile.emitScala3Sources
          )

          printer.add(s"""if (${field.collection.nonEmptyCheck(fieldNameSymbol)}) {
                         |  _output__.writeTag(${field.getNumber}, 2)
                         |  _output__.writeUInt32NoTag(${field.scalaName}SerializedSize)
                         |  ${field.collection.foreach}($writeFunc)
                         |};""".stripMargin)
        } else if (field.isRequired || field.noBoxRequired) {
          printer
            .add("")
            .add("{")
            .indent
            .add(s"val __v = ${toBaseType(field)(fieldNameSymbol)}")
            .call(generateWriteSingleValue(field, "__v"))
            .outdent
            .add("};")
        } else if (field.isSingular) {
          // Singular that are not required are written only if they don't equal their default
          // value.
          printer
            .add(s"{")
            .indent
            .add(s"val __v = ${toBaseType(field)(fieldNameSymbol)}")
            .add(s"if (${isNonEmpty("__v", field)}) {")
            .indent
            .call(generateWriteSingleValue(field, "__v"))
            .outdent
            .add("}")
            .outdent
            .add("};")
        } else {
          printer
            .when(field.isRepeated)(_.add(s"${field.collection.foreach} { __v =>"))
            .when(!field.isRepeated)(_.add(s"${fieldAccessorSymbol(field)}.foreach { __v =>"))
            .indent
            .add(s"val __m = ${toBaseType(field)("__v")}")
            .call(generateWriteSingleValue(field, "__m"))
            .outdent
            .add("};")
        }
      }
      .when(message.preservesUnknownFields)(_.add("unknownFields.writeTo(_output__)"))
      .outdent
      .add("}")

  def constructorFields(message: Descriptor): Seq[ConstructorField] = {
    def annotations(field: FieldDescriptor) =
      if (field.annotationList.nonEmpty) field.annotationList else Nil

    val regularFields = message.fields.collect {
      case field if !field.isInOneof =>
        val typeName = field.scalaTypeName
        val ctorDefaultValue: Option[String] =
          if (field.noDefaultValueInConstructor) None
          else if (field.isOptional && field.supportsPresence) Some(C.None)
          else if (field.isSingular && !field.isRequired && !field.noBoxRequired)
            Some(defaultValueForGet(field).toString)
          else if (field.isRepeated && !field.collection.nonEmptyType)
            Some(s"${field.collection.empty}")
          else None

        ConstructorField(
          name = field.scalaName.asSymbol,
          typeName = typeName,
          default = ctorDefaultValue,
          index = field.getIndex,
          annotations = annotations(field)
        )
    }
    val oneOfFields = message.getRealOneofs.asScala.map { oneOf =>
      val ctorDefaultValue: Option[String] =
        if (message.getFile.noDefaultValuesInConstructor) None
        else Some(oneOf.empty.fullNameWithMaybeRoot(message))

      ConstructorField(
        name = oneOf.scalaName.nameSymbol,
        typeName = oneOf.scalaType.fullNameWithMaybeRoot(message),
        default = ctorDefaultValue,
        index = oneOf.getField(0).getIndex
      )
    }
    val maybeUnknownFields =
      if (message.preservesUnknownFields) {
        Seq(ConstructorField.unknownFields(message.unknownFieldsAnnotations))
      } else Seq()

    (regularFields ++ oneOfFields ++ maybeUnknownFields).sortBy(_.index)
  }

  def printConstructorFieldList(
      message: Descriptor
  )(printer: FunctionalPrinter): FunctionalPrinter = {
    printer.addWithDelimiter(",")(constructorFields(message).map(_.fullString))
  }

  def generateToJavaProto(message: Descriptor)(printer: FunctionalPrinter): FunctionalPrinter = {
    val myFullScalaName = message.scalaType.fullName
    printer
      .add(s"def toJavaProto(scalaPbSource: $myFullScalaName): ${message.javaTypeName} = {")
      .indent
      .add(s"val javaPbOut = ${message.javaTypeName}.newBuilder")
      .print(message.fields) { case (printer, field) =>
        printer.add(assignScalaFieldToJava("scalaPbSource", "javaPbOut", field))
      }
      .add("javaPbOut.build")
      .outdent
      .add("}")
  }

  def generateFromJavaProto(message: Descriptor)(printer: FunctionalPrinter): FunctionalPrinter = {
    val myFullScalaName = message.scalaType.fullName
    printer
      .add(
        s"def fromJavaProto(javaPbSource: ${message.javaTypeName}): $myFullScalaName = $myFullScalaName("
      )
      .indent
      .call { printer =>
        val normal = message.fields.collect {
          case field if !field.isInOneof =>
            val conversion =
              if (field.isMapField) javaMapFieldToScala("javaPbSource", field)
              else javaFieldToScala("javaPbSource", field)
            Seq(s"${field.scalaName.asSymbol} = $conversion")
        }
        val oneOfs = message.getRealOneofs.asScala.map { case oneOf =>
          val head =
            s"${oneOf.scalaName.nameSymbol} = javaPbSource.${oneOf.javaEnumName}.getNumber match {"
          val body = oneOf.fields.map { field =>
            s"  case ${field.getNumber} => ${field.oneOfTypeName.fullName}(${javaFieldToScala("javaPbSource", field)})"
          }
          val tail = Seq(s"  case _ => ${oneOf.empty.fullName}", "}")
          Seq(head) ++ body ++ tail
        }
        printer.addGroupsWithDelimiter(",")(normal ++ oneOfs)
      }
      .outdent
      .add(")")
  }

  def generateNoDefaultArgsFactory(
      message: Descriptor
  )(printer: FunctionalPrinter): FunctionalPrinter = {
    val fields = constructorFields(message).filterNot(_.isUnknownFields)

    printer
      .add(
        s"def of("
      )
      .indented(
        _.addWithDelimiter(",")(fields.map(_.nameAndType))
      )
      .add(
        s"): ${message.scalaType.fullNameWithMaybeRoot} = ${message.scalaType.fullNameWithMaybeRoot}("
      )
      .indented(
        _.addWithDelimiter(",")(fields.map(_.name))
      )
      .add(")")
  }

  def generateMessageReads(message: Descriptor)(printer: FunctionalPrinter): FunctionalPrinter = {
    def transform(field: FieldDescriptor) =
      (if (!field.isEnum) Identity
       else
         (MethodApplication("number") andThen
           FunctionApplication(field.getEnumType.scalaType.fullName + ".fromValue"))) andThen
        toCustomTypeExpr(field)

    val myFullScalaName = message.scalaType.fullName
    printer
      .add(s"""implicit def messageReads: _root_.scalapb.descriptors.Reads[${myFullScalaName}] = _root_.scalapb.descriptors.Reads{
              |  case _root_.scalapb.descriptors.PMessage(__fieldsMap) =>
              |    _root_.scala.Predef.require(__fieldsMap.keys.forall(_.containingMessage eq scalaDescriptor), \"FieldDescriptor does not match message type.\")
              |    ${myFullScalaName}(""".stripMargin)
      .indent(3)
      .call { printer =>
        val fields = message.fields.collect {
          case field if !field.isInOneof =>
            val readsEnclosing =
              if (field.supportsPresence) EnclosingType.ScalaOption
              else if (field.isRepeated && field.isMapField)
                EnclosingType.Collection(DescriptorImplicits.ScalaSeq, None)
              else if (field.isRepeated && !field.isMapField) field.collection.adapter match {
                case Some(_) => EnclosingType.Collection(DescriptorImplicits.ScalaIterator, None)
                case None    => EnclosingType.Collection(field.collectionType, None)
              }
              else EnclosingType.None
            val baseTypeName = readsEnclosing.asType(
              if (field.isEnum) "_root_.scalapb.descriptors.EnumValueDescriptor"
              else field.baseSingleScalaTypeName
            )
            val value =
              s"__fieldsMap.get(scalaDescriptor.findFieldByNumber(${field.getNumber}).get)"
            val e =
              if (field.supportsPresence)
                s"$value.flatMap(_.as[$baseTypeName])"
              else if (field.isRepeated) {
                val empty = readsEnclosing match {
                  case EnclosingType.Collection(s, _) => s"$s.empty"
                  case _ =>
                    throw new GeneratorException(
                      "Expected a collection enclosing. Pleae report this as a bug."
                    )
                }
                s"$value.map(_.as[${baseTypeName}]).getOrElse($empty)"
              } else if (field.isRequired || field.noBoxRequired)
                s"$value.get.as[$baseTypeName]"
              else {
                // This is for proto3, no default value.
                val t = defaultValueForGet(field, uncustomized = true) + (if (field.isEnum)
                                                                            ".scalaValueDescriptor"
                                                                          else "")
                s"$value.map(_.as[$baseTypeName]).getOrElse($t)"
              }

            val itemTypeTranform = transform(field)(
              e,
              sourceType = readsEnclosing,
              targetType = if (field.isMapField) field.enclosingType else readsEnclosing
            )

            val expr = field.collection.adapter match {
              case Some(tc) if (!field.isMapField()) =>
                s"${tc.fullName}.fromIteratorUnsafe($itemTypeTranform)"
              case _ => itemTypeTranform
            }

            s"${field.scalaName.asSymbol} = $expr"
        }
        val oneOfs = message.getRealOneofs.asScala.map { oneOf =>
          val elems = oneOf.fields.map { field =>
            val value =
              s"__fieldsMap.get(scalaDescriptor.findFieldByNumber(${field.getNumber}).get)"
            val typeName =
              if (field.isEnum) "_root_.scalapb.descriptors.EnumValueDescriptor"
              else field.baseSingleScalaTypeName
            val e = s"$value.flatMap(_.as[_root_.scala.Option[$typeName]])"
            (transform(field) andThen FunctionApplication(field.oneOfTypeName.fullName)).apply(
              e,
              EnclosingType.ScalaOption
            )
          }
          val expr =
            elems.reduceLeft((acc, field) =>
              s"$acc\n    .orElse[${oneOf.scalaType.fullName}]($field)"
            )
          s"${oneOf.scalaName.nameSymbol} = $expr\n    .getOrElse(${oneOf.empty.fullName})"
        }
        printer.addWithDelimiter(",")(fields ++ oneOfs)
      }
      .outdent(3)
      .add(s"""    )
              |  case _ => throw new RuntimeException(\"Expected PMessage\")
              |}""".stripMargin)
  }

  def generateDescriptors(message: Descriptor)(printer: FunctionalPrinter): FunctionalPrinter = {
    printer
      .add(
        s"""def javaDescriptor: _root_.com.google.protobuf.Descriptors.Descriptor = ${message.javaDescriptorSource}
           |def scalaDescriptor: _root_.scalapb.descriptors.Descriptor = ${message.scalaDescriptorSource}""".stripMargin
      )
  }

  def generateDefaultInstance(
      message: Descriptor
  )(printer: FunctionalPrinter): FunctionalPrinter = {
    val myFullScalaName = message.scalaType.fullName
    printer
      .add(s"lazy val defaultInstance = $myFullScalaName(")
      .indent
      .addWithDelimiter(",")(message.fields.collect {
        case field if !field.isInOneof =>
          val default = defaultValueForDefaultInstance(field)
          s"${field.scalaName.asSymbol} = $default"
      } ++ message.getRealOneofs.asScala.map { oneof =>
        s"${oneof.scalaName.nameSymbol} = ${oneof.empty.fullName}"
      })
      .outdent
      .add(")")
  }

  def generateMessageLens(message: Descriptor)(printer: FunctionalPrinter): FunctionalPrinter = {
    val className = message.scalaType.name

    def lensType(s: String) = s"_root_.scalapb.lenses.Lens[UpperPB, $s]"

    printer
      .add(
        s"implicit class ${className}Lens[UpperPB](_l: _root_.scalapb.lenses.Lens[UpperPB, ${message.scalaType.fullName}]) extends ${message.V.MessageLens}[UpperPB, ${message.scalaType.fullName}](_l) {"
      )
      .indent
      .print(message.fields) { case (printer, field) =>
        val fieldName = field.scalaName.asSymbol
        if (!field.isInOneof) {
          if (field.supportsPresence) {
            val optionLensName = "optional" + field.upperScalaName
            printer
              .add(
                s"""def $fieldName: ${lensType(
                    field.singleScalaTypeName
                  )} = field(_.${field.getMethod})((c_, f_) => c_.copy($fieldName = _root_.scala.Option(f_)))
                   |def ${optionLensName}: ${lensType(
                    field.scalaTypeName
                  )} = field(_.$fieldName)((c_, f_) => c_.copy($fieldName = f_))""".stripMargin
              )
          } else
            printer.add(
              s"def $fieldName: ${lensType(field.scalaTypeName)} = field(_.$fieldName)((c_, f_) => c_.copy($fieldName = f_))"
            )
        } else {
          val oneofName = field.getContainingOneof.scalaName.nameSymbol
          printer
            .add(
              s"def $fieldName: ${lensType(field.scalaTypeName)} = field(_.${field.getMethod})((c_, f_) => c_.copy($oneofName = ${field.oneOfTypeName
                  .fullNameWithMaybeRoot(message)}(f_)))"
            )
        }
      }
      .print(message.getRealOneofs.asScala) { case (printer, oneof) =>
        val oneofName = oneof.scalaName.nameSymbol
        printer
          .add(
            s"def $oneofName: ${lensType(oneof.scalaType.fullNameWithMaybeRoot(message))} = field(_.$oneofName)((c_, f_) => c_.copy($oneofName = f_))"
          )
      }
      .outdent
      .add("}")
  }

  def generateFieldNumbers(message: Descriptor)(printer: FunctionalPrinter): FunctionalPrinter = {
    printer
      .print(message.fields) { case (printer, field) =>
        printer.add(s"final val ${field.fieldNumberConstantName} = ${field.getNumber}")
      }
  }

  def generateTypeMappers(
      fields: Seq[FieldDescriptor],
      generatePublicConstructorParameters: Boolean
  )(printer: FunctionalPrinter): FunctionalPrinter = {
    val customizedFields: Seq[(FieldDescriptor, String)] = for {
      field  <- fields
      custom <- field.customSingleScalaTypeName
    } yield (field, custom)

    printer
      .print(customizedFields) { case (printer, (field, customType)) =>
        val modifier = {
          if (generatePublicConstructorParameters) ""
          else if (field.getFile().scalaPackage.fullName.isEmpty) "private "
          else s"private[${field.getFile().scalaPackage.fullName.split('.').last}] "
        }
        printer
          .add("@transient")
          .add(
            s"${modifier}val ${field.typeMapperValName}: _root_.scalapb.TypeMapper[${field.baseSingleScalaTypeName}, ${customType}] = implicitly[_root_.scalapb.TypeMapper[${field.baseSingleScalaTypeName}, ${customType}]]"
          )
      }
  }

  def generateCollectionAdapters(
      fields: Seq[FieldDescriptor]
  )(printer: FunctionalPrinter): FunctionalPrinter = {
    val fieldsWithAdapter: Seq[(FieldDescriptor, String)] = for {
      field   <- fields
      adapter <- field.collection.adapterClass
    } yield (field, adapter)

    printer
      .print(fieldsWithAdapter) { case (printer, (field, adapter)) =>
        val modifier =
          if (field.getFile().scalaPackage.fullName.isEmpty) "private"
          else s"private[${field.getFile().scalaPackage.fullName.split('.').last}]"
        printer
          .add("@transient")
          .add(
            s"$modifier val ${field.collection.adapter.get.nameSymbol}: _root_.scalapb.CollectionAdapter[${field.singleScalaTypeName}, ${field.scalaTypeName}] = $adapter()"
          )
      }
  }

  def generateTypeMappersForMapEntry(
      message: Descriptor
  )(printer: FunctionalPrinter): FunctionalPrinter = {
    val pairToMessage = {
      val k = if (message.mapType.keyField.supportsPresence) "Some(__p._1)" else "__p._1"
      val v = if (message.mapType.valueField.supportsPresence) "Some(__p._2)" else "__p._2"
      s"__p => ${message.scalaType.fullName}($k, $v)"
    }

    val messageToPair = {
      val k = if (message.mapType.keyField.supportsPresence) "__m.getKey" else "__m.key"
      val v = if (message.mapType.valueField.supportsPresence) "__m.getValue" else "__m.value"
      s"__m => ($k, $v)"
    }

    printer
      .add(
        s"""@transient
           |implicit val keyValueMapper: _root_.scalapb.TypeMapper[${message.scalaType.fullName}, ${message.mapType.pairType}] =
           |  _root_.scalapb.TypeMapper[${message.scalaType.fullName}, ${message.mapType.pairType}]($messageToPair)($pairToMessage)""".stripMargin
      )
  }

  def generateMessageCompanionMatcher(
      methodName: String,
      messageNumbers: Seq[(Descriptor, Int)],
      wildcardType: String
  )(
      fp: FunctionalPrinter
  ): FunctionalPrinter = {
    val signature =
      s"def $methodName(__number: _root_.scala.Int): _root_.scalapb.GeneratedMessageCompanion[$wildcardType] = "

    // Due to https://issues.scala-lang.org/browse/SI-9111 we can't directly return the companion
    // object.

    val outStr =
      s"var __out: _root_.scalapb.GeneratedMessageCompanion[${wildcardType}] = null"
    if (messageNumbers.nonEmpty)
      fp.add(signature + "{")
        .indent
        .add(outStr)
        .add("(__number: @_root_.scala.unchecked) match {")
        .indent
        .print(messageNumbers) { case (fp, (f, number)) =>
          fp.add(s"case $number => __out = ${f.scalaType.fullName}")
        }
        .outdent
        .add("}")
        .add("__out")
        .outdent
        .add("}")
    else fp.add(signature + "throw new MatchError(__number)")
  }

  // Finding companion objects by field number
  def generateMessageCompanionForField(
      message: Descriptor
  )(fp: FunctionalPrinter): FunctionalPrinter =
    generateMessageCompanionMatcher(
      "messageCompanionForFieldNumber",
      message.fields.filter(_.isMessage).map(f => (f.getMessageType, f.getNumber)),
      message.V.WildcardType
    )(fp)

  // Finding companion objects for nested types.
  def generateNestedMessagesCompanions(
      message: Descriptor
  )(fp: FunctionalPrinter): FunctionalPrinter = {
    val signature =
      s"lazy val nestedMessagesCompanions: ${message.V.CompSeqType} ="
    if (message.nestedTypes.isEmpty)
      fp.add(signature + " Seq.empty")
    else
      fp.add(signature)
        .indent
        .add(message.V.CompSeqType + "(")
        .indent
        .addWithDelimiter(",")(message.nestedTypes.map(m => m.scalaType.fullNameWithMaybeRoot))
        .outdent
        .add(")")
        .outdent
  }

  // Finding companion objects for top-level types.
  def generateMessagesCompanions(file: FileDescriptor)(fp: FunctionalPrinter): FunctionalPrinter = {
    val signature = s"lazy val messagesCompanions: ${file.V.CompSeqType} ="
    if (file.getMessageTypes.isEmpty)
      fp.add(signature + " Seq.empty")
    else
      fp.add(signature)
        .indent
        .add(file.V.CompSeqType + "(")
        .indent
        .addWithDelimiter(",")(file.getMessageTypes.asScala.map(_.scalaType.fullName).toSeq)
        .outdent
        .add(")")
        .outdent
  }

  def generateEnumCompanionForField(
      message: Descriptor
  )(fp: FunctionalPrinter): FunctionalPrinter = {
    val signature =
      s"def enumCompanionForFieldNumber(__fieldNumber: _root_.scala.Int): _root_.scalapb.GeneratedEnumCompanion[${message.V.WildcardType}] = "
    if (message.fields.exists(_.isEnum))
      fp.add(signature + "{")
        .indent
        .add("(__fieldNumber: @_root_.scala.unchecked) match {")
        .indent
        .print(message.fields.filter(_.isEnum)) { case (fp, f) =>
          fp.add(s"case ${f.getNumber} => ${f.getEnumType.scalaType.fullName}")
        }
        .outdent
        .add("}")
        .outdent
        .add("}")
    else fp.add(signature + "throw new MatchError(__fieldNumber)")
  }

  def printExtension(fp: FunctionalPrinter, fd: FieldDescriptor) = {
    fp.add(
      s"val ${fd.scalaName.asSymbol}: _root_.scalapb.GeneratedExtension[${fd.getContainingType.scalaType.fullName}, ${fd.scalaTypeName}] ="
    ).call { fp =>
      val (listLens, fromFieldToBase, fromBaseToField) = fd.getType match {
        case Type.DOUBLE =>
          (
            "fixed64Lens",
            FunctionApplication("java.lang.Double.longBitsToDouble"),
            FunctionApplication("java.lang.Double.doubleToLongBits")
          )
        case Type.FLOAT =>
          (
            "fixed32Lens",
            FunctionApplication("java.lang.Float.intBitsToFloat"),
            FunctionApplication("java.lang.Float.floatToIntBits")
          )
        case Type.INT64   => ("varintLens", Identity, Identity)
        case Type.UINT64  => ("varintLens", Identity, Identity)
        case Type.INT32   => ("varintLens", MethodApplication("toInt"), MethodApplication("toLong"))
        case Type.FIXED64 => ("fixed64Lens", Identity, Identity)
        case Type.FIXED32 => ("fixed32Lens", Identity, Identity)
        case Type.BOOL =>
          (
            "varintLens",
            OperatorApplication("!= 0"),
            FunctionApplication("_root_.scalapb.GeneratedExtension.Internal.bool2Long")
          )
        case Type.STRING =>
          (
            "lengthDelimitedLens",
            MethodApplication("toStringUtf8()"),
            FunctionApplication("_root_.com.google.protobuf.ByteString.copyFromUtf8")
          )
        case Type.GROUP => throw new RuntimeException("Not supported")
        case Type.MESSAGE =>
          (
            "lengthDelimitedLens",
            FunctionApplication(
              s"_root_.scalapb.GeneratedExtension.readMessageFromByteString(${fd.baseSingleScalaTypeName})"
            ),
            MethodApplication(s"toByteString")
          )
        case Type.BYTES => ("lengthDelimitedLens", Identity, Identity)
        case Type.UINT32 =>
          ("varintLens", MethodApplication("toInt"), MethodApplication("toLong"))
        case Type.ENUM =>
          (
            "varintLens",
            MethodApplication("toInt") andThen FunctionApplication(
              fd.baseSingleScalaTypeName + ".fromValue"
            ),
            MethodApplication("value") andThen MethodApplication("toLong")
          )
        case Type.SFIXED32 => ("fixed32Lens", Identity, Identity)
        case Type.SFIXED64 => ("fixed64Lens", Identity, Identity)
        case Type.SINT32 =>
          (
            "varintLens",
            MethodApplication("toInt") andThen FunctionApplication(
              "_root_.com.google.protobuf.CodedInputStream.decodeZigZag32"
            ),
            FunctionApplication(
              "_root_.com.google.protobuf.CodedOutputStream.encodeZigZag32"
            ) andThen (
              MethodApplication("toLong")
            )
          )
        case Type.SINT64 =>
          (
            "varintLens",
            FunctionApplication("_root_.com.google.protobuf.CodedInputStream.decodeZigZag64"),
            FunctionApplication("_root_.com.google.protobuf.CodedOutputStream.encodeZigZag64")
          )
      }
      val fromFieldToCustom = fromFieldToBase andThen toCustomTypeExpr(fd)
      val fromCustomToField = toBaseTypeExpr(fd) andThen fromBaseToField

      val (factoryMethod, args) = {
        if (fd.supportsPresence && !fd.isMessage)
          ("_root_.scalapb.GeneratedExtension.forOptionalUnknownField", Seq.empty)
        else if (fd.supportsPresence && fd.isMessage)
          ("_root_.scalapb.GeneratedExtension.forOptionalUnknownMessageField", Seq.empty)
        else if (fd.isRepeated && fd.isPackable)
          (
            "_root_.scalapb.GeneratedExtension.forRepeatedPackable",
            Seq(fd.getType match {
              case Type.DOUBLE | Type.FIXED64 | Type.SFIXED64 => "_.readFixed64()"
              case Type.FLOAT | Type.FIXED32 | Type.SFIXED32  => "_.readFixed32()"
              case Type.UINT32 | Type.UINT64 | Type.INT32 | Type.INT64 | Type.ENUM |
                  Type.BOOL | Type.SINT32 | Type.SINT64 =>
                "_.readInt64()"
              case _ =>
                throw new GeneratorException(s"Unexpected packable type: ${fd.getType.name()}")
            })
          )
        else if (fd.isRepeated && !fd.isPackable) {
          ("_root_.scalapb.GeneratedExtension.forRepeatedUnpackable", Seq())
        } else
          (
            if (!fd.isMessage) "_root_.scalapb.GeneratedExtension.forSingularUnknownField"
            else "_root_.scalapb.GeneratedExtension.forSingularUnknownMessageField",
            Seq(defaultValueForGet(fd))
          )
      }
      val argList = Seq(
        s"{__valueIn => ${fromFieldToCustom("__valueIn", EnclosingType.None)}}",
        s"{(__valueIn: ${fd.singleScalaTypeName}) => ${fromCustomToField("__valueIn", EnclosingType.None)}}"
      ) ++ args
      fp.add(
        s"  $factoryMethod(${fd.getNumber}, _root_.scalapb.UnknownFieldSet.Field.$listLens)(${argList
            .mkString(", ")})"
      )
    }
  }

  def generateMessageCompanion(
      message: Descriptor
  )(printer: FunctionalPrinter): FunctionalPrinter = {
    val className         = message.scalaType.nameSymbol
    val companionType     = message.companionBaseClasses.mkString(" with ")
    val companionTypeDecl = message.companionBaseClasses.mkString(message.V.WithOperator)
    printer
      .seq(message.companionAnnotationList)
      .add(s"""object $className extends $companionType {
              |  implicit def messageCompanion: $companionTypeDecl = this""".stripMargin)
      .indent
      .when(message.javaConversions)(generateToJavaProto(message))
      .when(message.javaConversions)(generateFromJavaProto(message))
      .call(ParseFromGenerator.generateParseFrom(implicits, this, message))
      .call(generateMessageReads(message))
      .call(generateDescriptors(message))
      .call(generateMessageCompanionForField(message))
      .call(generateNestedMessagesCompanions(message))
      .call(generateEnumCompanionForField(message))
      .call(generateDefaultInstance(message))
      .print(message.getEnumTypes.asScala)(printEnum)
      .print(message.getRealOneofs.asScala)(printOneof)
      .print(message.nestedTypes)(printMessage)
      .print(message.getExtensions.asScala)(printExtension)
      .when(message.generateLenses)(generateMessageLens(message))
      .call(generateFieldNumbers(message))
      .call(
        generateTypeMappers(
          message.fields ++ message.getExtensions.asScala,
          message.getFile.generatePublicConstructorParameters
        )
      )
      .call(generateCollectionAdapters(message.fields ++ message.getExtensions.asScala))
      .when(message.isMapEntry)(generateTypeMappersForMapEntry(message))
      .call(generateNoDefaultArgsFactory(message))
      .add(s"// @@protoc_insertion_point(${message.messageCompanionInsertionPoint.insertionPoint})")
      .outdent
      .add("}")
      .add("")
  }

  def generateScalaDoc(enumDesc: EnumDescriptor): PrinterEndo = { fp =>
    val lines = asScalaDocBlock(enumDesc.comment.map(_.split('\n').toSeq).getOrElse(Seq.empty))
    fp.add(lines: _*)
  }

  def generateScalaDoc(enumValue: EnumValueDescriptor): PrinterEndo = { fp =>
    val lines = asScalaDocBlock(enumValue.comment.map(_.split('\n').toSeq).getOrElse(Seq.empty))
    fp.add(lines: _*)
  }

  def generateScalaDoc(message: Descriptor): PrinterEndo = { fp =>
    val mainDoc: Seq[String] = {
      val base = message.comment.map(_.split('\n').toSeq).getOrElse(Seq.empty)
      // Hack: there's an heading in `any.proto` that causes a Scaladoc
      // warning.
      if (message.getFullName() != "google.protobuf.Any")
        base
      else
        base.collect {
          case l if l.trim == "====" => ""
          case l                     => l
        }
    }

    val fieldsDoc: Seq[String] = message.fields
      .filterNot(_.isInOneof)
      .map { fd => (fd, fd.comment.map(_.split("\n").toSeq).getOrElse(Seq.empty)) }
      .filter(_._2.nonEmpty)
      .flatMap { case (fd, lines) =>
        Seq(s"@param ${fd.scalaName}") ++ lines.map("  " + _)
      }

    val sep = if (mainDoc.nonEmpty && fieldsDoc.nonEmpty) Seq("") else Seq.empty

    fp.add(asScalaDocBlock(mainDoc ++ sep ++ fieldsDoc): _*)
  }

  def printMessage(printer: FunctionalPrinter, message: Descriptor): FunctionalPrinter = {
    val fullName = message.scalaType.fullNameWithMaybeRoot(message)
    val derives =
      if (message.derives.nonEmpty) message.derives.mkString(" derives ", ", ", "") else ""
    printer
      .call(new SealedOneofsGenerator(message, implicits).generateSealedOneofTrait)
      .call(generateScalaDoc(message))
      .add(s"@SerialVersionUID(0L)")
      .seq(message.annotationList)
      .add(s"final case class ${message.scalaType.nameSymbol}(")
      .indent
      .indent
      .call(printConstructorFieldList(message))
      .add(s") extends ${message.baseClasses.mkString(" with ")}${derives} {")
      .call(generateSerializedSizeForPackedFields(message))
      .call(generateSerializedSize(message))
      .call(generateWriteTo(message))
      .print(message.fields) { case (printer, field) =>
        val withMethod  = "with" + field.upperScalaName
        val clearMethod = "clear" + field.upperScalaName
        val singleType  = field.singleScalaTypeName
        printer
          .when(
            (field.supportsPresence || field.isInOneof) && (message.generateGetters || message.generateLenses || message.isMapEntry)
          ) { p =>
            val default = defaultValueForGet(field)
            val scope   = if (message.generateGetters) "" else "private "
            p.add(
              s"${scope}def ${field.getMethod}: ${field.singleScalaTypeName} = ${fieldAccessorSymbol(field)}.getOrElse($default)"
            )
          }
          .when(field.supportsPresence) { p =>
            p.add(
              s"""def $clearMethod: ${message.scalaType.nameSymbol} = copy(${field.scalaName.asSymbol} = ${C.None})
                 |def $withMethod(__v: ${singleType}): ${message.scalaType.nameSymbol} = copy(${field.scalaName.asSymbol} = Option(__v))""".stripMargin
            )
          }
          .when(field.isInOneof) { p =>
            p.add(
              s"""def $withMethod(__v: ${singleType}): ${message.scalaType.nameSymbol} = copy(${field.getContainingOneof.scalaName.nameSymbol} = ${field.oneOfTypeName
                  .fullNameWithMaybeRoot(message)}(__v))"""
            )
          }
          .when(field.isRepeated) { p =>
            val concat = field.collection.concat(field.scalaName.asSymbol, "__vs")
            p.when(!field.collection.nonEmptyType)(
              _.add(
                s"def $clearMethod = copy(${field.scalaName.asSymbol} = ${field.collection.empty})"
              )
            ).add(
              s"""|def add${field.upperScalaName}(__vs: $singleType *): ${message.scalaType.nameSymbol} = addAll${field.upperScalaName}(__vs)
                  |def addAll${field.upperScalaName}(__vs: Iterable[$singleType]): ${message.scalaType.nameSymbol} = copy(${field.scalaName.asSymbol} = $concat)""".stripMargin
            )
          }
          .when(field.isRepeated || field.isSingular) {
            _.add(
              s"def $withMethod(__v: ${field.scalaTypeName}): ${message.scalaType.nameSymbol} = copy(${field.scalaName.asSymbol} = __v)"
            )
          }
      }
      .print(message.getRealOneofs.asScala) { case (printer, oneof) =>
        printer.add(
          s"""def clear${oneof.scalaType.name}: ${message.scalaType.nameSymbol} = copy(${oneof.scalaName.nameSymbol} = ${oneof.empty
              .fullNameWithMaybeRoot(message)})
             |def with${oneof.scalaType.name}(__v: ${oneof.scalaType.fullNameWithMaybeRoot(
              message
            )}): ${message.scalaType.nameSymbol} = copy(${oneof.scalaName.nameSymbol} = __v)""".stripMargin
        )
      }
      .when(message.preservesUnknownFields)(
        _.add(
          s"""def withUnknownFields(__v: ${C.UnknownFieldSet}) = copy(unknownFields = __v)
             |def discardUnknownFields = copy(unknownFields = ${C.UnknownFieldSetEmpty})""".stripMargin
        )
      )
      .call(generateGetField(message))
      .call(generateGetFieldPValue(message))
      .when(!params.singleLineToProtoString)(
        _.add(
          s"def toProtoString: _root_.scala.Predef.String = " +
            "_root_.scalapb.TextFormat.printToUnicodeString(this)"
        )
      )
      .when(params.singleLineToProtoString)(
        _.add(
          s"def toProtoString: _root_.scala.Predef.String = " +
            "_root_.scalapb.TextFormat.printToSingleLineUnicodeString(this)"
        )
      )
      .when(params.asciiFormatToString)(
        _.add("override def toString: _root_.scala.Predef.String = toProtoString")
      )
      .add(s"def companion: ${fullName}.type = ${fullName}")
      .when(message.isSealedOneofType) { fp =>
        val scalaType = message.sealedOneofScalaType
        val name      = message.sealedOneofTraitScalaType.name
        fp.add(
          s"def to$name: $scalaType = ${message.sealedOneofTypeMapper.fullName}.toCustom(this)"
        )
      }
      .add(s"// @@protoc_insertion_point(${message.messageClassInsertionPoint.insertionPoint})")
      .outdent
      .outdent
      .add(s"""}
              |""".stripMargin)
      .call(generateMessageCompanion(message))
  }

  @deprecated("Use overloaded method that takes includePreamble=true", "0.11.11")
  def scalaFileHeader(file: FileDescriptor, javaConverterImport: Boolean): FunctionalPrinter =
    scalaFileHeader(file, javaConverterImport, true)

  def scalaFileHeader(
      file: FileDescriptor,
      javaConverterImport: Boolean,
      includePreamble: Boolean
  ): FunctionalPrinter = {
    if (file.scalaOptions.getPreambleList.asScala.nonEmpty && !file.scalaOptions.getSingleFile) {
      throw new GeneratorException(
        s"${file.getName}: single_file must be true when a preamble is provided."
      )
    }
    new FunctionalPrinter()
      .add(s"""// Generated by the Scala Plugin for the Protocol Buffer Compiler.
              |// Do not edit!
              |""".stripMargin)
      .when(file.scalaPackage.fullName.nonEmpty)(
        _.add("package " + file.scalaPackage.fullName).add()
      )
      .when(javaConverterImport)(
        _.add(s"import _root_.scalapb.internal.compat.JavaConverters.${file.V.WildcardImport}")
          .add()
      )
      .print(file.scalaOptions.getImportList.asScala) { case (printer, i) =>
        printer.add(s"import $i")
      }
      .newline
      .when(includePreamble)(
        _.seq(file.scalaOptions.getPreambleList.asScala.toSeq)
          .when(file.scalaOptions.getPreambleList.asScala.nonEmpty)(_.add(""))
      )
  }

  def updateDescriptor(tmp: FileDescriptor): DescriptorProtos.FileDescriptorProto = {
    def updateField(field: FieldDescriptor): DescriptorProtos.FieldDescriptorProto = {
      val fb         = field.toProto.toBuilder
      val extBuilder = fb.getOptions.getExtension[FieldOptions](Scalapb.field).toBuilder
      assert(!extBuilder.hasScalaName || extBuilder.getScalaName == field.scalaName)
      extBuilder.setScalaName(field.scalaName)
      fb.getOptionsBuilder.setExtension(Scalapb.field, extBuilder.build())
      fb.build()
    }

    def updateEnum(enumDescriptor: EnumDescriptor): DescriptorProtos.EnumDescriptorProto = {
      enumDescriptor
        .toProto()
        .toBuilder()
        .clearValue()
        .addAllValue(
          enumDescriptor.getValues().asScala.map(updateEnumValue(_)).asJava
        )
        .build()
    }

    def updateEnumValue(
        enumValue: EnumValueDescriptor
    ): DescriptorProtos.EnumValueDescriptorProto = {
      val ev = enumValue.toProto().toBuilder()
      val extBuilder =
        enumValue.getOptions().getExtension[Scalapb.EnumValueOptions](Scalapb.enumValue).toBuilder
      assert(!extBuilder.hasScalaName || extBuilder.getScalaName == enumValue.scalaName)
      extBuilder.setScalaName(enumValue.scalaName)
      ev.getOptionsBuilder().setExtension(Scalapb.enumValue, extBuilder.build())
      ev.build()
    }

    def updateMessageType(msg: Descriptor): DescriptorProtos.DescriptorProto = {
      msg.toProto.toBuilder
        .clearField()
        .addAllField(msg.getFields.asScala.map(updateField(_)).asJava)
        .clearNestedType()
        .addAllNestedType(msg.getNestedTypes.asScala.map(updateMessageType(_)).asJava)
        .clearEnumType()
        .addAllEnumType(msg.getEnumTypes.asScala.map(updateEnum(_)).asJava)
        .build()
    }

    val fileProto = tmp.toProto
    fileProto.toBuilder
      .clearMessageType()
      .addAllMessageType(tmp.getMessageTypes().asScala.map(updateMessageType(_)).asJava)
      .clearEnumType()
      .addAllEnumType(tmp.getEnumTypes().asScala.map(updateEnum(_)).asJava)
      .build
  }

  def generateFileDescriptor(file: FileDescriptor)(fp: FunctionalPrinter): FunctionalPrinter = {
    val descriptor = {
      val withScalaName = updateDescriptor(file)

      if (file.retainSourceCodeInfo) withScalaName
      else withScalaName.toBuilder.clearSourceCodeInfo.build
    }

    // Encoding the file descriptor proto in base64. JVM has a limit on string literal to be up
    // to 64k, so we chunk it into a sequence and combining in run time.  The chunks are less
    // than 64k to account for indentation and new lines.
    val base64: Seq[Seq[String]] = scalapb.internal.Encoding
      .toBase64(descriptor.toByteArray)
      .grouped(55000)
      .map { group =>
        val lines = ("\"\"\"" + group).grouped(100).toSeq
        lines.dropRight(1) :+ (lines.last + "\"\"\"")
      }
      .toSeq
    fp.add("private lazy val ProtoBytes: _root_.scala.Array[Byte] =")
      .add("    scalapb.Encoding.fromBase64(scala.collection.immutable.Seq(")
      .addGroupsWithDelimiter(",")(base64)
      .add("    ).mkString)")
      .add("lazy val scalaDescriptor: _root_.scalapb.descriptors.FileDescriptor = {")
      .add(
        "  val scalaProto = com.google.protobuf.descriptor.FileDescriptorProto.parseFrom(ProtoBytes)"
      )
      .add(
        "  _root_.scalapb.descriptors.FileDescriptor.buildFrom(scalaProto, dependencies.map(_.scalaDescriptor))"
      )
      .add("}")
      .when(file.javaConversions) {
        _.add("lazy val javaDescriptor: com.google.protobuf.Descriptors.FileDescriptor =")
          .add(s"  ${file.javaFullOuterClassName}.getDescriptor()")
      }
      .when(!file.javaConversions) {
        _.add("lazy val javaDescriptor: com.google.protobuf.Descriptors.FileDescriptor = {")
          .add(
            "  val javaProto = com.google.protobuf.DescriptorProtos.FileDescriptorProto.parseFrom(ProtoBytes)"
          )
          .add(
            "  com.google.protobuf.Descriptors.FileDescriptor.buildFrom(javaProto, _root_.scala.Array("
          )
          .addWithDelimiter(",")(file.getDependencies.asScala.map { d =>
            s"    ${d.fileDescriptorObject.fullName}.javaDescriptor"
          }.toSeq)
          .add("  ))")
          .add("}")
      }
      .add(
        """@deprecated("Use javaDescriptor instead. In a future version this will refer to scalaDescriptor.", "ScalaPB 0.5.47")"""
      )
      .add("def descriptor: com.google.protobuf.Descriptors.FileDescriptor = javaDescriptor")
  }

  def generateServiceFiles(file: FileDescriptor): Seq[CodeGeneratorResponse.File] = {
    if (params.grpc) {
      file.getServices.asScala.map { service =>
        val p = new GrpcServicePrinter(service, implicits)
        val code = scalaFileHeader(
          file,
          file.javaConversions && file.getMessageTypes.asScala.exists(
            messageContainsRepeatedFields
          ),
          includePreamble = false
        ).call(p.printService).result()
        val b = CodeGeneratorResponse.File.newBuilder()
        b.setName(file.scalaDirectory + "/" + service.companionObject.name + ".scala")
        b.setContent(code)
        b.build
      }
    }.toSeq
    else Nil
  }

  def generateFileObject(file: FileDescriptor)(fp: FunctionalPrinter): FunctionalPrinter = {
    fp.add(
      s"object ${file.fileDescriptorObject.nameSymbol} extends _root_.scalapb.GeneratedFileObject {"
    ).indent
      .when(file.getDependencies.isEmpty) {
        _.add("lazy val dependencies: Seq[_root_.scalapb.GeneratedFileObject] = Seq.empty")
      }
      .when(!file.getDependencies.isEmpty) {
        _.add("lazy val dependencies: Seq[_root_.scalapb.GeneratedFileObject] = Seq(").indent
          .addWithDelimiter(",")(
            file.getDependencies.asScala.map(_.fileDescriptorObject.fullName).toSeq
          )
          .outdent
          .add(")")
      }
      .call(generateMessagesCompanions(file)(_))
      .call(generateFileDescriptor(file)(_))
      .print(file.getExtensions.asScala)(printExtension(_, _))
      .call(
        generateTypeMappers(
          file.getExtensions.asScala.toSeq,
          file.generatePublicConstructorParameters
        )
      )
      .outdent
      .add("}")
  }

  private def messageContainsRepeatedFields(message: Descriptor): Boolean = {
    message.fields.exists(_.isRepeated) || message.nestedTypes.exists(messageContainsRepeatedFields)
  }

  def generateSingleScalaFileForFileDescriptor(
      file: FileDescriptor
  ): Seq[CodeGeneratorResponse.File] = {
    val code =
      scalaFileHeader(
        file,
        file.javaConversions && file.getMessageTypes.asScala.exists(messageContainsRepeatedFields),
        includePreamble = true
      ).print(file.getEnumTypes.asScala)(printEnum)
        .print(file.getMessageTypes.asScala)(printMessage)
        .call(generateFileObject(file))
        .result()
    val b = CodeGeneratorResponse.File.newBuilder()
    b.setName(file.scalaFileName)
    b.setContent(code)
    generateServiceFiles(file) :+ b.build
  }

  def generateMultipleScalaFilesForFileDescriptor(
      file: FileDescriptor
  ): Seq[CodeGeneratorResponse.File] = {
    val serviceFiles = generateServiceFiles(file)

    val enumFiles = for {
      enumDesc <- file.getEnumTypes.asScala
    } yield {
      val b = CodeGeneratorResponse.File.newBuilder()
      b.setName(file.scalaDirectory + "/" + enumDesc.getName + ".scala")
      b.setContent(
        scalaFileHeader(file, false, false)
          .call(printEnum(_, enumDesc))
          .result()
      )
      b.build
    }

    val messageFiles = for {
      message <- file.getMessageTypes.asScala if !message.isSealedOneofCase
    } yield {
      val b     = CodeGeneratorResponse.File.newBuilder()
      val cases = message.sealedOneofCases.getOrElse(Seq.empty)
      b.setName(message.scalaFileName)
      b.setContent(
        scalaFileHeader(
          file,
          javaConverterImport = file.javaConversions && (messageContainsRepeatedFields(
            message
          ) || cases.exists(messageContainsRepeatedFields(_))),
          true
        ).call(printMessage(_, message))
          .print(cases)(printMessage)
          .result()
      )
      b.build
    }

    val fileDescriptorObjectFile = {
      val b = CodeGeneratorResponse.File.newBuilder()
      b.setName(file.scalaFileName)
      b.setContent(
        scalaFileHeader(file, false, true)
          .call(generateFileObject(file))
          .result()
      )
      b.build
    }

    serviceFiles ++ enumFiles ++ messageFiles :+ fileDescriptorObjectFile
  }
}

private[scalapb] object C {
  val None                 = "_root_.scala.None"
  val UnknownFieldSet      = "_root_.scalapb.UnknownFieldSet"
  val UnknownFieldSetEmpty = "_root_.scalapb.UnknownFieldSet.empty"
}

object ProtobufGenerator {
  def parseParameters(params: String): Either[String, GeneratorParams] =
    GeneratorParams.fromString(params)

  def handleCodeGeneratorRequest(request: CodeGenRequest): CodeGenResponse = {
    parseParameters(request.parameter) match {
      case Right(params) =>
        try {
          val implicits = DescriptorImplicits.fromCodeGenRequest(params, request)
          val generator = new ProtobufGenerator(params, implicits)
          val validator = new ProtoValidation(implicits)
          validator.validateFiles(request.allProtos)
          import implicits.ExtendedFileDescriptor
          val files = request.filesToGenerate.filterNot(_.disableOutput).flatMap { file =>
            if (file.scalaOptions.getSingleFile)
              generator.generateSingleScalaFileForFileDescriptor(file)
            else generator.generateMultipleScalaFilesForFileDescriptor(file)
          }
          CodeGenResponse.succeed(
            files,
            Set(
              CodeGeneratorResponse.Feature.FEATURE_PROTO3_OPTIONAL,
              CodeGeneratorResponse.Feature.FEATURE_SUPPORTS_EDITIONS
            )
          )
        } catch {
          case e: GeneratorException =>
            CodeGenResponse.fail(e.message)
        }
      case Left(error) =>
        CodeGenResponse.fail(error)
    }
  }

  def asScalaDocBlock(contentLines: Seq[String]): Seq[String] = {
    if (contentLines.nonEmpty) {
      contentLines.zipWithIndex.map { case (line, index) =>
        val prefix = if (index == 0) "/**" else "  *"
        if (line.startsWith(" ") || line.isEmpty) (prefix + line) else (prefix + " " + line)
      } :+ "  */"
    } else contentLines
  }

  val deprecatedAnnotation: String =
    """@scala.deprecated(message="Marked as deprecated in proto file", "")"""

  private[scalapb] def escapeScalaString(raw: String): String =
    raw
      .map {
        case '\b'                      => "\\b"
        case '\f'                      => "\\f"
        case '\n'                      => "\\n"
        case '\r'                      => "\\r"
        case '\t'                      => "\\t"
        case '\\'                      => "\\\\"
        case '\"'                      => "\\\""
        case '\''                      => "\\\'"
        case u if u >= ' ' && u <= '~' => u.toString
        case c: Char                   => "\\u%4s".format(c.toInt.toHexString).replace(' ', '0')
      }
      .mkString("\"", "", "\"")
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy