parcel 0.2.2
Wrappable, wire-transferable C++23 value system with JSON serialization
Loading...
Searching...
No Matches
chrono.h
Go to the documentation of this file.
1#pragma once
2
19#include <parcel/cell.h>
20#include <parcel/defaults.h>
21#include <parcel/descriptor.h>
22#include <parcel/json.h>
23
24#include <charconv>
25#include <chrono>
26#include <memory>
27#include <string>
28#include <string_view>
29
30namespace parcel {
31
38class SystemTimePointCell : public BaseCell<SystemTimePointCell, std::chrono::sys_seconds> {
40
41public:
42 using base_t::base_t;
43 using base_t::operator=;
44
45 static constexpr std::string_view kind_id = "time:sys_seconds";
46
47 [[nodiscard]] std::string to_string() const override {
48 return std::to_string(this->value.time_since_epoch().count());
49 }
50
51 [[nodiscard]] json_t to_json() const override {
52 json_t j{
53 {ICell::KEY_KIND, kind_id},
54 {ICell::KEY_VALUE, static_cast<std::int64_t>(this->value.time_since_epoch().count())},
55 };
56 this->inject_display_info(j);
57 return j;
58 }
59
60 static cell_t from_json(json_t const& j, ParcelRegistry const&) {
61 const auto epoch = base_t::cell_from_json<std::int64_t>(j, kind_id);
62 auto cell = std::make_shared<SystemTimePointCell>(
63 std::chrono::sys_seconds{std::chrono::seconds{epoch}});
65 return cell;
66 }
67
68 static cell_type_descriptor_t descriptor() {
69 static const auto d =
70 std::make_shared<SimpleCellTypeDescriptor<SystemTimePointCell>>(DisplayInfo{
71 .name = "SystemTimePoint", .description = "Wall-clock time, second resolution"});
72 return d;
73 }
74};
75
84 : public BaseCell<UnixMillisCell, std::chrono::sys_time<std::chrono::milliseconds>> {
86
87public:
88 using base_t::base_t;
89 using base_t::operator=;
90
91 static constexpr std::string_view kind_id = "time:unix_ms";
92
93 [[nodiscard]] std::string to_string() const override {
94 return std::to_string(this->value.time_since_epoch().count()) + "ms";
95 }
96
97 [[nodiscard]] json_t to_json() const override {
98 json_t j{
99 {ICell::KEY_KIND, kind_id},
100 {ICell::KEY_VALUE, static_cast<std::int64_t>(this->value.time_since_epoch().count())},
101 };
102 this->inject_display_info(j);
103 return j;
104 }
105
106 static cell_t from_json(json_t const& j, ParcelRegistry const&) {
107 const auto epoch = base_t::cell_from_json<std::int64_t>(j, kind_id);
108 auto cell = std::make_shared<UnixMillisCell>(
109 std::chrono::sys_time<std::chrono::milliseconds>{std::chrono::milliseconds{epoch}});
111 return cell;
112 }
113
114 static cell_type_descriptor_t descriptor() {
115 static const auto d =
116 std::make_shared<SimpleCellTypeDescriptor<UnixMillisCell>>(DisplayInfo{
117 .name = "UnixMillis", .description = "Wall-clock time as Unix epoch milliseconds"});
118 return d;
119 }
120};
121
124
130class DurationMsCell : public BaseCell<DurationMsCell, std::chrono::milliseconds> {
132
133public:
134 using base_t::base_t;
135 using base_t::operator=;
136
137 static constexpr std::string_view kind_id = "time:ms";
138
139 [[nodiscard]] std::string to_string() const override {
140 return std::to_string(this->value.count()) + "ms";
141 }
142
143 [[nodiscard]] json_t to_json() const override {
144 json_t j{
145 {ICell::KEY_KIND, kind_id},
146 {ICell::KEY_VALUE, static_cast<std::int64_t>(this->value.count())},
147 };
148 this->inject_display_info(j);
149 return j;
150 }
151
152 static cell_t from_json(json_t const& j, ParcelRegistry const&) {
153 const auto v = base_t::cell_from_json<std::int64_t>(j, kind_id);
154 auto cell = std::make_shared<DurationMsCell>(std::chrono::milliseconds{v});
156 return cell;
157 }
158
159 static cell_type_descriptor_t descriptor() {
160 static const auto d = std::make_shared<SimpleCellTypeDescriptor<DurationMsCell>>(
161 DisplayInfo{.name = "DurationMs", .description = "Duration in milliseconds"});
162 return d;
163 }
164};
165
171class YmdCell : public BaseCell<YmdCell, std::chrono::year_month_day> {
173
174public:
175 using base_t::base_t;
176 using base_t::operator=;
177
178 static constexpr std::string_view kind_id = "time:ymd";
179
180 [[nodiscard]] std::string to_string() const override {
181 return format_iso(this->value);
182 }
183
184 [[nodiscard]] json_t to_json() const override {
185 json_t j{
186 {ICell::KEY_KIND, kind_id},
187 {ICell::KEY_VALUE, format_iso(this->value)},
188 };
189 this->inject_display_info(j);
190 return j;
191 }
192
193 static cell_t from_json(json_t const& j, ParcelRegistry const&) {
194 const auto s = base_t::cell_from_json<std::string>(j, kind_id);
195 auto cell = std::make_shared<YmdCell>(parse_iso(s));
197 return cell;
198 }
199
200 static cell_type_descriptor_t descriptor() {
201 static const auto d = std::make_shared<SimpleCellTypeDescriptor<YmdCell>>(DisplayInfo{
202 .name = "YearMonthDay", .description = "Calendar date (ISO-8601 YYYY-MM-DD)"});
203 return d;
204 }
205
206private:
207 static std::string format_iso(std::chrono::year_month_day const& ymd) {
208 std::string out;
209 out.reserve(10);
210 const int y = static_cast<int>(ymd.year());
211 const unsigned m = static_cast<unsigned>(ymd.month());
212 const unsigned d = static_cast<unsigned>(ymd.day());
213 const bool neg = y < 0;
214 const auto ay = static_cast<unsigned>(neg ? -y : y);
215 // %04u allows 10-digit unsigned, plus sign + separators + null → up
216 // to 18 bytes; round to 24 to keep gcc's truncation analysis quiet.
217 char buf[24]; // NOLINT(modernize-avoid-c-arrays) — fixed-size local scratch for snprintf
218 // Hand-rolled to avoid <format>'s chrono extensions on older libc++.
219 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg, cert-err33-c)
220 std::snprintf(buf, sizeof(buf), "%s%04u-%02u-%02u", neg ? "-" : "", ay, m, d);
221 out = buf;
222 return out;
223 }
224
225 static std::chrono::year_month_day parse_iso(std::string const& s) {
226 // Accept "YYYY-MM-DD" or "-YYYY-MM-DD" (BCE).
227 const std::string_view sv{s};
228
229 auto fail = [&s] {
230 throw InvalidJsonException("YmdCell: invalid ISO-8601 date '" + s + "'",
231 std::string(YmdCell::kind_id));
232 };
233
234 int y = 0;
235 unsigned m = 0;
236 unsigned d = 0;
237
238 std::size_t first_dash = 4;
239 if (!sv.empty() && sv.front() == '-') {
240 first_dash = 5;
241 }
242
243 if (sv.size() < first_dash + 4 || sv[first_dash] != '-') {
244 fail();
245 }
246
247 auto const year_part = sv.substr(0, first_dash);
248 auto const month_part = sv.substr(first_dash + 1, 2);
249
250 if (sv[first_dash + 3] != '-') {
251 fail();
252 }
253
254 auto const day_part = sv.substr(first_dash + 4);
255
256 auto parse_int = [](const std::string_view part, auto& out) {
257 auto const* first = part.data();
258 auto const* last = part.data() + part.size();
259 auto const [ptr, ec] = std::from_chars(first, last, out);
260 return ec == std::errc{} && ptr == last;
261 };
262
263 if (!parse_int(year_part, y) || !parse_int(month_part, m) || !parse_int(day_part, d)) {
264 fail();
265 }
266
267 auto const result = std::chrono::year_month_day{
268 std::chrono::year{y}, std::chrono::month{m}, std::chrono::day{d}};
269
270 if (!result.ok()) {
271 fail();
272 }
273
274 return result;
275 }
276};
277
278} // namespace parcel
279
Core ICell interface, cell_t handle, BaseCell CRTP base, and CellLike concept.
std::shared_ptr< ICellTypeDescriptor > cell_type_descriptor_t
Shared handle to a runtime cell-type descriptor.
Definition cell.h:63
std::shared_ptr< ICell > cell_t
Shared handle to any ICell-derived value — the canonical cell pointer.
Definition cell.h:68
Duration in milliseconds.
Definition chrono.h:130
std::string to_string() const override
Render the cell's value as a compact human-readable string.
Definition chrono.h:139
json_t to_json() const override
Default JSON serialization for cells with JSON-convertible storage.
Definition chrono.h:143
Runtime catalog of cell-type descriptors, keyed by wire kind id.
Definition registry.h:115
Wall-clock time at second resolution (ISO-8601-friendly).
Definition chrono.h:38
std::string to_string() const override
Render the cell's value as a compact human-readable string.
Definition chrono.h:47
json_t to_json() const override
Default JSON serialization for cells with JSON-convertible storage.
Definition chrono.h:51
Wall-clock time at millisecond resolution.
Definition chrono.h:84
json_t to_json() const override
Default JSON serialization for cells with JSON-convertible storage.
Definition chrono.h:97
std::string to_string() const override
Render the cell's value as a compact human-readable string.
Definition chrono.h:93
Calendar date (no time of day).
Definition chrono.h:171
json_t to_json() const override
Default JSON serialization for cells with JSON-convertible storage.
Definition chrono.h:184
std::string to_string() const override
Render the cell's value as a compact human-readable string.
Definition chrono.h:180
comms::DisplayInfo DisplayInfo
Display info attached to a cell or descriptor — re-exported from comms::DisplayInfo.
Definition common.h:75
The default_cell_for<T> trait that drives FieldsBuilder field-type inference.
#define PARCEL_DEFAULT_CELL(CellT)
Register a cell type as the default wrapper for its payload type.
Definition defaults.h:269
auto cell(T &&v)
Wrap a raw value into its default cell, returning a shared_ptr to the cell.
Definition defaults.h:192
Runtime cell-type descriptors and the schema-graph mix-ins (IHasFields, ISubTypes).
nlohmann::json typedef shared across cell types.
nlohmann::json json_t
Project-wide alias for nlohmann::json.
Definition json.h:19
CRTP base providing default to_json / clone / kind plumbing on top of a storage type.
Definition cell.h:343
static void absorb_display_info(json_t const &j, Out &out)
Read "d" (if present) from a JSON object and assign it onto a cell.
Definition cell.h:499
std::chrono::sys_seconds value
Held value of the cell.
Definition cell.h:348
void inject_display_info(json_t &j) const
Copy this cell's display info (if any) into the JSON object under "d".
Definition cell.h:486
static constexpr std::string_view KEY_VALUE
JSON key for the value payload ("v").
Definition cell.h:88
static constexpr std::string_view KEY_KIND
JSON key for the kind id ("k").
Definition cell.h:86