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

scalapb.FieldMaskUtil.scala Maven / Gradle / Ivy

The newest version!
package scalapb

import com.google.protobuf.field_mask.FieldMask
import scala.annotation.tailrec

object FieldMaskUtil {
  private[this] def isLower(c: Char): Boolean = {
    'a' <= c && c <= 'z'
  }

  private[this] def isUpper(c: Char): Boolean = {
    'A' <= c && c <= 'Z'
  }

  private[this] def toUpper(c: Char): Char = {
    if (isLower(c)) {
      (c - 32).asInstanceOf[Char]
    } else {
      c
    }
  }

  private[this] def toLower(c: Char): Char = {
    if (isUpper(c)) {
      (c + 32).asInstanceOf[Char]
    } else {
      c
    }
  }

  private[this] def toLowerCase(s: String, b: Appendable): Unit = {
    @tailrec
    def loop(i: Int): Unit = {
      if (i < s.length) {
        b.append(toLower(s(i)))
        loop(i + 1)
      }
    }

    loop(0)
  }

  private[scalapb] def lowerSnakeCaseToCamelCase(name: String): String = {
    lowerSnakeCaseToCamelCaseWithBuffer(name, new java.lang.StringBuilder(name.length)).toString
  }

  private[scalapb] def lowerSnakeCaseToCamelCaseWithBuffer(
      name: String,
      buf: Appendable
  ): buf.type = {
    def toProperCase(s: String): Unit = if (!s.isEmpty) {
      buf.append(toUpper(s(0)))
      toLowerCase(s.substring(1), buf)
    }

    val array = name.split("\\_")

    @tailrec
    def loop(i: Int): Unit = {
      if (i < array.length) {
        toProperCase(array(i))
        loop(i + 1)
      }
    }

    if (array.nonEmpty) {
      toLowerCase(array(0), buf)
      loop(1)
    }
    buf
  }

  private[scalapb] def camelCaseToSnakeCase(str: String): String = {
    if (str.isEmpty) {
      ""
    } else {
      val buf = new java.lang.StringBuilder(str.length)
      buf.append(toLower(str(0)))

      @tailrec
      def loop(i: Int): String = {
        if (i < str.length) {
          val c = str(i)
          if (isUpper(c)) {
            buf.append('_')
            buf.append((c + 32).asInstanceOf[Char])
          } else {
            buf.append(c)
          }
          loop(i + 1)
        } else {
          buf.toString
        }
      }

      loop(1)
    }
  }

  def toJsonString(fieldMask: FieldMask): String = {
    val buf   = new java.lang.StringBuilder()
    var first = true
    fieldMask.paths.foreach { path =>
      if (!path.isEmpty) {
        if (!first) {
          buf.append(',')
        }
        lowerSnakeCaseToCamelCaseWithBuffer(path, buf)
        first = false
      }
    }
    buf.toString
  }

  def fromJsonString(value: String): FieldMask = {
    val result = value
      .split(",")
      .iterator
      .withFilter(_.nonEmpty)
      .map { path => camelCaseToSnakeCase(path) }
      .toList
    FieldMask(result)
  }

  /** Applies a field mask to a message.
    */
  def applyFieldMask[M <: GeneratedMessage: GeneratedMessageCompanion](
      message: M,
      fieldMask: FieldMask
  ): M = {
    FieldMaskTree(fieldMask).applyToMessage(message)
  }

  /** Checks that a field mask selects a certain message field number.
    */
  def containsFieldNumber[M <: GeneratedMessage: GeneratedMessageCompanion](
      fieldMask: FieldMask,
      fieldNumber: Int
  ): Boolean = {
    FieldMaskTree(fieldMask).containsField[M](fieldNumber)
  }

  /** Checks that field masks corresponds to message schema.
    */
  def isValid[M <: GeneratedMessage: GeneratedMessageCompanion](fieldMask: FieldMask): Boolean = {
    FieldMaskTree(fieldMask).isValidFor[M]
  }

  /** Constructs a field mask based on message field numbers.
    * @return
    *   Some(mask) if all fields number are valid, `None` otherwise.
    */
  def fromFieldNumbers[M <: GeneratedMessage: GeneratedMessageCompanion](
      fieldNumbers: Int*
  ): Option[FieldMask] = {
    val companion = implicitly[GeneratedMessageCompanion[M]]
    val fields = fieldNumbers.map { fieldNumber =>
      companion.scalaDescriptor.findFieldByNumber(fieldNumber)
    }
    val fieldNames = fields.foldLeft[Option[Vector[String]]](Some(Vector.empty)) {
      case (Some(acc), Some(field)) => Some(acc :+ field.name)
      case _                        => None
    }
    fieldNames.map(names => FieldMask(paths = names))
  }

  /** Constructs a field mask based on a predicate for message field numbers.
    */
  def selectFieldNumbers[M <: GeneratedMessage: GeneratedMessageCompanion](
      fieldNumberPredicate: Int => Boolean
  ): FieldMask = {
    val companion    = implicitly[GeneratedMessageCompanion[M]]
    val fieldNumbers = companion.scalaDescriptor.fields.map(_.number).filter(fieldNumberPredicate)
    fromFieldNumbers[M](fieldNumbers: _*).get
  }

  /** Unions two or more field masks.
    */
  def union(fieldMask: FieldMask, otherMasks: FieldMask*): FieldMask = {
    val tree = otherMasks.foldLeft(FieldMaskTree(fieldMask)) { case (acc, nextMask) =>
      FieldMaskTree.union(acc, FieldMaskTree(nextMask))
    }
    tree.fieldMask
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy