Coverage Report

Created: 2026-08-14 06:30

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