Coverage Report

Created: 2025-08-11 06:15

/src/swift-protobuf/Sources/SwiftProtobuf/Message.swift
Line
Count
Source (jump to first uncovered line)
1
// Sources/SwiftProtobuf/Message.swift - Message support
2
//
3
// Copyright (c) 2014 - 2016 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
/// The protocol which all generated protobuf messages implement.
11
/// `Message` is the protocol type you should use whenever
12
/// you need an argument or variable which holds "some message".
13
///
14
/// Generated messages also implement `Hashable`, and thus `Equatable`.
15
/// However, the protocol conformance is declared on a different protocol.
16
/// This allows you to use `Message` as a type directly:
17
///
18
///     func consume(message: Message) { ... }
19
///
20
/// Instead of needing to use it as a type constraint on a generic declaration:
21
///
22
///     func consume<M: Message>(message: M) { ... }
23
///
24
/// If you need to convince the compiler that your message is `Hashable` so
25
/// you can insert it into a `Set` or use it as a `Dictionary` key, use
26
/// a generic declaration with a type constraint:
27
///
28
///     func insertIntoSet<M: Message & Hashable>(message: M) {
29
///         mySet.insert(message)
30
///     }
31
///
32
/// The actual functionality is implemented either in the generated code or in
33
/// default implementations of the below methods and properties.
34
@preconcurrency
35
public protocol Message: Sendable, CustomDebugStringConvertible {
36
    /// Creates a new message with all of its fields initialized to their default
37
    /// values.
38
    init()
39
40
    // Metadata
41
    // Basic facts about this class and the proto message it was generated from
42
    // Used by various encoders and decoders
43
44
    /// The fully-scoped name of the message from the original .proto file,
45
    /// including any relevant package name.
46
    static var protoMessageName: String { get }
47
48
    /// True if all required fields (if any) on this message and any nested
49
    /// messages (recursively) have values set; otherwise, false.
50
    var isInitialized: Bool { get }
51
52
    /// Some formats include enough information to transport fields that were
53
    /// not known at generation time. When encountered, they are stored here.
54
    var unknownFields: UnknownStorage { get set }
55
56
    //
57
    // General serialization/deserialization machinery
58
    //
59
60
    /// Decode all of the fields from the given decoder.
61
    ///
62
    /// This is a simple loop that repeatedly gets the next field number
63
    /// from `decoder.nextFieldNumber()` and then uses the number returned
64
    /// and the type information from the original .proto file to decide
65
    /// what type of data should be decoded for that field.  The corresponding
66
    /// method on the decoder is then called to get the field value.
67
    ///
68
    /// This is the core method used by the deserialization machinery. It is
69
    /// `public` to enable users to implement their own encoding formats by
70
    /// conforming to `Decoder`; it should not be called otherwise.
71
    ///
72
    /// Note that this is not specific to binary encodng; formats that use
73
    /// textual identifiers translate those to field numbers and also go
74
    /// through this to decode messages.
75
    ///
76
    /// - Parameters:
77
    ///   - decoder: a `Decoder`; the `Message` will call the method
78
    ///     corresponding to the type of this field.
79
    /// - Throws: an error on failure or type mismatch.  The type of error
80
    ///     thrown depends on which decoder is used.
81
    mutating func decodeMessage<D: Decoder>(decoder: inout D) throws
82
83
    /// Traverses the fields of the message, calling the appropriate methods
84
    /// of the passed `Visitor` object.
85
    ///
86
    /// This is used internally by:
87
    ///
88
    /// * Protobuf binary serialization
89
    /// * JSON serialization (with some twists to account for specialty JSON)
90
    /// * Protobuf Text serialization
91
    /// * `Hashable` computation
92
    ///
93
    /// Conceptually, serializers create visitor objects that are
94
    /// then passed recursively to every message and field via generated
95
    /// `traverse` methods.  The details get a little involved due to
96
    /// the need to allow particular messages to override particular
97
    /// behaviors for specific encodings, but the general idea is quite simple.
98
    func traverse<V: Visitor>(visitor: inout V) throws
99
100
    // Standard utility properties and methods.
101
    // Most of these are simple wrappers on top of the visitor machinery.
102
    // They are implemented in the protocol, not in the generated structs,
103
    // so can be overridden in user code by defining custom extensions to
104
    // the generated struct.
105
106
    /// An implementation of hash(into:) to provide conformance with the
107
    /// `Hashable` protocol.
108
    func hash(into hasher: inout Hasher)
109
110
    /// Helper to compare `Message`s when not having a specific type to use
111
    /// normal `Equatable`. `Equatable` is provided with specific generated
112
    /// types.
113
    func isEqualTo(message: any Message) -> Bool
114
}
115
116
extension Message {
117
    /// Generated proto2 messages that contain required fields, nested messages
118
    /// that contain required fields, and/or extensions will provide their own
119
    /// implementation of this property that tests that all required fields are
120
    /// set. Users of the generated code SHOULD NOT override this property.
121
232k
    public var isInitialized: Bool {
122
232k
        // The generated code will include a specialization as needed.
123
232k
        true
124
232k
    }
125
126
    /// A hash based on the message's full contents.
127
0
    public func hash(into hasher: inout Hasher) {
128
0
        var visitor = HashVisitor(hasher)
129
0
        try? traverse(visitor: &visitor)
130
0
        hasher = visitor.hasher
131
0
    }
132
133
    /// A description generated by recursively visiting all fields in the message,
134
    /// including messages.
135
0
    public var debugDescription: String {
136
0
        #if DEBUG
137
0
        // TODO Ideally there would be something like serializeText() that can
138
0
        // take a prefix so we could do something like:
139
0
        //   [class name](
140
0
        //      [text format]
141
0
        //   )
142
0
        let className = String(reflecting: type(of: self))
143
0
        let header = "\(className):\n"
144
0
        return header + textFormatString()
145
0
        #else
146
0
        return String(reflecting: type(of: self))
147
0
        #endif
148
0
    }
149
150
    /// Creates an instance of the message type on which this method is called,
151
    /// executes the given block passing the message in as its sole `inout`
152
    /// argument, and then returns the message.
153
    ///
154
    /// This method acts essentially as a "builder" in that the initialization of
155
    /// the message is captured within the block, allowing the returned value to
156
    /// be set in an immutable variable. For example,
157
    ///
158
    ///     let msg = MyMessage.with { $0.myField = "foo" }
159
    ///     msg.myOtherField = 5  // error: msg is immutable
160
    ///
161
    /// - Parameter populator: A block or function that populates the new message,
162
    ///   which is passed into the block as an `inout` argument.
163
    /// - Returns: The message after execution of the block.
164
    public static func with(
165
        _ populator: (inout Self) throws -> Void
166
0
    ) rethrows -> Self {
167
0
        var message = Self()
168
0
        try populator(&message)
169
0
        return message
170
0
    }
171
}
172
173
/// Implementation base for all messages; not intended for client use.
174
///
175
/// In general, use `SwiftProtobuf.Message` instead when you need a variable or
176
/// argument that can hold any type of message. Occasionally, you can use
177
/// `SwiftProtobuf.Message & Equatable` or `SwiftProtobuf.Message & Hashable` as
178
/// generic constraints if you need to write generic code that can be applied to
179
/// multiple message types that uses equality tests, puts messages in a `Set`,
180
/// or uses them as `Dictionary` keys.
181
@preconcurrency
182
public protocol _MessageImplementationBase: Message, Hashable {
183
184
    // Legacy function; no longer used, but left to maintain source compatibility.
185
    func _protobuf_generated_isEqualTo(other: Self) -> Bool
186
}
187
188
extension _MessageImplementationBase {
189
0
    public func isEqualTo(message: any Message) -> Bool {
190
0
        guard let other = message as? Self else {
191
0
            return false
192
0
        }
193
0
        return self == other
194
0
    }
195
196
    // Legacy default implementation that is used by old generated code, current
197
    // versions of the plugin/generator provide this directly, but this is here
198
    // just to avoid breaking source compatibility.
199
0
    public static func == (lhs: Self, rhs: Self) -> Bool {
200
0
        lhs._protobuf_generated_isEqualTo(other: rhs)
201
0
    }
202
203
    // Legacy function that is generated by old versions of the plugin/generator,
204
    // defaulted to keep things simple without changing the api surface.
205
0
    public func _protobuf_generated_isEqualTo(other: Self) -> Bool {
206
0
        self == other
207
0
    }
208
}