Skip to content

Commit ef36ec2

Browse files
committed
make duplicate alias recoverable error
1 parent 109a2f8 commit ef36ec2

File tree

3 files changed

+275
-77
lines changed

3 files changed

+275
-77
lines changed

Firestore/Swift/Source/Helper/PipelineHelper.swift

Lines changed: 25 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,11 @@
1313
// limitations under the License.
1414

1515
enum Helper {
16+
17+
enum HelperError: Error {
18+
case duplicateAlias(String)
19+
}
20+
1621
static func sendableToExpr(_ value: Sendable?) -> Expression {
1722
guard let value = value else {
1823
return Constant.nil
@@ -31,34 +36,32 @@ enum Helper {
3136
}
3237
}
3338

34-
static func selectablesToMap(selectables: [Selectable]) -> [String: Expression] {
35-
let exprMap = selectables.reduce(into: [String: Expression]()) { result, selectable in
36-
guard let value = selectable as? SelectableWrapper else {
37-
fatalError("Selectable class must conform to SelectableWrapper.")
38-
}
39-
let alias = value.alias
40-
if result.keys.contains(alias) {
41-
// TODO: Add tests to verify the behaviour.
42-
fatalError("Duplicate alias '\(alias)' found in selectables.")
43-
}
44-
result[alias] = value.expr
39+
static func selectablesToMap(selectables: [Selectable]) -> ([String: Expression], Error?) {
40+
var exprMap = [String: Expression]()
41+
for selectable in selectables {
42+
guard let value = selectable as? SelectableWrapper else {
43+
fatalError("Selectable class must conform to SelectableWrapper.")
44+
}
45+
let alias = value.alias
46+
if exprMap.keys.contains(alias) {
47+
return ([:], HelperError.duplicateAlias("Duplicate alias '\(alias)' found in selectables."))
48+
}
49+
exprMap[alias] = value.expr
4550
}
46-
return exprMap
51+
return (exprMap, nil)
4752
}
4853

4954
static func aliasedAggregatesToMap(accumulators: [AliasedAggregate])
50-
-> [String: AggregateFunction] {
51-
let accumulatorMap = accumulators
52-
.reduce(into: [String: AggregateFunction]()) { result, aliasedAggregate in
53-
55+
-> ([String: AggregateFunction], Error?) {
56+
var accumulatorMap = [String: AggregateFunction]()
57+
for aliasedAggregate in accumulators {
5458
let alias = aliasedAggregate.alias
55-
if result.keys.contains(alias) {
56-
// TODO: Add tests to verify the behaviour.
57-
fatalError("Duplicate alias '\(alias)' found in accumulators.")
59+
if accumulatorMap.keys.contains(alias) {
60+
return ([:], HelperError.duplicateAlias("Duplicate alias '\(alias)' found in accumulators."))
5861
}
59-
result[alias] = aliasedAggregate.aggregate
60-
}
61-
return accumulatorMap
62+
accumulatorMap[alias] = aliasedAggregate.aggregate
63+
}
64+
return (accumulatorMap, nil)
6265
}
6366

6467
static func map(_ elements: [String: Sendable?]) -> FunctionExpression {

Firestore/Swift/Source/Stages.swift

Lines changed: 55 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,13 @@ import Foundation
2626
protocol Stage {
2727
var name: String { get }
2828
var bridge: StageBridge { get }
29+
var errorMessage: String? { get }
30+
}
31+
32+
extension Stage {
33+
var errorMessage: String? {
34+
return nil
35+
}
2936
}
3037

3138
@available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
@@ -147,12 +154,19 @@ class AddFields: Stage {
147154
let name: String = "add_fields"
148155
let bridge: StageBridge
149156
private var selectables: [Selectable]
157+
let errorMessage: String?
150158

151159
init(selectables: [Selectable]) {
152160
self.selectables = selectables
153-
let map = Helper.selectablesToMap(selectables: selectables)
154-
let objcAccumulators = map.mapValues { $0.toBridge() }
155-
bridge = AddFieldsStageBridge(fields: objcAccumulators)
161+
let (map, error) = Helper.selectablesToMap(selectables: selectables)
162+
if let error = error {
163+
self.errorMessage = error.localizedDescription
164+
self.bridge = AddFieldsStageBridge(fields: [:])
165+
} else {
166+
self.errorMessage = nil
167+
let objcAccumulators = map.mapValues { $0.toBridge() }
168+
self.bridge = AddFieldsStageBridge(fields: objcAccumulators)
169+
}
156170
}
157171
}
158172

@@ -177,23 +191,37 @@ class RemoveFieldsStage: Stage {
177191
class Select: Stage {
178192
let name: String = "select"
179193
let bridge: StageBridge
194+
let errorMessage: String?
180195

181196
init(selections: [Selectable]) {
182-
let map = Helper.selectablesToMap(selectables: selections)
183-
bridge = SelectStageBridge(selections: map
184-
.mapValues { Helper.sendableToExpr($0).toBridge() })
197+
let (map, error) = Helper.selectablesToMap(selectables: selections)
198+
if let error = error {
199+
self.errorMessage = error.localizedDescription
200+
self.bridge = SelectStageBridge(selections: [:])
201+
} else {
202+
self.errorMessage = nil
203+
let objcSelections = map.mapValues { Helper.sendableToExpr($0).toBridge() }
204+
self.bridge = SelectStageBridge(selections: objcSelections)
205+
}
185206
}
186207
}
187208

188209
@available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
189210
class Distinct: Stage {
190211
let name: String = "distinct"
191212
let bridge: StageBridge
213+
let errorMessage: String?
192214

193215
init(groups: [Selectable]) {
194-
let map = Helper.selectablesToMap(selectables: groups)
195-
bridge = DistinctStageBridge(groups: map
196-
.mapValues { Helper.sendableToExpr($0).toBridge() })
216+
let (map, error) = Helper.selectablesToMap(selectables: groups)
217+
if let error = error {
218+
self.errorMessage = error.localizedDescription
219+
self.bridge = DistinctStageBridge(groups: [:])
220+
} else {
221+
self.errorMessage = nil
222+
let objcGroups = map.mapValues { Helper.sendableToExpr($0).toBridge() }
223+
self.bridge = DistinctStageBridge(groups: objcGroups)
224+
}
197225
}
198226
}
199227

@@ -203,16 +231,30 @@ class Aggregate: Stage {
203231
let bridge: StageBridge
204232
private var accumulators: [AliasedAggregate]
205233
private var groups: [String: Expression] = [:]
234+
let errorMessage: String?
206235

207236
init(accumulators: [AliasedAggregate], groups: [Selectable]?) {
208237
self.accumulators = accumulators
209-
if groups != nil {
210-
self.groups = Helper.selectablesToMap(selectables: groups!)
238+
239+
if let groups = groups {
240+
let (map, error) = Helper.selectablesToMap(selectables: groups)
241+
if let error = error {
242+
self.errorMessage = error.localizedDescription
243+
self.bridge = AggregateStageBridge(accumulators: [:], groups: [:])
244+
return
245+
}
246+
self.groups = map
211247
}
212-
let accumulatorsMap = Helper.aliasedAggregatesToMap(accumulators: accumulators)
213248

249+
let (accumulatorsMap, error) = Helper.aliasedAggregatesToMap(accumulators: accumulators)
250+
if let error = error {
251+
self.errorMessage = error.localizedDescription
252+
self.bridge = AggregateStageBridge(accumulators: [:], groups: [:])
253+
return
254+
}
255+
256+
self.errorMessage = nil
214257
let accumulatorBridgesMap = accumulatorsMap.mapValues { $0.bridge }
215-
216258
bridge = AggregateStageBridge(
217259
accumulators: accumulatorBridgesMap,
218260
groups: self.groups.mapValues { Helper.sendableToExpr($0).toBridge() }

0 commit comments

Comments
 (0)