瀏覽代碼

Merge pull request #1505 from pouyayarandi/add-proto-reflection

Add FieldMask utilities to Message types
Tim Kientzle 1 年之前
父節點
當前提交
3a4c97df2e

+ 3 - 0
Sources/SwiftProtobuf/CMakeLists.txt

@@ -50,12 +50,15 @@ add_library(SwiftProtobuf
   MathUtils.swift
   Message+AnyAdditions.swift
   Message+BinaryAdditions.swift
+  Message+FieldMask.swift
   Message+JSONAdditions.swift
   Message+JSONArrayAdditions.swift
   Message+TextFormatAdditions.swift
   Message.swift
   MessageExtension.swift
   NameMap.swift
+  PathDecoder.swift
+  PathVisitor.swift
   ProtobufAPIVersionCheck.swift
   ProtobufMap.swift
   ProtoNameProviding.swift

+ 187 - 5
Sources/SwiftProtobuf/Google_Protobuf_FieldMask+Extensions.swift

@@ -13,11 +13,6 @@
 ///
 // -----------------------------------------------------------------------------
 
-// TODO: We should have utilities to apply a fieldmask to an arbitrary
-// message, intersect two fieldmasks, etc.
-// Google's C++ implementation does this by having utilities
-// to build a tree of field paths that can be easily intersected,
-// unioned, traversed to apply to submessages, etc.
 
 // True if the string only contains printable (non-control)
 // ASCII characters.  Note: This follows the ASCII standard;
@@ -184,3 +179,190 @@ extension Google_Protobuf_FieldMask: _CustomJSONCodable {
     return "\"" + jsonPaths.joined(separator: ",") + "\""
   }
 }
+
+extension Google_Protobuf_FieldMask {
+
+  /// Initiates a field mask with all fields of the message type.
+  ///
+  /// - Parameter messageType: Message type to get all paths from.
+  public init<M: Message & _ProtoNameProviding>(
+    allFieldsOf messageType: M.Type
+  ) {
+    self = .with { mask in
+      mask.paths = M.allProtoNames
+    }
+  }
+
+  /// Initiates a field mask from some particular field numbers of a message
+  ///
+  /// - Parameters:
+  ///   - messageType: Message type to get all paths from.
+  ///   - fieldNumbers: Field numbers of paths to be included.
+  /// - Returns: Field mask that include paths of corresponding field numbers.
+  /// - Throws: `FieldMaskError.invalidFieldNumber` if the field number
+  ///  is not on the message
+  public init<M: Message & _ProtoNameProviding>(
+    fieldNumbers: [Int],
+    of messageType: M.Type
+  ) throws {
+    var paths: [String] = []
+    for number in fieldNumbers {
+      guard let name = M.protoName(for: number) else {
+        throw FieldMaskError.invalidFieldNumber
+      }
+      paths.append(name)
+    }
+    self = .with { mask in
+      mask.paths = paths
+    }
+  }
+}
+
+extension Google_Protobuf_FieldMask {
+
+  /// Adds a path to FieldMask after checking whether the given path is valid.
+  /// This method check-fails if the path is not a valid path for Message type.
+  ///
+  /// - Parameters:
+  ///   - path: Path to be added to FieldMask.
+  ///   - messageType: Message type to check validity.
+  public mutating func addPath<M: Message>(
+    _ path: String,
+    of messageType: M.Type
+  ) throws {
+    guard M.isPathValid(path) else {
+      throw FieldMaskError.invalidPath
+    }
+    paths.append(path)
+  }
+
+  /// Converts a FieldMask to the canonical form. It will:
+  ///   1. Remove paths that are covered by another path. For example,
+  ///      "foo.bar" is covered by "foo" and will be removed if "foo"
+  ///      is also in the FieldMask.
+  ///   2. Sort all paths in alphabetical order.
+  public var canonical: Google_Protobuf_FieldMask {
+    var mask = Google_Protobuf_FieldMask()
+    let sortedPaths = self.paths.sorted()
+    for path in sortedPaths {
+      if let lastPath = mask.paths.last {
+        if path != lastPath, !path.hasPrefix("\(lastPath).") {
+          mask.paths.append(path)
+        }
+      } else {
+        mask.paths.append(path)
+      }
+    }
+    return mask
+  }
+
+  /// Creates an union of two FieldMasks.
+  ///
+  /// - Parameter mask: FieldMask to union with.
+  /// - Returns: FieldMask with union of two path sets.
+  public func union(
+    _ mask: Google_Protobuf_FieldMask
+  ) -> Google_Protobuf_FieldMask {
+    var buffer: Set<String> = .init()
+    var paths: [String] = []
+    let allPaths = self.paths + mask.paths
+    for path in allPaths where !buffer.contains(path) {
+      buffer.insert(path)
+      paths.append(path)
+    }
+    return .with { mask in
+      mask.paths = paths
+    }
+  }
+
+  /// Creates an intersection of two FieldMasks.
+  ///
+  /// - Parameter mask: FieldMask to intersect with.
+  /// - Returns: FieldMask with intersection of two path sets.
+  public func intersect(
+    _ mask: Google_Protobuf_FieldMask
+  ) -> Google_Protobuf_FieldMask {
+    let set = Set<String>(mask.paths)
+    var paths: [String] = []
+    var buffer = Set<String>()
+    for path in self.paths where set.contains(path) && !buffer.contains(path) {
+      buffer.insert(path)
+      paths.append(path)
+    }
+    return .with { mask in
+      mask.paths = paths
+    }
+  }
+
+  /// Creates a FieldMasks with paths of the original FieldMask
+  /// that does not included in mask.
+  ///
+  /// - Parameter mask: FieldMask with paths should be substracted.
+  /// - Returns: FieldMask with all paths does not included in mask.
+  public func subtract(
+    _ mask: Google_Protobuf_FieldMask
+  ) -> Google_Protobuf_FieldMask {
+    let set = Set<String>(mask.paths)
+    var paths: [String] = []
+    var buffer = Set<String>()
+    for path in self.paths where !set.contains(path) && !buffer.contains(path) {
+      buffer.insert(path)
+      paths.append(path)
+    }
+    return .with { mask in
+      mask.paths = paths
+    }
+  }
+
+  /// Returns true if path is covered by the given FieldMask. Note that path
+  /// "foo.bar" covers all paths like "foo.bar.baz", "foo.bar.quz.x", etc.
+  /// Also note that parent paths are not covered by explicit child path, i.e.
+  /// "foo.bar" does NOT cover "foo", even if "bar" is the only child.
+  ///
+  /// - Parameter path: Path to be checked.
+  /// - Returns: Boolean determines is path covered.
+  public func contains(_ path: String) -> Bool {
+    for fieldMaskPath in paths {
+      if path.hasPrefix("\(fieldMaskPath).") || fieldMaskPath == path {
+        return true
+      }
+    }
+    return false
+  }
+}
+
+extension Google_Protobuf_FieldMask {
+
+  /// Checks whether the given FieldMask is valid for type M.
+  ///
+  /// - Parameter messageType: Message type to paths check with.
+  /// - Returns: Boolean determines FieldMask is valid.
+  public func isValid<M: Message & _ProtoNameProviding>(
+    for messageType: M.Type
+  ) -> Bool {
+    var message = M()
+    return paths.allSatisfy { path in
+      message.isPathValid(path)
+    }
+  }
+}
+
+/// Describes errors could happen during FieldMask utilities.
+public enum FieldMaskError: Error {
+
+  /// Describes a path is invalid for a Message type.
+  case invalidPath
+
+  /// Describes a fieldNumber is invalid for a Message type.
+  case invalidFieldNumber
+}
+
+private extension Message where Self: _ProtoNameProviding {
+  static func protoName(for number: Int) -> String? {
+    Self._protobuf_nameMap.names(for: number)?.proto.description
+  }
+
+  static var allProtoNames: [String] {
+    Self._protobuf_nameMap.names.map(\.description)
+  }
+}

+ 132 - 0
Sources/SwiftProtobuf/Message+FieldMask.swift

@@ -0,0 +1,132 @@
+// Sources/SwiftProtobuf/Message+FieldMask.swift - Message field mask extensions
+//
+// Copyright (c) 2014 - 2023 Apple Inc. and the project authors
+// Licensed under Apache License v2.0 with Runtime Library Exception
+//
+// See LICENSE.txt for license information:
+// https://github.com/apple/swift-protobuf/blob/main/LICENSE.txt
+//
+// -----------------------------------------------------------------------------
+///
+/// Extend the Message types with FieldMask utilities.
+///
+// -----------------------------------------------------------------------------
+
+import Foundation
+
+extension Message {
+
+  /// Checks whether the given path is valid for Message type.
+  ///
+  /// - Parameter path: Path to be checked
+  /// - Returns: Boolean determines path is valid.
+  public static func isPathValid(
+    _ path: String
+  ) -> Bool {
+    var message = Self()
+    return message.hasPath(path: path)
+  }
+
+  internal mutating func hasPath(path: String) -> Bool {
+    do {
+      try set(path: path, value: nil, mergeOption: .init())
+      return true
+    } catch let error as PathDecodingError {
+      return error != .pathNotFound
+    } catch {
+      return false
+    }
+  }
+
+  internal mutating func isPathValid(
+    _ path: String
+  ) -> Bool {
+    hasPath(path: path)
+  }
+}
+
+extension Google_Protobuf_FieldMask {
+
+  /// Defines available options for merging two messages.
+  public struct MergeOptions {
+
+    public init() {}
+
+    /// The default merging behavior will append entries from the source
+    /// repeated field to the destination repeated field. If you only want
+    /// to keep the entries from the source repeated field, set this flag
+    /// to true.
+    public var replaceRepeatedFields = false
+  }
+}
+
+extension Message {
+
+  /// Merges fields specified in a FieldMask into another message.
+  ///
+  /// - Parameters:
+  ///   - source: Message that should be merged to the original one.
+  ///   - fieldMask: FieldMask specifies which fields should be merged.
+  public mutating func merge(
+    from source: Self,
+    fieldMask: Google_Protobuf_FieldMask,
+    mergeOption: Google_Protobuf_FieldMask.MergeOptions = .init()
+  ) throws {
+    var visitor = PathVisitor<Self>()
+    try source.traverse(visitor: &visitor)
+    let values = visitor.values
+    // TODO: setting all values with only one decoding
+    for path in fieldMask.paths {
+      try? set(
+        path: path,
+        value: values[path],
+        mergeOption: mergeOption
+      )
+    }
+  }
+}
+
+extension Message where Self: Equatable, Self: _ProtoNameProviding {
+
+  // TODO: Re-implement using clear fields instead of copying message
+
+  /// Removes from 'message' any field that is not represented in the given
+  /// FieldMask. If the FieldMask is empty, does nothing.
+  ///
+  /// - Parameter fieldMask: FieldMask specifies which fields should be kept.
+  /// - Returns: Boolean determines if the message is modified
+  @discardableResult
+  public mutating func trim(
+    keeping fieldMask: Google_Protobuf_FieldMask
+  ) -> Bool {
+    if !fieldMask.isValid(for: Self.self) {
+      return false
+    }
+    if fieldMask.paths.isEmpty {
+      return false
+    }
+    var tmp = Self(removingAllFieldsOf: self)
+    do {
+      try tmp.merge(from: self, fieldMask: fieldMask)
+      let changed = tmp != self
+      self = tmp
+      return changed
+    } catch {
+      return false
+    }
+  }
+}
+
+private extension Message {
+  init(removingAllFieldsOf message: Self) {
+    let newMessage: Self = .init()
+    if var newExtensible = newMessage as? any ExtensibleMessage,
+       let extensible = message as? any ExtensibleMessage {
+      newExtensible._protobuf_extensionFieldValues = extensible._protobuf_extensionFieldValues
+      self = newExtensible as? Self ?? newMessage
+    } else {
+      self = newMessage
+    }
+    self.unknownFields = message.unknownFields
+  }
+}

+ 5 - 0
Sources/SwiftProtobuf/NameMap.swift

@@ -278,6 +278,11 @@ public struct _NameMap: ExpressibleByDictionaryLiteral {
     let n = Name(transientUtf8Buffer: raw)
     return jsonToNumberMap[n]
   }
+  
+  /// Returns all proto names
+  internal var names: [Name] {
+    numberToNameMap.map(\.value.proto)
+  }
 }
 
 // The `_NameMap` (and supporting types) are only mutated during their initial

+ 440 - 0
Sources/SwiftProtobuf/PathDecoder.swift

@@ -0,0 +1,440 @@
+// Sources/SwiftProtobuf/PathDecoder.swift - Path decoder
+//
+// Copyright (c) 2014 - 2023 Apple Inc. and the project authors
+// Licensed under Apache License v2.0 with Runtime Library Exception
+//
+// See LICENSE.txt for license information:
+// https://github.com/apple/swift-protobuf/blob/main/LICENSE.txt
+//
+// -----------------------------------------------------------------------------
+///
+/// Decoder which sets value of a field by its path.
+///
+// -----------------------------------------------------------------------------
+
+import Foundation
+
+/// Describes errors can occure during decoding a proto by path.
+public enum PathDecodingError: Error {
+
+  /// Describes a mismatch in type of the fields.
+  ///
+  /// If a value of type A is applied to a path with type B.
+  /// this error will be thrown.
+  case typeMismatch
+
+  /// Describes path is not found in message type.
+  ///
+  /// If a message has no field with the given path this
+  /// error will be thrown.
+  case pathNotFound
+}
+
+extension Message {
+  static func number(for field: String) -> Int? {
+    guard let type = Self.self as? any _ProtoNameProviding.Type else {
+      return nil
+    }
+    guard let number = Array(field.utf8).withUnsafeBytes({ bytes in
+      type._protobuf_nameMap.number(forProtoName: bytes)
+    }) else {
+      return nil
+    }
+    if type._protobuf_nameMap.names(for: number)?.proto.description != field {
+      return nil
+    }
+    return number
+  }
+
+  static func name(for field: Int) -> String? {
+    guard let type = Self.self as? any _ProtoNameProviding.Type else {
+      return nil
+    }
+    return type._protobuf_nameMap.names(for: field)?.proto.description
+  }
+}
+
+// Decoder that set value of a message field by the given path
+struct PathDecoder<T: Message>: Decoder {
+
+  // The value should be set to the path
+  private let value: Any?
+
+  // Field number should be overriden by decoder
+  private var number: Int?
+
+  // The path only including sub-paths
+  private let nextPath: [String]
+
+  // Merge options to be concidered while setting value
+  private let mergeOption: Google_Protobuf_FieldMask.MergeOptions
+
+  private var replaceRepeatedFields: Bool {
+    mergeOption.replaceRepeatedFields
+  }
+
+  init(
+    path: [String],
+    value: Any?,
+    mergeOption: Google_Protobuf_FieldMask.MergeOptions
+  ) throws {
+    if let firstComponent = path.first,
+       let number = T.number(for: firstComponent) {
+      self.number = number
+      self.nextPath = .init(path.dropFirst())
+    } else {
+      throw PathDecodingError.pathNotFound
+    }
+    self.value = value
+    self.mergeOption = mergeOption
+  }
+
+  private func setValue<V>(_ value: inout V, defaultValue: V) throws {
+    if !nextPath.isEmpty {
+      throw PathDecodingError.pathNotFound
+    }
+    if self.value == nil {
+      value = defaultValue
+      return
+    }
+    guard let castedValue = self.value as? V else {
+      throw PathDecodingError.typeMismatch
+    }
+    value = castedValue
+  }
+
+  private func setRepeatedValue<V>(_ value: inout [V]) throws {
+    if !nextPath.isEmpty {
+      throw PathDecodingError.pathNotFound
+    }
+    var castedValue: [V] = []
+    if self.value != nil {
+      guard let v = self.value as? [V] else {
+        throw PathDecodingError.typeMismatch
+      }
+      castedValue = v
+    }
+    if replaceRepeatedFields {
+      value = castedValue
+    } else {
+      value.append(contentsOf: castedValue)
+    }
+  }
+
+  private func setMapValue<K, V>(
+    _ value: inout Dictionary<K, V>
+  ) throws {
+    if !nextPath.isEmpty {
+      throw PathDecodingError.pathNotFound
+    }
+    var castedValue: [K: V] = [:]
+    if self.value != nil {
+      guard let v = self.value as? Dictionary<K, V> else {
+        throw PathDecodingError.typeMismatch
+      }
+      castedValue = v
+    }
+    if replaceRepeatedFields {
+      value = castedValue
+    } else {
+      value.merge(castedValue) { _, new in
+        new
+      }
+    }
+  }
+
+  private func setMessageValue<M: Message>(
+    _ value: inout M?
+  ) throws {
+    if nextPath.isEmpty {
+      try setValue(&value, defaultValue: nil)
+      return
+    }
+    var decoder = try PathDecoder<M>(
+        path: nextPath,
+        value: self.value,
+        mergeOption: mergeOption
+    )
+    if value == nil {
+      value = .init()
+    }
+    try value?.decodeMessage(decoder: &decoder)
+  }
+
+  mutating func handleConflictingOneOf() throws {}
+
+  mutating func nextFieldNumber() throws -> Int? {
+    defer { number = nil }
+    return number
+  }
+
+  mutating func decodeSingularFloatField(value: inout Float) throws {
+    try setValue(&value, defaultValue: .init())
+  }
+
+  mutating func decodeSingularFloatField(value: inout Float?) throws {
+    try setValue(&value, defaultValue: nil)
+  }
+
+  mutating func decodeRepeatedFloatField(value: inout [Float]) throws {
+    try setRepeatedValue(&value)
+  }
+
+  mutating func decodeSingularDoubleField(value: inout Double) throws {
+    try setValue(&value, defaultValue: .init())
+  }
+
+  mutating func decodeSingularDoubleField(value: inout Double?) throws {
+    try setValue(&value, defaultValue: nil)
+  }
+
+  mutating func decodeRepeatedDoubleField(value: inout [Double]) throws {
+    try setRepeatedValue(&value)
+  }
+
+  mutating func decodeSingularInt32Field(value: inout Int32) throws {
+    try setValue(&value, defaultValue: .init())
+  }
+
+  mutating func decodeSingularInt32Field(value: inout Int32?) throws {
+    try setValue(&value, defaultValue: nil)
+  }
+
+  mutating func decodeRepeatedInt32Field(value: inout [Int32]) throws {
+    try setRepeatedValue(&value)
+  }
+
+  mutating func decodeSingularInt64Field(value: inout Int64) throws {
+    try setValue(&value, defaultValue: .init())
+  }
+
+  mutating func decodeSingularInt64Field(value: inout Int64?) throws {
+    try setValue(&value, defaultValue: nil)
+  }
+
+  mutating func decodeRepeatedInt64Field(value: inout [Int64]) throws {
+    try setRepeatedValue(&value)
+  }
+
+  mutating func decodeSingularUInt32Field(value: inout UInt32) throws {
+    try setValue(&value, defaultValue: .init())
+  }
+
+  mutating func decodeSingularUInt32Field(value: inout UInt32?) throws {
+    try setValue(&value, defaultValue: nil)
+  }
+
+  mutating func decodeRepeatedUInt32Field(value: inout [UInt32]) throws {
+    try setRepeatedValue(&value)
+  }
+
+  mutating func decodeSingularUInt64Field(value: inout UInt64) throws {
+    try setValue(&value, defaultValue: .init())
+  }
+
+  mutating func decodeSingularUInt64Field(value: inout UInt64?) throws {
+    try setValue(&value, defaultValue: nil)
+  }
+
+  mutating func decodeRepeatedUInt64Field(value: inout [UInt64]) throws {
+    try setRepeatedValue(&value)
+  }
+
+  mutating func decodeSingularSInt32Field(value: inout Int32) throws {
+    try setValue(&value, defaultValue: .init())
+  }
+
+  mutating func decodeSingularSInt32Field(value: inout Int32?) throws {
+    try setValue(&value, defaultValue: nil)
+  }
+
+  mutating func decodeRepeatedSInt32Field(value: inout [Int32]) throws {
+    try setRepeatedValue(&value)
+  }
+
+  mutating func decodeSingularSInt64Field(value: inout Int64) throws {
+    try setValue(&value, defaultValue: .init())
+  }
+
+  mutating func decodeSingularSInt64Field(value: inout Int64?) throws {
+    try setValue(&value, defaultValue: nil)
+  }
+
+  mutating func decodeRepeatedSInt64Field(value: inout [Int64]) throws {
+    try setRepeatedValue(&value)
+  }
+
+  mutating func decodeSingularFixed32Field(value: inout UInt32) throws {
+    try setValue(&value, defaultValue: .init())
+  }
+
+  mutating func decodeSingularFixed32Field(value: inout UInt32?) throws {
+    try setValue(&value, defaultValue: nil)
+  }
+
+  mutating func decodeRepeatedFixed32Field(value: inout [UInt32]) throws {
+    try setRepeatedValue(&value)
+  }
+
+  mutating func decodeSingularFixed64Field(value: inout UInt64) throws {
+    try setValue(&value, defaultValue: .init())
+  }
+
+  mutating func decodeSingularFixed64Field(value: inout UInt64?) throws {
+    try setValue(&value, defaultValue: nil)
+  }
+
+  mutating func decodeRepeatedFixed64Field(value: inout [UInt64]) throws {
+    try setRepeatedValue(&value)
+  }
+
+  mutating func decodeSingularSFixed32Field(value: inout Int32) throws {
+    try setValue(&value, defaultValue: .init())
+  }
+
+  mutating func decodeSingularSFixed32Field(value: inout Int32?) throws {
+    try setValue(&value, defaultValue: nil)
+  }
+
+  mutating func decodeRepeatedSFixed32Field(value: inout [Int32]) throws {
+    try setRepeatedValue(&value)
+  }
+
+  mutating func decodeSingularSFixed64Field(value: inout Int64) throws {
+    try setValue(&value, defaultValue: .init())
+  }
+
+  mutating func decodeSingularSFixed64Field(value: inout Int64?) throws {
+    try setValue(&value, defaultValue: nil)
+  }
+
+  mutating func decodeRepeatedSFixed64Field(value: inout [Int64]) throws {
+    try setRepeatedValue(&value)
+  }
+
+  mutating func decodeSingularBoolField(value: inout Bool) throws {
+    try setValue(&value, defaultValue: .init())
+  }
+
+  mutating func decodeSingularBoolField(value: inout Bool?) throws {
+    try setValue(&value, defaultValue: nil)
+  }
+
+  mutating func decodeRepeatedBoolField(value: inout [Bool]) throws {
+    try setRepeatedValue(&value)
+  }
+
+  mutating func decodeSingularStringField(value: inout String) throws {
+    try setValue(&value, defaultValue: .init())
+  }
+
+  mutating func decodeSingularStringField(value: inout String?) throws {
+    try setValue(&value, defaultValue: nil)
+  }
+
+  mutating func decodeRepeatedStringField(value: inout [String]) throws {
+    try setRepeatedValue(&value)
+  }
+
+  mutating func decodeSingularBytesField(value: inout Data) throws {
+    try setValue(&value, defaultValue: .init())
+  }
+
+  mutating func decodeSingularBytesField(value: inout Data?) throws {
+    try setValue(&value, defaultValue: nil)
+  }
+
+  mutating func decodeRepeatedBytesField(value: inout [Data]) throws {
+    try setRepeatedValue(&value)
+  }
+
+  mutating func decodeSingularEnumField<E>(
+    value: inout E
+  ) throws where E : Enum, E.RawValue == Int {
+    try setValue(&value, defaultValue: .init())
+  }
+
+  mutating func decodeSingularEnumField<E>(
+    value: inout E?
+  ) throws where E : Enum, E.RawValue == Int {
+    try setValue(&value, defaultValue: nil)
+  }
+
+  mutating func decodeRepeatedEnumField<E>(
+    value: inout [E]
+  ) throws where E : Enum, E.RawValue == Int {
+    try setRepeatedValue(&value)
+  }
+
+  mutating func decodeSingularMessageField<M>(
+    value: inout M?
+  ) throws where M : Message {
+    try setMessageValue(&value)
+  }
+
+  mutating func decodeRepeatedMessageField<M>(
+    value: inout [M]
+  ) throws where M : Message {
+    try setRepeatedValue(&value)
+  }
+
+  mutating func decodeSingularGroupField<G>(
+    value: inout G?
+  ) throws where G : Message {
+    try setMessageValue(&value)
+  }
+
+  mutating func decodeRepeatedGroupField<G>(
+    value: inout [G]
+  ) throws where G : Message {
+    try setRepeatedValue(&value)
+  }
+
+  mutating func decodeMapField<KeyType, ValueType>(
+    fieldType: _ProtobufMap<KeyType, ValueType>.Type,
+    value: inout _ProtobufMap<KeyType, ValueType>.BaseType
+  ) throws where KeyType : MapKeyType, ValueType : MapValueType {
+    try setMapValue(&value)
+  }
+
+  mutating func decodeMapField<KeyType, ValueType>(
+    fieldType: _ProtobufEnumMap<KeyType, ValueType>.Type,
+    value: inout _ProtobufEnumMap<KeyType, ValueType>.BaseType
+  ) throws where KeyType : MapKeyType, ValueType : Enum, ValueType.RawValue == Int {
+    try setMapValue(&value)
+  }
+
+  mutating func decodeMapField<KeyType, ValueType>(
+    fieldType: _ProtobufMessageMap<KeyType, ValueType>.Type,
+    value: inout _ProtobufMessageMap<KeyType, ValueType>.BaseType
+  ) throws where KeyType : MapKeyType, ValueType : Hashable, ValueType : Message {
+    try setMapValue(&value)
+  }
+
+  mutating func decodeExtensionField(
+    values: inout ExtensionFieldValueSet,
+    messageType: any Message.Type,
+    fieldNumber: Int
+  ) throws {
+    preconditionFailure(
+      "Internal Error: Path decoder should never decode an extension field"
+    )
+  }
+
+}
+
+extension Message {
+  mutating func `set`(
+    path: String,
+    value: Any?,
+    mergeOption: Google_Protobuf_FieldMask.MergeOptions
+  ) throws {
+    let _path = path.components(separatedBy: ".")
+    var decoder = try PathDecoder<Self>(
+      path: _path,
+      value: value,
+      mergeOption: mergeOption
+    )
+    try decodeMessage(decoder: &decoder)
+  }
+}

+ 284 - 0
Sources/SwiftProtobuf/PathVisitor.swift

@@ -0,0 +1,284 @@
+// Sources/SwiftProtobuf/PathVisitor.swift - Path visitor
+//
+// Copyright (c) 2014 - 2023 Apple Inc. and the project authors
+// Licensed under Apache License v2.0 with Runtime Library Exception
+//
+// See LICENSE.txt for license information:
+// https://github.com/apple/swift-protobuf/blob/main/LICENSE.txt
+//
+// -----------------------------------------------------------------------------
+///
+/// Visitor which captures a pair of paths and their values.
+///
+// -----------------------------------------------------------------------------
+
+import Foundation
+
+// Visitor captures all values of message with their paths
+struct PathVisitor<T: Message>: Visitor {
+
+  // The path contains parent components
+  private let prevPath: String?
+
+  // Captured values after visiting will be stored in this property
+  private(set) var values: [String: Any] = [:]
+
+  internal init(prevPath: String? = nil) {
+    self.prevPath = prevPath
+  }
+
+  mutating private func visit(_ value: Any, fieldNumber: Int) {
+    guard let name = T.name(for: fieldNumber) else {
+      return
+    }
+    if let prevPath {
+      values["\(prevPath).\(name)"] = value
+    } else {
+      values[name] = value
+    }
+  }
+
+  mutating private func visitMessageField<M: Message>(
+    _ value: M,
+    fieldNumber: Int
+  ) {
+    guard var path = T.name(for: fieldNumber) else {
+      return
+    }
+    if let prevPath {
+      path = "\(prevPath).\(path)"
+    }
+    values[path] = value
+    var visitor = PathVisitor<M>(prevPath: path)
+    try? value.traverse(visitor: &visitor)
+    values.merge(visitor.values) { _, new in
+      new
+    }
+  }
+
+  mutating func visitUnknown(bytes: Data) throws {}
+
+  mutating func visitSingularFloatField(value: Float, fieldNumber: Int) throws {
+    visit(value, fieldNumber: fieldNumber)
+  }
+
+  mutating func visitSingularDoubleField(value: Double, fieldNumber: Int) throws {
+    visit(value, fieldNumber: fieldNumber)
+  }
+
+  mutating func visitSingularInt32Field(value: Int32, fieldNumber: Int) throws {
+    visit(value, fieldNumber: fieldNumber)
+  }
+
+  mutating func visitSingularInt64Field(value: Int64, fieldNumber: Int) throws {
+    visit(value, fieldNumber: fieldNumber)
+  }
+
+  mutating func visitSingularUInt32Field(value: UInt32, fieldNumber: Int) throws {
+    visit(value, fieldNumber: fieldNumber)
+  }
+
+  mutating func visitSingularUInt64Field(value: UInt64, fieldNumber: Int) throws {
+    visit(value, fieldNumber: fieldNumber)
+  }
+
+  mutating func visitSingularSInt32Field(value: Int32, fieldNumber: Int) throws {
+    visit(value, fieldNumber: fieldNumber)
+  }
+
+  mutating func visitSingularSInt64Field(value: Int64, fieldNumber: Int) throws {
+    visit(value, fieldNumber: fieldNumber)
+  }
+
+  mutating func visitSingularFixed32Field(value: UInt32, fieldNumber: Int) throws {
+    visit(value, fieldNumber: fieldNumber)
+  }
+
+  mutating func visitSingularFixed64Field(value: UInt64, fieldNumber: Int) throws {
+    visit(value, fieldNumber: fieldNumber)
+  }
+
+  mutating func visitSingularSFixed32Field(value: Int32, fieldNumber: Int) throws {
+    visit(value, fieldNumber: fieldNumber)
+  }
+
+  mutating func visitSingularSFixed64Field(value: Int64, fieldNumber: Int) throws {
+    visit(value, fieldNumber: fieldNumber)
+  }
+
+  mutating func visitSingularBoolField(value: Bool, fieldNumber: Int) throws {
+    visit(value, fieldNumber: fieldNumber)
+  }
+
+  mutating func visitSingularStringField(value: String, fieldNumber: Int) throws {
+    visit(value, fieldNumber: fieldNumber)
+  }
+
+  mutating func visitSingularBytesField(value: Data, fieldNumber: Int) throws {
+    visit(value, fieldNumber: fieldNumber)
+  }
+
+  mutating func visitSingularEnumField<E: Enum>(value: E, fieldNumber: Int) throws {
+    visit(value, fieldNumber: fieldNumber)
+  }
+
+  mutating func visitSingularMessageField<M: Message>(value: M, fieldNumber: Int) throws {
+    visitMessageField(value, fieldNumber: fieldNumber)
+  }
+
+  mutating func visitSingularGroupField<G: Message>(value: G, fieldNumber: Int) throws {
+    visitMessageField(value, fieldNumber: fieldNumber)
+  }
+
+  mutating func visitRepeatedFloatField(value: [Float], fieldNumber: Int) throws {
+    visit(value, fieldNumber: fieldNumber)
+  }
+
+  mutating func visitRepeatedDoubleField(value: [Double], fieldNumber: Int) throws {
+    visit(value, fieldNumber: fieldNumber)
+  }
+
+  mutating func visitRepeatedInt32Field(value: [Int32], fieldNumber: Int) throws {
+    visit(value, fieldNumber: fieldNumber)
+  }
+
+  mutating func visitRepeatedInt64Field(value: [Int64], fieldNumber: Int) throws {
+    visit(value, fieldNumber: fieldNumber)
+  }
+
+  mutating func visitRepeatedUInt32Field(value: [UInt32], fieldNumber: Int) throws {
+    visit(value, fieldNumber: fieldNumber)
+  }
+
+  mutating func visitRepeatedUInt64Field(value: [UInt64], fieldNumber: Int) throws {
+    visit(value, fieldNumber: fieldNumber)
+  }
+
+  mutating func visitRepeatedSInt32Field(value: [Int32], fieldNumber: Int) throws {
+    visit(value, fieldNumber: fieldNumber)
+  }
+
+  mutating func visitRepeatedSInt64Field(value: [Int64], fieldNumber: Int) throws {
+    visit(value, fieldNumber: fieldNumber)
+  }
+
+  mutating func visitRepeatedFixed32Field(value: [UInt32], fieldNumber: Int) throws {
+    visit(value, fieldNumber: fieldNumber)
+  }
+
+  mutating func visitRepeatedFixed64Field(value: [UInt64], fieldNumber: Int) throws {
+    visit(value, fieldNumber: fieldNumber)
+  }
+
+  mutating func visitRepeatedSFixed32Field(value: [Int32], fieldNumber: Int) throws {
+    visit(value, fieldNumber: fieldNumber)
+  }
+
+  mutating func visitRepeatedSFixed64Field(value: [Int64], fieldNumber: Int) throws {
+    visit(value, fieldNumber: fieldNumber)
+  }
+
+  mutating func visitRepeatedBoolField(value: [Bool], fieldNumber: Int) throws {
+    visit(value, fieldNumber: fieldNumber)
+  }
+
+  mutating func visitRepeatedStringField(value: [String], fieldNumber: Int) throws {
+    visit(value, fieldNumber: fieldNumber)
+  }
+
+  mutating func visitRepeatedBytesField(value: [Data], fieldNumber: Int) throws {
+    visit(value, fieldNumber: fieldNumber)
+  }
+
+  mutating func visitRepeatedEnumField<E: Enum>(value: [E], fieldNumber: Int) throws {
+    visit(value, fieldNumber: fieldNumber)
+  }
+
+  mutating func visitRepeatedMessageField<M>(value: [M], fieldNumber: Int) throws where M : Message {
+    visit(value, fieldNumber: fieldNumber)
+  }
+
+  mutating func visitRepeatedGroupField<G: Message>(value: [G], fieldNumber: Int) throws {
+    visit(value, fieldNumber: fieldNumber)
+  }
+
+  mutating func visitPackedFloatField(value: [Float], fieldNumber: Int) throws {
+    visit(value, fieldNumber: fieldNumber)
+  }
+
+  mutating func visitPackedDoubleField(value: [Double], fieldNumber: Int) throws {
+    visit(value, fieldNumber: fieldNumber)
+  }
+
+  mutating func visitPackedInt32Field(value: [Int32], fieldNumber: Int) throws {
+    visit(value, fieldNumber: fieldNumber)
+  }
+
+  mutating func visitPackedInt64Field(value: [Int64], fieldNumber: Int) throws {
+    visit(value, fieldNumber: fieldNumber)
+  }
+
+  mutating func visitPackedUInt32Field(value: [UInt32], fieldNumber: Int) throws {
+    visit(value, fieldNumber: fieldNumber)
+  }
+
+  mutating func visitPackedUInt64Field(value: [UInt64], fieldNumber: Int) throws {
+    visit(value, fieldNumber: fieldNumber)
+  }
+
+  mutating func visitPackedSInt32Field(value: [Int32], fieldNumber: Int) throws {
+    visit(value, fieldNumber: fieldNumber)
+  }
+
+  mutating func visitPackedSInt64Field(value: [Int64], fieldNumber: Int) throws {
+    visit(value, fieldNumber: fieldNumber)
+  }
+
+  mutating func visitPackedFixed32Field(value: [UInt32], fieldNumber: Int) throws {
+    visit(value, fieldNumber: fieldNumber)
+  }
+
+  mutating func visitPackedFixed64Field(value: [UInt64], fieldNumber: Int) throws {
+    visit(value, fieldNumber: fieldNumber)
+  }
+
+  mutating func visitPackedSFixed32Field(value: [Int32], fieldNumber: Int) throws {
+    visit(value, fieldNumber: fieldNumber)
+  }
+
+  mutating func visitPackedSFixed64Field(value: [Int64], fieldNumber: Int) throws {
+    visit(value, fieldNumber: fieldNumber)
+  }
+
+  mutating func visitPackedBoolField(value: [Bool], fieldNumber: Int) throws {
+    visit(value, fieldNumber: fieldNumber)
+  }
+
+  mutating func visitPackedEnumField<E: Enum>(value: [E], fieldNumber: Int) throws {
+    visit(value, fieldNumber: fieldNumber)
+  }
+
+  mutating func visitMapField<KeyType, ValueType>(
+    fieldType: _ProtobufMap<KeyType, ValueType>.Type,
+    value: _ProtobufMap<KeyType, ValueType>.BaseType,
+    fieldNumber: Int
+  ) throws where KeyType : MapKeyType, ValueType : MapValueType {
+    visit(value, fieldNumber: fieldNumber)
+  }
+
+  mutating func visitMapField<KeyType, ValueType>(
+    fieldType: _ProtobufEnumMap<KeyType, ValueType>.Type,
+    value: _ProtobufEnumMap<KeyType, ValueType>.BaseType,
+    fieldNumber: Int
+  ) throws where KeyType : MapKeyType, ValueType : Enum, ValueType.RawValue == Int {
+    visit(value, fieldNumber: fieldNumber)
+  }
+
+  mutating func visitMapField<KeyType, ValueType>(
+    fieldType: _ProtobufMessageMap<KeyType, ValueType>.Type,
+    value: _ProtobufMessageMap<KeyType, ValueType>.BaseType,
+    fieldNumber: Int
+  ) throws where KeyType : MapKeyType, ValueType : Hashable, ValueType : Message {
+    visit(value, fieldNumber: fieldNumber)
+  }
+}

+ 708 - 4
Tests/SwiftProtobufTests/Test_FieldMask.swift

@@ -13,9 +13,6 @@
 ///
 // -----------------------------------------------------------------------------
 
-// TODO: We should have utility functions for applying a mask to an arbitrary
-// message, intersecting two masks, etc.
-
 import Foundation
 import XCTest
 import SwiftProtobuf
@@ -32,7 +29,7 @@ final class Test_FieldMask: XCTestCase, PBTestHelpers {
         }
         // assertJSONEncode doesn't want an empty object, hand roll it.
         let msg = MessageTestType.with { (o: inout MessageTestType) in
-          o.paths = []
+            o.paths = []
         }
         XCTAssertEqual(try msg.jsonString(), "\"\"")
         assertJSONDecodeSucceeds("\"foo\"") { $0.paths == ["foo"] }
@@ -109,4 +106,711 @@ final class Test_FieldMask: XCTestCase, PBTestHelpers {
             XCTAssertThrowsError(try m.jsonString())
         }
     }
+    
+    // Checks merge functionality for field masks.
+    func testMergeFieldsOfMessage() throws {
+        var message = SwiftProtoTesting_TestAllTypes.with { model in
+            model.optionalInt32 = 1
+            model.optionalNestedMessage = .with { nested in
+                nested.bb = 2
+            }
+        }
+
+        let secondMessage = SwiftProtoTesting_TestAllTypes.with { model in
+            model.optionalInt32 = 2
+            model.optionalNestedMessage = .with { nested in
+                nested.bb = 3
+            }
+        }
+
+        // Checks nested message merge
+        try message.merge(from: secondMessage, fieldMask: .init(protoPaths: "optional_nested_message.bb"))
+        XCTAssertEqual(message.optionalInt32, 1)
+        XCTAssertEqual(message.optionalNestedMessage.bb, 3)
+
+        // Checks primitive type merge
+        try message.merge(from: secondMessage, fieldMask: .init(protoPaths: "optional_int32"))
+        XCTAssertEqual(message.optionalInt32, 2)
+        XCTAssertEqual(message.optionalNestedMessage.bb, 3)
+    }
+
+    // Checks merge functionality for repeated field masks.
+    func testMergeRepeatedFieldsOfMessage() throws {
+        var message = SwiftProtoTesting_TestAllTypes.with { model in
+            model.repeatedInt32 = [1, 2]
+        }
+
+        let secondMessage = SwiftProtoTesting_TestAllTypes.with { model in
+            model.repeatedInt32 = [3, 4]
+        }
+
+        let fieldMask = Google_Protobuf_FieldMask(protoPaths: ["repeated_int32"])
+
+        // Checks without replacing repeated fields
+        try message.merge(from: secondMessage, fieldMask: fieldMask)
+        XCTAssertEqual(message.repeatedInt32, [1, 2, 3, 4])
+
+        // Checks with replacing repeated fields
+        var options = Google_Protobuf_FieldMask.MergeOptions()
+        options.replaceRepeatedFields = true
+        try message.merge(from: secondMessage, fieldMask: fieldMask, mergeOption: options)
+        XCTAssertEqual(message.repeatedInt32, [3, 4])
+    }
+
+    // Checks merge functionality for map field masks.
+    func testMergeMapFieldsOfMessage() throws {
+        var message = SwiftProtoTesting_Fuzz_Message.with { model in
+            model.mapInt32String = [1: "a", 2: "c"]
+        }
+
+        let secondMessage = SwiftProtoTesting_Fuzz_Message.with { model in
+            model.mapInt32String = [2: "b"]
+        }
+
+        let fieldMask = Google_Protobuf_FieldMask(protoPaths: ["map_int32_string"])
+
+        // Checks without replacing repeated fields
+        try message.merge(from: secondMessage, fieldMask: fieldMask)
+        XCTAssertEqual(message.mapInt32String, [1: "a", 2: "b"])
+
+        // Checks with replacing repeated fields
+        var options = Google_Protobuf_FieldMask.MergeOptions()
+        options.replaceRepeatedFields = true
+        try message.merge(from: secondMessage, fieldMask: fieldMask, mergeOption: options)
+        XCTAssertEqual(message.mapInt32String, [2: "b"])
+    }
+
+    // Checks trim functionality for field masks.
+    func testTrimFieldsOfMessage() throws {
+        var message = SwiftProtoTesting_TestAllTypes.with { model in
+            model.optionalInt32 = 1
+            model.optionalNestedMessage = .with { nested in
+                nested.bb = 2
+            }
+        }
+
+        // Checks trim to be successful.
+        let r1 = message.trim(keeping: .init(protoPaths: "optional_nested_message.bb"))
+        XCTAssertTrue(r1)
+        XCTAssertEqual(message.optionalInt32, 0)
+        XCTAssertEqual(message.optionalNestedMessage.bb, 2)
+
+        // Checks trim should do nothing with an empty fieldMask.
+        let r2 = message.trim(keeping: .init())
+        XCTAssertFalse(r2)
+
+        // Checks trim should return false if nothing has been changed.
+        let r3 = message.trim(keeping: .init(protoPaths: "optional_nested_message.bb"))
+        XCTAssertFalse(r3)
+
+        // Checks trim to be unsuccessful with an invalid fieldMask.
+        let r4 = message.trim(keeping: .init(protoPaths: "invalid_path"))
+        XCTAssertFalse(r4)
+    }
+
+    // Checks trim functionality for field masks when applies on a extensible message.
+    func testTrimFieldsOfMessageWithExtension() throws {
+        var message = SwiftProtoTesting_Fuzz_Message()
+        message.singularInt32 = 1
+        message.SwiftProtoTesting_Fuzz_singularInt32Ext = 1
+        let mask = Google_Protobuf_FieldMask(protoPaths: ["singular_string"])
+
+        // Checks trim should retain extensions while removes other fields.
+        let r1 = message.trim(keeping: mask)
+        XCTAssertTrue(r1)
+        XCTAssertEqual(message.SwiftProtoTesting_Fuzz_singularInt32Ext, .init(1))
+        XCTAssertEqual(message.singularInt32, .init(0))
+
+        // Checks trim should do nothing (fields are already removed) and still retain extension fields.
+        let r2 = message.trim(keeping: mask)
+        XCTAssertFalse(r2)
+        XCTAssertEqual(message.SwiftProtoTesting_Fuzz_singularInt32Ext, .init(1))
+    }
+
+    // Checks `isPathValid` func
+    // 1. Valid primitive path.
+    // 2, 3. Valid nested path. (for message and group)
+    // 4. Invalid primitive path.
+    // 5, 6. Invalid nested path.
+    // 7, 8. Invalid path after map and repeated.
+    // 9. Invalid path after group.
+    func testIsPathValid() {
+        XCTAssertTrue(SwiftProtoTesting_TestAllTypes.isPathValid("optional_int32"))
+        XCTAssertTrue(SwiftProtoTesting_TestAllTypes.isPathValid("optional_nested_message.bb"))
+        XCTAssertTrue(SwiftProtoTesting_Fuzz_Message.isPathValid("SingularGroup.group_field"))
+        XCTAssertFalse(SwiftProtoTesting_TestAllTypes.isPathValid("optional_int"))
+        XCTAssertFalse(SwiftProtoTesting_TestAllTypes.isPathValid("optional_nested_message.bc"))
+        XCTAssertFalse(SwiftProtoTesting_TestAllTypes.isPathValid("optional_nested_message.bb.a"))
+        XCTAssertFalse(SwiftProtoTesting_TestAllTypes.isPathValid("repeatedInt32.a"))
+        XCTAssertFalse(SwiftProtoTesting_Fuzz_Message.isPathValid("map_bool_int32.a"))
+        XCTAssertFalse(SwiftProtoTesting_Fuzz_Message.isPathValid("SingularGroup.a"))
+    }
+
+    // Checks `isValid` func of FieldMask.
+    // 1. Empty field mask is always valid.
+    // 2, 3. Valid field masks.
+    // 4, 5. Invalid field masks.
+    func testIsFieldMaskValid() {
+        let m1 = Google_Protobuf_FieldMask()
+        let m2 = Google_Protobuf_FieldMask(protoPaths: [
+            "optional_int32",
+            "optional_nested_message.bb"
+        ])
+        let m3 = Google_Protobuf_FieldMask(protoPaths: [
+            "optional_int32",
+            "optional_nested_message"
+        ])
+        let m4 = Google_Protobuf_FieldMask(protoPaths: [
+            "optional_int32",
+            "optional_nested_message.bc"
+        ])
+        let m5 = Google_Protobuf_FieldMask(protoPaths: [
+            "optional_int",
+            "optional_nested_message.bb"
+        ])
+        XCTAssertTrue(m1.isValid(for: SwiftProtoTesting_TestAllTypes.self))
+        XCTAssertTrue(m2.isValid(for: SwiftProtoTesting_TestAllTypes.self))
+        XCTAssertTrue(m3.isValid(for: SwiftProtoTesting_TestAllTypes.self))
+        XCTAssertFalse(m4.isValid(for: SwiftProtoTesting_TestAllTypes.self))
+        XCTAssertFalse(m5.isValid(for: SwiftProtoTesting_TestAllTypes.self))
+    }
+
+    // Checks canonincal form of field mask.
+    // 1. Sub-message with parent in the paths should be excluded.
+    // 2. Canonincal form should be sorted.
+    // 3. More nested levels of paths with duplicates.
+    // 4. Two siblings with their parent should be excluded.
+    func testCanonicalFieldMask() {
+        let m1 = Google_Protobuf_FieldMask(protoPaths: ["a.b", "a", "b"])
+        XCTAssertEqual(m1.canonical.paths, ["a", "b"])
+        let m2 = Google_Protobuf_FieldMask(protoPaths: ["b", "a"])
+        XCTAssertEqual(m2.canonical.paths, ["a", "b"])
+        let m3 = Google_Protobuf_FieldMask(protoPaths: ["c", "a.b.c", "a.b", "a.b", "a.b.c.d"])
+        XCTAssertEqual(m3.canonical.paths, ["a.b", "c"])
+        let m4 = Google_Protobuf_FieldMask(protoPaths: ["a.c", "a", "a.b"])
+        XCTAssertEqual(m4.canonical.paths, ["a"])
+    }
+
+    // Checks `addPath` func of fieldMask with:
+    //  - Valid primitive path should be added.
+    //  - Valid nested path should be added.
+    //  - Invalid path should throw error.
+    func testAddPathToFieldMask() throws {
+        var mask = Google_Protobuf_FieldMask()
+        XCTAssertNoThrow(try mask.addPath("optional_int32", of: SwiftProtoTesting_TestAllTypes.self))
+        XCTAssertEqual(mask.paths, ["optional_int32"])
+        XCTAssertNoThrow(try mask.addPath("optional_nested_message.bb", of: SwiftProtoTesting_TestAllTypes.self))
+        XCTAssertEqual(mask.paths, ["optional_int32", "optional_nested_message.bb"])
+        XCTAssertThrowsError(try mask.addPath("optional_int", of: SwiftProtoTesting_TestAllTypes.self))
+    }
+
+    // Check `contains` func of fieldMask.
+    // 1. Parent contains sub-message.
+    // 2. Path contains itself.
+    // 3. Sub-message does not contain its parent.
+    // 4. Two different paths does not contain each other.
+    // 5. Two different sub-paths does not contain each other.
+    func testPathContainsInFieldMask() {
+        let m1 = Google_Protobuf_FieldMask(protoPaths: ["a"])
+        XCTAssertTrue(m1.contains("a.b"))
+        let m2 = Google_Protobuf_FieldMask(protoPaths: ["a"])
+        XCTAssertTrue(m2.contains("a"))
+        let m3 = Google_Protobuf_FieldMask(protoPaths: ["a.b"])
+        XCTAssertFalse(m3.contains("a"))
+        let m4 = Google_Protobuf_FieldMask(protoPaths: ["a"])
+        XCTAssertFalse(m4.contains("b"))
+        let m5 = Google_Protobuf_FieldMask(protoPaths: ["a.b"])
+        XCTAssertFalse(m5.contains("a.c"))
+    }
+
+    // Checks inits of fieldMask with:
+    //  - All fields of a message type.
+    //  - Particular field numbers of a message type.
+    func testFieldPathMessageInits() throws {
+        let m1 = Google_Protobuf_FieldMask(allFieldsOf: SwiftProtoTesting_TestAny.self)
+        XCTAssertEqual(m1.paths.sorted(), ["any_value", "int32_value", "repeated_any_value", "text"])
+        let m2 = try Google_Protobuf_FieldMask(fieldNumbers: [1, 2], of: SwiftProtoTesting_TestAny.self)
+        XCTAssertEqual(m2.paths.sorted(), ["any_value", "int32_value"])
+        XCTAssertThrowsError(try Google_Protobuf_FieldMask(fieldNumbers: [10], of: SwiftProtoTesting_TestAny.self))
+    }
+
+    // Checks that json names of paths should not be contained in mask field with allFieldsOf init.
+    func testFieldMaskAllPathsWithUniqueName() {
+        let mask = Google_Protobuf_FieldMask(allFieldsOf: SwiftProtoTesting_Fuzz_Message.self)
+        // proto name is included
+        XCTAssertTrue(mask.paths.contains("SingularGroup"))
+        // json name is not included
+        XCTAssertFalse(mask.paths.contains("singulargroup"))
+    }
+
+    // Checks `union` func of fieldMask.
+    func testUnionFieldMasks() throws {
+        let m1 = Google_Protobuf_FieldMask(protoPaths: ["a", "b"])
+        let m2 = Google_Protobuf_FieldMask(protoPaths: ["b", "c"])
+        XCTAssertEqual(m1.union(m2).paths, ["a", "b", "c"])
+
+        let m3 = Google_Protobuf_FieldMask(protoPaths: ["a", "b"])
+        let m4 = Google_Protobuf_FieldMask(protoPaths: ["c", "d"])
+        XCTAssertEqual(m3.union(m4).paths, ["a", "b", "c", "d"])
+
+        let m5 = Google_Protobuf_FieldMask()
+        let m6 = Google_Protobuf_FieldMask(protoPaths: ["c", "d"])
+        XCTAssertEqual(m5.union(m6).paths, ["c", "d"])
+
+        let m7 = Google_Protobuf_FieldMask(protoPaths: ["a", "b"])
+        let m8 = Google_Protobuf_FieldMask(protoPaths: ["a", "b"])
+        XCTAssertEqual(m7.union(m8).paths, ["a", "b"])
+
+        let m9 = Google_Protobuf_FieldMask(protoPaths: ["a", "a"])
+        let m10 = Google_Protobuf_FieldMask(protoPaths: ["a", "b"])
+        XCTAssertEqual(m9.union(m10).paths, ["a", "b"])
+    }
+
+    // Checks `intersect` func of fieldMask.
+    func testIntersectFieldMasks() throws {
+        let m1 = Google_Protobuf_FieldMask(protoPaths: ["a", "b"])
+        let m2 = Google_Protobuf_FieldMask(protoPaths: ["b", "c"])
+        XCTAssertEqual(m1.intersect(m2).paths, ["b"])
+
+        let m3 = Google_Protobuf_FieldMask(protoPaths: ["a", "b"])
+        let m4 = Google_Protobuf_FieldMask(protoPaths: ["c", "d"])
+        XCTAssertEqual(m3.intersect(m4).paths, [])
+
+        let m5 = Google_Protobuf_FieldMask()
+        let m6 = Google_Protobuf_FieldMask(protoPaths: ["c", "d"])
+        XCTAssertEqual(m5.intersect(m6).paths, [])
+
+        let m7 = Google_Protobuf_FieldMask(protoPaths: ["a", "b"])
+        let m8 = Google_Protobuf_FieldMask(protoPaths: ["a", "b"])
+        XCTAssertEqual(m7.intersect(m8).paths, ["a", "b"])
+
+        let m9 = Google_Protobuf_FieldMask(protoPaths: ["a", "a"])
+        let m10 = Google_Protobuf_FieldMask(protoPaths: ["a", "b"])
+        XCTAssertEqual(m9.intersect(m10).paths, ["a"])
+    }
+
+    // Checks `substract` func of fieldMask.
+    func testSubtractFieldMasks() throws {
+        let m1 = Google_Protobuf_FieldMask(protoPaths: ["a", "b"])
+        let m2 = Google_Protobuf_FieldMask(protoPaths: ["b", "c"])
+        XCTAssertEqual(m1.subtract(m2).paths, ["a"])
+
+        let m3 = Google_Protobuf_FieldMask(protoPaths: ["a", "b"])
+        let m4 = Google_Protobuf_FieldMask(protoPaths: ["c", "d"])
+        XCTAssertEqual(m3.subtract(m4).paths, ["a", "b"])
+
+        let m5 = Google_Protobuf_FieldMask()
+        let m6 = Google_Protobuf_FieldMask(protoPaths: ["c", "d"])
+        XCTAssertEqual(m5.subtract(m6).paths, [])
+
+        let m7 = Google_Protobuf_FieldMask(protoPaths: ["a", "b"])
+        let m8 = Google_Protobuf_FieldMask(protoPaths: ["a", "b"])
+        XCTAssertEqual(m7.subtract(m8).paths, [])
+
+        let m9 = Google_Protobuf_FieldMask(protoPaths: ["a", "a"])
+        let m10 = Google_Protobuf_FieldMask(protoPaths: ["b"])
+        XCTAssertEqual(m9.subtract(m10).paths, ["a"])
+    }
+
+    // Checks whether all field types could be merged.
+    func testMergeAllFields() throws {
+        var m1 = SwiftProtoTesting_Fuzz_Message()
+        let m2 = SwiftProtoTesting_Fuzz_Message.with { m in
+            m.singularInt32 = 1
+            m.singularInt64 = 1
+            m.singularUint32 = 1
+            m.singularUint64 = 1
+            m.singularSint32 = 1
+            m.singularSint64 = 1
+            m.singularFixed32 = 1
+            m.singularFixed64 = 1
+            m.singularSfixed32 = 1
+            m.singularSfixed64 = 1
+            m.singularFloat = 1
+            m.singularDouble = 1
+            m.singularBool = true
+            m.singularString = "str"
+            m.singularBytes = "str".data(using: .utf8) ?? .init()
+            m.singularEnum = .two
+            m.singularGroup = .with { $0.groupField = 1 }
+            m.singularMessage = .with { $0.singularInt32 = 1 }
+            m.repeatedInt32 = [1]
+            m.repeatedInt64 = [1]
+            m.repeatedUint32 = [1]
+            m.repeatedUint64 = [1]
+            m.repeatedSint32 = [1]
+            m.repeatedSint64 = [1]
+            m.repeatedFixed32 = [1]
+            m.repeatedFixed64 = [1]
+            m.repeatedSfixed32 = [1]
+            m.repeatedSfixed64 = [1]
+            m.repeatedFloat = [1]
+            m.repeatedDouble = [1]
+            m.repeatedBool = [true]
+            m.repeatedString = ["str"]
+            m.repeatedBytes = ["str".data(using: .utf8) ?? .init()]
+            m.repeatedEnum = [.two]
+            m.repeatedGroup = [.with { $0.groupField = 1 }]
+            m.repeatedMessage = [.with { $0.singularInt32 = 1 }]
+            m.o = .oneofInt32(1)
+            m.repeatedPackedInt32 = [1]
+            m.repeatedPackedInt64 = [1]
+            m.repeatedPackedUint32 = [1]
+            m.repeatedPackedUint64 = [1]
+            m.repeatedPackedSint32 = [1]
+            m.repeatedPackedSint64 = [1]
+            m.repeatedPackedFixed32 = [1]
+            m.repeatedPackedFixed64 = [1]
+            m.repeatedPackedSfixed32 = [1]
+            m.repeatedPackedSfixed64 = [1]
+            m.repeatedPackedFloat = [1]
+            m.repeatedPackedDouble = [1]
+            m.repeatedPackedBool = [true]
+            m.repeatedPackedEnum = [.two]
+            m.mapInt32Int32 = [1: 1]
+            m.mapInt32Int64 = [1: 1]
+            m.mapInt32Uint32 = [1: 1]
+            m.mapInt32Uint64 = [1: 1]
+            m.mapInt32Sint32 = [1: 1]
+            m.mapInt32Sint64 = [1: 1]
+            m.mapInt32Fixed32 = [1: 1]
+            m.mapInt32Fixed64 = [1: 1]
+            m.mapInt32AnEnum = [1: .one]
+            m.mapInt32Message = [1: .init()]
+        }
+        try m1.merge(from: m2, fieldMask: .init(allFieldsOf: SwiftProtoTesting_Fuzz_Message.self))
+        XCTAssertEqual(m1.singularInt32, m2.singularInt32)
+        XCTAssertEqual(m1.singularInt64, m2.singularInt64)
+        XCTAssertEqual(m1.singularUint32, m2.singularUint32)
+        XCTAssertEqual(m1.singularUint64, m2.singularUint64)
+        XCTAssertEqual(m1.singularSint32, m2.singularSint32)
+        XCTAssertEqual(m1.singularSint64, m2.singularSint64)
+        XCTAssertEqual(m1.singularFixed32, m2.singularFixed32)
+        XCTAssertEqual(m1.singularFixed64, m2.singularFixed64)
+        XCTAssertEqual(m1.singularSfixed32, m2.singularSfixed32)
+        XCTAssertEqual(m1.singularSfixed64, m2.singularSfixed64)
+        XCTAssertEqual(m1.singularFloat, m2.singularFloat)
+        XCTAssertEqual(m1.singularDouble, m2.singularDouble)
+        XCTAssertEqual(m1.singularBool, m2.singularBool)
+        XCTAssertEqual(m1.singularString, m2.singularString)
+        XCTAssertEqual(m1.singularBytes, m2.singularBytes)
+        XCTAssertEqual(m1.singularEnum, m2.singularEnum)
+        XCTAssertEqual(m1.singularGroup, m2.singularGroup)
+        XCTAssertEqual(m1.singularMessage, m2.singularMessage)
+        XCTAssertEqual(m1.repeatedInt32, m2.repeatedInt32)
+        XCTAssertEqual(m1.repeatedInt64, m2.repeatedInt64)
+        XCTAssertEqual(m1.repeatedUint32, m2.repeatedUint32)
+        XCTAssertEqual(m1.repeatedUint64, m2.repeatedUint64)
+        XCTAssertEqual(m1.repeatedSint32, m2.repeatedSint32)
+        XCTAssertEqual(m1.repeatedSint64, m2.repeatedSint64)
+        XCTAssertEqual(m1.repeatedFixed32, m2.repeatedFixed32)
+        XCTAssertEqual(m1.repeatedFixed64, m2.repeatedFixed64)
+        XCTAssertEqual(m1.repeatedSfixed32, m2.repeatedSfixed32)
+        XCTAssertEqual(m1.repeatedSfixed64, m2.repeatedSfixed64)
+        XCTAssertEqual(m1.repeatedFloat, m2.repeatedFloat)
+        XCTAssertEqual(m1.repeatedDouble, m2.repeatedDouble)
+        XCTAssertEqual(m1.repeatedBool, m2.repeatedBool)
+        XCTAssertEqual(m1.repeatedString, m2.repeatedString)
+        XCTAssertEqual(m1.repeatedBytes, m2.repeatedBytes)
+        XCTAssertEqual(m1.repeatedEnum, m2.repeatedEnum)
+        XCTAssertEqual(m1.repeatedGroup, m2.repeatedGroup)
+        XCTAssertEqual(m1.repeatedMessage, m2.repeatedMessage)
+        XCTAssertEqual(m1.o, m2.o)
+        XCTAssertEqual(m1.repeatedPackedInt32, m2.repeatedPackedInt32)
+        XCTAssertEqual(m1.repeatedPackedInt64, m2.repeatedPackedInt64)
+        XCTAssertEqual(m1.repeatedPackedUint32, m2.repeatedPackedUint32)
+        XCTAssertEqual(m1.repeatedPackedUint64, m2.repeatedPackedUint64)
+        XCTAssertEqual(m1.repeatedPackedSint32, m2.repeatedPackedSint32)
+        XCTAssertEqual(m1.repeatedPackedSint64, m2.repeatedPackedSint64)
+        XCTAssertEqual(m1.repeatedPackedFixed32, m2.repeatedPackedFixed32)
+        XCTAssertEqual(m1.repeatedPackedFixed64, m2.repeatedPackedFixed64)
+        XCTAssertEqual(m1.repeatedPackedSfixed32, m2.repeatedPackedSfixed32)
+        XCTAssertEqual(m1.repeatedPackedSfixed64, m2.repeatedPackedSfixed64)
+        XCTAssertEqual(m1.repeatedPackedFloat, m2.repeatedPackedFloat)
+        XCTAssertEqual(m1.repeatedPackedDouble, m2.repeatedPackedDouble)
+        XCTAssertEqual(m1.repeatedPackedBool, m2.repeatedPackedBool)
+        XCTAssertEqual(m1.repeatedPackedEnum, m2.repeatedPackedEnum)
+        XCTAssertEqual(m1.mapInt32Int32, m2.mapInt32Int32)
+        XCTAssertEqual(m1.mapInt32Int64, m2.mapInt32Int64)
+        XCTAssertEqual(m1.mapInt32Uint32, m2.mapInt32Uint32)
+        XCTAssertEqual(m1.mapInt32Uint64, m2.mapInt32Uint64)
+        XCTAssertEqual(m1.mapInt32Sint32, m2.mapInt32Sint32)
+        XCTAssertEqual(m1.mapInt32Sint64, m2.mapInt32Sint64)
+        XCTAssertEqual(m1.mapInt32Fixed32, m2.mapInt32Fixed32)
+        XCTAssertEqual(m1.mapInt32Fixed64, m2.mapInt32Fixed64)
+        XCTAssertEqual(m1.mapInt32AnEnum, m2.mapInt32AnEnum)
+        XCTAssertEqual(m1.mapInt32Message, m2.mapInt32Message)
+    }
+
+    // Checks whether a group of fields could be merged without merging the others.
+    func testMergeFieldsPartially() throws {
+        var m1 = SwiftProtoTesting_Fuzz_Message()
+        let m2 = SwiftProtoTesting_Fuzz_Message.with { m in
+            m.singularInt32 = 1
+            m.singularInt64 = 1
+            m.singularUint32 = 1
+            m.singularUint64 = 1
+            m.singularSint32 = 1
+            m.singularSint64 = 1
+            m.singularFixed32 = 1
+            m.singularFixed64 = 1
+            m.singularSfixed32 = 1
+            m.singularSfixed64 = 1
+            m.singularFloat = 1
+            m.singularDouble = 1
+            m.singularBool = true
+            m.singularString = "str"
+            m.singularBytes = "str".data(using: .utf8) ?? .init()
+            m.singularEnum = .two
+            m.singularGroup = .with { $0.groupField = 1 }
+            m.singularMessage = .with { $0.singularInt32 = 1 }
+            m.repeatedInt32 = [1]
+            m.repeatedInt64 = [1]
+            m.repeatedUint32 = [1]
+            m.repeatedUint64 = [1]
+            m.repeatedSint32 = [1]
+            m.repeatedSint64 = [1]
+            m.repeatedFixed32 = [1]
+            m.repeatedFixed64 = [1]
+            m.repeatedSfixed32 = [1]
+            m.repeatedSfixed64 = [1]
+            m.repeatedFloat = [1]
+            m.repeatedDouble = [1]
+            m.repeatedBool = [true]
+            m.repeatedString = ["str"]
+            m.repeatedBytes = ["str".data(using: .utf8) ?? .init()]
+            m.repeatedEnum = [.two]
+            m.repeatedGroup = [.with { $0.groupField = 1 }]
+            m.repeatedMessage = [.with { $0.singularInt32 = 1 }]
+            m.o = .oneofInt32(1)
+            m.repeatedPackedInt32 = [1]
+            m.repeatedPackedInt64 = [1]
+            m.repeatedPackedUint32 = [1]
+            m.repeatedPackedUint64 = [1]
+            m.repeatedPackedSint32 = [1]
+            m.repeatedPackedSint64 = [1]
+            m.repeatedPackedFixed32 = [1]
+            m.repeatedPackedFixed64 = [1]
+            m.repeatedPackedSfixed32 = [1]
+            m.repeatedPackedSfixed64 = [1]
+            m.repeatedPackedFloat = [1]
+            m.repeatedPackedDouble = [1]
+            m.repeatedPackedBool = [true]
+            m.repeatedPackedEnum = [.two]
+            m.mapInt32Int32 = [1: 1]
+            m.mapInt32Int64 = [1: 1]
+            m.mapInt32Uint32 = [1: 1]
+            m.mapInt32Uint64 = [1: 1]
+            m.mapInt32Sint32 = [1: 1]
+            m.mapInt32Sint64 = [1: 1]
+            m.mapInt32Fixed32 = [1: 1]
+            m.mapInt32Fixed64 = [1: 1]
+            m.mapInt32AnEnum = [1: .one]
+            m.mapInt32Message = [1: .init()]
+        }
+        let mask = Google_Protobuf_FieldMask(protoPaths: [
+            "singular_int32",
+            "singular_int64",
+            "singular_uint32",
+            "singular_uint64",
+            "singular_sint32",
+            "singular_sint64",
+            "singular_fixed32",
+            "singular_fixed64",
+            "singular_sfixed32",
+            "singular_sfixed64"
+        ])
+        try m1.merge(from: m2, fieldMask: mask)
+        XCTAssertEqual(m1.singularInt32, m2.singularInt32)
+        XCTAssertEqual(m1.singularInt64, m2.singularInt64)
+        XCTAssertEqual(m1.singularUint32, m2.singularUint32)
+        XCTAssertEqual(m1.singularUint64, m2.singularUint64)
+        XCTAssertEqual(m1.singularSint32, m2.singularSint32)
+        XCTAssertEqual(m1.singularSint64, m2.singularSint64)
+        XCTAssertEqual(m1.singularFixed32, m2.singularFixed32)
+        XCTAssertEqual(m1.singularFixed64, m2.singularFixed64)
+        XCTAssertEqual(m1.singularSfixed32, m2.singularSfixed32)
+        XCTAssertEqual(m1.singularSfixed64, m2.singularSfixed64)
+        XCTAssertNotEqual(m1.singularFloat, m2.singularFloat)
+        XCTAssertNotEqual(m1.singularDouble, m2.singularDouble)
+        XCTAssertNotEqual(m1.singularBool, m2.singularBool)
+        XCTAssertNotEqual(m1.singularString, m2.singularString)
+        XCTAssertNotEqual(m1.singularBytes, m2.singularBytes)
+        XCTAssertNotEqual(m1.singularEnum, m2.singularEnum)
+        XCTAssertNotEqual(m1.singularGroup, m2.singularGroup)
+        XCTAssertNotEqual(m1.singularMessage, m2.singularMessage)
+        XCTAssertNotEqual(m1.repeatedInt32, m2.repeatedInt32)
+        XCTAssertNotEqual(m1.repeatedInt64, m2.repeatedInt64)
+        XCTAssertNotEqual(m1.repeatedUint32, m2.repeatedUint32)
+        XCTAssertNotEqual(m1.repeatedUint64, m2.repeatedUint64)
+        XCTAssertNotEqual(m1.repeatedSint32, m2.repeatedSint32)
+        XCTAssertNotEqual(m1.repeatedSint64, m2.repeatedSint64)
+        XCTAssertNotEqual(m1.repeatedFixed32, m2.repeatedFixed32)
+        XCTAssertNotEqual(m1.repeatedFixed64, m2.repeatedFixed64)
+        XCTAssertNotEqual(m1.repeatedSfixed32, m2.repeatedSfixed32)
+        XCTAssertNotEqual(m1.repeatedSfixed64, m2.repeatedSfixed64)
+        XCTAssertNotEqual(m1.repeatedFloat, m2.repeatedFloat)
+        XCTAssertNotEqual(m1.repeatedDouble, m2.repeatedDouble)
+        XCTAssertNotEqual(m1.repeatedBool, m2.repeatedBool)
+        XCTAssertNotEqual(m1.repeatedString, m2.repeatedString)
+        XCTAssertNotEqual(m1.repeatedBytes, m2.repeatedBytes)
+        XCTAssertNotEqual(m1.repeatedEnum, m2.repeatedEnum)
+        XCTAssertNotEqual(m1.repeatedGroup, m2.repeatedGroup)
+        XCTAssertNotEqual(m1.repeatedMessage, m2.repeatedMessage)
+        XCTAssertNotEqual(m1.o, m2.o)
+        XCTAssertNotEqual(m1.repeatedPackedInt32, m2.repeatedPackedInt32)
+        XCTAssertNotEqual(m1.repeatedPackedInt64, m2.repeatedPackedInt64)
+        XCTAssertNotEqual(m1.repeatedPackedUint32, m2.repeatedPackedUint32)
+        XCTAssertNotEqual(m1.repeatedPackedUint64, m2.repeatedPackedUint64)
+        XCTAssertNotEqual(m1.repeatedPackedSint32, m2.repeatedPackedSint32)
+        XCTAssertNotEqual(m1.repeatedPackedSint64, m2.repeatedPackedSint64)
+        XCTAssertNotEqual(m1.repeatedPackedFixed32, m2.repeatedPackedFixed32)
+        XCTAssertNotEqual(m1.repeatedPackedFixed64, m2.repeatedPackedFixed64)
+        XCTAssertNotEqual(m1.repeatedPackedSfixed32, m2.repeatedPackedSfixed32)
+        XCTAssertNotEqual(m1.repeatedPackedSfixed64, m2.repeatedPackedSfixed64)
+        XCTAssertNotEqual(m1.repeatedPackedFloat, m2.repeatedPackedFloat)
+        XCTAssertNotEqual(m1.repeatedPackedDouble, m2.repeatedPackedDouble)
+        XCTAssertNotEqual(m1.repeatedPackedBool, m2.repeatedPackedBool)
+        XCTAssertNotEqual(m1.repeatedPackedEnum, m2.repeatedPackedEnum)
+        XCTAssertNotEqual(m1.mapInt32Int32, m2.mapInt32Int32)
+        XCTAssertNotEqual(m1.mapInt32Int64, m2.mapInt32Int64)
+        XCTAssertNotEqual(m1.mapInt32Uint32, m2.mapInt32Uint32)
+        XCTAssertNotEqual(m1.mapInt32Uint64, m2.mapInt32Uint64)
+        XCTAssertNotEqual(m1.mapInt32Sint32, m2.mapInt32Sint32)
+        XCTAssertNotEqual(m1.mapInt32Sint64, m2.mapInt32Sint64)
+        XCTAssertNotEqual(m1.mapInt32Fixed32, m2.mapInt32Fixed32)
+        XCTAssertNotEqual(m1.mapInt32Fixed64, m2.mapInt32Fixed64)
+        XCTAssertNotEqual(m1.mapInt32AnEnum, m2.mapInt32AnEnum)
+        XCTAssertNotEqual(m1.mapInt32Message, m2.mapInt32Message)
+    }
+
+    // Checks merge could be done for an optional path with nil value.
+    func testMergeOptionalValue() throws {
+        var m1 = SwiftProtoTesting_Fuzz_Message.with { m in
+            m.singularInt32 = 1
+        }
+        let m2 = SwiftProtoTesting_Fuzz_Message()
+        try m1.merge(from: m2, fieldMask: .init(protoPaths: ["singular_int32"]))
+        XCTAssertEqual(m1.singularInt32, m2.singularInt32)
+    }
+
+    // Checks merge could be done for an optional path with default value.
+    func testMergeDefaultValue() throws {
+        var m1 = SwiftProtoTesting_TestAllTypes.with { m in
+            m.defaultInt32 = 1
+        }
+        let m2 = SwiftProtoTesting_TestAllTypes()
+        try m1.merge(from: m2, fieldMask: .init(protoPaths: ["default_int32"]))
+        XCTAssertEqual(m1.defaultInt32, m2.defaultInt32)
+    }
+
+    // Checks merge could be done for non-optional paths.
+    func testMergeNonOptionalValues() throws {
+        var m1 = try SwiftProtoTesting_Proto3_TestAllTypes.with { m in
+            m.optionalInt32 = 1
+            m.optionalInt64 = 1
+            m.optionalDouble = 1
+            m.optionalFloat = 1
+            m.optionalString = "str"
+            m.optionalBool = true
+            m.optionalBytes = try XCTUnwrap("str".data(using: .utf8))
+            m.optionalUint32 = 1
+            m.optionalUint64 = 1
+            m.optionalSint32 = 1
+            m.optionalSint64 = 1
+            m.optionalFixed32 = 1
+            m.optionalFixed64 = 1
+            m.optionalSfixed32 = 1
+            m.optionalSfixed64 = 1
+            m.optionalNestedEnum = .bar
+        }
+        let m2 = SwiftProtoTesting_Proto3_TestAllTypes()
+        try m1.merge(from: m2, fieldMask: .init(protoPaths: [
+            "optional_int32",
+            "optional_int64",
+            "optional_double",
+            "optional_float",
+            "optional_string",
+            "optional_bool",
+            "optional_bytes",
+            "optional_uint32",
+            "optional_uint64",
+            "optional_sint32",
+            "optional_sint64",
+            "optional_fixed32",
+            "optional_fixed64",
+            "optional_sfixed32",
+            "optional_sfixed64",
+            "optional_nested_enum"
+        ]))
+        XCTAssertEqual(m1.optionalInt32, m2.optionalInt32)
+        XCTAssertEqual(m1.optionalInt64, m2.optionalInt64)
+        XCTAssertEqual(m1.optionalDouble, m2.optionalDouble)
+        XCTAssertEqual(m1.optionalFloat, m2.optionalFloat)
+        XCTAssertEqual(m1.optionalString, m2.optionalString)
+        XCTAssertEqual(m1.optionalBool, m2.optionalBool)
+        XCTAssertEqual(m1.optionalBytes, m2.optionalBytes)
+        XCTAssertEqual(m1.optionalUint32, m2.optionalUint32)
+        XCTAssertEqual(m1.optionalUint64, m2.optionalUint64)
+        XCTAssertEqual(m1.optionalSint32, m2.optionalSint32)
+        XCTAssertEqual(m1.optionalSint64, m2.optionalSint64)
+        XCTAssertEqual(m1.optionalFixed32, m2.optionalFixed32)
+        XCTAssertEqual(m1.optionalFixed64, m2.optionalFixed64)
+        XCTAssertEqual(m1.optionalSfixed32, m2.optionalSfixed32)
+        XCTAssertEqual(m1.optionalSfixed64, m2.optionalSfixed64)
+        XCTAssertEqual(m1.optionalNestedEnum, m2.optionalNestedEnum)
+        XCTAssertEqual(m1.optionalSint32, m2.optionalSint32)
+    }
+
+    // Checks if merge works with nested proto messages
+    func testMergeNestedMessages() throws {
+        var m1 = SwiftProtoTesting_Fuzz_Message()
+        let m2 = SwiftProtoTesting_Fuzz_Message.with { m in
+            m.singularMessage = .with { _m in
+                _m.singularMessage = .with { __m in
+                    __m.singularInt32 = 1
+                }
+            }
+        }
+        let m3 = SwiftProtoTesting_Fuzz_Message.with { m in
+            m.singularMessage = .with { _m in
+                _m.singularMessage = .with { __m in
+                    __m.singularInt32 = 2
+                }
+            }
+        }
+        try m1.merge(from: m2, fieldMask: .init(protoPaths: ["singular_message.singular_message"]))
+        XCTAssertEqual(m1.singularMessage.singularMessage.singularInt32, Int32(1))
+        try m1.merge(from: m3, fieldMask: .init(protoPaths: ["singular_message.singular_message.singular_int32"]))
+        XCTAssertEqual(m1.singularMessage.singularMessage.singularInt32, Int32(2))
+    }
+
+    // Checks merging nested path inside groups
+    func testMergeNestedGroups() throws {
+        var m1 = SwiftProtoTesting_Fuzz_Message()
+        let m2 = SwiftProtoTesting_Fuzz_Message.with { m in
+            m.singularGroup = .with { _m in
+                _m.groupField = 1
+            }
+        }
+        try m1.merge(from: m2, fieldMask: .init(protoPaths: ["SingularGroup.group_field"]))
+        XCTAssertEqual(m1.singularGroup.groupField, m2.singularGroup.groupField)
+    }
+
+    // Checks that merging with json path should do nothing. Path should only be merged using proto names.
+    func testMergeFieldWithJSONName() throws {
+        var m1 = SwiftProtoTesting_Fuzz_Message()
+        let m2 = SwiftProtoTesting_Fuzz_Message.with { m in
+            m.singularGroup = .with { $0.groupField = 1 }
+        }
+        // should do nothing with json path (should not merge)
+        try m1.merge(from: m2, fieldMask: .with({ $0.paths = ["singulargroup"] }))
+        XCTAssertNotEqual(m1.singularGroup, m2.singularGroup)
+        // should merge with proto path
+        try m1.merge(from: m2, fieldMask: .with({ $0.paths = ["SingularGroup"] }))
+        XCTAssertEqual(m1.singularGroup, m2.singularGroup)
+        // should do nothing with json path (do not clear field)
+        try m1.merge(from: m2, fieldMask: .with({ $0.paths = ["singulargroup"] }))
+        XCTAssertEqual(m1.singularGroup, m2.singularGroup)
+    }
 }