Skip to content

Commit 18e51cc

Browse files
authored
Rewrite C++ document classes as polymorphic value types (#3529)
* Rewrite Document-related classes as a polymorphic value type * Pass ObjectValue without rvalue references * Rename HasLocalMutations * Port C++ users to new Documents
1 parent 0f85690 commit 18e51cc

20 files changed

+731
-429
lines changed

Firestore/core/src/firebase/firestore/local/local_serializer.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ firestore_client_MaybeDocument LocalSerializer::EncodeMaybeDocument(
8484
// result.has_committed_mutations = true;
8585
return result;
8686

87-
case MaybeDocument::Type::Unknown:
87+
case MaybeDocument::Type::Invalid:
8888
// TODO(rsgowman): Error handling
8989
abort();
9090
}

Firestore/core/src/firebase/firestore/model/document.cc

Lines changed: 125 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -26,50 +26,144 @@ namespace firebase {
2626
namespace firestore {
2727
namespace model {
2828

29-
std::ostream& operator<<(std::ostream& os, DocumentState state) {
30-
switch (state) {
31-
case DocumentState::kCommittedMutations:
32-
return os << "kCommittedMutations";
33-
case DocumentState::kLocalMutations:
34-
return os << "kLocalMutations";
35-
case DocumentState::kSynced:
36-
return os << "kLocalMutations";
29+
static_assert(
30+
sizeof(MaybeDocument) == sizeof(Document),
31+
"Document may not have additional members (everything goes in Rep)");
32+
33+
class Document::Rep : public MaybeDocument::Rep {
34+
public:
35+
Rep(ObjectValue&& data,
36+
DocumentKey&& key,
37+
SnapshotVersion version,
38+
DocumentState document_state)
39+
: MaybeDocument::Rep(Type::Document, std::move(key), version),
40+
data_(std::move(data)),
41+
document_state_(document_state) {
3742
}
3843

39-
UNREACHABLE();
40-
}
44+
Rep(ObjectValue&& data,
45+
DocumentKey&& key,
46+
SnapshotVersion version,
47+
DocumentState document_state,
48+
absl::any proto)
49+
: Rep(std::move(data), std::move(key), version, document_state) {
50+
proto_ = std::move(proto);
51+
}
52+
53+
const ObjectValue& data() const {
54+
return data_;
55+
}
56+
57+
DocumentState document_state() const {
58+
return document_state_;
59+
}
60+
61+
bool has_local_mutations() const {
62+
return document_state_ == DocumentState::kLocalMutations;
63+
}
64+
65+
bool has_committed_mutations() const {
66+
return document_state_ == DocumentState::kCommittedMutations;
67+
}
68+
69+
bool has_pending_writes() const override {
70+
return has_local_mutations() || has_committed_mutations();
71+
}
72+
73+
bool Equals(const MaybeDocument::Rep& other) const override {
74+
if (!MaybeDocument::Rep::Equals(other)) return false;
75+
76+
const auto& other_rep = static_cast<const Rep&>(other);
77+
return document_state_ == other_rep.document_state_ &&
78+
data_ == other_rep.data_;
79+
}
80+
81+
size_t Hash() const override {
82+
return util::Hash(MaybeDocument::Rep::Hash(), data_, document_state_);
83+
}
84+
85+
std::string ToString() const override {
86+
return absl::StrCat(
87+
"Document(key=", key().ToString(), ", version=", version().ToString(),
88+
", document_state=", document_state_, ", data=", data_.ToString(), ")");
89+
}
4190

42-
Document::Document(ObjectValue&& data,
91+
private:
92+
friend class Document;
93+
94+
ObjectValue data_;
95+
DocumentState document_state_;
96+
absl::any proto_;
97+
};
98+
99+
Document::Document(ObjectValue data,
43100
DocumentKey key,
44101
SnapshotVersion version,
45102
DocumentState document_state)
46-
: MaybeDocument(std::move(key), std::move(version)),
47-
data_(std::move(data)),
48-
document_state_(document_state) {
49-
set_type(Type::Document);
103+
: MaybeDocument(std::make_shared<Rep>(
104+
std::move(data), std::move(key), version, document_state)) {
105+
}
106+
107+
Document::Document(ObjectValue data,
108+
DocumentKey key,
109+
SnapshotVersion version,
110+
DocumentState document_state,
111+
absl::any proto)
112+
: MaybeDocument(std::make_shared<Rep>(std::move(data),
113+
std::move(key),
114+
version,
115+
document_state,
116+
std::move(proto))) {
117+
}
118+
119+
Document::Document(const MaybeDocument& document) : MaybeDocument(document) {
120+
HARD_ASSERT(type() == Type::Document);
50121
}
51122

52-
std::string Document::ToString() const {
53-
std::ostringstream out;
54-
out << *this;
55-
return out.str();
123+
const ObjectValue& Document::data() const {
124+
return doc_rep().data();
56125
}
57126

58-
std::ostream& operator<<(std::ostream& os, const Document& doc) {
59-
return os << "Document(key=" << doc.key()
60-
<< ", version=" << doc.version().timestamp()
61-
<< ", document_state=" << doc.document_state_
62-
<< ", data=" << doc.data() << ")";
127+
absl::optional<FieldValue> Document::field(const FieldPath& path) const {
128+
return data().Get(path);
63129
}
64130

65-
bool Document::Equals(const MaybeDocument& other) const {
66-
if (other.type() != Type::Document) {
67-
return false;
131+
DocumentState Document::document_state() const {
132+
return doc_rep().document_state_;
133+
}
134+
135+
bool Document::has_local_mutations() const {
136+
return doc_rep().has_local_mutations();
137+
}
138+
139+
bool Document::has_committed_mutations() const {
140+
return doc_rep().has_committed_mutations();
141+
}
142+
143+
const absl::any& Document::proto() const {
144+
return doc_rep().proto_;
145+
}
146+
147+
const Document::Rep& Document::doc_rep() const {
148+
return static_cast<const Rep&>(MaybeDocument::rep());
149+
}
150+
151+
std::ostream& operator<<(std::ostream& os, DocumentState state) {
152+
switch (state) {
153+
case DocumentState::kCommittedMutations:
154+
return os << "kCommittedMutations";
155+
case DocumentState::kLocalMutations:
156+
return os << "kLocalMutations";
157+
case DocumentState::kSynced:
158+
return os << "kLocalSynced";
68159
}
69-
auto& other_doc = static_cast<const Document&>(other);
70-
return MaybeDocument::Equals(other) &&
71-
document_state_ == other_doc.document_state_ &&
72-
data_ == other_doc.data_;
160+
161+
UNREACHABLE();
162+
}
163+
164+
/** Compares against another Document. */
165+
bool operator==(const Document& lhs, const Document& rhs) {
166+
return lhs.doc_rep().Equals(rhs.doc_rep());
73167
}
74168

75169
} // namespace model

Firestore/core/src/firebase/firestore/model/document.h

Lines changed: 32 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#define FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_MODEL_DOCUMENT_H_
1919

2020
#include <iosfwd>
21+
#include <memory>
2122
#include <string>
2223

2324
#if __OBJC__
@@ -27,13 +28,14 @@
2728
#include "Firestore/core/src/firebase/firestore/model/field_path.h"
2829
#include "Firestore/core/src/firebase/firestore/model/field_value.h"
2930
#include "Firestore/core/src/firebase/firestore/model/maybe_document.h"
31+
#include "absl/types/any.h"
3032
#include "absl/types/optional.h"
3133

3234
namespace firebase {
3335
namespace firestore {
3436
namespace model {
3537

36-
/** Describes the `hasPendingWrites` state of a document. */
38+
/** Describes the `has_pending_writes` state of a document. */
3739
enum class DocumentState {
3840
/**
3941
* Local mutations applied via the mutation queue. Document is potentially
@@ -59,67 +61,58 @@ std::ostream& operator<<(std::ostream& os, DocumentState state);
5961
*/
6062
class Document : public MaybeDocument {
6163
public:
62-
/**
63-
* Construct a document. ObjectValue must be passed by rvalue.
64-
*/
65-
Document(ObjectValue&& data,
64+
Document() = default;
65+
66+
Document(ObjectValue data,
6667
DocumentKey key,
6768
SnapshotVersion version,
6869
DocumentState document_state);
6970

71+
Document(ObjectValue data,
72+
DocumentKey key,
73+
SnapshotVersion version,
74+
DocumentState document_state,
75+
absl::any proto);
76+
77+
/**
78+
* Casts a MaybeDocument to a Document. This is a checked operation that will
79+
* assert if the type of the MaybeDocument isn't actually Type::Document.
80+
*/
81+
explicit Document(const MaybeDocument& document);
82+
7083
#if __OBJC__
7184
explicit Document(FSTDocument* doc)
72-
: MaybeDocument(doc.key, doc.version),
73-
data_(doc.data),
74-
document_state_(doc.documentState) {
85+
: Document(doc.data, doc.key, doc.version, doc.documentState) {
7586
}
7687

7788
FSTDocument* ToDocument() const {
78-
return [FSTDocument documentWithData:data_
89+
return [FSTDocument documentWithData:data()
7990
key:key()
8091
version:version()
81-
state:document_state_];
92+
state:document_state()];
8293
}
8394
#endif // __OBJC__
8495

85-
const ObjectValue& data() const {
86-
return data_;
87-
}
96+
const ObjectValue& data() const;
8897

89-
absl::optional<FieldValue> field(const FieldPath& path) const {
90-
return data_.Get(path);
91-
}
98+
absl::optional<FieldValue> field(const FieldPath& path) const;
9299

93-
bool HasLocalMutations() const {
94-
return document_state_ == DocumentState::kLocalMutations;
95-
}
100+
DocumentState document_state() const;
96101

97-
bool HasCommittedMutations() const {
98-
return document_state_ == DocumentState::kCommittedMutations;
99-
}
102+
bool has_local_mutations() const;
100103

101-
bool HasPendingWrites() const override {
102-
return HasLocalMutations() || HasCommittedMutations();
103-
}
104+
bool has_committed_mutations() const;
104105

105-
std::string ToString() const;
106+
const absl::any& proto() const;
106107

107-
friend std::ostream& operator<<(std::ostream& os, const Document& doc);
108-
109-
protected:
110-
bool Equals(const MaybeDocument& other) const override;
108+
/** Compares against another Document. */
109+
friend bool operator==(const Document& lhs, const Document& rhs);
111110

112111
private:
113-
ObjectValue data_;
114-
DocumentState document_state_;
115-
};
112+
class Rep;
116113

117-
/** Compares against another Document. */
118-
inline bool operator==(const Document& lhs, const Document& rhs) {
119-
return lhs.version() == rhs.version() && lhs.key() == rhs.key() &&
120-
lhs.HasLocalMutations() == rhs.HasLocalMutations() &&
121-
lhs.data() == rhs.data();
122-
}
114+
const Rep& doc_rep() const;
115+
};
123116

124117
inline bool operator!=(const Document& lhs, const Document& rhs) {
125118
return !(lhs == rhs);

Firestore/core/src/firebase/firestore/model/maybe_document.cc

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,19 +16,30 @@
1616

1717
#include "Firestore/core/src/firebase/firestore/model/maybe_document.h"
1818

19+
#include <ostream>
1920
#include <utility>
2021

2122
namespace firebase {
2223
namespace firestore {
2324
namespace model {
2425

25-
MaybeDocument::MaybeDocument(DocumentKey key, SnapshotVersion version)
26-
: key_(std::move(key)), version_(std::move(version)) {
26+
bool MaybeDocument::Rep::Equals(const MaybeDocument::Rep& other) const {
27+
return type() == other.type() && version() == other.version() &&
28+
key() == other.key();
2729
}
2830

29-
bool MaybeDocument::Equals(const MaybeDocument& other) const {
30-
return type_ == other.type_ && version_ == other.version_ &&
31-
key_ == other.key_;
31+
size_t MaybeDocument::Rep::Hash() const {
32+
return util::Hash(type_, key_, version_);
33+
}
34+
35+
std::ostream& operator<<(std::ostream& os, const MaybeDocument& doc) {
36+
return os << doc.rep_->ToString();
37+
}
38+
39+
bool operator==(const MaybeDocument& lhs, const MaybeDocument& rhs) {
40+
return lhs.rep_ == nullptr
41+
? rhs.rep_ == nullptr
42+
: (rhs.rep_ != nullptr && lhs.rep_->Equals(*rhs.rep_));
3243
}
3344

3445
} // namespace model

0 commit comments

Comments
 (0)