aboutsummaryrefslogtreecommitdiffhomepage
path: root/Firestore/Source/API/FSTUserDataConverter.mm
diff options
context:
space:
mode:
Diffstat (limited to 'Firestore/Source/API/FSTUserDataConverter.mm')
-rw-r--r--Firestore/Source/API/FSTUserDataConverter.mm160
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.