diff options
author | zxu <zxu@google.com> | 2018-03-14 08:57:31 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-03-14 08:57:31 -0400 |
commit | 9e815620e9f7f43b42e03db4e5118d7ad03ddee7 (patch) | |
tree | 86f362bad99281325fca7ee3ae116b6a0f36d511 /Firestore/Source/API | |
parent | d4d73ea53ecdf1e8ade3d00921419645dd5d66f7 (diff) |
grand PR to port the remaining paths (FieldPath and ResourcePath). (#865)
* naively remove FSTPath import and source/test files.
* port FieldPath, part I
* port FieldPath, part II
* port ResourcePath, part I
* port ResourcePath, part II
* the grand commit to fix build errors
* use testutil:: helper instead of those from FSTHelpers
* fix test and lint
* use c_str in errmsg directly
* fix
* fix
* make code clean
* fix integration test I missed
* fix to avoid naming collision in preprocessor
* address changes
* address changes
* address changes
* fix: fieldMask are actually shared with different context.
* address changes
* address changes
Diffstat (limited to 'Firestore/Source/API')
-rw-r--r-- | Firestore/Source/API/FIRCollectionReference+Internal.h | 7 | ||||
-rw-r--r-- | Firestore/Source/API/FIRCollectionReference.mm | 20 | ||||
-rw-r--r-- | Firestore/Source/API/FIRDocumentReference+Internal.h | 6 | ||||
-rw-r--r-- | Firestore/Source/API/FIRDocumentReference.mm | 22 | ||||
-rw-r--r-- | Firestore/Source/API/FIRDocumentSnapshot.mm | 12 | ||||
-rw-r--r-- | Firestore/Source/API/FIRFieldPath+Internal.h | 8 | ||||
-rw-r--r-- | Firestore/Source/API/FIRFieldPath.mm | 40 | ||||
-rw-r--r-- | Firestore/Source/API/FIRFirestore.mm | 11 | ||||
-rw-r--r-- | Firestore/Source/API/FIRQuery.mm | 49 | ||||
-rw-r--r-- | Firestore/Source/API/FSTUserDataConverter.mm | 160 |
10 files changed, 194 insertions, 141 deletions
diff --git a/Firestore/Source/API/FIRCollectionReference+Internal.h b/Firestore/Source/API/FIRCollectionReference+Internal.h index 1d00cbb..9d36ede 100644 --- a/Firestore/Source/API/FIRCollectionReference+Internal.h +++ b/Firestore/Source/API/FIRCollectionReference+Internal.h @@ -16,13 +16,14 @@ #import "FIRCollectionReference.h" -NS_ASSUME_NONNULL_BEGIN +#include "Firestore/core/src/firebase/firestore/model/resource_path.h" -@class FSTResourcePath; +NS_ASSUME_NONNULL_BEGIN /** Internal FIRCollectionReference API we don't want exposed in our public header files. */ @interface FIRCollectionReference (Internal) -+ (instancetype)referenceWithPath:(FSTResourcePath *)path firestore:(FIRFirestore *)firestore; ++ (instancetype)referenceWithPath:(const firebase::firestore::model::ResourcePath &)path + firestore:(FIRFirestore *)firestore; @end NS_ASSUME_NONNULL_END diff --git a/Firestore/Source/API/FIRCollectionReference.mm b/Firestore/Source/API/FIRCollectionReference.mm index 9560d13..dfd06c9 100644 --- a/Firestore/Source/API/FIRCollectionReference.mm +++ b/Firestore/Source/API/FIRCollectionReference.mm @@ -24,7 +24,6 @@ #import "Firestore/Source/API/FIRQuery_Init.h" #import "Firestore/Source/Core/FSTQuery.h" #import "Firestore/Source/Model/FSTDocumentKey.h" -#import "Firestore/Source/Model/FSTPath.h" #import "Firestore/Source/Util/FSTAssert.h" #import "Firestore/Source/Util/FSTUsageValidation.h" @@ -38,7 +37,7 @@ using firebase::firestore::util::CreateAutoId; NS_ASSUME_NONNULL_BEGIN @interface FIRCollectionReference () -- (instancetype)initWithPath:(FSTResourcePath *)path +- (instancetype)initWithPath:(const ResourcePath &)path firestore:(FIRFirestore *)firestore NS_DESIGNATED_INITIALIZER; // Mark the super class designated initializer unavailable. @@ -48,22 +47,21 @@ NS_ASSUME_NONNULL_BEGIN @end @implementation FIRCollectionReference (Internal) -+ (instancetype)referenceWithPath:(FSTResourcePath *)path firestore:(FIRFirestore *)firestore { ++ (instancetype)referenceWithPath:(const ResourcePath &)path firestore:(FIRFirestore *)firestore { return [[FIRCollectionReference alloc] initWithPath:path firestore:firestore]; } @end @implementation FIRCollectionReference -- (instancetype)initWithPath:(FSTResourcePath *)path firestore:(FIRFirestore *)firestore { - if (path.length % 2 != 1) { +- (instancetype)initWithPath:(const ResourcePath &)path firestore:(FIRFirestore *)firestore { + if (path.size() % 2 != 1) { FSTThrowInvalidArgument( @"Invalid collection reference. Collection references must have an odd " - "number of segments, but %@ has %d", - path.canonicalString, path.length); + "number of segments, but %s has %zu", + path.CanonicalString().c_str(), path.size()); } - self = - [super initWithQuery:[FSTQuery queryWithPath:[path toCPPResourcePath]] firestore:firestore]; + self = [super initWithQuery:[FSTQuery queryWithPath:path] firestore:firestore]; return self; } @@ -116,9 +114,7 @@ NS_ASSUME_NONNULL_BEGIN } const ResourcePath subPath = ResourcePath::FromString(util::MakeStringView(documentPath)); const ResourcePath path = self.query.path.Append(subPath); - return - [FIRDocumentReference referenceWithPath:[FSTResourcePath resourcePathWithCPPResourcePath:path] - firestore:self.firestore]; + return [FIRDocumentReference referenceWithPath:path firestore:self.firestore]; } - (FIRDocumentReference *)addDocumentWithData:(NSDictionary<NSString *, id> *)data { diff --git a/Firestore/Source/API/FIRDocumentReference+Internal.h b/Firestore/Source/API/FIRDocumentReference+Internal.h index 5e12ddc..706e8db 100644 --- a/Firestore/Source/API/FIRDocumentReference+Internal.h +++ b/Firestore/Source/API/FIRDocumentReference+Internal.h @@ -16,15 +16,17 @@ #import "FIRDocumentReference.h" +#include "Firestore/core/src/firebase/firestore/model/resource_path.h" + NS_ASSUME_NONNULL_BEGIN @class FSTDocumentKey; -@class FSTResourcePath; /** Internal FIRDocumentReference API we don't want exposed in our public header files. */ @interface FIRDocumentReference (Internal) -+ (instancetype)referenceWithPath:(FSTResourcePath *)path firestore:(FIRFirestore *)firestore; ++ (instancetype)referenceWithPath:(const firebase::firestore::model::ResourcePath &)path + firestore:(FIRFirestore *)firestore; + (instancetype)referenceWithKey:(FSTDocumentKey *)key firestore:(FIRFirestore *)firestore; @property(nonatomic, strong, readonly) FSTDocumentKey *key; diff --git a/Firestore/Source/API/FIRDocumentReference.mm b/Firestore/Source/API/FIRDocumentReference.mm index cc14af5..5968fb2 100644 --- a/Firestore/Source/API/FIRDocumentReference.mm +++ b/Firestore/Source/API/FIRDocumentReference.mm @@ -34,7 +34,6 @@ #import "Firestore/Source/Model/FSTDocumentSet.h" #import "Firestore/Source/Model/FSTFieldValue.h" #import "Firestore/Source/Model/FSTMutation.h" -#import "Firestore/Source/Model/FSTPath.h" #import "Firestore/Source/Util/FSTAssert.h" #import "Firestore/Source/Util/FSTAsyncQueryListener.h" #import "Firestore/Source/Util/FSTUsageValidation.h" @@ -91,16 +90,15 @@ NS_ASSUME_NONNULL_BEGIN @implementation FIRDocumentReference (Internal) -+ (instancetype)referenceWithPath:(FSTResourcePath *)path firestore:(FIRFirestore *)firestore { - if (path.length % 2 != 0) { ++ (instancetype)referenceWithPath:(const ResourcePath &)path firestore:(FIRFirestore *)firestore { + if (path.size() % 2 != 0) { FSTThrowInvalidArgument( @"Invalid document reference. Document references must have an even " - "number of segments, but %@ has %d", - path.canonicalString, path.length); + "number of segments, but %s has %zu", + path.CanonicalString().c_str(), path.size()); } return - [FIRDocumentReference referenceWithKey:[FSTDocumentKey keyWithPath:[path toCPPResourcePath]] - firestore:firestore]; + [FIRDocumentReference referenceWithKey:[FSTDocumentKey keyWithPath:path] firestore:firestore]; } + (instancetype)referenceWithKey:(FSTDocumentKey *)key firestore:(FIRFirestore *)firestore { @@ -147,9 +145,8 @@ NS_ASSUME_NONNULL_BEGIN } - (FIRCollectionReference *)parent { - return [FIRCollectionReference - referenceWithPath:[FSTResourcePath resourcePathWithCPPResourcePath:self.key.path.PopLast()] - firestore:self.firestore]; + return + [FIRCollectionReference referenceWithPath:self.key.path.PopLast() firestore:self.firestore]; } - (NSString *)path { @@ -160,9 +157,8 @@ NS_ASSUME_NONNULL_BEGIN if (!collectionPath) { FSTThrowInvalidArgument(@"Collection path cannot be nil."); } - FSTResourcePath *subPath = [FSTResourcePath pathWithString:collectionPath]; - FSTResourcePath *path = [FSTResourcePath - resourcePathWithCPPResourcePath:self.key.path.Append([subPath toCPPResourcePath])]; + const ResourcePath subPath = ResourcePath::FromString(util::MakeStringView(collectionPath)); + const ResourcePath path = self.key.path.Append(subPath); return [FIRCollectionReference referenceWithPath:path firestore:self.firestore]; } diff --git a/Firestore/Source/API/FIRDocumentSnapshot.mm b/Firestore/Source/API/FIRDocumentSnapshot.mm index 8e731da..4d82986 100644 --- a/Firestore/Source/API/FIRDocumentSnapshot.mm +++ b/Firestore/Source/API/FIRDocumentSnapshot.mm @@ -24,7 +24,6 @@ #import "Firestore/Source/Model/FSTDocument.h" #import "Firestore/Source/Model/FSTDocumentKey.h" #import "Firestore/Source/Model/FSTFieldValue.h" -#import "Firestore/Source/Model/FSTPath.h" #import "Firestore/Source/Util/FSTAssert.h" #import "Firestore/Source/Util/FSTUsageValidation.h" @@ -184,12 +183,11 @@ NS_ASSUME_NONNULL_BEGIN // TODO(b/32073923): Log this as a proper warning. NSLog( @"WARNING: Document %@ contains a document reference within a different database " - "(%@/%@) which is not supported. It will be treated as a reference within the " - "current database (%@/%@) instead.", - self.reference.path, util::WrapNSStringNoCopy(refDatabase->project_id()), - util::WrapNSStringNoCopy(refDatabase->database_id()), - util::WrapNSStringNoCopy(database->project_id()), - util::WrapNSStringNoCopy(database->database_id())); + "(%s/%s) which is not supported. It will be treated as a reference within the " + "current database (%s/%s) instead.", + self.reference.path, refDatabase->project_id().c_str(), + refDatabase->database_id().c_str(), database->project_id().c_str(), + database->database_id().c_str()); } return [FIRDocumentReference referenceWithKey:[ref valueWithOptions:options] firestore:self.firestore]; diff --git a/Firestore/Source/API/FIRFieldPath+Internal.h b/Firestore/Source/API/FIRFieldPath+Internal.h index 227cdad..6fb6add 100644 --- a/Firestore/Source/API/FIRFieldPath+Internal.h +++ b/Firestore/Source/API/FIRFieldPath+Internal.h @@ -16,16 +16,16 @@ #import "FIRFieldPath.h" -@class FSTFieldPath; +#include "Firestore/core/src/firebase/firestore/model/field_path.h" NS_ASSUME_NONNULL_BEGIN @interface FIRFieldPath () -- (instancetype)initPrivate:(FSTFieldPath *)path NS_DESIGNATED_INITIALIZER; - /** Internal field path representation */ -@property(nonatomic, strong, readonly) FSTFieldPath *internalValue; +- (const firebase::firestore::model::FieldPath &)internalValue; + +- (instancetype)initPrivate:(firebase::firestore::model::FieldPath)path NS_DESIGNATED_INITIALIZER; @end diff --git a/Firestore/Source/API/FIRFieldPath.mm b/Firestore/Source/API/FIRFieldPath.mm index f4e532f..c651160 100644 --- a/Firestore/Source/API/FIRFieldPath.mm +++ b/Firestore/Source/API/FIRFieldPath.mm @@ -16,11 +16,28 @@ #import "Firestore/Source/API/FIRFieldPath+Internal.h" -#import "Firestore/Source/Model/FSTPath.h" +#include <functional> +#include <string> +#include <utility> +#include <vector> + #import "Firestore/Source/Util/FSTUsageValidation.h" +#include "Firestore/core/src/firebase/firestore/model/field_path.h" +#include "Firestore/core/src/firebase/firestore/util/string_apple.h" + +namespace util = firebase::firestore::util; +using firebase::firestore::model::FieldPath; + NS_ASSUME_NONNULL_BEGIN +@interface FIRFieldPath () { + /** Internal field path representation */ + firebase::firestore::model::FieldPath _internalValue; +} + +@end + @implementation FIRFieldPath - (instancetype)initWithFields:(NSArray<NSString *> *)fieldNames { @@ -28,22 +45,25 @@ NS_ASSUME_NONNULL_BEGIN FSTThrowInvalidArgument(@"Invalid field path. Provided names must not be empty."); } + std::vector<std::string> field_names{}; + field_names.reserve(fieldNames.count); for (int i = 0; i < fieldNames.count; ++i) { if (fieldNames[i].length == 0) { FSTThrowInvalidArgument(@"Invalid field name at index %d. Field names must not be empty.", i); } + field_names.emplace_back(util::MakeString(fieldNames[i])); } - return [self initPrivate:[FSTFieldPath pathWithSegments:fieldNames]]; + return [self initPrivate:FieldPath(std::move(field_names))]; } + (instancetype)documentID { - return [[FIRFieldPath alloc] initPrivate:FSTFieldPath.keyFieldPath]; + return [[FIRFieldPath alloc] initPrivate:FieldPath::KeyFieldPath()]; } -- (instancetype)initPrivate:(FSTFieldPath *)fieldPath { +- (instancetype)initPrivate:(FieldPath)fieldPath { if (self = [super init]) { - _internalValue = fieldPath; + _internalValue = std::move(fieldPath); } return self; } @@ -77,7 +97,7 @@ NS_ASSUME_NONNULL_BEGIN } - (id)copyWithZone:(NSZone *__nullable)zone { - return [[[self class] alloc] initPrivate:self.internalValue]; + return [[[self class] alloc] initPrivate:_internalValue]; } - (BOOL)isEqual:(nullable id)object { @@ -89,11 +109,15 @@ NS_ASSUME_NONNULL_BEGIN return NO; } - return [self.internalValue isEqual:((FIRFieldPath *)object).internalValue]; + return _internalValue == ((FIRFieldPath *)object)->_internalValue; } - (NSUInteger)hash { - return [self.internalValue hash]; + return _internalValue.Hash(); +} + +- (const firebase::firestore::model::FieldPath &)internalValue { + return _internalValue; } @end diff --git a/Firestore/Source/API/FIRFirestore.mm b/Firestore/Source/API/FIRFirestore.mm index eff0605..f3769ed 100644 --- a/Firestore/Source/API/FIRFirestore.mm +++ b/Firestore/Source/API/FIRFirestore.mm @@ -34,7 +34,6 @@ #import "Firestore/Source/Core/FSTFirestoreClient.h" #import "Firestore/Source/Model/FSTDocumentKey.h" -#import "Firestore/Source/Model/FSTPath.h" #import "Firestore/Source/Util/FSTAssert.h" #import "Firestore/Source/Util/FSTDispatchQueue.h" #import "Firestore/Source/Util/FSTLogger.h" @@ -44,6 +43,7 @@ #include "Firestore/core/src/firebase/firestore/auth/firebase_credentials_provider_apple.h" #include "Firestore/core/src/firebase/firestore/core/database_info.h" #include "Firestore/core/src/firebase/firestore/model/database_id.h" +#include "Firestore/core/src/firebase/firestore/model/resource_path.h" #include "Firestore/core/src/firebase/firestore/util/string_apple.h" #include "absl/memory/memory.h" @@ -52,6 +52,7 @@ using firebase::firestore::auth::CredentialsProvider; using firebase::firestore::auth::FirebaseCredentialsProvider; using firebase::firestore::core::DatabaseInfo; using firebase::firestore::model::DatabaseId; +using firebase::firestore::model::ResourcePath; NS_ASSUME_NONNULL_BEGIN @@ -141,9 +142,9 @@ extern "C" NSString *const FIRFirestoreErrorDomain = @"FIRFirestoreErrorDomain"; } if (!database) { FSTThrowInvalidArgument( - @"database identifier may not be nil. Use '%@' if you want the default " + @"database identifier may not be nil. Use '%s' if you want the default " "database", - util::WrapNSStringNoCopy(DatabaseId::kDefault)); + DatabaseId::kDefault); } // Note: If the key format changes, please change the code that detects FIRApps being deleted @@ -261,7 +262,7 @@ extern "C" NSString *const FIRFirestoreErrorDomain = @"FIRFirestoreErrorDomain"; FSTThrowInvalidArgument(@"Collection path cannot be nil."); } [self ensureClientConfigured]; - FSTResourcePath *path = [FSTResourcePath pathWithString:collectionPath]; + const ResourcePath path = ResourcePath::FromString(util::MakeStringView(collectionPath)); return [FIRCollectionReference referenceWithPath:path firestore:self]; } @@ -270,7 +271,7 @@ extern "C" NSString *const FIRFirestoreErrorDomain = @"FIRFirestoreErrorDomain"; FSTThrowInvalidArgument(@"Document path cannot be nil."); } [self ensureClientConfigured]; - FSTResourcePath *path = [FSTResourcePath pathWithString:documentPath]; + const ResourcePath path = ResourcePath::FromString(util::MakeStringView(documentPath)); return [FIRDocumentReference referenceWithPath:path firestore:self]; } diff --git a/Firestore/Source/API/FIRQuery.mm b/Firestore/Source/API/FIRQuery.mm index c277561..07dac39 100644 --- a/Firestore/Source/API/FIRQuery.mm +++ b/Firestore/Source/API/FIRQuery.mm @@ -33,7 +33,6 @@ #import "Firestore/Source/Model/FSTDocument.h" #import "Firestore/Source/Model/FSTDocumentKey.h" #import "Firestore/Source/Model/FSTFieldValue.h" -#import "Firestore/Source/Model/FSTPath.h" #import "Firestore/Source/Util/FSTAssert.h" #import "Firestore/Source/Util/FSTAsyncQueryListener.h" #import "Firestore/Source/Util/FSTUsageValidation.h" @@ -379,8 +378,7 @@ addSnapshotListenerInternalWithOptions:(FSTListenOptions *)internalOptions @"Invalid query. You must not specify an ending point before specifying the order by."); } FSTSortOrder *sortOrder = - [FSTSortOrder sortOrderWithFieldPath:[fieldPath.internalValue toCPPFieldPath] - ascending:!descending]; + [FSTSortOrder sortOrderWithFieldPath:fieldPath.internalValue ascending:!descending]; return [FIRQuery referenceWithQuery:[self.query queryByAddingSortOrder:sortOrder] firestore:self.firestore]; } @@ -453,10 +451,10 @@ addSnapshotListenerInternalWithOptions:(FSTListenOptions *)internalOptions } - (FIRQuery *)queryWithFilterOperator:(FSTRelationFilterOperator)filterOperator - path:(FSTFieldPath *)fieldPath + path:(const FieldPath &)fieldPath value:(id)value { FSTFieldValue *fieldValue; - if ([fieldPath isKeyFieldPath]) { + if (fieldPath.IsKeyFieldPath()) { if ([value isKindOfClass:[NSString class]]) { NSString *documentKey = (NSString *)value; if ([documentKey containsString:@"/"]) { @@ -492,15 +490,15 @@ addSnapshotListenerInternalWithOptions:(FSTListenOptions *)internalOptions @"Invalid Query. You can only perform equality comparisons on nil / " "NSNull."); } - filter = [[FSTNullFilter alloc] initWithField:[fieldPath toCPPFieldPath]]; + filter = [[FSTNullFilter alloc] initWithField:fieldPath]; } else if ([fieldValue isEqual:[FSTDoubleValue nanValue]]) { if (filterOperator != FSTRelationFilterOperatorEqual) { FSTThrowInvalidUsage(@"InvalidQueryException", @"Invalid Query. You can only perform equality comparisons on NaN."); } - filter = [[FSTNanFilter alloc] initWithField:[fieldPath toCPPFieldPath]]; + filter = [[FSTNanFilter alloc] initWithField:fieldPath]; } else { - filter = [FSTRelationFilter filterWithField:[fieldPath toCPPFieldPath] + filter = [FSTRelationFilter filterWithField:fieldPath filterOperator:filterOperator value:fieldValue]; [self validateNewRelationFilter:filter]; @@ -517,40 +515,38 @@ addSnapshotListenerInternalWithOptions:(FSTListenOptions *)internalOptions @"InvalidQueryException", @"Invalid Query. All where filters with an inequality " "(lessThan, lessThanOrEqual, greaterThan, or greaterThanOrEqual) must be on the same " - "field. But you have inequality filters on '%@' and '%@'", - util::WrapNSStringNoCopy(existingField->CanonicalString()), - util::WrapNSStringNoCopy(filter.field.CanonicalString())); + "field. But you have inequality filters on '%s' and '%s'", + existingField->CanonicalString().c_str(), filter.field.CanonicalString().c_str()); } const FieldPath *firstOrderByField = [self.query firstSortOrderField]; if (firstOrderByField) { - [self validateOrderByField:[FSTFieldPath fieldPathWithCPPFieldPath:*firstOrderByField] - matchesInequalityField:[FSTFieldPath fieldPathWithCPPFieldPath:filter.field]]; + [self validateOrderByField:*firstOrderByField matchesInequalityField:filter.field]; } } } -- (void)validateNewOrderByPath:(FSTFieldPath *)fieldPath { +- (void)validateNewOrderByPath:(const FieldPath &)fieldPath { if (![self.query firstSortOrderField]) { // This is the first order by. It must match any inequality. const FieldPath *inequalityField = [self.query inequalityFilterField]; if (inequalityField) { - [self validateOrderByField:fieldPath - matchesInequalityField:[FSTFieldPath fieldPathWithCPPFieldPath:*inequalityField]]; + [self validateOrderByField:fieldPath matchesInequalityField:*inequalityField]; } } } -- (void)validateOrderByField:(FSTFieldPath *)orderByField - matchesInequalityField:(FSTFieldPath *)inequalityField { - if (!([orderByField isEqual:inequalityField])) { +- (void)validateOrderByField:(const FieldPath &)orderByField + matchesInequalityField:(const FieldPath &)inequalityField { + if (orderByField != inequalityField) { FSTThrowInvalidUsage( @"InvalidQueryException", @"Invalid query. You have a where filter with an " - "inequality (lessThan, lessThanOrEqual, greaterThan, or greaterThanOrEqual) on field '%@' " - "and so you must also use '%@' as your first queryOrderedBy field, but your first " - "queryOrderedBy is currently on field '%@' instead.", - inequalityField, inequalityField, orderByField); + "inequality (lessThan, lessThanOrEqual, greaterThan, or greaterThanOrEqual) on field '%s' " + "and so you must also use '%s' as your first queryOrderedBy field, but your first " + "queryOrderedBy is currently on field '%s' instead.", + inequalityField.CanonicalString().c_str(), inequalityField.CanonicalString().c_str(), + orderByField.CanonicalString().c_str()); } } @@ -581,16 +577,15 @@ addSnapshotListenerInternalWithOptions:(FSTListenOptions *)internalOptions [components addObject:[FSTReferenceValue referenceValue:document.key databaseID:self.firestore.databaseID]]; } else { - FSTFieldValue *value = - [document fieldForPath:[FSTFieldPath fieldPathWithCPPFieldPath:sortOrder.field]]; + FSTFieldValue *value = [document fieldForPath:sortOrder.field]; if (value != nil) { [components addObject:value]; } else { FSTThrowInvalidUsage(@"InvalidQueryException", @"Invalid query. You are trying to start or end a query using a " - "document for which the field '%@' (used as the order by) " + "document for which the field '%s' (used as the order by) " "does not exist.", - util::WrapNSStringNoCopy(sortOrder.field.CanonicalString())); + sortOrder.field.CanonicalString().c_str()); } } } diff --git a/Firestore/Source/API/FSTUserDataConverter.mm b/Firestore/Source/API/FSTUserDataConverter.mm index 5f89b8e..e418996 100644 --- a/Firestore/Source/API/FSTUserDataConverter.mm +++ b/Firestore/Source/API/FSTUserDataConverter.mm @@ -16,6 +16,10 @@ #import "Firestore/Source/API/FSTUserDataConverter.h" +#include <memory> +#include <utility> +#include <vector> + #import "FIRTimestamp.h" #import "FIRGeoPoint.h" @@ -27,15 +31,17 @@ #import "Firestore/Source/Model/FSTDocumentKey.h" #import "Firestore/Source/Model/FSTFieldValue.h" #import "Firestore/Source/Model/FSTMutation.h" -#import "Firestore/Source/Model/FSTPath.h" #import "Firestore/Source/Util/FSTAssert.h" #import "Firestore/Source/Util/FSTUsageValidation.h" #include "Firestore/core/src/firebase/firestore/model/database_id.h" +#include "Firestore/core/src/firebase/firestore/model/field_path.h" #include "Firestore/core/src/firebase/firestore/util/string_apple.h" +#include "absl/memory/memory.h" namespace util = firebase::firestore::util; using firebase::firestore::model::DatabaseId; +using firebase::firestore::model::FieldPath; NS_ASSUME_NONNULL_BEGIN @@ -126,9 +132,6 @@ typedef NS_ENUM(NSInteger, FSTUserDataSource) { * A "context" object passed around while parsing user data. */ @interface FSTParseContext : NSObject -/** The current path being parsed. */ -// TODO(b/34871131): path should never be nil, but we don't support array paths right now. -@property(nonatomic, strong, readonly, nullable) FSTFieldPath *path; /** Whether or not this context corresponds to an element of an array. */ @property(nonatomic, assign, readonly, getter=isArrayElement) BOOL arrayElement; @@ -139,7 +142,6 @@ typedef NS_ENUM(NSInteger, FSTUserDataSource) { */ @property(nonatomic, assign) FSTUserDataSource dataSource; @property(nonatomic, strong, readonly) NSMutableArray<FSTFieldTransform *> *fieldTransforms; -@property(nonatomic, strong, readonly) NSMutableArray<FSTFieldPath *> *fieldMask; - (instancetype)init NS_UNAVAILABLE; /** @@ -150,71 +152,94 @@ typedef NS_ENUM(NSInteger, FSTUserDataSource) { * the context represents the root of the data being parsed), or a nonempty path (indicating the * context represents a nested location within the data). * - * TODO(b/34871131): We don't support array paths right now, so path can be nil to indicate + * TODO(b/34871131): We don't support array paths right now, so path can be nullptr to indicate * the context represents any location within an array (in which case certain features will not work * and errors will be somewhat compromised). */ - (instancetype)initWithSource:(FSTUserDataSource)dataSource - path:(nullable FSTFieldPath *)path + path:(std::unique_ptr<FieldPath>)path arrayElement:(BOOL)arrayElement fieldTransforms:(NSMutableArray<FSTFieldTransform *> *)fieldTransforms - fieldMask:(NSMutableArray<FSTFieldPath *> *)fieldMask + fieldMask:(std::shared_ptr<std::vector<FieldPath>>)fieldMask NS_DESIGNATED_INITIALIZER; // Helpers to get a FSTParseContext for a child field. - (instancetype)contextForField:(NSString *)fieldName; -- (instancetype)contextForFieldPath:(FSTFieldPath *)fieldPath; +- (instancetype)contextForFieldPath:(const FieldPath &)fieldPath; - (instancetype)contextForArrayIndex:(NSUInteger)index; /** Returns true for the non-query parse contexts (Set, MergeSet and Update) */ - (BOOL)isWrite; + +- (const FieldPath *)path; + +- (const std::vector<FieldPath> *)fieldMask; + +- (void)appendToFieldMaskWithFieldPath:(FieldPath)fieldPath; + @end -@implementation FSTParseContext +@implementation FSTParseContext { + /** The current path being parsed. */ + // TODO(b/34871131): path should never be nullptr, but we don't support array paths right now. + std::unique_ptr<FieldPath> _path; + // _fieldMask is shared across all active context objects to accumulate the result. For example, + // the result of calling any of contextForField, contextForFieldPath and contextForArrayIndex + // shares the ownership of _fieldMask. + std::shared_ptr<std::vector<FieldPath>> _fieldMask; +} -+ (instancetype)contextWithSource:(FSTUserDataSource)dataSource path:(nullable FSTFieldPath *)path { - FSTParseContext *context = [[FSTParseContext alloc] initWithSource:dataSource - path:path - arrayElement:NO - fieldTransforms:[NSMutableArray array] - fieldMask:[NSMutableArray array]]; ++ (instancetype)contextWithSource:(FSTUserDataSource)dataSource + path:(std::unique_ptr<FieldPath>)path { + FSTParseContext *context = + [[FSTParseContext alloc] initWithSource:dataSource + path:std::move(path) + arrayElement:NO + fieldTransforms:[NSMutableArray array] + fieldMask:std::make_shared<std::vector<FieldPath>>()]; [context validatePath]; return context; } - (instancetype)initWithSource:(FSTUserDataSource)dataSource - path:(nullable FSTFieldPath *)path + path:(std::unique_ptr<FieldPath>)path arrayElement:(BOOL)arrayElement fieldTransforms:(NSMutableArray<FSTFieldTransform *> *)fieldTransforms - fieldMask:(NSMutableArray<FSTFieldPath *> *)fieldMask { + fieldMask:(std::shared_ptr<std::vector<FieldPath>>)fieldMask { if (self = [super init]) { _dataSource = dataSource; - _path = path; + _path = std::move(path); _arrayElement = arrayElement; _fieldTransforms = fieldTransforms; - _fieldMask = fieldMask; + _fieldMask = std::move(fieldMask); } return self; } - (instancetype)contextForField:(NSString *)fieldName { - FSTParseContext *context = - [[FSTParseContext alloc] initWithSource:self.dataSource - path:[self.path pathByAppendingSegment:fieldName] - arrayElement:NO - fieldTransforms:self.fieldTransforms - fieldMask:self.fieldMask]; + std::unique_ptr<FieldPath> path{}; + if (_path) { + path = absl::make_unique<FieldPath>(_path->Append(util::MakeString(fieldName))); + } + FSTParseContext *context = [[FSTParseContext alloc] initWithSource:self.dataSource + path:std::move(path) + arrayElement:NO + fieldTransforms:self.fieldTransforms + fieldMask:_fieldMask]; [context validatePathSegment:fieldName]; return context; } -- (instancetype)contextForFieldPath:(FSTFieldPath *)fieldPath { - FSTParseContext *context = - [[FSTParseContext alloc] initWithSource:self.dataSource - path:[self.path pathByAppendingPath:fieldPath] - arrayElement:NO - fieldTransforms:self.fieldTransforms - fieldMask:self.fieldMask]; +- (instancetype)contextForFieldPath:(const FieldPath &)fieldPath { + std::unique_ptr<FieldPath> path{}; + if (_path) { + path = absl::make_unique<FieldPath>(_path->Append(fieldPath)); + } + FSTParseContext *context = [[FSTParseContext alloc] initWithSource:self.dataSource + path:std::move(path) + arrayElement:NO + fieldTransforms:self.fieldTransforms + fieldMask:_fieldMask]; [context validatePath]; return context; } @@ -225,7 +250,7 @@ typedef NS_ENUM(NSInteger, FSTUserDataSource) { path:nil arrayElement:YES fieldTransforms:self.fieldTransforms - fieldMask:self.fieldMask]; + fieldMask:_fieldMask]; } /** @@ -233,10 +258,10 @@ typedef NS_ENUM(NSInteger, FSTUserDataSource) { */ - (NSString *)fieldDescription { // TODO(b/34871131): Remove nil check once we have proper paths for fields within arrays. - if (!self.path || self.path.empty) { + if (!_path || _path->empty()) { return @""; } else { - return [NSString stringWithFormat:@" (found in field %@)", self.path]; + return [NSString stringWithFormat:@" (found in field %s)", _path->CanonicalString().c_str()]; } } @@ -255,11 +280,11 @@ typedef NS_ENUM(NSInteger, FSTUserDataSource) { - (void)validatePath { // TODO(b/34871131): Remove nil check once we have proper paths for fields within arrays. - if (self.path == nil) { + if (_path == nullptr) { return; } - for (int i = 0; i < self.path.length; i++) { - [self validatePathSegment:[self.path segmentAtIndex:i]]; + for (const auto &segment : *_path) { + [self validatePathSegment:util::WrapNSStringNoCopy(segment)]; } } @@ -271,6 +296,18 @@ typedef NS_ENUM(NSInteger, FSTUserDataSource) { } } +- (const FieldPath *)path { + return _path.get(); +} + +- (const std::vector<FieldPath> *)fieldMask { + return _fieldMask.get(); +} + +- (void)appendToFieldMaskWithFieldPath:(FieldPath)fieldPath { + _fieldMask->push_back(std::move(fieldPath)); +} + @end #pragma mark - FSTDocumentKeyReference @@ -316,13 +353,14 @@ typedef NS_ENUM(NSInteger, FSTUserDataSource) { } FSTParseContext *context = - [FSTParseContext contextWithSource:FSTUserDataSourceMergeSet path:[FSTFieldPath emptyPath]]; + [FSTParseContext contextWithSource:FSTUserDataSourceMergeSet + path:absl::make_unique<FieldPath>(FieldPath::EmptyPath())]; FSTObjectValue *updateData = (FSTObjectValue *)[self parseData:input context:context]; - return - [[FSTParsedSetData alloc] initWithData:updateData - fieldMask:[[FSTFieldMask alloc] initWithFields:context.fieldMask] - fieldTransforms:context.fieldTransforms]; + return [[FSTParsedSetData alloc] + initWithData:updateData + fieldMask:[[FSTFieldMask alloc] initWithFields:*context.fieldMask] + fieldTransforms:context.fieldTransforms]; } - (FSTParsedSetData *)parsedSetData:(id)input { @@ -333,7 +371,8 @@ typedef NS_ENUM(NSInteger, FSTUserDataSource) { } FSTParseContext *context = - [FSTParseContext contextWithSource:FSTUserDataSourceSet path:[FSTFieldPath emptyPath]]; + [FSTParseContext contextWithSource:FSTUserDataSourceSet + path:absl::make_unique<FieldPath>(FieldPath::EmptyPath())]; FSTObjectValue *updateData = (FSTObjectValue *)[self parseData:input context:context]; return [[FSTParsedSetData alloc] initWithData:updateData @@ -350,13 +389,14 @@ typedef NS_ENUM(NSInteger, FSTUserDataSource) { NSDictionary *dict = input; - NSMutableArray<FSTFieldPath *> *fieldMaskPaths = [NSMutableArray array]; + __block std::vector<FieldPath> fieldMaskPaths{}; __block FSTObjectValue *updateData = [FSTObjectValue objectValue]; FSTParseContext *context = - [FSTParseContext contextWithSource:FSTUserDataSourceUpdate path:[FSTFieldPath emptyPath]]; + [FSTParseContext contextWithSource:FSTUserDataSourceUpdate + path:absl::make_unique<FieldPath>(FieldPath::EmptyPath())]; [dict enumerateKeysAndObjectsUsingBlock:^(id key, id value, BOOL *stop) { - FSTFieldPath *path; + FieldPath path{}; if ([key isKindOfClass:[NSString class]]) { path = [FIRFieldPath pathWithDotSeparatedString:key].internalValue; @@ -370,12 +410,12 @@ typedef NS_ENUM(NSInteger, FSTUserDataSource) { value = self.preConverter(value); if ([value isKindOfClass:[FSTDeleteFieldValue class]]) { // Add it to the field mask, but don't add anything to updateData. - [fieldMaskPaths addObject:path]; + fieldMaskPaths.push_back(path); } else { FSTFieldValue *_Nullable parsedValue = [self parseData:value context:[context contextForFieldPath:path]]; if (parsedValue) { - [fieldMaskPaths addObject:path]; + fieldMaskPaths.push_back(path); updateData = [updateData objectBySettingValue:parsedValue forPath:path]; } } @@ -389,7 +429,8 @@ typedef NS_ENUM(NSInteger, FSTUserDataSource) { - (FSTFieldValue *)parsedQueryValue:(id)input { FSTParseContext *context = - [FSTParseContext contextWithSource:FSTUserDataSourceQueryValue path:[FSTFieldPath emptyPath]]; + [FSTParseContext contextWithSource:FSTUserDataSourceQueryValue + path:absl::make_unique<FieldPath>(FieldPath::EmptyPath())]; FSTFieldValue *_Nullable parsed = [self parseData:input context:context]; FSTAssert(parsed, @"Parsed data should not be nil."); FSTAssert(context.fieldTransforms.count == 0, @"Field transforms should have been disallowed."); @@ -427,7 +468,7 @@ typedef NS_ENUM(NSInteger, FSTUserDataSource) { // If context.path is nil we are already inside an array and we don't support field mask paths // more granular than the top-level array. if (context.path) { - [context.fieldMask addObject:context.path]; + [context appendToFieldMaskWithFieldPath:*context.path]; } return [[FSTArrayValue alloc] initWithValueNoCopy:result]; @@ -448,7 +489,7 @@ typedef NS_ENUM(NSInteger, FSTUserDataSource) { // If context.path is null, we are inside an array and we should have already added the root of // the array to the field mask. if (context.path) { - [context.fieldMask addObject:context.path]; + [context appendToFieldMaskWithFieldPath:*context.path]; } return [self parseScalarValue:input context:context]; } @@ -557,11 +598,10 @@ typedef NS_ENUM(NSInteger, FSTUserDataSource) { if (*reference.databaseID != *self.databaseID) { const DatabaseId *other = reference.databaseID; FSTThrowInvalidArgument( - @"Document Reference is for database %@/%@ but should be for database %@/%@%@", - util::WrapNSStringNoCopy(other->project_id()), - util::WrapNSStringNoCopy(other->database_id()), - util::WrapNSStringNoCopy(self.databaseID->project_id()), - util::WrapNSStringNoCopy(self.databaseID->database_id()), [context fieldDescription]); + @"Document Reference is for database %s/%s but should be for database %s/%s%@", + other->project_id().c_str(), other->database_id().c_str(), + self.databaseID->project_id().c_str(), self.databaseID->database_id().c_str(), + [context fieldDescription]); } return [FSTReferenceValue referenceValue:reference.key databaseID:self.databaseID]; @@ -570,7 +610,7 @@ typedef NS_ENUM(NSInteger, FSTUserDataSource) { if (context.dataSource == FSTUserDataSourceMergeSet) { return nil; } else if (context.dataSource == FSTUserDataSourceUpdate) { - FSTAssert(context.path.length > 0, + FSTAssert(context.path->size() > 0, @"FieldValue.delete() at the top level should have already been handled."); FSTThrowInvalidArgument( @"FieldValue.delete() can only appear at the top level of your " @@ -594,7 +634,7 @@ typedef NS_ENUM(NSInteger, FSTUserDataSource) { } [context.fieldTransforms addObject:[[FSTFieldTransform alloc] - initWithPath:context.path + initWithPath:*context.path transform:[FSTServerTimestampTransform serverTimestampTransform]]]; // Return nil so this value is omitted from the parsed result. |