diff options
Diffstat (limited to 'Firestore/Source/API/FSTUserDataConverter.mm')
-rw-r--r-- | Firestore/Source/API/FSTUserDataConverter.mm | 160 |
1 files changed, 100 insertions, 60 deletions
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. |