Skip to content

Commit 1c94065

Browse files
author
Pouya Yarandi
committed
Add comments
1 parent da24d19 commit 1c94065

File tree

3 files changed

+115
-21
lines changed

3 files changed

+115
-21
lines changed

Sources/SwiftProtobuf/Google_Protobuf_FieldMask+Extensions.swift

Lines changed: 58 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,6 @@
1313
///
1414
// -----------------------------------------------------------------------------
1515

16-
// TODO: We should have utilities to apply a fieldmask to an arbitrary
17-
// message, intersect two fieldmasks, etc.
18-
// Google's C++ implementation does this by having utilities
19-
// to build a tree of field paths that can be easily intersected,
20-
// unioned, traversed to apply to submessages, etc.
2116

2217
// True if the string only contains printable (non-control)
2318
// ASCII characters. Note: This follows the ASCII standard;
@@ -188,6 +183,7 @@ extension Google_Protobuf_FieldMask: _CustomJSONCodable {
188183
extension Google_Protobuf_FieldMask {
189184

190185
/// Initiates a field mask with all fields of the message type.
186+
///
191187
/// - Parameter messageType: Message type to get all paths from.
192188
public init<M: Message & _ProtoNameProviding>(
193189
allFieldsOf messageType: M.Type
@@ -198,6 +194,7 @@ extension Google_Protobuf_FieldMask {
198194
}
199195

200196
/// Initiates a field mask from some particular field numbers of a message
197+
///
201198
/// - Parameters:
202199
/// - messageType: Message type to get all paths from.
203200
/// - fieldNumbers: Field numbers of paths to be included.
@@ -219,12 +216,14 @@ extension Google_Protobuf_FieldMask {
219216
}
220217
}
221218

222-
public enum FieldMaskUtilsError: Error {
223-
case invalidPath
224-
case invalidFieldNumber
225-
}
226-
227219
extension Google_Protobuf_FieldMask {
220+
221+
/// Adds a path to FieldMask after checking whether the given path is valid.
222+
/// This method check-fails if the path is not a valid path for Message type.
223+
///
224+
/// - Parameters:
225+
/// - path: Path to be added to FieldMask.
226+
/// - messageType: Message type to check validity.
228227
public mutating func addPath<M: Message>(
229228
_ path: String,
230229
of messageType: M.Type
@@ -235,6 +234,11 @@ extension Google_Protobuf_FieldMask {
235234
paths.append(path)
236235
}
237236

237+
/// Converts a FieldMask to the canonical form. It will:
238+
/// 1. Remove paths that are covered by another path. For example,
239+
/// "foo.bar" is covered by "foo" and will be removed if "foo"
240+
/// is also in the FieldMask.
241+
/// 2. Sort all paths in alphabetical order.
238242
public var canonical: Google_Protobuf_FieldMask {
239243
var mask = Google_Protobuf_FieldMask()
240244
let sortedPaths = self.paths.sorted()
@@ -248,6 +252,10 @@ extension Google_Protobuf_FieldMask {
248252
return mask
249253
}
250254

255+
/// Creates an union of two FieldMasks.
256+
///
257+
/// - Parameter mask: FieldMask to union with.
258+
/// - Returns: FieldMask with union of two path sets.
251259
public func union(
252260
_ mask: Google_Protobuf_FieldMask
253261
) -> Google_Protobuf_FieldMask {
@@ -263,10 +271,10 @@ extension Google_Protobuf_FieldMask {
263271
}
264272
}
265273

266-
private var pathsSet: Set<String> {
267-
.init(paths)
268-
}
269-
274+
/// Creates an intersection of two FieldMasks.
275+
///
276+
/// - Parameter mask: FieldMask to intersect with.
277+
/// - Returns: FieldMask with intersection of two path sets.
270278
public func intersect(
271279
_ mask: Google_Protobuf_FieldMask
272280
) -> Google_Protobuf_FieldMask {
@@ -280,6 +288,11 @@ extension Google_Protobuf_FieldMask {
280288
}
281289
}
282290

291+
/// Creates a FieldMasks with paths of the original FieldMask
292+
/// that does not included in mask.
293+
///
294+
/// - Parameter mask: FieldMask with paths should be substracted.
295+
/// - Returns: FieldMask with all paths does not included in mask.
283296
public func subtract(
284297
_ mask: Google_Protobuf_FieldMask
285298
) -> Google_Protobuf_FieldMask {
@@ -293,6 +306,22 @@ extension Google_Protobuf_FieldMask {
293306
}
294307
}
295308

309+
/// Returns true if path is covered by the given FieldMask. Note that path
310+
/// "foo.bar" covers all paths like "foo.bar.baz", "foo.bar.quz.x", etc.
311+
/// Also note that parent paths are not covered by explicit child path, i.e.
312+
/// "foo.bar" does NOT cover "foo", even if "bar" is the only child.
313+
///
314+
/// - Parameter path: Path to be checked.
315+
/// - Returns: Boolean determines is path covered.
316+
public func contains(_ path: String) -> Bool {
317+
contains(path, in: pathsSet)
318+
}
319+
320+
// Set containing paths of FieldMask
321+
private var pathsSet: Set<String> {
322+
.init(paths)
323+
}
324+
296325
private func levels(path: String) -> [String] {
297326
let comps = path.components(separatedBy: ".")
298327
return (0..<comps.count).map {
@@ -311,13 +340,14 @@ extension Google_Protobuf_FieldMask {
311340
}
312341
return false
313342
}
314-
315-
public func contains(_ path: String) -> Bool {
316-
contains(path, in: pathsSet)
317-
}
318343
}
319344

320345
extension Google_Protobuf_FieldMask {
346+
347+
/// Checks whether the given FieldMask is valid for type M.
348+
///
349+
/// - Parameter messageType: Message type to paths check with.
350+
/// - Returns: Boolean determines FieldMask is valid.
321351
public func isValid<M: Message & _ProtoNameProviding>(
322352
for messageType: M.Type
323353
) -> Bool {
@@ -328,6 +358,16 @@ extension Google_Protobuf_FieldMask {
328358
}
329359
}
330360

361+
/// Describes errors could happen during FieldMask utilities.
362+
public enum FieldMaskUtilsError: Error {
363+
364+
/// Describes a path is invalid for a Message type.
365+
case invalidPath
366+
367+
/// Describes a fieldNumber is invalid for a Message type.
368+
case invalidFieldNumber
369+
}
370+
331371
private extension Message where Self: _ProtoNameProviding {
332372
static func protoName(for number: Int) -> String? {
333373
Self._protobuf_nameMap.names(for: number)?.proto.description

Sources/SwiftProtobuf/Message+FieldMask.swift

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,11 @@
1515
import Foundation
1616

1717
extension Message {
18+
19+
/// Checks whether the given path is valid for Message type.
20+
///
21+
/// - Parameter path: Path to be checked
22+
/// - Returns: Boolean determines path is valid.
1823
public static func isPathValid(
1924
_ path: String
2025
) -> Bool {
@@ -29,6 +34,12 @@ extension Message {
2934
}
3035

3136
extension Message {
37+
38+
/// Merges fields specified in a FieldMask into another message.
39+
///
40+
/// - Parameters:
41+
/// - source: Message should be merged to the original one.
42+
/// - fieldMask: FieldMask specifies which fields should be merged.
3243
public mutating func merge(
3344
to source: Self,
3445
fieldMask: Google_Protobuf_FieldMask
@@ -46,7 +57,13 @@ extension Message {
4657
}
4758

4859
extension Message where Self: Equatable, Self: _ProtoNameProviding {
60+
4961
@discardableResult
62+
/// Removes from 'message' any field that is not represented in the given
63+
/// FieldMask. If the FieldMask is empty, does nothing.
64+
///
65+
/// - Parameter fieldMask: FieldMask specifies which fields should be kept.
66+
/// - Returns: Boolean determines if the message is modified
5067
public mutating func trim(
5168
fieldMask: Google_Protobuf_FieldMask
5269
) -> Bool {

Tests/SwiftProtobufTests/Test_FieldMask.swift

Lines changed: 40 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,8 @@ final class Test_FieldMask: XCTestCase, PBTestHelpers {
106106
XCTAssertThrowsError(try m.jsonString())
107107
}
108108
}
109-
109+
110+
// Checks merge functionality for field masks.
110111
func testMergeFieldsOfMessage() throws {
111112
var message = SwiftProtoTesting_TestAllTypes.with { model in
112113
model.optionalInt32 = 1
@@ -122,15 +123,18 @@ final class Test_FieldMask: XCTestCase, PBTestHelpers {
122123
}
123124
}
124125

126+
// Checks nested message merge
125127
try message.merge(to: secondMessage, fieldMask: .init(protoPaths: "optional_nested_message.bb"))
126128
XCTAssertEqual(message.optionalInt32, 1)
127129
XCTAssertEqual(message.optionalNestedMessage.bb, 3)
128130

131+
// Checks primitive type merge
129132
try message.merge(to: secondMessage, fieldMask: .init(protoPaths: "optional_int32"))
130133
XCTAssertEqual(message.optionalInt32, 2)
131134
XCTAssertEqual(message.optionalNestedMessage.bb, 3)
132135
}
133136

137+
// Checks trim functionality for field masks.
134138
func testTrimFieldsOfMessage() throws {
135139
var message = SwiftProtoTesting_TestAllTypes.with { model in
136140
model.optionalInt32 = 1
@@ -139,28 +143,41 @@ final class Test_FieldMask: XCTestCase, PBTestHelpers {
139143
}
140144
}
141145

146+
// Checks trim to be successful.
142147
let r1 = message.trim(fieldMask: .init(protoPaths: "optional_nested_message.bb"))
143148
XCTAssertTrue(r1)
144149
XCTAssertEqual(message.optionalInt32, 0)
145150
XCTAssertEqual(message.optionalNestedMessage.bb, 2)
146151

152+
// Checks trim should does nothing with an empty fieldMask.
147153
let r2 = message.trim(fieldMask: .init())
148154
XCTAssertFalse(r2)
149155

156+
// Checks trim should return false if nothing has been changed.
150157
let r3 = message.trim(fieldMask: .init(protoPaths: "optional_nested_message.bb"))
151158
XCTAssertFalse(r3)
152159

160+
// Checks trim to be unsuccessful with an invalid fieldMask.
153161
let r4 = message.trim(fieldMask: .init(protoPaths: "invalid_path"))
154162
XCTAssertFalse(r4)
155163
}
156164

165+
// Checks `isPathValid` func
166+
// 1. Valid primitive path should be valid.
167+
// 2. Valid nested path should be valid.
168+
// 3. Invalid primitive path should be valid.
169+
// 4. Invalid nested path should be valid.
157170
func testIsPathValid() {
158171
XCTAssertTrue(SwiftProtoTesting_TestAllTypes.isPathValid("optional_int32"))
159172
XCTAssertTrue(SwiftProtoTesting_TestAllTypes.isPathValid("optional_nested_message.bb"))
160173
XCTAssertFalse(SwiftProtoTesting_TestAllTypes.isPathValid("optional_int"))
161174
XCTAssertFalse(SwiftProtoTesting_TestAllTypes.isPathValid("optional_nested_message.bc"))
162175
}
163176

177+
// Checks `isValid` func of FieldMask.
178+
// 1. Empty field mask is always valid.
179+
// 2, 3. Valid field masks.
180+
// 4, 5. Invalid field masks.
164181
func testIsFieldMaskValid() {
165182
let m1 = Google_Protobuf_FieldMask()
166183
let m2 = Google_Protobuf_FieldMask(protoPaths: [
@@ -186,15 +203,23 @@ final class Test_FieldMask: XCTestCase, PBTestHelpers {
186203
XCTAssertFalse(m5.isValid(for: SwiftProtoTesting_TestAllTypes.self))
187204
}
188205

206+
// Checks canonincal form of field mask.
207+
// 1. Sub-message with parent in the paths should be excluded.
208+
// 2. Canonincal form should be sorted.
209+
// 3. More nested levels of paths.
189210
func testCanonicalFieldMask() {
190-
let m1 = Google_Protobuf_FieldMask(protoPaths: ["a.b", "b", "a"])
211+
let m1 = Google_Protobuf_FieldMask(protoPaths: ["a.b", "a", "b"])
191212
XCTAssertEqual(m1.canonical.paths, ["a", "b"])
192-
let m2 = Google_Protobuf_FieldMask(protoPaths: ["a", "b"])
213+
let m2 = Google_Protobuf_FieldMask(protoPaths: ["b", "a"])
193214
XCTAssertEqual(m2.canonical.paths, ["a", "b"])
194215
let m3 = Google_Protobuf_FieldMask(protoPaths: ["c", "a.b.c", "a.b", "a.b.c.d"])
195216
XCTAssertEqual(m3.canonical.paths, ["a.b", "c"])
196217
}
197218

219+
// Checks `addPath` func of fieldMask with:
220+
// - Valid primitive path should be added.
221+
// - Valid nested path should be added.
222+
// - Invalid path should throw error.
198223
func testAddPathToFieldMask() throws {
199224
var mask = Google_Protobuf_FieldMask()
200225
XCTAssertNoThrow(try mask.addPath("optional_int32", of: SwiftProtoTesting_TestAllTypes.self))
@@ -204,6 +229,12 @@ final class Test_FieldMask: XCTestCase, PBTestHelpers {
204229
XCTAssertThrowsError(try mask.addPath("optional_int", of: SwiftProtoTesting_TestAllTypes.self))
205230
}
206231

232+
// Check `contains` func of fieldMask.
233+
// 1. Parent contains sub-message.
234+
// 2. Path contains itself.
235+
// 3. Sub-message does not contain its parent.
236+
// 4. Two different paths does not contain each other.
237+
// 5. Two different sub-paths does not contain each other.
207238
func testPathContainsInFieldMask() {
208239
let m1 = Google_Protobuf_FieldMask(protoPaths: ["a"])
209240
XCTAssertTrue(m1.contains("a.b"))
@@ -217,6 +248,9 @@ final class Test_FieldMask: XCTestCase, PBTestHelpers {
217248
XCTAssertFalse(m5.contains("a.c"))
218249
}
219250

251+
// Checks inits of fieldMask with:
252+
// - All fields of a message type.
253+
// - Particular field numbers of a message type.
220254
func testFieldPathMessageInits() throws {
221255
let m1 = Google_Protobuf_FieldMask(allFieldsOf: SwiftProtoTesting_TestAny.self)
222256
XCTAssertEqual(m1.paths.sorted(), ["any_value", "int32_value", "repeated_any_value", "text"])
@@ -225,6 +259,7 @@ final class Test_FieldMask: XCTestCase, PBTestHelpers {
225259
XCTAssertThrowsError(try Google_Protobuf_FieldMask(fieldNumbers: [10], of: SwiftProtoTesting_TestAny.self))
226260
}
227261

262+
// Checks `union` func of fieldMask.
228263
func testUnionFieldMasks() throws {
229264
let m1 = Google_Protobuf_FieldMask(protoPaths: ["a", "b"])
230265
let m2 = Google_Protobuf_FieldMask(protoPaths: ["b", "c"])
@@ -243,6 +278,7 @@ final class Test_FieldMask: XCTestCase, PBTestHelpers {
243278
XCTAssertEqual(m7.union(m8).paths, ["a", "b"])
244279
}
245280

281+
// Checks `intersect` func of fieldMask.
246282
func testIntersectFieldMasks() throws {
247283
let m1 = Google_Protobuf_FieldMask(protoPaths: ["a", "b"])
248284
let m2 = Google_Protobuf_FieldMask(protoPaths: ["b", "c"])
@@ -261,6 +297,7 @@ final class Test_FieldMask: XCTestCase, PBTestHelpers {
261297
XCTAssertEqual(m7.intersect(m8).paths, ["a", "b"])
262298
}
263299

300+
// Checks `substract` func of fieldMask.
264301
func testSubtractFieldMasks() throws {
265302
let m1 = Google_Protobuf_FieldMask(protoPaths: ["a", "b"])
266303
let m2 = Google_Protobuf_FieldMask(protoPaths: ["b", "c"])

0 commit comments

Comments
 (0)