42#if COMMONS_WITH_NLOHMANN_JSON
56#include <nlohmann/json.hpp>
72template <std::
size_t N>
73inline void to_json(::nlohmann::json& j,
const FixedString<N>& s) {
74 j = std::string{s.view()};
77template <std::
size_t N>
78inline void from_json(const ::nlohmann::json& j, FixedString<N>& s) {
79 const auto str = j.template get<std::string>();
80 if (str.size() > FixedString<N>::size()) {
81 throw ::nlohmann::detail::other_error::create(
83 "commons: string of length " + std::to_string(str.size()) +
84 " does not fit FixedString<" + std::to_string(N) +
"> (capacity " +
85 std::to_string(FixedString<N>::size()) +
")",
89 for (; i < str.size(); ++i) {
99inline void to_json(::nlohmann::json& j,
const Color& c) {
100 j = c.to_hex_string();
103inline void from_json(const ::nlohmann::json& j, Color& c) {
104 const auto str = j.template get<std::string>();
107 throw ::nlohmann::detail::other_error::create(
108 502,
"commons: '" + str +
"' is not a valid color", &j);
115inline void to_json(::nlohmann::json& j,
const Icon& i) {
119inline void from_json(const ::nlohmann::json& j, Icon& i) {
120 const auto str = j.template get<std::string>();
123 throw ::nlohmann::detail::other_error::create(
124 502,
"commons: '" + str +
"' is not a valid icon", &j);
131inline void to_json(::nlohmann::json& j,
const DisplayInfo& d) {
132 j = ::nlohmann::json::object();
137 j[
"description"] = *d.description;
143 j[
"color"] = *d.color;
147inline void from_json(const ::nlohmann::json& j, DisplayInfo& d) {
149 if (
const auto it = j.find(
"name"); it != j.end() && !it->is_null()) {
150 d.name = it->template get<std::string>();
152 if (
const auto it = j.find(
"description"); it != j.end() && !it->is_null()) {
153 d.description = it->template get<std::string>();
155 if (
const auto it = j.find(
"icon"); it != j.end() && !it->is_null()) {
156 d.icon = it->template get<Icon>();
158 if (
const auto it = j.find(
"color"); it != j.end() && !it->is_null()) {
159 d.color = it->template get<Color>();
168inline void to_json(::nlohmann::json& j,
const FlagRef& f) {
169 j = std::string{f.name};
172inline void from_json(const ::nlohmann::json& j, FlagRef& f) {
173 const auto name = j.template get<std::string>();
174 const auto found = GlobalFlagRegistry::instance().find(name);
176 throw ::nlohmann::detail::other_error::create(
177 502,
"commons: '" + name +
"' is not a registered flag", &j);
184inline void to_json(::nlohmann::json& j,
const FlagSet& s) {
185 j = ::nlohmann::json::array();
186 for (
const auto& f : s) {
187 j.push_back(std::string{f.name});
191inline void from_json(const ::nlohmann::json& j, FlagSet& s) {
193 for (
const auto& elem : j) {
194 s.insert(elem.template get<FlagRef>());
207concept JsonSerializable =
requires(::nlohmann::json& j,
const T& v) { j = v; };
210concept JsonDeserializable =
requires(const ::nlohmann::json& j, T& v) { j.get_to(v); };
215 requires detail::JsonSerializable<T>
216inline void to_json(::nlohmann::json& j,
const WithPriority<T>& w) {
217 j = ::nlohmann::json{{
"priority", w.priority()}, {
"value", w.value()}};
221 requires detail::JsonDeserializable<T>
222inline void from_json(const ::nlohmann::json& j, WithPriority<T>& w) {
223 j.at(
"value").get_to(w.value());
224 w.set_priority(j.at(
"priority").template get<int>());
228 requires detail::JsonSerializable<T>
229inline void to_json(::nlohmann::json& j,
const PrioritizedSet<T>& s) {
230 j = ::nlohmann::json::array();
231 for (
const auto& v : s) {
239 requires detail::JsonDeserializable<T> &&
240 (std::is_base_of_v<Prioritized, T> || Prioritizable<T>)
241inline void from_json(const ::nlohmann::json& j, PrioritizedSet<T>& s) {
243 throw ::nlohmann::detail::other_error::create(
244 502,
"commons: PrioritizedSet expects a JSON array", &j);
247 for (
const auto& elem : j) {
248 T value = elem.template get<T>();
250 s.insert(p, std::move(value));
256inline void to_json(::nlohmann::json& j,
const Hsl& c) {
257 j = ::nlohmann::json{{
"h", c.h}, {
"s", c.s}, {
"l", c.l}, {
"a", c.a}};
260inline void from_json(const ::nlohmann::json& j, Hsl& c) {
261 c.h = j.at(
"h").template get<f64>();
262 c.s = j.at(
"s").template get<f64>();
263 c.l = j.at(
"l").template get<f64>();
264 c.a = j.at(
"a").template get<f64>();
267inline void to_json(::nlohmann::json& j,
const Hsv& c) {
268 j = ::nlohmann::json{{
"h", c.h}, {
"s", c.s}, {
"v", c.v}, {
"a", c.a}};
271inline void from_json(const ::nlohmann::json& j, Hsv& c) {
272 c.h = j.at(
"h").template get<f64>();
273 c.s = j.at(
"s").template get<f64>();
274 c.v = j.at(
"v").template get<f64>();
275 c.a = j.at(
"a").template get<f64>();
280inline void to_json(::nlohmann::json& j,
const SemVer& v) {
284inline void from_json(const ::nlohmann::json& j, SemVer& v) {
285 const auto str = j.template get<std::string>();
288 throw ::nlohmann::detail::other_error::create(
289 502,
"commons: '" + str +
"' is not a valid semantic version", &j);
298inline void to_json(::nlohmann::json& j,
const VersionConstraint& v) {
302inline void from_json(const ::nlohmann::json& j, VersionConstraint& v) {
303 const auto str = j.template get<std::string>();
306 }
catch (
const std::exception&) {
307 throw ::nlohmann::detail::other_error::create(
308 502,
"commons: '" + str +
"' is not a valid version constraint", &j);
317template <
class Tag,
class Repr>
318inline void to_json(::nlohmann::json& j,
const Id<Tag, Repr>&
id) {
322template <
class Tag,
class Repr>
323inline void from_json(const ::nlohmann::json& j, Id<Tag, Repr>&
id) {
324 id = Id<Tag, Repr>{j.template get<Repr>()};
333inline void to_json(::nlohmann::json& j,
const CoreOrigin& o) {
334 j = ::nlohmann::json{{
"kind", std::string{o.kind()}}};
336inline void from_json(const ::nlohmann::json& , CoreOrigin& ) {}
338inline void to_json(::nlohmann::json& j,
const InternalOrigin& o) {
339 j = ::nlohmann::json{{
"kind", std::string{o.kind()}}};
341inline void from_json(const ::nlohmann::json& , InternalOrigin& ) {}
343inline void to_json(::nlohmann::json& j,
const ExternalOrigin& o) {
344 j = ::nlohmann::json{{
"kind", std::string{o.kind()}}};
345 if (!o.source.empty()) {
346 j[
"source"] = o.source;
349inline void from_json(const ::nlohmann::json& j, ExternalOrigin& o) {
350 if (
const auto it = j.find(
"source"); it != j.end() && !it->is_null()) {
351 it->get_to(o.source);
355inline void to_json(::nlohmann::json& j,
const UnknownOrigin& o) {
356 j = ::nlohmann::json{{
"kind", std::string{o.kind()}}};
358inline void from_json(const ::nlohmann::json& , UnknownOrigin& ) {}
362inline void to_json(::nlohmann::json& j,
const IOrigin& o) {
364 j =
static_cast<const CoreOrigin&
>(o);
366 j =
static_cast<const InternalOrigin&
>(o);
368 j =
static_cast<const ExternalOrigin&
>(o);
370 j =
static_cast<const UnknownOrigin&
>(o);
372 j = ::nlohmann::json{{
"kind", std::string{k}}};
376#if defined(COMMONS_HAS_INT128)
380[[nodiscard]]
inline std::string u128_to_string(u128 v) {
385 std::array<char, 40> buf{};
386 std::size_t pos = buf.size();
388 buf[--pos] =
static_cast<char>(
'0' +
static_cast<int>(v % 10));
391 return std::string{buf.data() + pos, buf.size() - pos};
394[[nodiscard]]
inline std::string i128_to_string(
const i128 v) {
395 const bool negative = v < 0;
397 const u128 mag = negative ? (~static_cast<u128>(v) + 1) : static_cast<u128>(v);
398 std::string digits = u128_to_string(mag);
399 return negative ?
"-" + digits : digits;
406template <
typename BasicJsonType>
407[[nodiscard]] u128 json_to_u128(
const BasicJsonType& j) {
408 if (!j.is_string()) {
409 throw ::nlohmann::detail::other_error::create(
410 502,
"commons: u128 must be encoded as a JSON string", &j);
412 const auto& s = j.template get_ref<const std::string&>();
414 throw ::nlohmann::detail::other_error::create(502,
"commons: u128 string is empty", &j);
416 constexpr u128 u128_max = ~static_cast<u128>(0);
418 for (
const char c : s) {
419 if (c <
'0' || c >
'9') {
420 throw ::nlohmann::detail::other_error::create(
421 502,
"commons: non-digit character in u128 string", &j);
423 const auto d =
static_cast<u128
>(c -
'0');
424 if (v > (u128_max - d) / 10) {
425 throw ::nlohmann::detail::other_error::create(
426 502,
"commons: u128 string out of range", &j);
436template <
typename BasicJsonType>
437[[nodiscard]] i128 json_to_i128(
const BasicJsonType& j) {
438 if (!j.is_string()) {
439 throw ::nlohmann::detail::other_error::create(
440 502,
"commons: i128 must be encoded as a JSON string", &j);
442 const auto& s = j.template get_ref<const std::string&>();
444 throw ::nlohmann::detail::other_error::create(502,
"commons: i128 string is empty", &j);
447 bool negative =
false;
451 }
else if (s[0] ==
'+') {
455 throw ::nlohmann::detail::other_error::create(
456 502,
"commons: i128 string has sign but no digits", &j);
460 constexpr u128 neg_mag_max =
static_cast<u128
>(1) << 127;
461 constexpr u128 pos_mag_max = neg_mag_max - 1;
463 for (; i < s.size(); ++i) {
465 if (c <
'0' || c >
'9') {
466 throw ::nlohmann::detail::other_error::create(
467 502,
"commons: non-digit character in i128 string", &j);
469 const auto d =
static_cast<u128
>(c -
'0');
470 if (mag > (neg_mag_max - d) / 10) {
471 throw ::nlohmann::detail::other_error::create(
472 502,
"commons: i128 string out of range", &j);
476 if (!negative && mag > pos_mag_max) {
477 throw ::nlohmann::detail::other_error::create(502,
"commons: i128 string out of range", &j);
482 const u128 mm1 = mag - 1;
483 const i128 partial = -
static_cast<i128
>(mm1);
486 return static_cast<i128
>(mag);
495#if defined(COMMONS_HAS_INT128)
503struct adl_serializer<::comms::i128> {
504 template <
typename BasicJsonType>
505 static void to_json(BasicJsonType& j, const ::comms::i128 v) {
506 j = ::comms::detail::i128_to_string(v);
509 template <
typename BasicJsonType>
510 static void from_json(
const BasicJsonType& j, ::comms::i128& v) {
511 v = ::comms::detail::json_to_i128(j);
516struct adl_serializer<::comms::u128> {
517 template <
typename BasicJsonType>
518 static void to_json(BasicJsonType& j, const ::comms::u128 v) {
519 j = ::comms::detail::u128_to_string(v);
522 template <
typename BasicJsonType>
523 static void from_json(
const BasicJsonType& j, ::comms::u128& v) {
524 v = ::comms::detail::json_to_u128(j);
538struct nlohmann::adl_serializer<std::complex<T>> {
539 template <
typename BasicJsonType>
540 static void to_json(BasicJsonType& j,
const std::complex<T>& c) {
541 j = BasicJsonType::array();
542 j.push_back(c.real());
543 j.push_back(c.imag());
546 template <
typename BasicJsonType>
547 static void from_json(
const BasicJsonType& j, std::complex<T>& c) {
548 c.real(j.at(0).template get<T>());
549 c.imag(j.at(1).template get<T>());
558struct nlohmann::adl_serializer<std::optional<T>> {
559 template <
typename BasicJsonType>
560 static void to_json(BasicJsonType& j,
const std::optional<T>& opt) {
561 if (opt.has_value()) {
568 template <
typename BasicJsonType>
569 static void from_json(
const BasicJsonType& j, std::optional<T>& opt) {
573 opt = j.template get<T>();
585 template <
typename BasicJsonType>
586 static void to_json(BasicJsonType& j, const ::comms::OriginPtr& o) {
594 template <
typename BasicJsonType>
600 if (!j.is_object()) {
601 throw ::nlohmann::detail::other_error::create(
602 502,
"commons: origin must be a JSON object", &j);
604 const auto it = j.find(
"kind");
606 throw ::nlohmann::detail::other_error::create(
607 502,
"commons: origin missing 'kind'", &j);
609 const auto kind = it->template get<std::string>();
610 auto created = ::comms::GlobalOriginRegistry::instance().create(kind);
612 throw ::nlohmann::detail::other_error::create(
613 502,
"commons: unknown origin kind '" + kind +
"'", &j);
620 o = std::move(created);
The definition came from an external source, named by source.
Definition origin.hpp:137
static constexpr std::string_view KIND
Compile-time discriminator.
Definition origin.hpp:93
static VersionConstraint parse(const std::string_view s)
Parse a range string into a constraint.
Definition version_constraint.hpp:65
A tiny RGBA color container with rich, mostly-constexpr color manipulation, plus the Hsl/Hsv model st...
Central feature-gate header for Commons' optional integrations.
Presentation metadata for a type/value — name, description, icon, color, all optional — plus the trai...
NTTP-friendly fixed-size string usable as a non-type template parameter.
Compile-time named flags grouped into categories, plus a runtime FlagSet, a program-wide GlobalFlagRe...
A tiny value type carrying an Iconify icon identifier such as mdi:abacus (a set:name pair).
A strong-typed identifier — comms::Id<Tag, Repr> — that wraps an allowed representation in a phantom ...
A polymorphic provenance envelope — comms::IOrigin — that records where a definition came from,...
std::unique_ptr< IOrigin > OriginPtr
A heap-owned origin. The canonical way to carry an IOrigin by value.
Definition origin.hpp:79
Priorities for orderable things (adapters, transports, …) plus the helpers to sort them deterministic...
int get_priority(const T &value) noexcept
Priority of a value or reference.
Definition prioritized.hpp:164
A value type for a Semantic Versioning 2.0.0 version — major.minor.patch with optional prerelease and...
static constexpr std::optional< Color > parse(std::string_view s)
Parse any supported textual color: hex (see parse_hex), CSS-functional rgb()/rgba()/hsl()/hsla(),...
Definition color.hpp:1327
static constexpr std::optional< Icon > parse(const std::string_view value)
Non-throwing validation: returns the Icon for a well-formed value (exactly one :, non-empty set and n...
Definition icon.hpp:96
static std::optional< SemVer > parse(std::string_view s)
Non-throwing parse.
Definition semver.hpp:67
Fixed-width numeric aliases shared across the C++ libraries.
A semver range constraint that answers satisfies(SemVer).