qtty-cpp 0.4.5
Header-only C++ wrapper for qtty
Loading...
Searching...
No Matches
serialization.hpp
Go to the documentation of this file.
1// SPDX-License-Identifier: BSD-3-Clause
2// Copyright (C) 2026 Vallés Puig, Ramon
3
4#pragma once
5
10
11#include <stdexcept>
12#include <string>
13#include <string_view>
14#include <type_traits>
15extern "C" {
16#include "qtty_ffi.h"
17}
18#include "ffi_core.hpp"
19
20// Forward declarations for JSON-related FFI functions. These may not be present
21// in older generated headers; we declare them here to ensure availability at
22// compile time when linking against a serde-enabled qtty-ffi.
23extern "C" {
24int32_t qtty_quantity_to_json_value(qtty_quantity_t src, char **out_json);
25int32_t qtty_quantity_from_json_value(UnitId unit, const char *json, qtty_quantity_t *out);
26int32_t qtty_quantity_to_json(qtty_quantity_t src, char **out_json);
27int32_t qtty_quantity_from_json(const char *json, qtty_quantity_t *out);
28int32_t qtty_derived_to_json(qtty_derived_quantity_t src, char **out);
29int32_t qtty_derived_from_json(const char *json, qtty_derived_quantity_t *out);
30void qtty_string_free(char *s);
31}
32
33namespace qtty {
34namespace serialization {
35
36// Thin wrappers over Rust FFI JSON serialize/deserialize.
37// Requires qtty-ffi to be built with the `qtty_serde` Cargo feature.
38
44inline std::string from_owned_c(char *ptr) {
45 if (!ptr)
46 return {};
47 std::string s(ptr);
48 // Free via FFI allocator
50 return s;
51}
52
53// Serialize only the numeric value as a JSON number string.
54// Mirrors Rust's default serde for quantities.
55
62template <typename UnitTag> std::string to_json_value(const Quantity<UnitTag> &q) {
63 qtty_quantity_t src{};
64 int32_t status = qtty_quantity_make(q.value(), UnitTraits<UnitTag>::unit_id(), &src);
65 check_status(static_cast<QttyStatus>(status), "Creating source quantity for to_json_value");
66
67 char *out = nullptr;
68 status = qtty_quantity_to_json_value(src, &out);
69 check_status(static_cast<QttyStatus>(status), "Serializing value to JSON");
70 return from_owned_c(out);
71}
72
79template <typename T>
81 using UnitTag = typename ExtractTag<T>::type;
82 qtty_quantity_t out_qty{};
83 int32_t status =
85 check_status(static_cast<QttyStatus>(status), "Deserializing value from JSON");
86 return Quantity<UnitTag>(out_qty.value);
87}
88
89// Serialize value and unit_id into an object {"value":<f64>, "unit_id":<u32>}.
90
97template <typename UnitTag> std::string to_json(const Quantity<UnitTag> &q) {
98 qtty_quantity_t src{};
99 int32_t status = qtty_quantity_make(q.value(), UnitTraits<UnitTag>::unit_id(), &src);
100 check_status(static_cast<QttyStatus>(status), "Creating source quantity for to_json");
101
102 char *out = nullptr;
103 status = qtty_quantity_to_json(src, &out);
104 check_status(static_cast<QttyStatus>(status), "Serializing quantity to JSON");
105 return from_owned_c(out);
106}
107
108inline UnitId unit_id_from_u32(uint32_t raw) {
109 // cbindgen exposes UnitId directly; cast is safe for values from Rust
110 return static_cast<UnitId>(raw);
111}
112
113// Parse {"value":<f64>, "unit_id":<u32>} and construct a typed quantity.
114// Rejects mismatched dimensions and unknown unit_ids at the Rust boundary.
115
122template <typename T> Quantity<typename ExtractTag<T>::type> from_json(std::string_view json) {
123 using UnitTag = typename ExtractTag<T>::type;
124 qtty_quantity_t out_qty{};
125 int32_t status = qtty_quantity_from_json(json.data(), &out_qty);
126 check_status(static_cast<QttyStatus>(status), "Deserializing quantity from JSON");
127
128 // Convert to requested UnitTag if needed; Rust returns the unit in JSON
129 if (out_qty.unit != UnitTraits<UnitTag>::unit_id()) {
130 qtty_quantity_t conv{};
131 status = qtty_quantity_convert(out_qty, UnitTraits<UnitTag>::unit_id(), &conv);
132 check_status(static_cast<QttyStatus>(status),
133 "Converting deserialized quantity to target unit");
134 return Quantity<UnitTag>(conv.value);
135 }
136 return Quantity<UnitTag>(out_qty.value);
137}
138
139} // namespace serialization
140
141// ============================================================================
142// Compound Quantity Serialization
143// ============================================================================
144// Serialize/deserialize derived quantities (e.g., velocity) via the Rust FFI.
145
147
154template <typename Tag> std::string to_json(const Quantity<Tag> &q) {
155 static_assert(is_compound_v<Tag>, "derived_serialization::to_json requires a compound quantity");
156 qtty_derived_quantity_t src{};
157 int32_t status = qtty_derived_make(q.value(), UnitTraits<Tag>::numerator_unit_id(),
159 check_status(static_cast<QttyStatus>(status), "Creating derived quantity for serialization");
160
161 char *out = nullptr;
162 status = qtty_derived_to_json(src, &out);
163 check_status(static_cast<QttyStatus>(status), "Serializing derived quantity to JSON");
164 return serialization::from_owned_c(out);
165}
166
173template <typename T> Quantity<typename ExtractTag<T>::type> from_json(std::string_view json) {
174 using Tag = typename ExtractTag<T>::type;
175 static_assert(is_compound_v<Tag>, "derived_serialization::from_json requires a compound type");
176 qtty_derived_quantity_t out_qty{};
177 int32_t status = qtty_derived_from_json(json.data(), &out_qty);
178 check_status(static_cast<QttyStatus>(status), "Deserializing derived quantity from JSON");
179
180 // Convert to requested units if needed
181 if (out_qty.numerator != UnitTraits<Tag>::numerator_unit_id() ||
182 out_qty.denominator != UnitTraits<Tag>::denominator_unit_id()) {
183 qtty_derived_quantity_t conv{};
184 status = qtty_derived_convert(out_qty, UnitTraits<Tag>::numerator_unit_id(),
186 check_status(static_cast<QttyStatus>(status), "Converting deserialized derived quantity");
187 return Quantity<Tag>(conv.value);
188 }
189 return Quantity<Tag>(out_qty.value);
190}
191
192} // namespace derived_serialization
193} // namespace qtty
constexpr double value() const
Definition ffi_core.hpp:193
Core quantity template and error translation utilities.
Quantity< typename ExtractTag< T >::type > from_json(std::string_view json)
Deserialize a JSON string into a compound quantity.
std::string to_json(const Quantity< Tag > &q)
Serialize a compound quantity as JSON.
Quantity< typename ExtractTag< T >::type > from_json_value(std::string_view json)
Deserialize a JSON numeric value into a typed quantity.
std::string to_json(const Quantity< UnitTag > &q)
Serialize a typed quantity as JSON object with value and unit id.
std::string to_json_value(const Quantity< UnitTag > &q)
Serialize only the numeric value as JSON for a typed quantity.
UnitId unit_id_from_u32(uint32_t raw)
Quantity< typename ExtractTag< T >::type > from_json(std::string_view json)
Deserialize a JSON quantity object into a requested target type.
std::string from_owned_c(char *ptr)
Convert an owned C string from FFI into std::string and free it.
constexpr bool is_compound_v
Definition ffi_core.hpp:122
void check_status(QttyStatus status, const char *operation)
Convert qtty FFI status codes into typed C++ exceptions.
Definition ffi_core.hpp:91
int32_t qtty_quantity_from_json_value(UnitId unit, const char *json, qtty_quantity_t *out)
void qtty_string_free(char *s)
int32_t qtty_derived_from_json(const char *json, qtty_derived_quantity_t *out)
int32_t qtty_quantity_from_json(const char *json, qtty_quantity_t *out)
int32_t qtty_quantity_to_json_value(qtty_quantity_t src, char **out_json)
int32_t qtty_derived_to_json(qtty_derived_quantity_t src, char **out)
int32_t qtty_quantity_to_json(qtty_quantity_t src, char **out_json)