30#if FL_PLATFORM_HAS_LARGE_MEMORY
38#if FL_PLATFORM_HAS_LARGE_MEMORY
39 mResponseStreamSink =
fl::move(sink);
60#if FL_PLATFORM_HAS_LARGE_MEMORY
85#if FL_PLATFORM_HAS_LARGE_MEMORY
91 entry.mIsStreaming =
true;
120#if FL_PLATFORM_HAS_LARGE_MEMORY
121# define FL_RPC_ERR_NO_METHOD "Invalid Request: missing 'method'"
122# define FL_RPC_ERR_METHOD_NOT_STR "Invalid Request: 'method' must be a string"
123# define FL_RPC_ERR_METHOD_NOT_FOUND_PREFIX "Method not found: "
124# define FL_RPC_ERR_PARAMS_NOT_ARRAY "Invalid params: must be an array"
125# define FL_RPC_ERR_INVALID_PARAMS_PREFIX "Invalid params: "
127# define FL_RPC_ERR_NO_METHOD "method"
128# define FL_RPC_ERR_METHOD_NOT_STR "method"
129# define FL_RPC_ERR_METHOD_NOT_FOUND_PREFIX "404: "
130# define FL_RPC_ERR_PARAMS_NOT_ARRAY "params"
131# define FL_RPC_ERR_INVALID_PARAMS_PREFIX "params: "
136 if (!request.contains(
"method")) {
137 FL_ERROR(
"RPC: Invalid Request - missing 'method' field");
141 auto methodOpt = request[
"method"].as_string();
142 if (!methodOpt.has_value()) {
143 FL_ERROR(
"RPC: Invalid Request - 'method' must be a string");
148#if FL_PLATFORM_HAS_LARGE_MEMORY
155 if (methodName ==
"rpc.discover") {
160 const bool scheduled = request.contains(
"timestamp") &&
161 request[
"timestamp"].as_int().value_or(0) != 0;
162 if (!scheduled && mResponseStreamSink &&
163 (!mResponseStreamAvailable || mResponseStreamAvailable())) {
165 const bool includeId = request.contains(
"id");
166 const Rpc* self =
this;
171 self->writeSchema(resultWriter);
175 skip.set(
"noEnqueue",
true);
179 response.set(
"jsonrpc",
"2.0");
180 response.set(
"result",
schema());
181 if (request.contains(
"id")) {
182 response.set(
"id", request[
"id"]);
191 FL_WARN(
"RPC: Method not found: " << methodName.
c_str());
198 FL_ERROR(
"RPC: Invalid params - must be an array for method: " << methodName.
c_str());
204#if FL_PLATFORM_HAS_LARGE_MEMORY
205 if (entry.mIsStreaming) {
206 if (!mResponseStreamSink) {
213 const bool includeId = request.contains(
"id");
221 streamFn(resultWriter, paramsCopy,
requestId);
229 skip.set(
"noEnqueue",
true);
241 ack.
set(
"jsonrpc",
"2.0");
242 ack.
set(
"id", request[
"id"]);
245 ackResult.
set(
"acknowledged",
true);
246 ack.
set(
"result", ackResult);
249 FL_DBG(
"RPC: Sent ACK for async method: " << methodName.
c_str());
255#if FL_PLATFORM_HAS_LARGE_MEMORY
257 if (isResponseAware) {
269 resultTuple = entry.
mInvoker->invoke(params);
275 resultTuple = entry.
mInvoker->invoke(params);
282 if (!convResult.
ok()) {
289 response.set(
"jsonrpc",
"2.0");
290 response.set(
"result", returnVal);
293 if (request.contains(
"id")) {
294 response.set(
"id", request[
"id"]);
297#if FL_PLATFORM_HAS_LARGE_MEMORY
305 for (fl::size i = 0; i < convResult.
warnings().size(); ++i) {
308 response.set(
"warnings", warnings);
316 response.set(
"ackOnly",
true);
329 if (!request.contains(
"id")) {
331 if (request.contains(
"method")) {
332 auto methodOpt = request[
"method"].as_string();
333 if (methodOpt.has_value()) {
339 it->second.mInvoker->invoke(params);
357 for (fl::size i = 0; i < it->second.mTags.size(); ++i) {
359 for (fl::size j = 0; j <
result.size(); ++j) {
360 if (
result[j] == it->second.mTags[i]) {
366 result.push_back(it->second.mTags[i]);
382 methodTuple.
push_back(it->first.c_str());
383 methodTuple.
push_back(it->second.mSchemaGenerator->resultTypeName());
384 methodTuple.
push_back(it->second.mSchemaGenerator->params());
387 const char* modeStr = (it->second.mMode ==
RpcMode::ASYNC) ?
"async" :
"sync";
405#if FL_PLATFORM_HAS_LARGE_MEMORY
407 writer.beginObject();
408 writer.key(
"schema");
410 for (
auto it = mRegistry.begin(); it != mRegistry.end(); ++it) {
412 writer.value(it->first.c_str());
413 writer.value(it->second.mSchemaGenerator->resultTypeName());
414 writer.value(it->second.mSchemaGenerator->params());
415 writer.value((it->second.mMode ==
RpcMode::ASYNC) ?
"async" :
"sync");
void valueNull() FL_NO_EXCEPT
Helper class for sending responses in async/streaming RPC methods.
void bindAsync(const char *name, fl::function< void(ResponseSend &, const json &)> fn, fl::RpcMode mode=fl::RpcMode::ASYNC) FL_NO_EXCEPT
Bind async method with ResponseSend& parameter (for ASYNC/ASYNC_STREAM modes) Signature: void(Respons...
fl::unordered_map< fl::string, detail::RpcEntry > mRegistry
void bindStreaming(const char *name, fl::StreamingRpcHandler fn) FL_NO_EXCEPT
Bind a method that streams its JSON-RPC result directly to the transport.
fl::optional< json > handle_maybe(const json &request) FL_NO_EXCEPT
For notifications (no id), returns nullopt.
void setResponseStreamSink(fl::ResponseStreamSink sink) FL_NO_EXCEPT
Set response stream sink for methods registered with bindStreaming().
Rpc() FL_NO_EXCEPT=default
fl::function< void(const fl::json &)> mResponseSink
json schema() const FL_NO_EXCEPT
Returns flat schema document.
json handle(const json &request) FL_NO_EXCEPT
Process a JSON-RPC request.
void setResponseSink(fl::function< void(const fl::json &)> sink) FL_NO_EXCEPT
Set response sink for sending ACK responses (used by async functions)
fl::vector< fl::string > tags() const FL_NO_EXCEPT
Returns list of unique tags used across all methods.
void setResponseStreamAvailable(fl::function< bool()> available) FL_NO_EXCEPT
Reports whether the stream sink currently reaches a transport.
json methods() const FL_NO_EXCEPT
Returns flat method array: [["name", "returnType", [["param1", "type1"], ...]], .....
bool hasWarning() const FL_NO_EXCEPT
const fl::vector< fl::string > & warnings() const FL_NO_EXCEPT
const fl::string & errorMessage() const FL_NO_EXCEPT
static TypeConversionResult success() FL_NO_EXCEPT
bool ok() const FL_NO_EXCEPT
const char * c_str() const FL_NO_EXCEPT
void set(const fl::string &key, const json &value) FL_NO_EXCEPT
bool is_array() const FL_NO_EXCEPT
void push_back(const json &value) FL_NO_EXCEPT
bool contains(size_t idx) const FL_NO_EXCEPT
static json parse(const fl::string &txt) FL_NO_EXCEPT
static json object() FL_NO_EXCEPT
static json array() FL_NO_EXCEPT
FastLED's Elegant JSON Library: fl::json
Centralized logging categories for FastLED hardware interfaces and subsystems.
tuple< typename fl::decay< Ts >::type... > make_tuple(Ts &&... args) FL_NO_EXCEPT
auto invoke(F &&f, T1 &&t1, Args &&... args) FL_NO_EXCEPT -> enable_if_t< is_member_function_pointer< typename remove_reference< F >::type >::value &&!detail::use_pointer_syntax< T1 >::value, decltype((fl::forward< T1 >(t1).*f)(fl::forward< Args >(args)...))>
shared_ptr< T > make_shared(Args &&... args) FL_NO_EXCEPT
json makeJsonRpcError(int code, const fl::string &message, const json &id) FL_NO_EXCEPT
fl::shared_ptr< ErasedInvoker > mInvoker
fl::function< void(ResponseSend &, const json &)> mResponseAwareFn
fl::shared_ptr< ErasedSchemaGenerator > mSchemaGenerator
fl::vector< fl::string > mTags
constexpr remove_reference< T >::type && move(T &&t) FL_NO_EXCEPT
u8 u8 fract8 blur_amount FL_NO_EXCEPT
fl::function< void(fl::JsonStreamWriter &, const fl::json &, const fl::json &)> StreamingRpcHandler
expected< T, E > result
Alias for expected (Rust-style naming)
void streamJsonRpcResultEnvelope(fl::JsonStreamWriter &writer, const fl::json &requestId, bool includeId, const fl::JsonStreamCallback &writeResult) FL_NO_EXCEPT
constexpr nullopt_t nullopt
pair_element< I, T1, T2 >::type & get(pair< T1, T2 > &p) FL_NO_EXCEPT
fl::function< void(fl::JsonStreamCallback)> ResponseStreamSink
int available() FL_NO_EXCEPT
Base definition for an LED controller.
#define FL_RPC_ERR_NO_METHOD
#define FL_RPC_ERR_INVALID_PARAMS_PREFIX
#define FL_RPC_ERR_METHOD_NOT_STR
#define FL_RPC_ERR_PARAMS_NOT_ARRAY
#define FL_RPC_ERR_METHOD_NOT_FOUND_PREFIX