66
77#pragma once
88
9+ #include < charconv>
910#include < string>
1011#include < string_view>
1112#include < vector>
1213#include < functional>
1314#include < exception>
15+ #include < system_error>
1416#include < unordered_map>
17+ #include < utility>
1518
1619#include < nlohmann/json.hpp>
1720
@@ -47,38 +50,82 @@ namespace foundry_local::detail {
4750 return core->call (command, logger, &payload, callback, userData);
4851 }
4952
53+ inline bool TryParseFloatToken (std::string_view token, float & value) {
54+ if (token.empty ()) {
55+ return false ;
56+ }
57+
58+ const auto * begin = token.data ();
59+ const auto * end = begin + token.size ();
60+ const auto result = std::from_chars (begin, end, value);
61+ return result.ec == std::errc{} && result.ptr == end;
62+ }
63+
64+ inline bool TryParseDoubleToken (std::string_view token, double & value) {
65+ if (token.empty ()) {
66+ return false ;
67+ }
68+
69+ const auto * begin = token.data ();
70+ const auto * end = begin + token.size ();
71+ const auto result = std::from_chars (begin, end, value);
72+ return result.ec == std::errc{} && result.ptr == end;
73+ }
74+
5075 // Serialize + call with a streaming chunk handler.
5176 // Wraps the caller-supplied onChunk with the native callback boilerplate
52- // (null/length checks, exception capture, rethrow after the call).
77+ // (null/length checks, exception capture, cancellation, rethrow after the call).
5378 // The errorContext string is used to prefix any core-layer error message.
5479 inline CoreResponse CallWithStreamingCallback (Internal::IFoundryLocalCore* core, std::string_view command,
55- const std::string& payload, ILogger& logger,
56- const std::function<void (const std::string&)>& onChunk,
57- std::string_view errorContext) {
80+ const std::string* payload, ILogger& logger,
81+ const std::function<bool (const std::string&)>& onChunk,
82+ std::string_view errorContext,
83+ CancellationCallback isCancellationRequested = nullptr) {
5884 struct State {
59- const std::function<void (const std::string&)>* cb;
85+ const std::function<bool (const std::string&)>* cb;
86+ CancellationCallback isCancellationRequested;
87+ bool cancellationObserved = false ;
6088 std::exception_ptr exception;
61- } state{&onChunk, nullptr };
89+ } state{&onChunk, std::move (isCancellationRequested), false , nullptr };
6290
63- auto nativeCallback = [](void * data, int32_t len, void * user) -> int {
64- if (!data || len <= 0 )
91+ auto nativeCallback = [](const void * data, int32_t len, void * user) -> int32_t {
92+ auto * st = static_cast <State*>(user);
93+ if (!st) {
6594 return 0 ;
95+ }
6696
67- auto * st = static_cast <State*>(user);
68- if (st->exception )
97+ if (st->exception || st->cancellationObserved ) {
98+ return 1 ;
99+ }
100+
101+ if (!data || len <= 0 )
69102 return 0 ;
70103
71104 try {
105+ if (st->isCancellationRequested && st->isCancellationRequested ()) {
106+ st->cancellationObserved = true ;
107+ return 1 ;
108+ }
109+
72110 std::string chunk (static_cast <const char *>(data), static_cast <size_t >(len));
73- (*(st->cb ))(chunk);
111+ if (!(*(st->cb ))(chunk)) {
112+ st->cancellationObserved = true ;
113+ return 1 ;
114+ }
74115 }
75116 catch (...) {
76117 st->exception = std::current_exception ();
118+ return 1 ;
77119 }
120+
78121 return 0 ;
79122 };
80123
81- auto response = core->call (command, logger, &payload, +nativeCallback, &state);
124+ auto response = core->call (command, logger, payload, +nativeCallback, &state);
125+ if (state.cancellationObserved ) {
126+ throw Exception (" Operation cancelled" , logger);
127+ }
128+
82129 if (response.HasError ()) {
83130 throw Exception (std::string (errorContext) + response.error , logger);
84131 }
@@ -90,6 +137,38 @@ namespace foundry_local::detail {
90137 return response;
91138 }
92139
140+ inline CoreResponse CallWithStreamingCallback (Internal::IFoundryLocalCore* core, std::string_view command,
141+ const std::string* payload, ILogger& logger,
142+ const std::function<void (const std::string&)>& onChunk,
143+ std::string_view errorContext,
144+ CancellationCallback isCancellationRequested = nullptr) {
145+ const std::function<bool (const std::string&)> continuingOnChunk =
146+ [&onChunk](const std::string& chunk) {
147+ onChunk (chunk);
148+ return true ;
149+ };
150+ return CallWithStreamingCallback (core, command, payload, logger, continuingOnChunk, errorContext,
151+ std::move (isCancellationRequested));
152+ }
153+
154+ inline CoreResponse CallWithStreamingCallback (Internal::IFoundryLocalCore* core, std::string_view command,
155+ const std::string& payload, ILogger& logger,
156+ const std::function<bool (const std::string&)>& onChunk,
157+ std::string_view errorContext,
158+ CancellationCallback isCancellationRequested = nullptr) {
159+ return CallWithStreamingCallback (core, command, &payload, logger, onChunk, errorContext,
160+ std::move (isCancellationRequested));
161+ }
162+
163+ inline CoreResponse CallWithStreamingCallback (Internal::IFoundryLocalCore* core, std::string_view command,
164+ const std::string& payload, ILogger& logger,
165+ const std::function<void (const std::string&)>& onChunk,
166+ std::string_view errorContext,
167+ CancellationCallback isCancellationRequested = nullptr) {
168+ return CallWithStreamingCallback (core, command, &payload, logger, onChunk, errorContext,
169+ std::move (isCancellationRequested));
170+ }
171+
93172 // Overload: allow Params object directly
94173 inline CoreResponse CallWithParams (Internal::IFoundryLocalCore* core, std::string_view command,
95174 const nlohmann::json& params, ILogger& logger) {
0 commit comments