Google_Protobuf_Any+Extensions.swift 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  1. // Sources/SwiftProtobuf/Google_Protobuf_Any+Extensions.swift - Well-known Any type
  2. //
  3. // Copyright (c) 2014 - 2017 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. /// Extends the `Google_Protobuf_Any` type with various custom behaviors.
  12. ///
  13. // -----------------------------------------------------------------------------
  14. // Explicit import of Foundation is necessary on Linux,
  15. // don't remove unless obsolete on all platforms
  16. import Foundation
  17. public let defaultAnyTypeURLPrefix: String = "type.googleapis.com"
  18. extension Google_Protobuf_Any {
  19. /// Initialize an Any object from the provided message.
  20. ///
  21. /// This corresponds to the `pack` operation in the C++ API.
  22. ///
  23. /// Unlike the C++ implementation, the message is not immediately
  24. /// serialized; it is merely stored until the Any object itself
  25. /// needs to be serialized. This design avoids unnecessary
  26. /// decoding/recoding when writing JSON format.
  27. ///
  28. /// - Parameters:
  29. /// - partial: If `false` (the default), this method will check
  30. /// ``Message/isInitialized-6abgi`` before encoding to verify that all required
  31. /// fields are present. If any are missing, this method throws
  32. /// ``BinaryEncodingError/missingRequiredFields``.
  33. /// - typePrefix: The prefix to be used when building the `type_url`.
  34. /// Defaults to "type.googleapis.com".
  35. /// - Throws: ``BinaryEncodingError/missingRequiredFields`` if
  36. /// `partial` is false and `message` wasn't fully initialized.
  37. public init(
  38. message: any Message,
  39. partial: Bool = false,
  40. typePrefix: String = defaultAnyTypeURLPrefix
  41. ) throws {
  42. if !partial && !message.isInitialized {
  43. throw BinaryEncodingError.missingRequiredFields
  44. }
  45. self.init()
  46. typeURL = buildTypeURL(forMessage: message, typePrefix: typePrefix)
  47. _storage.state = .message(message)
  48. }
  49. /// Creates a new `Google_Protobuf_Any` by decoding the given string
  50. /// containing a serialized message in Protocol Buffer text format.
  51. ///
  52. /// - Parameters:
  53. /// - textFormatString: The text format string to decode.
  54. /// - extensions: An `ExtensionMap` used to look up and decode any
  55. /// extensions in this message or messages nested within this message's
  56. /// fields.
  57. /// - Throws: an instance of `TextFormatDecodingError` on failure.
  58. @_disfavoredOverload
  59. public init(
  60. textFormatString: String,
  61. extensions: (any ExtensionMap)? = nil
  62. ) throws {
  63. // TODO: Remove this api and default the options instead when we do a major release.
  64. try self.init(
  65. textFormatString: textFormatString,
  66. options: TextFormatDecodingOptions(),
  67. extensions: extensions
  68. )
  69. }
  70. /// Creates a new `Google_Protobuf_Any` by decoding the given string
  71. /// containing a serialized message in Protocol Buffer text format.
  72. ///
  73. /// - Parameters:
  74. /// - textFormatString: The text format string to decode.
  75. /// - options: The ``TextFormatDecodingOptions`` to use.
  76. /// - extensions: An ``ExtensionMap`` used to look up and decode any
  77. /// extensions in this message or messages nested within this message's
  78. /// fields.
  79. /// - Throws: ``TextFormatDecodingError`` on failure.
  80. public init(
  81. textFormatString: String,
  82. options: TextFormatDecodingOptions = TextFormatDecodingOptions(),
  83. extensions: (any ExtensionMap)? = nil
  84. ) throws {
  85. self.init()
  86. if !textFormatString.isEmpty {
  87. if let data = textFormatString.data(using: String.Encoding.utf8) {
  88. try data.withUnsafeBytes { (body: UnsafeRawBufferPointer) in
  89. if let baseAddress = body.baseAddress, body.count > 0 {
  90. var textDecoder = try TextFormatDecoder(
  91. messageType: Google_Protobuf_Any.self,
  92. utf8Pointer: baseAddress,
  93. count: body.count,
  94. options: options,
  95. extensions: extensions
  96. )
  97. try decodeTextFormat(decoder: &textDecoder)
  98. if !textDecoder.complete {
  99. throw TextFormatDecodingError.trailingGarbage
  100. }
  101. }
  102. }
  103. }
  104. }
  105. }
  106. /// Returns true if this `Google_Protobuf_Any` message contains the given
  107. /// message type.
  108. ///
  109. /// The check is performed by looking at the passed `Message.Type` and the
  110. /// `typeURL` of this message.
  111. ///
  112. /// - Parameter type: The concrete message type.
  113. /// - Returns: True if the receiver contains the given message type.
  114. public func isA<M: Message>(_ type: M.Type) -> Bool {
  115. _storage.isA(type)
  116. }
  117. public func hash(into hasher: inout Hasher) {
  118. _storage.hash(into: &hasher)
  119. }
  120. }
  121. extension Google_Protobuf_Any {
  122. internal func textTraverse(visitor: inout TextFormatEncodingVisitor) {
  123. _storage.textTraverse(visitor: &visitor)
  124. try! unknownFields.traverse(visitor: &visitor)
  125. }
  126. }
  127. extension Google_Protobuf_Any {
  128. // Custom text format decoding support for Any objects.
  129. // (Note: This is not a part of any protocol; it's invoked
  130. // directly from TextFormatDecoder whenever it sees an attempt
  131. // to decode an Any object)
  132. internal mutating func decodeTextFormat(
  133. decoder: inout TextFormatDecoder
  134. ) throws {
  135. // First, check if this uses the "verbose" Any encoding.
  136. // If it does, and we have the type available, we can
  137. // eagerly decode the contained Message object.
  138. if let url = try decoder.scanner.nextOptionalAnyURL() {
  139. try _uniqueStorage().decodeTextFormat(typeURL: url, decoder: &decoder)
  140. } else {
  141. // This is not using the specialized encoding, so we can use the
  142. // standard path to decode the binary value.
  143. // First, clear the fields so we don't waste time re-serializing
  144. // the previous contents as this instances get replaced with a
  145. // new value (can happen when a field name/number is repeated in
  146. // the TextFormat input).
  147. self.typeURL = ""
  148. self.value = Data()
  149. try decodeMessage(decoder: &decoder)
  150. }
  151. }
  152. }
  153. extension Google_Protobuf_Any: _CustomJSONCodable {
  154. internal func encodedJSONString(options: JSONEncodingOptions) throws -> String {
  155. try _storage.encodedJSONString(options: options)
  156. }
  157. internal mutating func decodeJSON(from decoder: inout JSONDecoder) throws {
  158. try _uniqueStorage().decodeJSON(from: &decoder)
  159. }
  160. }