FastLED 3.10.8
Loading...
Searching...
No Matches
json_to_type.h
Go to the documentation of this file.
1#pragma once
2
3#include "fl/stl/json.h"
5#include "fl/remote/rpc/json_dispatch.h" // type-erased conversion cores (#3247)
6#include "fl/stl/tuple.h"
8#include "fl/stl/string.h"
9#include "fl/stl/move.h"
10#include "fl/stl/vector.h"
11#include "fl/stl/span.h"
13#include "fl/stl/noexcept.h"
14namespace fl {
15
16// Helper wrapper for const char* parameters in RPC
17// Stores fl::string internally, provides implicit conversion to const char*
20
22 ConstCharPtrWrapper(const fl::string& s) FL_NO_EXCEPT : value(s) {}
24
25 operator const char*() const FL_NO_EXCEPT { return value.c_str(); }
26 const char* c_str() const FL_NO_EXCEPT { return value.c_str(); }
27};
28
29// Helper wrapper for span<const T> parameters in RPC
30// Stores fl::vector<T> internally, provides implicit conversion to span<const T>
31template <typename T>
47
48namespace detail {
49
50// =============================================================================
51// JsonToType - Primary template and specializations using visitor pattern
52// =============================================================================
53
54// Primary template declaration for JsonToType (with two template parameters for SFINAE)
55template <typename T, typename Enable = void>
56struct JsonToType {
58 (void)j;
60 result.setError("unsupported type for JSON conversion");
61 return fl::make_tuple(T(), result);
62 }
63};
64
65// Integer conversion (excluding bool) - dispatches through the shared
66// non-template `json_convert_to_i64_core` (FastLED #3247). Per-T narrowing
67// + overflow detection happen here; the heavy variant-walking lives once
68// in the core.
69template <typename T>
70struct JsonToType<T, typename fl::enable_if<fl::is_integral<T>::value && !fl::is_same<T, bool>::value>::type> {
72 const json_value* val = j.internal_value();
73 if (!val) {
75 result.setError("failed to access JSON value");
76 return fl::make_tuple(T(), result);
77 }
78 fl::i64 raw = 0;
80 T narrowed = static_cast<T>(raw);
81 if (static_cast<fl::i64>(narrowed) != raw) {
82 result.addWarning(fl::string("integer overflow/truncation: ") +
83 fl::to_string(raw) + " converted to " +
84 fl::to_string(static_cast<fl::i64>(narrowed)));
85 }
86 return fl::make_tuple(narrowed, result);
87 }
88};
89
90// Boolean conversion - dispatches through `json_convert_to_bool_core`.
91template <>
92struct JsonToType<bool, void> {
94 const json_value* val = j.internal_value();
95 if (!val) {
97 result.setError("failed to access JSON value");
98 return fl::make_tuple(bool(false), result);
99 }
100 bool out = false;
102 return fl::make_tuple(out, result);
103 }
104};
105
106// Float / double conversion - dispatches through `json_convert_to_float_core`.
107// The core works in float precision (no double soft-FP on no-FPU targets);
108// the per-T narrowing here lifts to double for `T == double` if requested.
109template <typename T>
110struct JsonToType<T, typename fl::enable_if<fl::is_floating_point<T>::value>::type> {
112 const json_value* val = j.internal_value();
113 if (!val) {
115 result.setError("failed to access JSON value");
116 return fl::make_tuple(T(), result);
117 }
118 float raw = 0.0f;
120 return fl::make_tuple(static_cast<T>(raw), result);
121 }
122};
123
124// String conversion - dispatches through `json_convert_to_string_core`.
125template <>
126struct JsonToType<fl::string, void> {
128 const json_value* val = j.internal_value();
129 if (!val) {
131 result.setError("failed to access JSON value");
133 }
134 fl::string out;
136 return fl::make_tuple(out, result);
137 }
138};
139
140// json identity conversion - pass json through unchanged
141// This enables RPC methods to accept fl::json parameters for dynamic typing
142template <>
143struct JsonToType<fl::json, void> {
145 TypeConversionResult result; // Success by default
146 return fl::make_tuple(j, result);
147 }
148};
149
150// ConstCharPtrWrapper conversion - converts JSON string to wrapper
151// The wrapper stores fl::string and converts to const char* on access
152template <>
155 // Reuse the string converter
156 auto stringResult = JsonToType<fl::string>::convert(j);
157 fl::string str = fl::get<0>(stringResult);
158 TypeConversionResult result = fl::get<1>(stringResult);
159
161 }
162};
163
164// ConstSpanWrapper conversion - converts JSON array to vector wrapper
165template <typename T>
166struct JsonToType<fl::ConstSpanWrapper<T>, void> {
169
170 if (!j.is_array()) {
171 result.setError("expected array for span parameter");
173 }
174
175 fl::vector<T> vec;
176 for (fl::size i = 0; i < j.size(); i++) {
177 auto elemResult = JsonToType<T>::convert(j[i]);
178 T elem = fl::get<0>(elemResult);
179 TypeConversionResult elemConvResult = fl::get<1>(elemResult);
180
181 if (elemConvResult.hasError()) {
182 result.setError("element " + fl::to_string(static_cast<fl::i64>(i)) + ": " + elemConvResult.errorMessage());
184 }
185
186 vec.push_back(elem);
187 }
188
190 }
191};
192
193// fl::vector<T> conversion - converts JSON array to vector
194// Works for any T that has a JsonToType specialization
195template <typename T>
196struct JsonToType<fl::vector<T>, void> {
199
200 if (!j.is_array()) {
201 result.setError("expected array for vector parameter");
203 }
204
205 fl::vector<T> vec;
206 for (fl::size i = 0; i < j.size(); i++) {
207 auto elemResult = JsonToType<T>::convert(j[i]);
208 T elem = fl::get<0>(elemResult);
209 TypeConversionResult elemConvResult = fl::get<1>(elemResult);
210
211 if (elemConvResult.hasError()) {
212 result.setError("element " + fl::to_string(static_cast<fl::i64>(i)) + ": " + elemConvResult.errorMessage());
214 }
215
216 vec.push_back(fl::move(elem));
217 }
218
219 return fl::make_tuple(fl::move(vec), result);
220 }
221};
222
223// fl::vector<fl::u8> specialization - accepts base64 string OR integer array.
224// Base64 strings provide compact binary transport for JSON-RPC.
225template <>
226struct JsonToType<fl::vector<fl::u8>, void> {
229
230 if (j.is_string()) {
231 // Decode base64 string to binary
232 fl::string encoded;
233 auto strResult = JsonToType<fl::string>::convert(j);
234 encoded = fl::get<0>(strResult);
235 TypeConversionResult strConvResult = fl::get<1>(strResult);
236 if (strConvResult.hasError()) {
237 return fl::make_tuple(fl::vector<fl::u8>(), strConvResult);
238 }
239 fl::vector<fl::u8> decoded = fl::base64_decode(encoded);
240 if (decoded.empty() && !encoded.empty()) {
241 result.setError("invalid base64 string");
243 }
244 return fl::make_tuple(fl::move(decoded), result);
245 }
246
247 if (j.is_array()) {
248 // Fall back to integer array
250 for (fl::size i = 0; i < j.size(); i++) {
251 auto elemResult = JsonToType<fl::u8>::convert(j[i]);
252 fl::u8 elem = fl::get<0>(elemResult);
253 TypeConversionResult elemConvResult = fl::get<1>(elemResult);
254 if (elemConvResult.hasError()) {
255 result.setError("element " + fl::to_string(static_cast<fl::i64>(i)) + ": " + elemConvResult.errorMessage());
257 }
258 vec.push_back(elem);
259 }
260 return fl::make_tuple(fl::move(vec), result);
261 }
262
263 result.setError("expected base64 string or integer array for byte vector");
265 }
266};
267
268} // namespace detail
269} // namespace fl
bool hasError() const FL_NO_EXCEPT
const fl::string & errorMessage() const FL_NO_EXCEPT
bool empty() const FL_NO_EXCEPT
bool empty() const FL_NO_EXCEPT
void push_back(const T &value) FL_NO_EXCEPT
Definition vector.h:624
FastLED's Elegant JSON Library: fl::json
constexpr remove_reference< T >::type && move(T &&t) FL_NO_EXCEPT
Definition move.h:30
tuple< typename fl::decay< Ts >::type... > make_tuple(Ts &&... args) FL_NO_EXCEPT
Definition tuple.h:106
FL_NO_INLINE TypeConversionResult json_convert_to_i64_core(const json_value &val, fl::i64 *out_value) FL_NO_EXCEPT
FL_NO_INLINE TypeConversionResult json_convert_to_float_core(const json_value &val, float *out_value) FL_NO_EXCEPT
FL_NO_INLINE TypeConversionResult json_convert_to_bool_core(const json_value &val, bool *out_value) FL_NO_EXCEPT
FL_NO_INLINE TypeConversionResult json_convert_to_string_core(const json_value &val, fl::string *out_value) FL_NO_EXCEPT
unsigned char u8
Definition stdint.h:131
u8 u8 fract8 blur_amount FL_NO_EXCEPT
Definition blur.h:186
fl::vector< fl::u8 > base64_decode(const fl::string &encoded) FL_NO_EXCEPT
expected< T, E > result
Alias for expected (Rust-style naming)
Definition result.h:31
string to_string(T value) FL_NO_EXCEPT
Definition string.h:459
pair_element< I, T1, T2 >::type & get(pair< T1, T2 > &p) FL_NO_EXCEPT
Definition pair.h:119
Base definition for an LED controller.
Definition crgb.hpp:179
const char * c_str() const FL_NO_EXCEPT
ConstCharPtrWrapper(fl::string &&s) FL_NO_EXCEPT
ConstCharPtrWrapper() FL_NO_EXCEPT=default
ConstSpanWrapper() FL_NO_EXCEPT=default
ConstSpanWrapper(const fl::vector< T > &v) FL_NO_EXCEPT
fl::span< const T > get() const FL_NO_EXCEPT
fl::vector< int > value
static fl::tuple< T, TypeConversionResult > convert(const json &j) FL_NO_EXCEPT
static fl::tuple< bool, TypeConversionResult > convert(const json &j) FL_NO_EXCEPT
static fl::tuple< fl::ConstCharPtrWrapper, TypeConversionResult > convert(const json &j) FL_NO_EXCEPT
static fl::tuple< fl::ConstSpanWrapper< T >, TypeConversionResult > convert(const json &j) FL_NO_EXCEPT
static fl::tuple< fl::json, TypeConversionResult > convert(const json &j) FL_NO_EXCEPT
static fl::tuple< fl::string, TypeConversionResult > convert(const json &j) FL_NO_EXCEPT
static fl::tuple< fl::vector< T >, TypeConversionResult > convert(const json &j) FL_NO_EXCEPT
static fl::tuple< fl::vector< fl::u8 >, TypeConversionResult > convert(const json &j) FL_NO_EXCEPT
static fl::tuple< T, TypeConversionResult > convert(const json &j) FL_NO_EXCEPT