/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 | | } |