Message+FieldMask.swift 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133
  1. // Sources/SwiftProtobuf/Message+FieldMask.swift - Message field mask extensions
  2. //
  3. // Copyright (c) 2014 - 2023 Apple Inc. and the project authors
  4. // Licensed under Apache License v2.0 with Runtime Library Exception
  5. //
  6. // See LICENSE.txt for license information:
  7. // https://github.com/apple/swift-protobuf/blob/main/LICENSE.txt
  8. //
  9. // -----------------------------------------------------------------------------
  10. ///
  11. /// Extend the Message types with FieldMask utilities.
  12. ///
  13. // -----------------------------------------------------------------------------
  14. import Foundation
  15. extension Message {
  16. /// Checks whether the given path is valid for Message type.
  17. ///
  18. /// - Parameter path: Path to be checked
  19. /// - Returns: Boolean determines path is valid.
  20. public static func isPathValid(
  21. _ path: String
  22. ) -> Bool {
  23. var message = Self()
  24. return message.hasPath(path: path)
  25. }
  26. internal mutating func hasPath(path: String) -> Bool {
  27. do {
  28. try set(path: path, value: nil, mergeOption: .init())
  29. return true
  30. } catch let error as PathDecodingError {
  31. return error != .pathNotFound
  32. } catch {
  33. return false
  34. }
  35. }
  36. internal mutating func isPathValid(
  37. _ path: String
  38. ) -> Bool {
  39. hasPath(path: path)
  40. }
  41. }
  42. extension Google_Protobuf_FieldMask {
  43. /// Defines available options for merging two messages.
  44. public struct MergeOptions {
  45. public init() {}
  46. /// The default merging behavior will append entries from the source
  47. /// repeated field to the destination repeated field. If you only want
  48. /// to keep the entries from the source repeated field, set this flag
  49. /// to true.
  50. public var replaceRepeatedFields = false
  51. }
  52. }
  53. extension Message {
  54. /// Merges fields specified in a FieldMask into another message.
  55. ///
  56. /// - Parameters:
  57. /// - source: Message that should be merged to the original one.
  58. /// - fieldMask: FieldMask specifies which fields should be merged.
  59. public mutating func merge(
  60. from source: Self,
  61. fieldMask: Google_Protobuf_FieldMask,
  62. mergeOption: Google_Protobuf_FieldMask.MergeOptions = .init()
  63. ) throws {
  64. var visitor = PathVisitor<Self>()
  65. try source.traverse(visitor: &visitor)
  66. let values = visitor.values
  67. // TODO: setting all values with only one decoding
  68. for path in fieldMask.paths {
  69. try? set(
  70. path: path,
  71. value: values[path],
  72. mergeOption: mergeOption
  73. )
  74. }
  75. }
  76. }
  77. extension Message where Self: Equatable, Self: _ProtoNameProviding {
  78. // TODO: Re-implement using clear fields instead of copying message
  79. /// Removes from 'message' any field that is not represented in the given
  80. /// FieldMask. If the FieldMask is empty, does nothing.
  81. ///
  82. /// - Parameter fieldMask: FieldMask specifies which fields should be kept.
  83. /// - Returns: Boolean determines if the message is modified
  84. @discardableResult
  85. public mutating func trim(
  86. keeping fieldMask: Google_Protobuf_FieldMask
  87. ) -> Bool {
  88. if !fieldMask.isValid(for: Self.self) {
  89. return false
  90. }
  91. if fieldMask.paths.isEmpty {
  92. return false
  93. }
  94. var tmp = Self(removingAllFieldsOf: self)
  95. do {
  96. try tmp.merge(from: self, fieldMask: fieldMask)
  97. let changed = tmp != self
  98. self = tmp
  99. return changed
  100. } catch {
  101. return false
  102. }
  103. }
  104. }
  105. extension Message {
  106. fileprivate init(removingAllFieldsOf message: Self) {
  107. let newMessage: Self = .init()
  108. if var newExtensible = newMessage as? any ExtensibleMessage,
  109. let extensible = message as? any ExtensibleMessage
  110. {
  111. newExtensible._protobuf_extensionFieldValues = extensible._protobuf_extensionFieldValues
  112. self = newExtensible as? Self ?? newMessage
  113. } else {
  114. self = newMessage
  115. }
  116. self.unknownFields = message.unknownFields
  117. }
  118. }