Action Engine
Loading...
Searching...
No Matches
serialization.h
1// Copyright 2025 Google LLC
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15#ifndef ACTIONENGINE_DATA_SERIALIZATION_H_
16#define ACTIONENGINE_DATA_SERIALIZATION_H_
17
18#include <any>
19#include <functional>
20#include <string>
21#include <string_view>
22
23#include <absl/container/flat_hash_map.h>
24
26
27namespace act {
28
29using Bytes = std::string;
30using Serializer = std::function<absl::StatusOr<Bytes>(std::any)>;
31using Deserializer = std::function<absl::StatusOr<std::any>(Bytes)>;
32
33class SerializerRegistry {
34 public:
35 template <typename T>
36 [[nodiscard]] absl::StatusOr<Bytes> Serialize(
37 T value, std::string_view mimetype) const;
38
39 [[nodiscard]] absl::StatusOr<std::any> Deserialize(
40 Bytes data, std::string_view mimetype) const;
41
42 // The registered deserializer must return an std::any which actually
43 // contains the type T. This is not checked at compile time and is not
44 // the responsibility of the registry. Essentially, this method is just
45 // a convenience wrapper for std::any_cast<T>(result)-or-status.
46 template <typename T>
47 [[nodiscard]] absl::StatusOr<T> DeserializeAs(
48 const Bytes& data, std::string_view mimetype) const;
49
50 void RegisterSerializer(std::string_view mimetype, Serializer serializer);
51
52 void RegisterDeserializer(std::string_view mimetype,
53 Deserializer deserializer);
54
55 [[nodiscard]] bool HasSerializers(std::string_view mimetype) const;
56
57 [[nodiscard]] bool HasDeserializers(std::string_view mimetype) const;
58
59 [[nodiscard]] void* GetUserData() const;
60
61 void SetUserData(std::shared_ptr<void> user_data);
62
63 protected:
64 absl::flat_hash_map<std::string, absl::InlinedVector<Serializer, 2>>
65 mime_serializers_;
66 absl::flat_hash_map<std::string, absl::InlinedVector<Deserializer, 2>>
67 mime_deserializers_;
68 std::shared_ptr<void> user_data_ =
69 nullptr; // Optional user data for custom use.
70};
71
72template <typename T>
73absl::StatusOr<Bytes> SerializerRegistry::Serialize(
74 T value, std::string_view mimetype) const {
75 if (mimetype.empty()) {
76 return absl::InvalidArgumentError(
77 "Serialize(value, mimetype) was called with an empty mimetype.");
78 }
79
80 const auto it = mime_serializers_.find(mimetype);
81 if (it == mime_serializers_.end()) {
82 return absl::UnimplementedError(absl::StrFormat(
83 "No serializer is registered for mimetype %v.", mimetype));
84 }
85
86 for (const auto& serializer : it->second | std::views::reverse) {
87 // Attempt to serialize the value using the registered serializer.
88 auto result = serializer(std::move(value));
89 if (result.ok()) {
90 return std::move(*result);
91 }
92 }
93
94 return absl::UnimplementedError(
95 absl::StrFormat("No serializer could handle value of type %s for "
96 "mimetype %v.",
97 typeid(T).name(), mimetype));
98}
99
100template <typename T>
101absl::StatusOr<T> SerializerRegistry::DeserializeAs(
102 const Bytes& data, std::string_view mimetype) const {
103 auto deserialized = Deserialize(data, mimetype);
104 if (!deserialized.ok()) {
105 return deserialized.status();
106 }
107 if (std::any_cast<T>(&*deserialized) == nullptr) {
108 return absl::InvalidArgumentError(
109 absl::StrFormat("Deserialized type does not match expected type %s.",
110 typeid(T).name()));
111 }
112 return std::any_cast<T>(*std::move(deserialized));
113}
114
115void InitSerializerRegistryWithDefaults(SerializerRegistry* registry);
116
117SerializerRegistry& GetGlobalSerializerRegistry();
118
119inline SerializerRegistry* GetGlobalSerializerRegistryPtr() {
120 return &GetGlobalSerializerRegistry();
121}
122
123void SetGlobalSerializerRegistry(const SerializerRegistry& registry);
124
125template <typename T>
126absl::StatusOr<Chunk> ToChunk(T value, std::string_view mimetype = {},
127 SerializerRegistry* const registry = nullptr) {
128 if (mimetype.empty()) {
129 return absl::FailedPreconditionError(absl::StrFormat(
130 "Serialize(value, mimetype) was called with an empty mimetype, and "
131 "value's type %s is not a candidate for ADL-based conversion.",
132 typeid(T).name()));
133 }
134
135 SerializerRegistry* resolved_registry =
136 registry ? registry : GetGlobalSerializerRegistryPtr();
137
138 auto data = resolved_registry->Serialize(std::move(value), mimetype);
139 if (!data.ok()) {
140 return data.status();
141 }
142 return Chunk{.metadata = ChunkMetadata{.mimetype = std::string(mimetype)},
143 .data = std::move(*data)};
144}
145
146template <ConvertibleToChunk T>
147absl::StatusOr<Chunk> ToChunk(T value, std::string_view mimetype = {},
148 SerializerRegistry* const registry = nullptr) {
149 if (mimetype.empty()) {
150 return act::ConvertToOrDie<Chunk>(std::move(value));
151 }
152
153 SerializerRegistry* resolved_registry =
154 registry ? registry : GetGlobalSerializerRegistryPtr();
155
156 auto data = resolved_registry->Serialize(std::move(value), mimetype);
157 if (!data.ok()) {
158 return data.status();
159 }
160 return Chunk{.metadata = ChunkMetadata{.mimetype = std::string(mimetype)},
161 .data = std::move(*data)};
162}
163
164template <typename T>
165absl::StatusOr<T> FromChunkAs(Chunk chunk, std::string_view mimetype = {},
166 SerializerRegistry* const registry = nullptr)
167 requires(ConvertibleFromChunk<T>) {
168 if (mimetype.empty()) {
169 return act::ConvertToOrDie<T>(std::move(chunk));
170 }
171
172 if (!chunk.metadata) {
173 chunk.metadata.emplace();
174 }
175
176 if (chunk.metadata->mimetype.empty()) {
177 chunk.metadata->mimetype = mimetype;
178 return act::ConvertToOrDie<T>(std::move(chunk));
179 }
180
181 const SerializerRegistry* resolved_registry =
182 registry ? registry : GetGlobalSerializerRegistryPtr();
183
184 return resolved_registry->DeserializeAs<T>(std::move(chunk).data, mimetype);
185}
186
187absl::StatusOr<std::any> FromChunk(
188 Chunk chunk, std::string_view mimetype = {},
189 const SerializerRegistry* registry = nullptr);
190
191template <typename T>
192absl::StatusOr<T> FromChunkAs(Chunk chunk, std::string_view mimetype = {},
193 SerializerRegistry* const registry = nullptr)
194 requires(!ConvertibleFromChunk<T>) {
195 if (mimetype.empty()) {
196 return absl::FailedPreconditionError(
197 "FromChunkAs(chunk, mimetype) was called with an empty mimetype, "
198 "and chunk's type is not a candidate for ADL-based conversion.");
199 }
200
201 SerializerRegistry* resolved_registry =
202 registry ? registry : GetGlobalSerializerRegistryPtr();
203
204 return resolved_registry->DeserializeAs<T>(std::move(chunk), mimetype);
205}
206
207} // namespace act
208
209#endif //ACTIONENGINE_DATA_SERIALIZATION_H_
Definition types.h:104
Definition types.h:63
ActionEngine data structures used to implement actions and nodes (data streams).