From 1169408da1795db2a2f350ca11ed2a58532bd668 Mon Sep 17 00:00:00 2001 From: Jackson Coxson Date: Fri, 29 Aug 2025 14:19:28 -0600 Subject: [PATCH] Remove cpp 17 features and implement Rust into CPP --- cpp/.clang-format | 4 + cpp/examples/CMakeLists.txt | 2 +- cpp/examples/app_service.cpp | 145 ++++---- cpp/examples/debug_proxy.cpp | 105 +++--- cpp/examples/diagnosticsservice.cpp | 135 ++++---- cpp/examples/idevice_id.cpp | 23 +- cpp/examples/ideviceinfo.cpp | 73 +++-- cpp/examples/location_simulation.cpp | 105 +++--- cpp/idevice++.xcodeproj/project.pbxproj | 24 ++ cpp/include/idevice++/adapter_stream.hpp | 14 +- cpp/include/idevice++/app_service.hpp | 76 ++--- cpp/include/idevice++/core_device_proxy.hpp | 63 ++-- cpp/include/idevice++/debug_proxy.hpp | 36 +- cpp/include/idevice++/diagnosticsservice.hpp | 25 +- cpp/include/idevice++/ffi.hpp | 5 +- cpp/include/idevice++/idevice.hpp | 18 +- cpp/include/idevice++/location_simulation.hpp | 8 +- cpp/include/idevice++/lockdown.hpp | 12 +- cpp/include/idevice++/option.hpp | 184 +++++++++++ cpp/include/idevice++/pairing_file.hpp | 12 +- cpp/include/idevice++/provider.hpp | 42 ++- cpp/include/idevice++/remote_server.hpp | 6 +- cpp/include/idevice++/result.hpp | 231 +++++++++++++ cpp/include/idevice++/rsd.hpp | 12 +- cpp/include/idevice++/tcp_object_stack.hpp | 45 +-- cpp/include/idevice++/usbmuxd.hpp | 63 ++-- cpp/src/adapter_stream.cpp | 67 ++-- cpp/src/app_service.cpp | 310 +++++++++--------- cpp/src/core_device.cpp | 165 +++++----- cpp/src/debug_proxy.cpp | 206 ++++++------ cpp/src/diagnosticsservice.cpp | 74 +++-- cpp/src/ffi.cpp | 15 + cpp/src/idevice.cpp | 57 ++-- cpp/src/location_simulation.cpp | 30 +- cpp/src/lockdown.cpp | 61 ++-- cpp/src/pairing_file.cpp | 36 +- cpp/src/provider.cpp | 63 ++-- cpp/src/remote_server.cpp | 21 +- cpp/src/rsd.cpp | 82 ++--- cpp/src/tcp_callback_feeder.cpp | 61 ++-- cpp/src/usbmuxd.cpp | 134 ++++---- 41 files changed, 1638 insertions(+), 1212 deletions(-) create mode 100644 cpp/include/idevice++/option.hpp create mode 100644 cpp/include/idevice++/result.hpp diff --git a/cpp/.clang-format b/cpp/.clang-format index b084d5a..4491888 100644 --- a/cpp/.clang-format +++ b/cpp/.clang-format @@ -26,3 +26,7 @@ ReflowComments: true PointerAlignment: Left BinPackArguments: false BinPackParameters: false +AllowShortBlocksOnASingleLine: Never +MacroBlockBegin: "^#define" +MacroBlockEnd: "^#undef" +InsertBraces: true diff --git a/cpp/examples/CMakeLists.txt b/cpp/examples/CMakeLists.txt index f832b74..bce540a 100644 --- a/cpp/examples/CMakeLists.txt +++ b/cpp/examples/CMakeLists.txt @@ -3,7 +3,7 @@ cmake_minimum_required(VERSION 3.15) project(IdeviceFFI CXX) -set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_EXPORT_COMPILE_COMMANDS ON) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) diff --git a/cpp/examples/app_service.cpp b/cpp/examples/app_service.cpp index 6b5c861..c9cf136 100644 --- a/cpp/examples/app_service.cpp +++ b/cpp/examples/app_service.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -16,8 +17,9 @@ using namespace IdeviceFFI; +[[noreturn]] static void die(const char* msg, const FfiError& e) { - std::cerr << msg << ": " << e.message << "\n"; + std::cerr << msg << ": " << e.message << "(" << e.code << ")\n"; std::exit(1); } @@ -42,26 +44,25 @@ int main(int argc, char** argv) { FfiError err; // 1) Connect to usbmuxd and pick first device - auto mux = UsbmuxdConnection::default_new(/*tag*/ 0, err); - if (!mux) - die("failed to connect to usbmuxd", err); + auto mux = UsbmuxdConnection::default_new(/*tag*/ 0); + if_let_err(mux, err, { die("failed to connect to usbmuxd", err); }); - auto devices = mux->get_devices(err); - if (!devices) - die("failed to list devices", err); - if (devices->empty()) { + auto devices_res = mux.unwrap().get_devices(); + if_let_err(devices_res, err, { die("failed to list devices", err); }); + auto& devices = devices_res.unwrap(); + if (devices.empty()) { std::cerr << "no devices connected\n"; return 1; } - auto& dev = (*devices)[0]; + auto& dev = (devices)[0]; auto udid = dev.get_udid(); - if (!udid) { + if (udid.is_none()) { std::cerr << "device has no UDID\n"; return 1; } auto mux_id = dev.get_id(); - if (!mux_id) { + if (mux_id.is_none()) { std::cerr << "device has no mux id\n"; return 1; } @@ -72,53 +73,49 @@ int main(int argc, char** argv) { const uint32_t tag = 0; const std::string label = "app_service-jkcoxson"; - auto provider = Provider::usbmuxd_new(std::move(addr), tag, *udid, *mux_id, label, err); - if (!provider) - die("failed to create provider", err); + auto provider_res = + Provider::usbmuxd_new(std::move(addr), tag, udid.unwrap(), mux_id.unwrap(), label); + if_let_err(provider_res, err, { die("failed to create provider", err); }); + auto& provider = provider_res.unwrap(); // 3) CoreDeviceProxy - auto cdp = CoreDeviceProxy::connect(*provider, err); - if (!cdp) - die("failed to connect CoreDeviceProxy", err); + auto cdp = CoreDeviceProxy::connect(provider).unwrap_or_else( + [](FfiError e) -> CoreDeviceProxy { die("failed to connect CoreDeviceProxy", e); }); - auto rsd_port = cdp->get_server_rsd_port(err); - if (!rsd_port) - die("failed to get server RSD port", err); + auto rsd_port = cdp.get_server_rsd_port().unwrap_or_else( + [](FfiError err) -> uint16_t { die("failed to get server RSD port", err); }); // 4) Create software tunnel adapter (consumes proxy) - auto adapter = std::move(*cdp).create_tcp_adapter(err); - if (!adapter) - die("failed to create software tunnel adapter", err); + auto adapter = std::move(cdp).create_tcp_adapter(); + if_let_err(adapter, err, { die("failed to create software tunnel adapter", err); }); // 5) Connect adapter to RSD → ReadWrite stream - auto stream = adapter->connect(*rsd_port, err); - if (!stream) - die("failed to connect RSD stream", err); + auto stream = adapter.unwrap().connect(rsd_port); + if_let_err(stream, err, { die("failed to connect RSD stream", err); }); // 6) RSD handshake (consumes stream) - auto rsd = RsdHandshake::from_socket(std::move(*stream), err); - if (!rsd) - die("failed RSD handshake", err); + auto rsd = RsdHandshake::from_socket(std::move(stream.unwrap())); + if_let_err(rsd, err, { die("failed RSD handshake", err); }); // 7) AppService over RSD (borrows adapter + handshake) - auto app = AppService::connect_rsd(*adapter, *rsd, err); - if (!app) - die("failed to connect AppService", err); + auto app = AppService::connect_rsd(adapter.unwrap(), rsd.unwrap()) + .unwrap_or_else([&](FfiError e) -> AppService { + die("failed to connect AppService", e); // never returns + }); // 8) Commands if (cmd == "list") { - auto apps = app->list_apps(/*app_clips*/ true, - /*removable*/ true, - /*hidden*/ true, - /*internal*/ true, - /*default_apps*/ true, - err); - if (!apps) - die("list_apps failed", err); + auto apps = app.list_apps(/*app_clips*/ true, + /*removable*/ true, + /*hidden*/ true, + /*internal*/ true, + /*default_apps*/ true) + .unwrap_or_else( + [](FfiError e) -> std::vector { die("list_apps failed", e); }); - for (const auto& a : *apps) { - std::cout << "- " << a.bundle_identifier << " | name=" << a.name - << " | version=" << (a.version ? *a.version : std::string("")) + for (const auto& a : apps) { + std::cout << "- " << a.bundle_identifier << " | name=" << a.name << " | version=" + << (a.version.is_some() ? a.version.unwrap() : std::string("")) << " | dev=" << (a.is_developer_app ? "y" : "n") << " | hidden=" << (a.is_hidden ? "y" : "n") << "\n"; } @@ -132,27 +129,27 @@ int main(int argc, char** argv) { std::string bundle_id = argv[2]; std::vector args; // empty in this example - auto resp = app->launch(bundle_id, - args, - /*kill_existing*/ false, - /*start_suspended*/ false, - err); - if (!resp) - die("launch failed", err); + auto resp = + app.launch(bundle_id, + args, + /*kill_existing*/ false, + /*start_suspended*/ false) + .unwrap_or_else([](FfiError e) -> LaunchResponse { die("launch failed", e); }); - std::cout << "Launched pid=" << resp->pid << " exe=" << resp->executable_url - << " piv=" << resp->process_identifier_version - << " audit_token_len=" << resp->audit_token.size() << "\n"; + std::cout << "Launched pid=" << resp.pid << " exe=" << resp.executable_url + << " piv=" << resp.process_identifier_version + << " audit_token_len=" << resp.audit_token.size() << "\n"; return 0; } else if (cmd == "processes") { - auto procs = app->list_processes(err); - if (!procs) - die("list_processes failed", err); + auto procs = app.list_processes().unwrap_or_else( + [](FfiError e) -> std::vector { die("list_processes failed", e); }); - for (const auto& p : *procs) { + for (const auto& p : procs) { std::cout << p.pid << " : " - << (p.executable_url ? *p.executable_url : std::string("")) << "\n"; + << (p.executable_url.is_some() ? p.executable_url.unwrap() + : std::string("")) + << "\n"; } return 0; @@ -163,8 +160,7 @@ int main(int argc, char** argv) { } std::string bundle_id = argv[2]; - if (!app->uninstall(bundle_id, err)) - die("uninstall failed", err); + if_let_err(app.uninstall(bundle_id), err, { die("uninstall failed", err); }); std::cout << "Uninstalled " << bundle_id << "\n"; return 0; @@ -176,13 +172,14 @@ int main(int argc, char** argv) { uint32_t pid = static_cast(std::stoul(argv[2])); uint32_t signal = static_cast(std::stoul(argv[3])); - auto res = app->send_signal(pid, signal, err); - if (!res) - die("send_signal failed", err); + auto res = app.send_signal(pid, signal).unwrap_or_else([](FfiError e) -> SignalResponse { + die("send_signal failed", e); + }); - std::cout << "Signaled pid=" << res->pid << " signal=" << res->signal - << " ts_ms=" << res->device_timestamp_ms - << " exe=" << (res->executable_url ? *res->executable_url : std::string("")) + std::cout << "Signaled pid=" << res.pid << " signal=" << res.signal + << " ts_ms=" << res.device_timestamp_ms << " exe=" + << (res.executable_url.is_some() ? res.executable_url.unwrap() + : std::string("")) << "\n"; return 0; @@ -196,22 +193,22 @@ int main(int argc, char** argv) { float hw = (argc >= 5) ? std::stof(argv[4]) : 1.0f; float scale = (argc >= 6) ? std::stof(argv[5]) : 1.0f; - auto icon = app->fetch_icon(bundle_id, hw, hw, scale, /*allow_placeholder*/ true, err); - if (!icon) - die("fetch_app_icon failed", err); + auto icon = + app.fetch_icon(bundle_id, hw, hw, scale, /*allow_placeholder*/ true) + .unwrap_or_else([](FfiError e) -> IconData { die("fetch_app_icon failed", e); }); std::ofstream out(save_path, std::ios::binary); if (!out) { std::cerr << "Failed to open " << save_path << " for writing\n"; return 1; } - out.write(reinterpret_cast(icon->data.data()), - static_cast(icon->data.size())); + out.write(reinterpret_cast(icon.data.data()), + static_cast(icon.data.size())); out.close(); - std::cout << "Saved icon to " << save_path << " (" << icon->data.size() << " bytes, " - << icon->icon_width << "x" << icon->icon_height << ", min " << icon->minimum_width - << "x" << icon->minimum_height << ")\n"; + std::cout << "Saved icon to " << save_path << " (" << icon.data.size() << " bytes, " + << icon.icon_width << "x" << icon.icon_height << ", min " << icon.minimum_width + << "x" << icon.minimum_height << ")\n"; return 0; } else { diff --git a/cpp/examples/debug_proxy.cpp b/cpp/examples/debug_proxy.cpp index 55a24c4..182b85d 100644 --- a/cpp/examples/debug_proxy.cpp +++ b/cpp/examples/debug_proxy.cpp @@ -8,10 +8,14 @@ #include #include #include +#include #include #include #include +using namespace IdeviceFFI; + +[[noreturn]] static void die(const char* msg, const IdeviceFFI::FfiError& e) { std::cerr << msg << ": " << e.message << "\n"; std::exit(1); @@ -21,8 +25,9 @@ static std::vector split_args(const std::string& line) { std::istringstream iss(line); std::vector toks; std::string tok; - while (iss >> tok) + while (iss >> tok) { toks.push_back(tok); + } return toks; } @@ -30,103 +35,99 @@ int main() { IdeviceFFI::FfiError err; // 1) usbmuxd → pick first device - auto mux = IdeviceFFI::UsbmuxdConnection::default_new(/*tag*/ 0, err); - if (!mux) - die("failed to connect to usbmuxd", err); + auto mux = IdeviceFFI::UsbmuxdConnection::default_new(/*tag*/ 0); + if_let_err(mux, err, { die("failed to connect to usbmuxd", err); }); - auto devices = mux->get_devices(err); - if (!devices) - die("failed to list devices", err); - if (devices->empty()) { + auto devices = mux.unwrap().get_devices(); + if_let_err(devices, err, { die("failed to list devices", err); }); + if (devices.unwrap().empty()) { std::cerr << "no devices connected\n"; return 1; } - auto& dev = (*devices)[0]; + auto& dev = (devices.unwrap())[0]; auto udid = dev.get_udid(); - if (!udid) { + if (udid.is_none()) { std::cerr << "device has no UDID\n"; return 1; } auto mux_id = dev.get_id(); - if (!mux_id) { + if (mux_id.is_none()) { std::cerr << "device has no mux id\n"; return 1; } // 2) Provider via default usbmuxd addr - auto addr = IdeviceFFI::UsbmuxdAddr::default_new(); + auto addr = IdeviceFFI::UsbmuxdAddr::default_new(); - const uint32_t tag = 0; - const std::string label = "debug-proxy-jkcoxson"; - auto provider = - IdeviceFFI::Provider::usbmuxd_new(std::move(addr), tag, *udid, *mux_id, label, err); - if (!provider) - die("failed to create provider", err); + const uint32_t tag = 0; + const std::string label = "debug-proxy-jkcoxson"; + auto provider = IdeviceFFI::Provider::usbmuxd_new( + std::move(addr), tag, udid.unwrap(), mux_id.unwrap(), label); + if_let_err(provider, err, { die("failed to create provider", err); }); // 3) CoreDeviceProxy - auto cdp = IdeviceFFI::CoreDeviceProxy::connect(*provider, err); - if (!cdp) - die("failed CoreDeviceProxy connect", err); + auto cdp = CoreDeviceProxy::connect(provider.unwrap()) + .unwrap_or_else([](FfiError e) -> CoreDeviceProxy { + die("failed to connect CoreDeviceProxy", e); + }); - auto rsd_port = cdp->get_server_rsd_port(err); - if (!rsd_port) - die("failed to get RSD port", err); + auto rsd_port = cdp.get_server_rsd_port().unwrap_or_else( + [](FfiError err) -> uint16_t { die("failed to get server RSD port", err); }); - // 4) Software tunnel → stream - auto adapter = std::move(*cdp).create_tcp_adapter(err); - if (!adapter) - die("failed to create software tunnel adapter", err); + // 4) Create software tunnel adapter (consumes proxy) + auto adapter = std::move(cdp).create_tcp_adapter(); + if_let_err(adapter, err, { die("failed to create software tunnel adapter", err); }); - auto stream = adapter->connect(*rsd_port, err); - if (!stream) - die("failed to connect RSD stream", err); + // 5) Connect adapter to RSD → ReadWrite stream + auto stream = adapter.unwrap().connect(rsd_port); + if_let_err(stream, err, { die("failed to connect RSD stream", err); }); - // 5) RSD handshake - auto rsd = IdeviceFFI::RsdHandshake::from_socket(std::move(*stream), err); - if (!rsd) - die("failed RSD handshake", err); + // 6) RSD handshake (consumes stream) + auto rsd = RsdHandshake::from_socket(std::move(stream.unwrap())); + if_let_err(rsd, err, { die("failed RSD handshake", err); }); // 6) DebugProxy over RSD - auto dbg = IdeviceFFI::DebugProxy::connect_rsd(*adapter, *rsd, err); - if (!dbg) - die("failed to connect DebugProxy", err); + auto dbg_res = IdeviceFFI::DebugProxy::connect_rsd(adapter.unwrap(), rsd.unwrap()); + if_let_err(dbg_res, err, { die("failed to connect DebugProxy", err); }); + auto& dbg = dbg_res.unwrap(); std::cout << "Shell connected! Type 'exit' to quit.\n"; for (;;) { std::cout << "> " << std::flush; std::string line; - if (!std::getline(std::cin, line)) + if (!std::getline(std::cin, line)) { break; + } // trim auto first = line.find_first_not_of(" \t\r\n"); - if (first == std::string::npos) + if (first == std::string::npos) { continue; + } auto last = line.find_last_not_of(" \t\r\n"); line = line.substr(first, last - first + 1); - if (line == "exit") + if (line == "exit") { break; + } // Interpret: first token = command name, rest = argv auto toks = split_args(line); - if (toks.empty()) + if (toks.empty()) { continue; + } std::string name = toks.front(); std::vector argv(toks.begin() + 1, toks.end()); - auto res = dbg->send_command(name, argv, err); - if (!res && err) { - std::cerr << "send_command failed: " << err.message << "\n"; - // clear error for next loop - err = IdeviceFFI::FfiError{}; - continue; - } - if (res && !res->empty()) { - std::cout << *res << "\n"; - } + auto res = dbg.send_command(name, argv); + match_result( + res, + ok_value, + { if_let_some(ok_value, some_value, { std::cout << some_value << "\n"; }); }, + err_value, + { std::cerr << "send_command failed: " << err_value.message << "\n"; }); } return 0; diff --git a/cpp/examples/diagnosticsservice.cpp b/cpp/examples/diagnosticsservice.cpp index 46bfbb1..bcf2eb0 100644 --- a/cpp/examples/diagnosticsservice.cpp +++ b/cpp/examples/diagnosticsservice.cpp @@ -3,7 +3,6 @@ #include #include #include -#include #include #include @@ -16,10 +15,12 @@ using namespace IdeviceFFI; -static void fail(const char* msg, const FfiError& e) { +[[noreturn]] +static void die(const char* msg, const FfiError& e) { std::cerr << msg; - if (e) + if (e) { std::cerr << ": " << e.message; + } std::cerr << "\n"; std::exit(1); } @@ -28,101 +29,105 @@ int main() { idevice_init_logger(Debug, Disabled, NULL); FfiError err; - // 1) usbmuxd, pick first device - auto mux = UsbmuxdConnection::default_new(/*tag*/ 0, err); - if (!mux) - fail("failed to connect to usbmuxd", err); + // 1) usbmuxd → pick first device + auto mux = IdeviceFFI::UsbmuxdConnection::default_new(/*tag*/ 0); + if_let_err(mux, err, { die("failed to connect to usbmuxd", err); }); - auto devices = mux->get_devices(err); - if (!devices) - fail("failed to list devices", err); - if (devices->empty()) { + auto devices = mux.unwrap().get_devices(); + if_let_err(devices, err, { die("failed to list devices", err); }); + if (devices.unwrap().empty()) { std::cerr << "no devices connected\n"; return 1; } - auto& dev = (*devices)[0]; - auto udid = dev.get_udid(); - auto mux_id = dev.get_id(); - if (!udid || !mux_id) { - std::cerr << "device missing udid or mux id\n"; + auto& dev = (devices.unwrap())[0]; + auto udid = dev.get_udid(); + if (udid.is_none()) { + std::cerr << "device has no UDID\n"; + return 1; + } + auto mux_id = dev.get_id(); + if (mux_id.is_none()) { + std::cerr << "device has no mux id\n"; return 1; } // 2) Provider via default usbmuxd addr - auto addr = UsbmuxdAddr::default_new(); + auto addr = IdeviceFFI::UsbmuxdAddr::default_new(); - const uint32_t tag = 0; - const std::string label = "diagnosticsservice-jkcoxson"; - auto provider = Provider::usbmuxd_new(std::move(addr), tag, *udid, *mux_id, label, err); - if (!provider) - fail("failed to create provider", err); + const uint32_t tag = 0; + const std::string label = "debug-proxy-jkcoxson"; + auto provider = IdeviceFFI::Provider::usbmuxd_new( + std::move(addr), tag, udid.unwrap(), mux_id.unwrap(), label); + if_let_err(provider, err, { die("failed to create provider", err); }); // 3) CoreDeviceProxy - auto cdp = CoreDeviceProxy::connect(*provider, err); - if (!cdp) - fail("failed CoreDeviceProxy connect", err); + auto cdp = CoreDeviceProxy::connect(provider.unwrap()) + .unwrap_or_else([](FfiError e) -> CoreDeviceProxy { + die("failed to connect CoreDeviceProxy", e); + }); - auto rsd_port = cdp->get_server_rsd_port(err); - if (!rsd_port) - fail("failed to get RSD port", err); + auto rsd_port = cdp.get_server_rsd_port().unwrap_or_else( + [](FfiError err) -> uint16_t { die("failed to get server RSD port", err); }); - // 4) Software tunnel → connect to RSD - auto adapter = std::move(*cdp).create_tcp_adapter(err); - if (!adapter) - fail("failed to create software tunnel adapter", err); + // 4) Create software tunnel adapter (consumes proxy) + auto adapter = std::move(cdp).create_tcp_adapter(); + if_let_err(adapter, err, { die("failed to create software tunnel adapter", err); }); - auto stream = adapter->connect(*rsd_port, err); - if (!stream) - fail("failed to connect RSD stream", err); + // 5) Connect adapter to RSD → ReadWrite stream + auto stream = adapter.unwrap().connect(rsd_port); + if_let_err(stream, err, { die("failed to connect RSD stream", err); }); - // 5) RSD handshake - auto rsd = RsdHandshake::from_socket(std::move(*stream), err); - if (!rsd) - fail("failed RSD handshake", err); - // 6) Diagnostics Service over RSD - auto diag = DiagnosticsService::connect_rsd(*adapter, *rsd, err); - if (!diag) - fail("failed to connect DiagnosticsService", err); + // 6) RSD handshake (consumes stream) + auto rsd = RsdHandshake::from_socket(std::move(stream.unwrap())); + if_let_err(rsd, err, { die("failed RSD handshake", err); }); + + // 6) DebugProxy over RSD + auto diag = DiagnosticsService::connect_rsd(adapter.unwrap(), rsd.unwrap()); + if_let_err(diag, err, { die("failed to connect DebugProxy", err); }); std::cout << "Getting sysdiagnose, this takes a while! iOS is slow...\n"; - auto cap = diag->capture_sysdiagnose(/*dry_run=*/false, err); - if (!cap) - fail("capture_sysdiagnose failed", err); + auto cap = diag.unwrap().capture_sysdiagnose(/*dry_run=*/false); + if_let_err(cap, err, { die("capture_sysdiagnose failed", err); }); - std::cout << "Got sysdiagnose! Saving to file: " << cap->preferred_filename << "\n"; + std::cout << "Got sysdiagnose! Saving to file: " << cap.unwrap().preferred_filename << "\n"; // 7) Stream to file with progress - std::ofstream out(cap->preferred_filename, std::ios::binary); + std::ofstream out(cap.unwrap().preferred_filename, std::ios::binary); if (!out) { std::cerr << "failed to open output file\n"; return 1; } std::size_t written = 0; - const std::size_t total = cap->expected_length; + const std::size_t total = cap.unwrap().expected_length; for (;;) { - auto chunk = cap->stream.next_chunk(err); - if (!chunk) { - if (err) - fail("stream error", err); // err set only on real error - break; // nullptr means end-of-stream - } - if (!chunk->empty()) { - out.write(reinterpret_cast(chunk->data()), - static_cast(chunk->size())); - if (!out) { - std::cerr << "write failed\n"; - return 1; - } - written += chunk->size(); - } + auto chunk = cap.unwrap().stream.next_chunk(); + match_result( + chunk, + res, + { + if_let_some(res, chunk_res, { + out.write(reinterpret_cast(chunk_res.data()), + static_cast(chunk_res.size())); + if (!out) { + std::cerr << "write failed\n"; + return 1; + } + written += chunk_res.size(); + }); + if (res.is_none()) { + break; + } + }, + err, + { die("stream error", err); }); std::cout << "wrote " << written << "/" << total << " bytes\r" << std::flush; } out.flush(); - std::cout << "\nDone! Saved to " << cap->preferred_filename << "\n"; + std::cout << "\nDone! Saved to " << cap.unwrap().preferred_filename << "\n"; return 0; } diff --git a/cpp/examples/idevice_id.cpp b/cpp/examples/idevice_id.cpp index 7ebabf7..c99f254 100644 --- a/cpp/examples/idevice_id.cpp +++ b/cpp/examples/idevice_id.cpp @@ -2,34 +2,31 @@ #include #include -#include int main() { - IdeviceFFI::FfiError e; - std::optional u = - IdeviceFFI::UsbmuxdConnection::default_new(0, e); - if (u == std::nullopt) { + auto u = IdeviceFFI::UsbmuxdConnection::default_new(0); + if_let_err(u, e, { std::cerr << "failed to connect to usbmuxd"; std::cerr << e.message; - } + }); - auto devices = u->get_devices(e); - if (u == std::nullopt) { + auto devices = u.unwrap().get_devices(); + if_let_err(devices, e, { std::cerr << "failed to get devices from usbmuxd"; std::cerr << e.message; - } + }); - for (IdeviceFFI::UsbmuxdDevice& d : *devices) { + for (IdeviceFFI::UsbmuxdDevice& d : devices.unwrap()) { auto udid = d.get_udid(); - if (!udid) { + if (udid.is_none()) { std::cerr << "failed to get udid"; continue; } auto connection_type = d.get_connection_type(); - if (!connection_type) { + if (connection_type.is_none()) { std::cerr << "failed to get connection type"; continue; } - std::cout << *udid << " (" << connection_type->to_string() << ")" << "\n"; + std::cout << udid.unwrap() << " (" << connection_type.unwrap().to_string() << ")" << "\n"; } } diff --git a/cpp/examples/ideviceinfo.cpp b/cpp/examples/ideviceinfo.cpp index ae9cfc4..d93756d 100644 --- a/cpp/examples/ideviceinfo.cpp +++ b/cpp/examples/ideviceinfo.cpp @@ -4,73 +4,80 @@ #include #include #include -#include #include int main() { idevice_init_logger(Debug, Disabled, NULL); - IdeviceFFI::FfiError e; - std::optional u = - IdeviceFFI::UsbmuxdConnection::default_new(0, e); - if (!u) { + auto u_res = IdeviceFFI::UsbmuxdConnection::default_new(0); + if_let_err(u_res, e, { std::cerr << "failed to connect to usbmuxd"; std::cerr << e.message; return 1; - } + }); + auto& u = u_res.unwrap(); - auto devices = u->get_devices(e); - if (!devices) { + auto devices_res = u.get_devices(); + if_let_err(devices_res, e, { std::cerr << "failed to get devices from usbmuxd"; std::cerr << e.message; return 1; - } - if (devices->empty()) { + }); + auto devices = std::move(devices_res).unwrap(); + + if (devices.empty()) { std::cerr << "no devices connected"; - std::cerr << e.message; return 1; } - auto& dev = (*devices)[0]; + auto& dev = (devices)[0]; auto udid = dev.get_udid(); - if (!udid) { + if (udid.is_none()) { std::cerr << "no udid\n"; return 1; } auto id = dev.get_id(); - if (!id) { + if (id.is_none()) { std::cerr << "no id\n"; return 1; } - IdeviceFFI::UsbmuxdAddr addr = IdeviceFFI::UsbmuxdAddr::default_new(); - auto prov = - IdeviceFFI::Provider::usbmuxd_new(std::move(addr), /*tag*/ 0, *udid, *id, "reeeeeeeee", e); - if (!prov) { + IdeviceFFI::UsbmuxdAddr addr = IdeviceFFI::UsbmuxdAddr::default_new(); + auto prov_res = IdeviceFFI::Provider::usbmuxd_new( + std::move(addr), /*tag*/ 0, udid.unwrap(), id.unwrap(), "reeeeeeeee"); + if_let_err(prov_res, e, { std::cerr << "provider failed: " << e.message << "\n"; return 1; - } + }); + auto& prov = prov_res.unwrap(); - auto client = IdeviceFFI::Lockdown::connect(*prov, e); - if (!client) { + auto client_res = IdeviceFFI::Lockdown::connect(prov); + if_let_err(client_res, e, { std::cerr << "lockdown connect failed: " << e.message << "\n"; return 1; - } + }); + auto& client = client_res.unwrap(); - auto pf = prov->get_pairing_file(e); - if (!pf) { + auto pf = prov.get_pairing_file(); + if_let_err(pf, e, { std::cerr << "failed to get pairing file: " << e.message << "\n"; return 1; - } - client->start_session(*pf, e); + }); + client.start_session(pf.unwrap()); - auto values = client->get_value(NULL, NULL, e); - if (!values) { - std::cerr << "get values failed: " << e.message << "\n"; - return 1; - } - PList::Dictionary res = PList::Dictionary(*values); - std::cout << res.ToXml(); + auto values = client.get_value(NULL, NULL); + match_result( + values, + ok_val, + { + PList::Dictionary res = PList::Dictionary(ok_val); + std::cout << res.ToXml(); + }, + e, + { + std::cerr << "get values failed: " << e.message << "\n"; + return 1; + }); } diff --git a/cpp/examples/location_simulation.cpp b/cpp/examples/location_simulation.cpp index 824eebc..9211ae1 100644 --- a/cpp/examples/location_simulation.cpp +++ b/cpp/examples/location_simulation.cpp @@ -2,7 +2,6 @@ #include #include -#include #include #include @@ -17,6 +16,7 @@ using namespace IdeviceFFI; +[[noreturn]] static void die(const char* msg, const FfiError& e) { std::cerr << msg << ": " << e.message << "\n"; std::exit(1); @@ -26,8 +26,8 @@ int main(int argc, char** argv) { // Usage: // simulate_location clear // simulate_location set - bool do_clear = false; - std::optional lat, lon; + bool do_clear = false; + Option lat, lon; if (argc == 2 && std::string(argv[1]) == "clear") { do_clear = true; @@ -41,95 +41,82 @@ int main(int argc, char** argv) { return 2; } - FfiError err; + // 1) usbmuxd → pick first device + auto mux = IdeviceFFI::UsbmuxdConnection::default_new(/*tag*/ 0); + if_let_err(mux, err, { die("failed to connect to usbmuxd", err); }); - // 1) Connect to usbmuxd and pick first device - auto mux = UsbmuxdConnection::default_new(/*tag*/ 0, err); - if (!mux) - die("failed to connect to usbmuxd", err); - - auto devices = mux->get_devices(err); - if (!devices) - die("failed to list devices", err); - if (devices->empty()) { + auto devices = mux.unwrap().get_devices(); + if_let_err(devices, err, { die("failed to list devices", err); }); + if (devices.unwrap().empty()) { std::cerr << "no devices connected\n"; return 1; } - auto& dev = (*devices)[0]; - auto udidOpt = dev.get_udid(); - if (!udidOpt) { + auto& dev = (devices.unwrap())[0]; + auto udid = dev.get_udid(); + if (udid.is_none()) { std::cerr << "device has no UDID\n"; return 1; } - auto idOpt = dev.get_id(); - if (!idOpt) { + auto mux_id = dev.get_id(); + if (mux_id.is_none()) { std::cerr << "device has no mux id\n"; return 1; } - // 2) Make a Provider for this device via default addr - auto addr = UsbmuxdAddr::default_new(); + // 2) Provider via default usbmuxd addr + auto addr = IdeviceFFI::UsbmuxdAddr::default_new(); - const uint32_t tag = 0; - const std::string label = "simulate_location-jkcoxson"; + const uint32_t tag = 0; + const std::string label = "debug-proxy-jkcoxson"; + auto provider = IdeviceFFI::Provider::usbmuxd_new( + std::move(addr), tag, udid.unwrap(), mux_id.unwrap(), label); + if_let_err(provider, err, { die("failed to create provider", err); }); - auto provider = Provider::usbmuxd_new(std::move(addr), tag, *udidOpt, *idOpt, label, err); - if (!provider) - die("failed to create provider", err); + // 3) CoreDeviceProxy + auto cdp = CoreDeviceProxy::connect(provider.unwrap()) + .unwrap_or_else([](FfiError e) -> CoreDeviceProxy { + die("failed to connect CoreDeviceProxy", e); + }); - // 3) Connect CoreDeviceProxy (borrow provider) - auto cdp = CoreDeviceProxy::connect(*provider, err); - if (!cdp) - die("failed to connect CoreDeviceProxy", err); + auto rsd_port = cdp.get_server_rsd_port().unwrap_or_else( + [](FfiError err) -> uint16_t { die("failed to get server RSD port", err); }); - // 4) Read handshake’s server RSD port - auto rsd_port = cdp->get_server_rsd_port(err); - if (!rsd_port) - die("failed to get server RSD port", err); + // 4) Create software tunnel adapter (consumes proxy) + auto adapter = std::move(cdp).create_tcp_adapter(); + if_let_err(adapter, err, { die("failed to create software tunnel adapter", err); }); - // 5) Create software tunnel adapter (consumes proxy) - auto adapter = std::move(*cdp).create_tcp_adapter(err); - if (!adapter) - die("failed to create software tunnel adapter", err); + // 5) Connect adapter to RSD → ReadWrite stream + auto stream = adapter.unwrap().connect(rsd_port); + if_let_err(stream, err, { die("failed to connect RSD stream", err); }); - // 6) Connect adapter to RSD port → ReadWrite stream - auto stream = adapter->connect(*rsd_port, err); - if (!stream) - die("failed to connect RSD stream", err); - - // 7) RSD handshake (consumes stream) - auto rsd = RsdHandshake::from_socket(std::move(*stream), err); - if (!rsd) - die("failed RSD handshake", err); + // 6) RSD handshake (consumes stream) + auto rsd = RsdHandshake::from_socket(std::move(stream.unwrap())); + if_let_err(rsd, err, { die("failed RSD handshake", err); }); // 8) RemoteServer over RSD (borrows adapter + handshake) - auto rs = RemoteServer::connect_rsd(*adapter, *rsd, err); - if (!rs) - die("failed to connect RemoteServer", err); + auto rs = RemoteServer::connect_rsd(adapter.unwrap(), rsd.unwrap()); + if_let_err(rs, err, { die("failed to connect RemoteServer", err); }); // 9) LocationSimulation client (borrows RemoteServer) - auto sim = LocationSimulation::create(*rs, err); - if (!sim) - die("failed to create LocationSimulation client", err); + auto sim_res = LocationSimulation::create(rs.unwrap()); + if_let_err(sim_res, err, { die("failed to create LocationSimulation client", err); }); + auto& sim = sim_res.unwrap(); if (do_clear) { - if (!sim->clear(err)) - die("clear failed", err); + if_let_err(sim.clear(), err, { die("clear failed", err); }); std::cout << "Location cleared!\n"; return 0; } // set path - if (!sim->set(*lat, *lon, err)) - die("set failed", err); - std::cout << "Location set to (" << *lat << ", " << *lon << ")\n"; + if_let_err(sim.set(lat.unwrap(), lon.unwrap()), err, { die("set failed", err); }); + std::cout << "Location set to (" << lat.unwrap() << ", " << lon.unwrap() << ")\n"; std::cout << "Press Ctrl-C to stop\n"; // keep process alive like the Rust example for (;;) { - if (!sim->set(*lat, *lon, err)) - die("set failed", err); + if_let_err(sim.set(lat.unwrap(), lon.unwrap()), err, { die("set failed", err); }); std::this_thread::sleep_for(std::chrono::seconds(3)); } } diff --git a/cpp/idevice++.xcodeproj/project.pbxproj b/cpp/idevice++.xcodeproj/project.pbxproj index fda15b7..13e2dcf 100644 --- a/cpp/idevice++.xcodeproj/project.pbxproj +++ b/cpp/idevice++.xcodeproj/project.pbxproj @@ -7,6 +7,8 @@ objects = { /* Begin PBXBuildFile section */ + 1914C7972E623CC2002EAB6E /* option.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 1914C7962E623CC2002EAB6E /* option.hpp */; }; + 1914C7992E623CC8002EAB6E /* result.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 1914C7982E623CC8002EAB6E /* result.hpp */; }; 198077932E5CA6EF00CB501E /* adapter_stream.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1980776C2E5CA69800CB501E /* adapter_stream.cpp */; }; 198077942E5CA6EF00CB501E /* app_service.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1980776D2E5CA69800CB501E /* app_service.cpp */; }; 198077952E5CA6EF00CB501E /* core_device.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1980776E2E5CA69800CB501E /* core_device.cpp */; }; @@ -44,6 +46,8 @@ /* End PBXBuildFile section */ /* Begin PBXFileReference section */ + 1914C7962E623CC2002EAB6E /* option.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = option.hpp; sourceTree = ""; }; + 1914C7982E623CC8002EAB6E /* result.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = result.hpp; sourceTree = ""; }; 1980776C2E5CA69800CB501E /* adapter_stream.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = adapter_stream.cpp; sourceTree = ""; }; 1980776D2E5CA69800CB501E /* app_service.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = app_service.cpp; sourceTree = ""; }; 1980776E2E5CA69800CB501E /* core_device.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = core_device.cpp; sourceTree = ""; }; @@ -155,6 +159,8 @@ 198077B12E5CA6FC00CB501E /* rsd.hpp */, 198077B22E5CA6FC00CB501E /* tcp_object_stack.hpp */, 198077B32E5CA6FC00CB501E /* usbmuxd.hpp */, + 1914C7962E623CC2002EAB6E /* option.hpp */, + 1914C7982E623CC8002EAB6E /* result.hpp */, ); path = "idevice++"; sourceTree = ""; @@ -196,11 +202,13 @@ 198077C12E5CA6FC00CB501E /* readwrite.hpp in Headers */, 198077C22E5CA6FC00CB501E /* location_simulation.hpp in Headers */, 198077C32E5CA6FC00CB501E /* adapter_stream.hpp in Headers */, + 1914C7972E623CC2002EAB6E /* option.hpp in Headers */, 198077C42E5CA6FC00CB501E /* lockdown.hpp in Headers */, 198077C52E5CA6FC00CB501E /* usbmuxd.hpp in Headers */, 198077C62E5CA6FC00CB501E /* app_service.hpp in Headers */, 198077C72E5CA6FC00CB501E /* idevice.hpp in Headers */, 198077C82E5CA6FC00CB501E /* provider.hpp in Headers */, + 1914C7992E623CC8002EAB6E /* result.hpp in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -433,14 +441,22 @@ 198077912E5CA6C700CB501E /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES; CODE_SIGN_STYLE = Automatic; + DEAD_CODE_STRIPPING = YES; DEVELOPMENT_TEAM = 4FW3Q8784L; EXECUTABLE_PREFIX = lib; + GCC_WARN_INHIBIT_ALL_WARNINGS = NO; + GCC_WARN_SIGN_COMPARE = YES; + GCC_WARN_UNKNOWN_PRAGMAS = YES; + GCC_WARN_UNUSED_LABEL = YES; + GCC_WARN_UNUSED_PARAMETER = YES; LIBRARY_SEARCH_PATHS = ( "${TARGET_BUILD_DIR}/**", "$(PROJECT_DIR)/${DESTINATION_PATH}", ); MACOSX_DEPLOYMENT_TARGET = 15.5; + OTHER_LDFLAGS = "-Wall"; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = macosx; SKIP_INSTALL = YES; @@ -453,14 +469,22 @@ 198077922E5CA6C700CB501E /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES; CODE_SIGN_STYLE = Automatic; + DEAD_CODE_STRIPPING = YES; DEVELOPMENT_TEAM = 4FW3Q8784L; EXECUTABLE_PREFIX = lib; + GCC_WARN_INHIBIT_ALL_WARNINGS = NO; + GCC_WARN_SIGN_COMPARE = YES; + GCC_WARN_UNKNOWN_PRAGMAS = YES; + GCC_WARN_UNUSED_LABEL = YES; + GCC_WARN_UNUSED_PARAMETER = YES; LIBRARY_SEARCH_PATHS = ( "${TARGET_BUILD_DIR}/**", "$(PROJECT_DIR)/${DESTINATION_PATH}", ); MACOSX_DEPLOYMENT_TARGET = 15.5; + OTHER_LDFLAGS = "-Wall"; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = macosx; SKIP_INSTALL = YES; diff --git a/cpp/include/idevice++/adapter_stream.hpp b/cpp/include/idevice++/adapter_stream.hpp index 862e91c..f92a5b1 100644 --- a/cpp/include/idevice++/adapter_stream.hpp +++ b/cpp/include/idevice++/adapter_stream.hpp @@ -5,6 +5,8 @@ #include #include #include +#include +#include #include struct IdeviceFfiError; @@ -31,16 +33,16 @@ class AdapterStream { ~AdapterStream() noexcept = default; // no auto-close; caller controls - AdapterStreamHandle* raw() const noexcept { return h_; } + AdapterStreamHandle* raw() const noexcept { return h_; } - bool close(FfiError& err); - bool send(const uint8_t* data, size_t len, FfiError& err); - bool send(const std::vector& buf, FfiError& err) { - return send(buf.data(), buf.size(), err); + Result close(); + Result send(const uint8_t* data, size_t len); + Result send(const std::vector& buf) { + return send(buf.data(), buf.size()); } // recv into caller-provided buffer (resizes to actual length) - bool recv(std::vector& out, FfiError& err, size_t max_hint = 2048); + Result, FfiError> recv(size_t max_hint = 2048); private: AdapterStreamHandle* h_{}; diff --git a/cpp/include/idevice++/app_service.hpp b/cpp/include/idevice++/app_service.hpp index aada1a6..0adfb9c 100644 --- a/cpp/include/idevice++/app_service.hpp +++ b/cpp/include/idevice++/app_service.hpp @@ -9,7 +9,6 @@ #include #include #include -#include #include #include @@ -19,17 +18,17 @@ using AppServicePtr = std::unique_ptr>; struct AppInfo { - bool is_removable{}; - std::string name; - bool is_first_party{}; - std::string path; - std::string bundle_identifier; - bool is_developer_app{}; - std::optional bundle_version; - bool is_internal{}; - bool is_hidden{}; - bool is_app_clip{}; - std::optional version; + bool is_removable{}; + std::string name; + bool is_first_party{}; + std::string path; + std::string bundle_identifier; + bool is_developer_app{}; + Option bundle_version; + bool is_internal{}; + bool is_hidden{}; + bool is_app_clip{}; + Option version; }; struct LaunchResponse { @@ -40,15 +39,15 @@ struct LaunchResponse { }; struct ProcessToken { - uint32_t pid{}; - std::optional executable_url; + uint32_t pid{}; + Option executable_url; }; struct SignalResponse { - uint32_t pid{}; - std::optional executable_url; - uint64_t device_timestamp_ms{}; - uint32_t signal{}; + uint32_t pid{}; + Option executable_url; + uint64_t device_timestamp_ms{}; + uint32_t signal{}; }; struct IconData { @@ -62,41 +61,34 @@ struct IconData { class AppService { public: // Factory: connect via RSD (borrows adapter & handshake) - static std::optional - connect_rsd(Adapter& adapter, RsdHandshake& rsd, FfiError& err); + static Result connect_rsd(Adapter& adapter, RsdHandshake& rsd); // Factory: from socket Box (consumes it). - static std::optional from_readwrite_ptr(ReadWriteOpaque* consumed, FfiError& err); + static Result from_readwrite_ptr(ReadWriteOpaque* consumed); // nice ergonomic overload: consume a C++ ReadWrite by releasing it - static std::optional from_readwrite(ReadWrite&& rw, FfiError& err); + static Result from_readwrite(ReadWrite&& rw); // API - std::optional> list_apps(bool app_clips, - bool removable, - bool hidden, - bool internal, - bool default_apps, - FfiError& err) const; + Result, FfiError> + list_apps(bool app_clips, bool removable, bool hidden, bool internal, bool default_apps) const; - std::optional launch(const std::string& bundle_id, - const std::vector& argv, - bool kill_existing, - bool start_suspended, - FfiError& err); + Result launch(const std::string& bundle_id, + const std::vector& argv, + bool kill_existing, + bool start_suspended); - std::optional> list_processes(FfiError& err) const; + Result, FfiError> list_processes() const; - bool uninstall(const std::string& bundle_id, FfiError& err); + Result uninstall(const std::string& bundle_id); - std::optional send_signal(uint32_t pid, uint32_t signal, FfiError& err); + Result send_signal(uint32_t pid, uint32_t signal); - std::optional fetch_icon(const std::string& bundle_id, - float width, - float height, - float scale, - bool allow_placeholder, - FfiError& err); + Result fetch_icon(const std::string& bundle_id, + float width, + float height, + float scale, + bool allow_placeholder); // RAII / moves ~AppService() noexcept = default; diff --git a/cpp/include/idevice++/core_device_proxy.hpp b/cpp/include/idevice++/core_device_proxy.hpp index 2b392d2..730fca4 100644 --- a/cpp/include/idevice++/core_device_proxy.hpp +++ b/cpp/include/idevice++/core_device_proxy.hpp @@ -5,8 +5,10 @@ #include #include +#include #include #include +#include namespace IdeviceFFI { @@ -22,32 +24,33 @@ struct CoreClientParams { class Adapter { public: - ~Adapter() noexcept = default; - Adapter(Adapter&&) noexcept = default; - Adapter& operator=(Adapter&&) noexcept = default; - Adapter(const Adapter&) = delete; - Adapter& operator=(const Adapter&) = delete; + ~Adapter() noexcept = default; + Adapter(Adapter&&) noexcept = default; + Adapter& operator=(Adapter&&) noexcept = default; + Adapter(const Adapter&) = delete; + Adapter& operator=(const Adapter&) = delete; - static Adapter adopt(AdapterHandle* h) noexcept { return Adapter(h); } - AdapterHandle* raw() const noexcept { return handle_.get(); } + static Adapter adopt(AdapterHandle* h) noexcept { return Adapter(h); } + AdapterHandle* raw() const noexcept { return handle_.get(); } // Enable PCAP - bool pcap(const std::string& path, FfiError& err) { - if (IdeviceFfiError* e = ::adapter_pcap(handle_.get(), path.c_str())) { - err = FfiError(e); - return false; + Result pcap(const std::string& path) { + FfiError e(::adapter_pcap(handle_.get(), path.c_str())); + if (e) { + return Err(e); } - return true; + return Ok(); } - // Connect to a port, returns a ReadWrite stream (to be consumed by RSD/CoreDeviceProxy) - std::optional connect(uint16_t port, FfiError& err) { + // Connect to a port, returns a ReadWrite stream (to be consumed by + // RSD/CoreDeviceProxy) + Result connect(uint16_t port) { ReadWriteOpaque* s = nullptr; - if (IdeviceFfiError* e = ::adapter_connect(handle_.get(), port, &s)) { - err = FfiError(e); - return std::nullopt; + FfiError e(::adapter_connect(handle_.get(), port, &s)); + if (e) { + return Err(e); } - return ReadWrite::adopt(s); + return Ok(ReadWrite::adopt(s)); } private: @@ -58,28 +61,28 @@ class Adapter { class CoreDeviceProxy { public: // Factory: connect using a Provider (NOT consumed on success or error) - static std::optional connect(Provider& provider, FfiError& err); + static Result connect(Provider& provider); - // Factory: from a socket; Rust consumes the socket regardless of result → we release before - // call - static std::optional from_socket(Idevice&& socket, FfiError& err); + // Factory: from a socket; Rust consumes the socket regardless of result → we + // release before call + static Result from_socket(Idevice&& socket); // Send/recv - bool send(const uint8_t* data, size_t len, FfiError& err); - bool send(const std::vector& buf, FfiError& err) { - return send(buf.data(), buf.size(), err); + Result send(const uint8_t* data, size_t len); + Result send(const std::vector& buf) { + return send(buf.data(), buf.size()); } // recv into a pre-sized buffer; resizes to actual bytes received - bool recv(std::vector& out, FfiError& err); + Result recv(std::vector& out); // Handshake info - std::optional get_client_parameters(FfiError& err) const; - std::optional get_server_address(FfiError& err) const; - std::optional get_server_rsd_port(FfiError& err) const; + Result get_client_parameters() const; + Result get_server_address() const; + Result get_server_rsd_port() const; // Consuming creation of a TCP adapter: Rust consumes the proxy handle - std::optional create_tcp_adapter(FfiError& err) &&; + Result create_tcp_adapter() &&; // RAII / moves ~CoreDeviceProxy() noexcept = default; diff --git a/cpp/include/idevice++/debug_proxy.hpp b/cpp/include/idevice++/debug_proxy.hpp index cafd386..1e8b832 100644 --- a/cpp/include/idevice++/debug_proxy.hpp +++ b/cpp/include/idevice++/debug_proxy.hpp @@ -4,13 +4,13 @@ #include #include #include -#include #include #include // Bring in the global C ABI (all C structs/functions are global) #include #include +#include #include namespace IdeviceFFI { @@ -33,32 +33,33 @@ class DebugProxy { ~DebugProxy() { reset(); } - // Factory: connect over RSD (borrows adapter & handshake; does not consume them) - static std::optional - connect_rsd(Adapter& adapter, RsdHandshake& rsd, FfiError& err); + // Factory: connect over RSD (borrows adapter & handshake; does not consume + // them) + static Result connect_rsd(Adapter& adapter, RsdHandshake& rsd); // Factory: consume a ReadWrite stream (fat pointer) - static std::optional from_readwrite_ptr(::ReadWriteOpaque* consumed, FfiError& err); + static Result from_readwrite_ptr(::ReadWriteOpaque* consumed); // Convenience: consume a C++ ReadWrite wrapper by releasing it into the ABI - static std::optional from_readwrite(ReadWrite&& rw, FfiError& err); + static Result from_readwrite(ReadWrite&& rw); // API - std::optional - send_command(const std::string& name, const std::vector& argv, FfiError& err); + Result, FfiError> send_command(const std::string& name, + const std::vector& argv); - std::optional read_response(FfiError& err); + Result, FfiError> read_response(); - bool send_raw(const std::vector& data, FfiError& err); + Result send_raw(const std::vector& data); - // Reads up to `len` bytes; ABI returns a heap C string (we treat as bytes → string) - std::optional read(std::size_t len, FfiError& err); + // Reads up to `len` bytes; ABI returns a heap C string (we treat as bytes → + // string) + Result, FfiError> read(std::size_t len); // Sets argv, returns textual reply (OK/echo/etc) - std::optional set_argv(const std::vector& argv, FfiError& err); + Result, FfiError> set_argv(const std::vector& argv); - bool send_ack(FfiError& err); - bool send_nack(FfiError& err); + Result send_ack(); + Result send_nack(); // No error object in ABI; immediate effect void set_ack_mode(bool enabled) { ::debug_proxy_set_ack_mode(handle_, enabled ? 1 : 0); } @@ -99,10 +100,9 @@ class DebugCommand { ~DebugCommand() { reset(); } - static std::optional make(const std::string& name, - const std::vector& argv); + static Option make(const std::string& name, const std::vector& argv); - ::DebugserverCommandHandle* raw() const { return handle_; } + ::DebugserverCommandHandle* raw() const { return handle_; } private: explicit DebugCommand(::DebugserverCommandHandle* h) : handle_(h) {} diff --git a/cpp/include/idevice++/diagnosticsservice.hpp b/cpp/include/idevice++/diagnosticsservice.hpp index b70e848..b0c81a0 100644 --- a/cpp/include/idevice++/diagnosticsservice.hpp +++ b/cpp/include/idevice++/diagnosticsservice.hpp @@ -3,7 +3,6 @@ #pragma once #include #include -#include #include #include @@ -32,10 +31,11 @@ class SysdiagnoseStream { ~SysdiagnoseStream() { reset(); } - // Pull next chunk. Returns nullopt on end-of-stream. On error, returns nullopt and sets `err`. - std::optional> next_chunk(FfiError& err); + // Pull next chunk. Returns nullopt on end-of-stream. On error, returns + // nullopt and sets `err`. + Result>, FfiError> next_chunk(); - SysdiagnoseStreamHandle* raw() const { return h_; } + SysdiagnoseStreamHandle* raw() const { return h_; } private: friend class DiagnosticsService; @@ -78,21 +78,20 @@ class DiagnosticsService { ~DiagnosticsService() { reset(); } // Connect via RSD (borrows adapter & handshake; does not consume them) - static std::optional - connect_rsd(Adapter& adapter, RsdHandshake& rsd, FfiError& err); + static Result connect_rsd(Adapter& adapter, RsdHandshake& rsd); // Create from a ReadWrite stream (consumes it) - static std::optional from_stream_ptr(::ReadWriteOpaque* consumed, - FfiError& err); + static Result from_stream_ptr(::ReadWriteOpaque* consumed); - static std::optional from_stream(ReadWrite&& rw, FfiError& err) { - return from_stream_ptr(rw.release(), err); + static Result from_stream(ReadWrite&& rw) { + return from_stream_ptr(rw.release()); } - // Start sysdiagnose capture; on success returns filename, length and a byte stream - std::optional capture_sysdiagnose(bool dry_run, FfiError& err); + // Start sysdiagnose capture; on success returns filename, length and a byte + // stream + Result capture_sysdiagnose(bool dry_run); - ::DiagnosticsServiceHandle* raw() const { return h_; } + ::DiagnosticsServiceHandle* raw() const { return h_; } private: explicit DiagnosticsService(::DiagnosticsServiceHandle* h) : h_(h) {} diff --git a/cpp/include/idevice++/ffi.hpp b/cpp/include/idevice++/ffi.hpp index 75cea52..73cdf06 100644 --- a/cpp/include/idevice++/ffi.hpp +++ b/cpp/include/idevice++/ffi.hpp @@ -15,7 +15,10 @@ class FfiError { FfiError(const IdeviceFfiError* err); FfiError(); - explicit operator bool() const { return code != 0; } + explicit operator bool() const { return code != 0; } + + static FfiError NotConnected(); + static FfiError InvalidArgument(); }; } // namespace IdeviceFFI #endif diff --git a/cpp/include/idevice++/idevice.hpp b/cpp/include/idevice++/idevice.hpp index c5ce89f..2fe93ce 100644 --- a/cpp/include/idevice++/idevice.hpp +++ b/cpp/include/idevice++/idevice.hpp @@ -5,7 +5,7 @@ #include #include -#include +#include #include #if defined(_WIN32) && !defined(__MINGW32__) @@ -21,8 +21,9 @@ namespace IdeviceFFI { // Generic “bind a free function” deleter template struct FnDeleter { void operator()(T* p) const noexcept { - if (p) + if (p) { FreeFn(p); + } } }; @@ -30,16 +31,15 @@ using IdevicePtr = std::unique_ptr - create(IdeviceSocketHandle* socket, const std::string& label, FfiError& err); + static Result create(IdeviceSocketHandle* socket, const std::string& label); - static std::optional - create_tcp(const sockaddr* addr, socklen_t addr_len, const std::string& label, FfiError& err); + static Result + create_tcp(const sockaddr* addr, socklen_t addr_len, const std::string& label); // Methods - std::optional get_type(FfiError& err) const; - bool rsd_checkin(FfiError& err); - bool start_session(const PairingFile& pairing_file, FfiError& err); + Result get_type() const; + Result rsd_checkin(); + Result start_session(const PairingFile& pairing_file); // Ownership/RAII ~Idevice() noexcept = default; diff --git a/cpp/include/idevice++/location_simulation.hpp b/cpp/include/idevice++/location_simulation.hpp index 5324292..be4dd85 100644 --- a/cpp/include/idevice++/location_simulation.hpp +++ b/cpp/include/idevice++/location_simulation.hpp @@ -3,8 +3,8 @@ #pragma once #include #include +#include #include -#include namespace IdeviceFFI { @@ -14,10 +14,10 @@ using LocSimPtr = std::unique_ptr create(RemoteServer& server, FfiError& err); + static Result create(RemoteServer& server); - bool clear(FfiError& err); - bool set(double latitude, double longitude, FfiError& err); + Result clear(); + Result set(double latitude, double longitude); ~LocationSimulation() noexcept = default; LocationSimulation(LocationSimulation&&) noexcept = default; diff --git a/cpp/include/idevice++/lockdown.hpp b/cpp/include/idevice++/lockdown.hpp index 9c962ae..14dfdff 100644 --- a/cpp/include/idevice++/lockdown.hpp +++ b/cpp/include/idevice++/lockdown.hpp @@ -5,7 +5,6 @@ #include #include #include -#include #include namespace IdeviceFFI { @@ -16,16 +15,15 @@ using LockdownPtr = class Lockdown { public: // Factory: connect via Provider - static std::optional connect(Provider& provider, FfiError& err); + static Result connect(Provider& provider); // Factory: wrap an existing Idevice socket (consumes it on success) - static std::optional from_socket(Idevice&& socket, FfiError& err); + static Result from_socket(Idevice&& socket); // Ops - bool start_session(const PairingFile& pf, FfiError& err); - std::optional> start_service(const std::string& identifier, - FfiError& err); - std::optional get_value(const char* key, const char* domain, FfiError& err); + Result start_session(const PairingFile& pf); + Result, FfiError> start_service(const std::string& identifier); + Result get_value(const char* key, const char* domain); // RAII / moves ~Lockdown() noexcept = default; diff --git a/cpp/include/idevice++/option.hpp b/cpp/include/idevice++/option.hpp new file mode 100644 index 0000000..b522b92 --- /dev/null +++ b/cpp/include/idevice++/option.hpp @@ -0,0 +1,184 @@ +// So here's the thing, std::optional and friends weren't added until C++17. +// Some consumers of this codebase aren't on C++17 yet, so this won't work. +// Plus, as a professional Rust evangelist, it's my duty to place as many Rust +// idioms into other languages as possible to give everyone a taste of greatness. +// Required error handling is correct error handling. And they called me a mad man. + +// Heavily influced from https://github.com/oktal/result, thank you + +#pragma once + +#include +#include +#include +#include + +namespace IdeviceFFI { + +struct none_t {}; +constexpr none_t None{}; + +template class Option { + bool has_; + typename std::aligned_storage::type storage_; + + T* ptr() { return reinterpret_cast(&storage_); } + const T* ptr() const { return reinterpret_cast(&storage_); } + + public: + // None + Option() noexcept : has_(false) {} + Option(none_t) noexcept : has_(false) {} + + // Some + Option(const T& v) : has_(true) { ::new (ptr()) T(v); } + Option(T&& v) : has_(true) { ::new (ptr()) T(std::move(v)); } + + // Copy / move + Option(const Option& o) : has_(o.has_) { + if (has_) { + ::new (ptr()) T(*o.ptr()); + } + } + Option(Option&& o) noexcept(std::is_nothrow_move_constructible::value) : has_(o.has_) { + if (has_) { + ::new (ptr()) T(std::move(*o.ptr())); + o.reset(); + } + } + + Option& operator=(Option o) noexcept(std::is_nothrow_move_constructible::value + && std::is_nothrow_move_assignable::value) { + swap(o); + return *this; + } + + ~Option() { reset(); } + + void reset() noexcept { + if (has_) { + ptr()->~T(); + has_ = false; + } + } + + void swap(Option& other) noexcept(std::is_nothrow_move_constructible::value) { + if (has_ && other.has_) { + using std::swap; + swap(*ptr(), *other.ptr()); + } else if (has_ && !other.has_) { + ::new (other.ptr()) T(std::move(*ptr())); + other.has_ = true; + reset(); + } else if (!has_ && other.has_) { + ::new (ptr()) T(std::move(*other.ptr())); + has_ = true; + other.reset(); + } + } + + // State + bool is_some() const noexcept { return has_; } + bool is_none() const noexcept { return !has_; } + + // Unwraps (ref-qualified) + T& unwrap() & { + if (!has_) { + throw std::runtime_error("unwrap on None"); + } + return *ptr(); + } + const T& unwrap() const& { + if (!has_) { + throw std::runtime_error("unwrap on None"); + } + return *ptr(); + } + T unwrap() && { + if (!has_) { + throw std::runtime_error("unwrap on None"); + } + T tmp = std::move(*ptr()); + reset(); + return tmp; + } + + // unwrap_or / unwrap_or_else + T unwrap_or(T default_value) const& { return has_ ? *ptr() : std::move(default_value); } + T unwrap_or(T default_value) && { return has_ ? std::move(*ptr()) : std::move(default_value); } + + template T unwrap_or_else(F&& f) const& { + return has_ ? *ptr() : static_cast(f()); + } + template T unwrap_or_else(F&& f) && { + return has_ ? std::move(*ptr()) : static_cast(f()); + } + + // map + template + auto map(F&& f) const -> Option::type> { + using U = typename std::decay::type; + if (has_) { + return Option(f(*ptr())); + } + return Option(None); + } +}; + +// Helpers +template inline Option::type> Some(T&& v) { + return Option::type>(std::forward(v)); +} +inline Option Some() = delete; // no Option + +// template inline Option None() { +// return Option(none); +// } // still needs T specified + +// Prefer this at call sites (lets return-type drive the type): +// return none; + +#define match_option(opt, SOME, NONE) \ + if ((opt).is_some()) { \ + auto&& SOME = (opt).unwrap(); +#define or_else \ + } \ + else { \ + NONE; \ + } + +// --- Option helpers: if_let_some / if_let_some_move / if_let_none --- + +#define _opt_concat(a, b) a##b +#define _opt_unique(base) _opt_concat(base, __LINE__) + +/* Bind a reference to the contained value if Some(...) */ +#define if_let_some(expr, name, block) \ + do { \ + auto _opt_unique(_opt_) = (expr); \ + if (_opt_unique(_opt_).is_some()) { \ + auto&& name = _opt_unique(_opt_).unwrap(); \ + block \ + } \ + } while (0) + +/* Move the contained value out (consumes the Option) if Some(...) */ +#define if_let_some_move(expr, name, block) \ + do { \ + auto _opt_unique(_opt_) = (expr); \ + if (_opt_unique(_opt_).is_some()) { \ + auto name = std::move(_opt_unique(_opt_)).unwrap(); \ + block \ + } \ + } while (0) + +/* Run a block if the option is None */ +#define if_let_none(expr, block) \ + do { \ + auto _opt_unique(_opt_) = (expr); \ + if (_opt_unique(_opt_).is_none()) { \ + block \ + } \ + } while (0) + +} // namespace IdeviceFFI diff --git a/cpp/include/idevice++/pairing_file.hpp b/cpp/include/idevice++/pairing_file.hpp index 46203e9..724c836 100644 --- a/cpp/include/idevice++/pairing_file.hpp +++ b/cpp/include/idevice++/pairing_file.hpp @@ -6,10 +6,10 @@ #pragma once #include -#include +#include +#include #include #include -#include namespace IdeviceFFI { struct PairingFileDeleter { @@ -20,8 +20,8 @@ using PairingFilePtr = std::unique_ptr; class PairingFile { public: - static std::optional read(const std::string& path, FfiError& err); - static std::optional from_bytes(const uint8_t* data, size_t size, FfiError& err); + static Result read(const std::string& path); + static Result from_bytes(const uint8_t* data, size_t size); ~PairingFile() noexcept = default; // unique_ptr handles destruction @@ -29,9 +29,9 @@ class PairingFile { PairingFile& operator=(const PairingFile&) = delete; PairingFile(PairingFile&&) noexcept = default; // move is correct by default - PairingFile& operator=(PairingFile&&) noexcept = default; + PairingFile& operator=(PairingFile&&) noexcept = default; - std::optional> serialize(FfiError& err) const; + Result, FfiError> serialize() const; explicit PairingFile(IdevicePairingFile* ptr) noexcept : ptr_(ptr) {} IdevicePairingFile* raw() const noexcept { return ptr_.get(); } diff --git a/cpp/include/idevice++/provider.hpp b/cpp/include/idevice++/provider.hpp index e976058..65c1383 100644 --- a/cpp/include/idevice++/provider.hpp +++ b/cpp/include/idevice++/provider.hpp @@ -5,43 +5,41 @@ #include #include #include -#include #include namespace IdeviceFFI { class FfiError; -class PairingFile; // has: IdevicePairingFile* raw() const; void release_on_success(); -class UsbmuxdAddr; // has: UsbmuxdAddrHandle* raw() const; void release_on_success(); +class PairingFile; // has: IdevicePairingFile* raw() const; void + // release_on_success(); +class UsbmuxdAddr; // has: UsbmuxdAddrHandle* raw() const; void + // release_on_success(); using ProviderPtr = std::unique_ptr>; class Provider { public: - static std::optional tcp_new(const idevice_sockaddr* ip, - PairingFile&& pairing, - const std::string& label, - FfiError& err); + static Result + tcp_new(const idevice_sockaddr* ip, PairingFile&& pairing, const std::string& label); - static std::optional usbmuxd_new(UsbmuxdAddr&& addr, - uint32_t tag, - const std::string& udid, - uint32_t device_id, - const std::string& label, - FfiError& err); + static Result usbmuxd_new(UsbmuxdAddr&& addr, + uint32_t tag, + const std::string& udid, + uint32_t device_id, + const std::string& label); - ~Provider() noexcept = default; - Provider(Provider&&) noexcept = default; - Provider& operator=(Provider&&) noexcept = default; - Provider(const Provider&) = delete; - Provider& operator=(const Provider&) = delete; + ~Provider() noexcept = default; + Provider(Provider&&) noexcept = default; + Provider& operator=(Provider&&) noexcept = default; + Provider(const Provider&) = delete; + Provider& operator=(const Provider&) = delete; - std::optional get_pairing_file(FfiError& err); + Result get_pairing_file(); - IdeviceProviderHandle* raw() const noexcept { return handle_.get(); } - static Provider adopt(IdeviceProviderHandle* h) noexcept { return Provider(h); } - IdeviceProviderHandle* release() noexcept { return handle_.release(); } + IdeviceProviderHandle* raw() const noexcept { return handle_.get(); } + static Provider adopt(IdeviceProviderHandle* h) noexcept { return Provider(h); } + IdeviceProviderHandle* release() noexcept { return handle_.release(); } private: explicit Provider(IdeviceProviderHandle* h) noexcept : handle_(h) {} diff --git a/cpp/include/idevice++/remote_server.hpp b/cpp/include/idevice++/remote_server.hpp index 51f8d98..09b1022 100644 --- a/cpp/include/idevice++/remote_server.hpp +++ b/cpp/include/idevice++/remote_server.hpp @@ -9,7 +9,6 @@ #include #include #include -#include namespace IdeviceFFI { @@ -19,11 +18,10 @@ using RemoteServerPtr = class RemoteServer { public: // Factory: consumes the ReadWrite stream regardless of result - static std::optional from_socket(ReadWrite&& rw, FfiError& err); + static Result from_socket(ReadWrite&& rw); // Factory: borrows adapter + handshake (neither is consumed) - static std::optional - connect_rsd(Adapter& adapter, RsdHandshake& rsd, FfiError& err); + static Result connect_rsd(Adapter& adapter, RsdHandshake& rsd); // RAII / moves ~RemoteServer() noexcept = default; diff --git a/cpp/include/idevice++/result.hpp b/cpp/include/idevice++/result.hpp new file mode 100644 index 0000000..fd4872e --- /dev/null +++ b/cpp/include/idevice++/result.hpp @@ -0,0 +1,231 @@ +// Jackson Coxson + +#pragma once + +#include +#include +#include +#include + +namespace IdeviceFFI { +namespace types { +template struct Ok { + T val; + + Ok(const T& val) : val(val) {} + Ok(T&& val) : val(std::move(val)) {} +}; + +template <> struct Ok {}; + +template struct Err { + E val; + + Err(const E& val) : val(val) {} + Err(E&& val) : val(std::move(val)) {} +}; +} // namespace types + +template inline types::Ok::type> Ok(T&& val) { + return types::Ok::type>(std::forward(val)); +} + +inline types::Ok Ok() { + return types::Ok(); +} + +template inline types::Err::type> Err(E&& val) { + return types::Err::type>(std::forward(val)); +} + +// ======================= +// Result +// ======================= +template class Result { + bool is_ok_; + union { + T ok_value_; + E err_value_; + }; + + public: + Result(types::Ok ok_val) : is_ok_(true), ok_value_(std::move(ok_val.val)) {} + Result(types::Err err_val) : is_ok_(false), err_value_(std::move(err_val.val)) {} + + Result(const Result& other) : is_ok_(other.is_ok_) { + if (is_ok_) { + new (&ok_value_) T(other.ok_value_); + } else { + new (&err_value_) E(other.err_value_); + } + } + + Result(Result&& other) noexcept : is_ok_(other.is_ok_) { + if (is_ok_) { + new (&ok_value_) T(std::move(other.ok_value_)); + } else { + new (&err_value_) E(std::move(other.err_value_)); + } + } + + ~Result() { + if (is_ok_) { + ok_value_.~T(); + } else { + err_value_.~E(); + } + } + + bool is_ok() const { return is_ok_; } + bool is_err() const { return !is_ok_; } + + // lvalue (mutable) + T& unwrap() & { + if (!is_ok_) { + std::fprintf(stderr, "unwrap on Err\n"); + std::terminate(); + } + return ok_value_; + } + + // lvalue (const) + const T& unwrap() const& { + if (!is_ok_) { + std::fprintf(stderr, "unwrap on Err\n"); + std::terminate(); + } + return ok_value_; + } + + // rvalue (consume/move) + T unwrap() && { + if (!is_ok_) { + std::fprintf(stderr, "unwrap on Err\n"); + std::terminate(); + } + return std::move(ok_value_); + } + + E& unwrap_err() & { + if (is_ok_) { + std::fprintf(stderr, "unwrap_err on Ok\n"); + std::terminate(); + } + return err_value_; + } + + const E& unwrap_err() const& { + if (is_ok_) { + std::fprintf(stderr, "unwrap_err on Ok\n"); + std::terminate(); + } + return err_value_; + } + + E unwrap_err() && { + if (is_ok_) { + std::fprintf(stderr, "unwrap_err on Ok\n"); + std::terminate(); + } + return std::move(err_value_); + } + + T unwrap_or(T&& default_value) const { return is_ok_ ? ok_value_ : std::move(default_value); } + + template T unwrap_or_else(F&& f) & { + return is_ok_ ? ok_value_ : static_cast(f(err_value_)); + } + + // const lvalue: returns T by copy + template T unwrap_or_else(F&& f) const& { + return is_ok_ ? ok_value_ : static_cast(f(err_value_)); + } + + // rvalue: moves Ok(T) out; on Err(E), allow the handler to consume/move E + template T unwrap_or_else(F&& f) && { + if (is_ok_) { + return std::move(ok_value_); + } + return static_cast(std::forward(f)(std::move(err_value_))); + } +}; + +// Result specialization + +template class Result { + bool is_ok_; + union { + char dummy_; + E err_value_; + }; + + public: + Result(types::Ok) : is_ok_(true), dummy_() {} + Result(types::Err err_val) : is_ok_(false), err_value_(std::move(err_val.val)) {} + + Result(const Result& other) : is_ok_(other.is_ok_) { + if (!is_ok_) { + new (&err_value_) E(other.err_value_); + } + } + + Result(Result&& other) noexcept : is_ok_(other.is_ok_) { + if (!is_ok_) { + new (&err_value_) E(std::move(other.err_value_)); + } + } + + ~Result() { + if (!is_ok_) { + err_value_.~E(); + } + } + + bool is_ok() const { return is_ok_; } + bool is_err() const { return !is_ok_; } + + void unwrap() const { + if (!is_ok_) { + std::fprintf(stderr, "Attempted to unwrap an error Result\n"); + std::terminate(); + } + } + + const E& unwrap_err() const { + if (is_ok_) { + std::fprintf(stderr, "Attempted to unwrap_err on an ok Result\n"); + std::terminate(); + } + return err_value_; + } + + E& unwrap_err() { + if (is_ok_) { + std::fprintf(stderr, "Attempted to unwrap_err on an ok Result\n"); + std::terminate(); + } + return err_value_; + } +}; + +#define match_result(res, ok_name, ok_block, err_name, err_block) \ + if ((res).is_ok()) { \ + auto&& ok_name = (res).unwrap(); \ + ok_block \ + } else { \ + auto&& err_name = (res).unwrap_err(); \ + err_block \ + } + +#define if_let_err(res, name, block) \ + if ((res).is_err()) { \ + auto&& name = (res).unwrap_err(); \ + block \ + } + +#define if_let_ok(res, name, block) \ + if ((res).is_ok()) { \ + auto&& name = (res).unwrap(); \ + block \ + } +} // namespace IdeviceFFI diff --git a/cpp/include/idevice++/rsd.hpp b/cpp/include/idevice++/rsd.hpp index 1231a87..6532c3e 100644 --- a/cpp/include/idevice++/rsd.hpp +++ b/cpp/include/idevice++/rsd.hpp @@ -25,16 +25,16 @@ using RsdPtr = class RsdHandshake { public: // Factory: consumes the ReadWrite socket regardless of result - static std::optional from_socket(ReadWrite&& rw, FfiError& err); + static Result from_socket(ReadWrite&& rw); // Basic info - std::optional protocol_version(FfiError& err) const; - std::optional uuid(FfiError& err) const; + Result protocol_version() const; + Result uuid() const; // Services - std::optional> services(FfiError& err) const; - std::optional service_available(const std::string& name, FfiError& err) const; - std::optional service_info(const std::string& name, FfiError& err) const; + Result, FfiError> services() const; + Result service_available(const std::string& name) const; + Result service_info(const std::string& name) const; // RAII / moves ~RsdHandshake() noexcept = default; diff --git a/cpp/include/idevice++/tcp_object_stack.hpp b/cpp/include/idevice++/tcp_object_stack.hpp index d4e1363..7670ca0 100644 --- a/cpp/include/idevice++/tcp_object_stack.hpp +++ b/cpp/include/idevice++/tcp_object_stack.hpp @@ -4,7 +4,6 @@ #include #include #include -#include #include #include @@ -12,7 +11,8 @@ namespace IdeviceFFI { -// ---------------- OwnedBuffer: RAII for zero-copy read buffers ---------------- +// ---------------- OwnedBuffer: RAII for zero-copy read buffers +// ---------------- class OwnedBuffer { public: OwnedBuffer() noexcept : p_(nullptr), n_(0) {} @@ -117,7 +117,8 @@ class TcpObjectStackEater { ~TcpObjectStackEater() { reset(); } // Blocks until a packet is available. On success, 'out' adopts the buffer - // and you must keep 'out' alive until done (RAII frees via idevice_data_free). + // and you must keep 'out' alive until done (RAII frees via + // idevice_data_free). bool read(OwnedBuffer& out, FfiError& err) const; ::TcpEatObject* raw() const { return h_; } @@ -139,34 +140,34 @@ class TcpObjectStackEater { // ---------------- Stack builder: returns feeder + eater + adapter ------------ class TcpObjectStack { public: - TcpObjectStack() = default; - TcpObjectStack(const TcpObjectStack&) = delete; // no sharing - TcpObjectStack& operator=(const TcpObjectStack&) = delete; - TcpObjectStack(TcpObjectStack&&) noexcept = default; // movable - TcpObjectStack& operator=(TcpObjectStack&&) noexcept = default; + TcpObjectStack() = default; + TcpObjectStack(const TcpObjectStack&) = delete; // no sharing + TcpObjectStack& operator=(const TcpObjectStack&) = delete; + TcpObjectStack(TcpObjectStack&&) noexcept = default; // movable + TcpObjectStack& operator=(TcpObjectStack&&) noexcept = default; // Build the stack (dual-handle). Name kept to minimize churn. - static std::optional - create(const std::string& our_ip, const std::string& their_ip, FfiError& err); + static Result create(const std::string& our_ip, + const std::string& their_ip); - TcpObjectStackFeeder& feeder(); - const TcpObjectStackFeeder& feeder() const; + TcpObjectStackFeeder& feeder(); + const TcpObjectStackFeeder& feeder() const; - TcpObjectStackEater& eater(); - const TcpObjectStackEater& eater() const; + TcpObjectStackEater& eater(); + const TcpObjectStackEater& eater() const; - Adapter& adapter(); - const Adapter& adapter() const; + Adapter& adapter(); + const Adapter& adapter() const; - std::optional release_feeder(); // nullptr inside wrapper after call - std::optional release_eater(); // nullptr inside wrapper after call - std::optional release_adapter(); + Option release_feeder(); // nullptr inside wrapper after call + Option release_eater(); // nullptr inside wrapper after call + Option release_adapter(); private: struct Impl { - TcpObjectStackFeeder feeder; - TcpObjectStackEater eater; - std::optional adapter; + TcpObjectStackFeeder feeder; + TcpObjectStackEater eater; + Option adapter; }; // Unique ownership so there’s a single point of truth to release from std::unique_ptr impl_; diff --git a/cpp/include/idevice++/usbmuxd.hpp b/cpp/include/idevice++/usbmuxd.hpp index 91fc7b4..018f312 100644 --- a/cpp/include/idevice++/usbmuxd.hpp +++ b/cpp/include/idevice++/usbmuxd.hpp @@ -5,8 +5,8 @@ #include #include +#include #include -#include #include #include @@ -29,10 +29,9 @@ using ConnectionPtr = class UsbmuxdAddr { public: - static std::optional - tcp_new(const sockaddr* addr, socklen_t addr_len, FfiError& err); + static Result tcp_new(const sockaddr* addr, socklen_t addr_len); #if defined(__unix__) || defined(__APPLE__) - static std::optional unix_new(const std::string& path, FfiError& err); + static Result unix_new(const std::string& path); #endif static UsbmuxdAddr default_new(); @@ -66,19 +65,19 @@ class UsbmuxdConnectionType { class UsbmuxdDevice { public: - ~UsbmuxdDevice() noexcept = default; - UsbmuxdDevice(UsbmuxdDevice&&) noexcept = default; - UsbmuxdDevice& operator=(UsbmuxdDevice&&) noexcept = default; - UsbmuxdDevice(const UsbmuxdDevice&) = delete; - UsbmuxdDevice& operator=(const UsbmuxdDevice&) = delete; + ~UsbmuxdDevice() noexcept = default; + UsbmuxdDevice(UsbmuxdDevice&&) noexcept = default; + UsbmuxdDevice& operator=(UsbmuxdDevice&&) noexcept = default; + UsbmuxdDevice(const UsbmuxdDevice&) = delete; + UsbmuxdDevice& operator=(const UsbmuxdDevice&) = delete; - static UsbmuxdDevice adopt(UsbmuxdDeviceHandle* h) noexcept { return UsbmuxdDevice(h); } + static UsbmuxdDevice adopt(UsbmuxdDeviceHandle* h) noexcept { return UsbmuxdDevice(h); } - UsbmuxdDeviceHandle* raw() const noexcept { return handle_.get(); } + UsbmuxdDeviceHandle* raw() const noexcept { return handle_.get(); } - std::optional get_udid() const; - std::optional get_id() const; - std::optional get_connection_type() const; + Option get_udid() const; + Option get_id() const; + Option get_connection_type() const; private: explicit UsbmuxdDevice(UsbmuxdDeviceHandle* h) noexcept : handle_(h) {} @@ -91,30 +90,28 @@ class PairingFile; class UsbmuxdConnection { public: - static std::optional - tcp_new(const idevice_sockaddr* addr, idevice_socklen_t addr_len, uint32_t tag, FfiError& err); + static Result + tcp_new(const idevice_sockaddr* addr, idevice_socklen_t addr_len, uint32_t tag); #if defined(__unix__) || defined(__APPLE__) - static std::optional - unix_new(const std::string& path, uint32_t tag, FfiError& err); + static Result unix_new(const std::string& path, uint32_t tag); #endif - static std::optional default_new(uint32_t tag, FfiError& err); + static Result default_new(uint32_t tag); - ~UsbmuxdConnection() noexcept = default; - UsbmuxdConnection(UsbmuxdConnection&&) noexcept = default; - UsbmuxdConnection& operator=(UsbmuxdConnection&&) noexcept = default; - UsbmuxdConnection(const UsbmuxdConnection&) = delete; - UsbmuxdConnection& operator=(const UsbmuxdConnection&) = delete; + ~UsbmuxdConnection() noexcept = default; + UsbmuxdConnection(UsbmuxdConnection&&) noexcept = default; + UsbmuxdConnection& operator=(UsbmuxdConnection&&) noexcept = default; + UsbmuxdConnection(const UsbmuxdConnection&) = delete; + UsbmuxdConnection& operator=(const UsbmuxdConnection&) = delete; - std::optional> get_devices(FfiError& err) const; - std::optional get_buid(FfiError& err) const; - std::optional get_pair_record(const std::string& udid, FfiError& err); + Result, FfiError> get_devices() const; + Result get_buid() const; + Result get_pair_record(const std::string& udid); - std::optional - connect_to_device(uint32_t device_id, uint16_t port, const std::string& path, FfiError& err) &&; - std::optional - connect_to_device(uint32_t, uint16_t, const std::string&, FfiError&) & = delete; - std::optional - connect_to_device(uint32_t, uint16_t, const std::string&, FfiError&) const& = delete; + Result + connect_to_device(uint32_t device_id, uint16_t port, const std::string& path) &&; + Result connect_to_device(uint32_t, uint16_t, const std::string&) & = delete; + Result + connect_to_device(uint32_t, uint16_t, const std::string&) const& = delete; UsbmuxdConnectionHandle* raw() const noexcept { return handle_.get(); } diff --git a/cpp/src/adapter_stream.cpp b/cpp/src/adapter_stream.cpp index 2d782bf..2c2df76 100644 --- a/cpp/src/adapter_stream.cpp +++ b/cpp/src/adapter_stream.cpp @@ -1,43 +1,50 @@ // Jackson Coxson #include +#include namespace IdeviceFFI { -bool AdapterStream::close(FfiError& err) { - if (!h_) - return true; - if (IdeviceFfiError* e = ::adapter_close(h_)) { - err = FfiError(e); - return false; - } - h_ = nullptr; - return true; +Result AdapterStream::close() { + if (!h_) + return Ok(); + + FfiError e(::adapter_close(h_)); + if (e) { + return Err(e); + } + + h_ = nullptr; + return Ok(); } -bool AdapterStream::send(const uint8_t* data, size_t len, FfiError& err) { - if (!h_) - return false; - if (IdeviceFfiError* e = ::adapter_send(h_, data, len)) { - err = FfiError(e); - return false; - } - return true; +Result AdapterStream::send(const uint8_t *data, size_t len) { + if (!h_) + return Err(FfiError::NotConnected()); + FfiError e(::adapter_send(h_, data, len)); + if (e) { + return Err(e); + } + return Ok(); } -bool AdapterStream::recv(std::vector& out, FfiError& err, size_t max_hint) { - if (!h_) - return false; - if (max_hint == 0) - max_hint = 2048; - out.resize(max_hint); - size_t actual = 0; - if (IdeviceFfiError* e = ::adapter_recv(h_, out.data(), &actual, out.size())) { - err = FfiError(e); - return false; - } - out.resize(actual); - return true; +Result, FfiError> AdapterStream::recv(size_t max_hint) { + if (!h_) + return Err(FfiError::NotConnected()); + + if (max_hint == 0) + max_hint = 2048; + + std::vector out(max_hint); + size_t actual = 0; + + FfiError e(::adapter_recv(h_, out.data(), &actual, out.size())); + if (e) { + return Err(e); + } + + out.resize(actual); + return Ok(std::move(out)); } } // namespace IdeviceFFI diff --git a/cpp/src/app_service.cpp b/cpp/src/app_service.cpp index c009aaf..ae9d9d9 100644 --- a/cpp/src/app_service.cpp +++ b/cpp/src/app_service.cpp @@ -5,193 +5,177 @@ namespace IdeviceFFI { // ---- Factories ---- -std::optional -AppService::connect_rsd(Adapter& adapter, RsdHandshake& rsd, FfiError& err) { - AppServiceHandle* out = nullptr; - if (IdeviceFfiError* e = ::app_service_connect_rsd(adapter.raw(), rsd.raw(), &out)) { - err = FfiError(e); - return std::nullopt; - } - return AppService::adopt(out); +Result AppService::connect_rsd(Adapter &adapter, + RsdHandshake &rsd) { + AppServiceHandle *out = nullptr; + if (IdeviceFfiError *e = + ::app_service_connect_rsd(adapter.raw(), rsd.raw(), &out)) { + return Err(FfiError(e)); + } + return Ok(AppService::adopt(out)); } -std::optional AppService::from_readwrite_ptr(ReadWriteOpaque* consumed, FfiError& err) { - AppServiceHandle* out = nullptr; - if (IdeviceFfiError* e = ::app_service_new(consumed, &out)) { - err = FfiError(e); - return std::nullopt; - } - return AppService::adopt(out); +Result +AppService::from_readwrite_ptr(ReadWriteOpaque *consumed) { + AppServiceHandle *out = nullptr; + if (IdeviceFfiError *e = ::app_service_new(consumed, &out)) { + return Err(FfiError(e)); + } + return Ok(AppService::adopt(out)); } -std::optional AppService::from_readwrite(ReadWrite&& rw, FfiError& err) { - // Rust consumes the stream regardless of result → release BEFORE call - return from_readwrite_ptr(rw.release(), err); +Result AppService::from_readwrite(ReadWrite &&rw) { + // Rust consumes the stream regardless of result → release BEFORE call + return from_readwrite_ptr(rw.release()); } // ---- Helpers to copy/free C arrays ---- -static std::vector copy_and_free_app_list(AppListEntryC* arr, size_t n) { - std::vector out; - out.reserve(n); - for (size_t i = 0; i < n; ++i) { - const auto& c = arr[i]; - AppInfo a; - a.is_removable = c.is_removable != 0; - if (c.name) - a.name = c.name; - a.is_first_party = c.is_first_party != 0; - if (c.path) - a.path = c.path; - if (c.bundle_identifier) - a.bundle_identifier = c.bundle_identifier; - a.is_developer_app = c.is_developer_app != 0; - if (c.bundle_version) - a.bundle_version = std::string(c.bundle_version); - a.is_internal = c.is_internal != 0; - a.is_hidden = c.is_hidden != 0; - a.is_app_clip = c.is_app_clip != 0; - if (c.version) - a.version = std::string(c.version); - out.emplace_back(std::move(a)); - } - ::app_service_free_app_list(arr, n); - return out; +static std::vector copy_and_free_app_list(AppListEntryC *arr, + size_t n) { + std::vector out; + out.reserve(n); + for (size_t i = 0; i < n; ++i) { + const auto &c = arr[i]; + AppInfo a; + a.is_removable = c.is_removable != 0; + if (c.name) + a.name = c.name; + a.is_first_party = c.is_first_party != 0; + if (c.path) + a.path = c.path; + if (c.bundle_identifier) + a.bundle_identifier = c.bundle_identifier; + a.is_developer_app = c.is_developer_app != 0; + if (c.bundle_version) + a.bundle_version = std::string(c.bundle_version); + a.is_internal = c.is_internal != 0; + a.is_hidden = c.is_hidden != 0; + a.is_app_clip = c.is_app_clip != 0; + if (c.version) + a.version = std::string(c.version); + out.emplace_back(std::move(a)); + } + ::app_service_free_app_list(arr, n); + return out; } -static std::vector copy_and_free_process_list(ProcessTokenC* arr, size_t n) { - std::vector out; - out.reserve(n); - for (size_t i = 0; i < n; ++i) { - ProcessToken p; - p.pid = arr[i].pid; - if (arr[i].executable_url) - p.executable_url = std::string(arr[i].executable_url); - out.emplace_back(std::move(p)); - } - ::app_service_free_process_list(arr, n); - return out; +static std::vector copy_and_free_process_list(ProcessTokenC *arr, + size_t n) { + std::vector out; + out.reserve(n); + for (size_t i = 0; i < n; ++i) { + ProcessToken p; + p.pid = arr[i].pid; + if (arr[i].executable_url) + p.executable_url = std::string(arr[i].executable_url); + out.emplace_back(std::move(p)); + } + ::app_service_free_process_list(arr, n); + return out; } // ---- API impls ---- -std::optional> AppService::list_apps(bool app_clips, - bool removable, - bool hidden, - bool internal, - bool default_apps, - FfiError& err) const { - AppListEntryC* arr = nullptr; - size_t n = 0; - if (IdeviceFfiError* e = ::app_service_list_apps(handle_.get(), - app_clips ? 1 : 0, - removable ? 1 : 0, - hidden ? 1 : 0, - internal ? 1 : 0, - default_apps ? 1 : 0, - &arr, - &n)) { - err = FfiError(e); - return std::nullopt; - } - return copy_and_free_app_list(arr, n); +Result, FfiError> +AppService::list_apps(bool app_clips, bool removable, bool hidden, + bool internal, bool default_apps) const { + AppListEntryC *arr = nullptr; + size_t n = 0; + if (IdeviceFfiError *e = ::app_service_list_apps( + handle_.get(), app_clips ? 1 : 0, removable ? 1 : 0, hidden ? 1 : 0, + internal ? 1 : 0, default_apps ? 1 : 0, &arr, &n)) { + + return Err(FfiError(e)); + } + return Ok(copy_and_free_app_list(arr, n)); } -std::optional AppService::launch(const std::string& bundle_id, - const std::vector& argv, - bool kill_existing, - bool start_suspended, - FfiError& err) { - std::vector c_argv; - c_argv.reserve(argv.size()); - for (auto& s : argv) - c_argv.push_back(s.c_str()); +Result +AppService::launch(const std::string &bundle_id, + const std::vector &argv, bool kill_existing, + bool start_suspended) { + std::vector c_argv; + c_argv.reserve(argv.size()); + for (auto &s : argv) + c_argv.push_back(s.c_str()); - LaunchResponseC* resp = nullptr; - if (IdeviceFfiError* e = ::app_service_launch_app(handle_.get(), - bundle_id.c_str(), - c_argv.empty() ? nullptr : c_argv.data(), - c_argv.size(), - kill_existing ? 1 : 0, - start_suspended ? 1 : 0, - NULL, // TODO: stdio handling - &resp)) { - err = FfiError(e); - return std::nullopt; - } + LaunchResponseC *resp = nullptr; + if (IdeviceFfiError *e = ::app_service_launch_app( + handle_.get(), bundle_id.c_str(), + c_argv.empty() ? nullptr : c_argv.data(), c_argv.size(), + kill_existing ? 1 : 0, start_suspended ? 1 : 0, + NULL, // TODO: stdio handling + &resp)) { + return Err(FfiError(e)); + } - LaunchResponse out; - out.process_identifier_version = resp->process_identifier_version; - out.pid = resp->pid; - if (resp->executable_url) - out.executable_url = resp->executable_url; - if (resp->audit_token && resp->audit_token_len > 0) { - out.audit_token.assign(resp->audit_token, resp->audit_token + resp->audit_token_len); - } - ::app_service_free_launch_response(resp); - return out; + LaunchResponse out; + out.process_identifier_version = resp->process_identifier_version; + out.pid = resp->pid; + if (resp->executable_url) + out.executable_url = resp->executable_url; + if (resp->audit_token && resp->audit_token_len > 0) { + out.audit_token.assign(resp->audit_token, + resp->audit_token + resp->audit_token_len); + } + ::app_service_free_launch_response(resp); + return Ok(std::move(out)); } -std::optional> AppService::list_processes(FfiError& err) const { - ProcessTokenC* arr = nullptr; - size_t n = 0; - if (IdeviceFfiError* e = ::app_service_list_processes(handle_.get(), &arr, &n)) { - err = FfiError(e); - return std::nullopt; - } - return copy_and_free_process_list(arr, n); +Result, FfiError> AppService::list_processes() const { + ProcessTokenC *arr = nullptr; + size_t n = 0; + if (IdeviceFfiError *e = + ::app_service_list_processes(handle_.get(), &arr, &n)) { + return Err(FfiError(e)); + } + return Ok(copy_and_free_process_list(arr, n)); } -bool AppService::uninstall(const std::string& bundle_id, FfiError& err) { - if (IdeviceFfiError* e = ::app_service_uninstall_app(handle_.get(), bundle_id.c_str())) { - err = FfiError(e); - return false; - } - return true; +Result AppService::uninstall(const std::string &bundle_id) { + if (IdeviceFfiError *e = + ::app_service_uninstall_app(handle_.get(), bundle_id.c_str())) { + return Err(FfiError(e)); + } + return Ok(); } -std::optional -AppService::send_signal(uint32_t pid, uint32_t signal, FfiError& err) { - SignalResponseC* c = nullptr; - if (IdeviceFfiError* e = ::app_service_send_signal(handle_.get(), pid, signal, &c)) { - err = FfiError(e); - return std::nullopt; - } - SignalResponse out; - out.pid = c->pid; - if (c->executable_url) - out.executable_url = std::string(c->executable_url); - out.device_timestamp_ms = c->device_timestamp; - out.signal = c->signal; - ::app_service_free_signal_response(c); - return out; +Result AppService::send_signal(uint32_t pid, + uint32_t signal) { + SignalResponseC *c = nullptr; + if (IdeviceFfiError *e = + ::app_service_send_signal(handle_.get(), pid, signal, &c)) { + return Err(FfiError(e)); + } + SignalResponse out; + out.pid = c->pid; + if (c->executable_url) + out.executable_url = std::string(c->executable_url); + out.device_timestamp_ms = c->device_timestamp; + out.signal = c->signal; + ::app_service_free_signal_response(c); + return Ok(std::move(out)); } -std::optional AppService::fetch_icon(const std::string& bundle_id, - float width, - float height, - float scale, - bool allow_placeholder, - FfiError& err) { - IconDataC* c = nullptr; - if (IdeviceFfiError* e = ::app_service_fetch_app_icon(handle_.get(), - bundle_id.c_str(), - width, - height, - scale, - allow_placeholder ? 1 : 0, - &c)) { - err = FfiError(e); - return std::nullopt; - } - IconData out; - if (c->data && c->data_len) { - out.data.assign(c->data, c->data + c->data_len); - } - out.icon_width = c->icon_width; - out.icon_height = c->icon_height; - out.minimum_width = c->minimum_width; - out.minimum_height = c->minimum_height; - ::app_service_free_icon_data(c); - return out; +Result AppService::fetch_icon(const std::string &bundle_id, + float width, float height, + float scale, + bool allow_placeholder) { + IconDataC *c = nullptr; + if (IdeviceFfiError *e = ::app_service_fetch_app_icon( + handle_.get(), bundle_id.c_str(), width, height, scale, + allow_placeholder ? 1 : 0, &c)) { + return Err(FfiError(e)); + } + IconData out; + if (c->data && c->data_len) { + out.data.assign(c->data, c->data + c->data_len); + } + out.icon_width = c->icon_width; + out.icon_height = c->icon_height; + out.minimum_width = c->minimum_width; + out.minimum_height = c->minimum_height; + ::app_service_free_icon_data(c); + return Ok(std::move(out)); } } // namespace IdeviceFFI diff --git a/cpp/src/core_device.cpp b/cpp/src/core_device.cpp index 07e91e3..5349211 100644 --- a/cpp/src/core_device.cpp +++ b/cpp/src/core_device.cpp @@ -6,114 +6,115 @@ namespace IdeviceFFI { // ---- Factories ---- -std::optional CoreDeviceProxy::connect(Provider& provider, FfiError& err) { - CoreDeviceProxyHandle* out = nullptr; - if (IdeviceFfiError* e = ::core_device_proxy_connect(provider.raw(), &out)) { - err = FfiError(e); - return std::nullopt; - } - return CoreDeviceProxy::adopt(out); +Result CoreDeviceProxy::connect(Provider &provider) { + CoreDeviceProxyHandle *out = nullptr; + FfiError e(::core_device_proxy_connect(provider.raw(), &out)); + if (e) { + return Err(e); + } + return Ok(CoreDeviceProxy::adopt(out)); } -std::optional CoreDeviceProxy::from_socket(Idevice&& socket, FfiError& err) { - CoreDeviceProxyHandle* out = nullptr; +Result +CoreDeviceProxy::from_socket(Idevice &&socket) { + CoreDeviceProxyHandle *out = nullptr; - // Rust consumes the socket regardless of result → release BEFORE call - IdeviceHandle* raw = socket.release(); + // Rust consumes the socket regardless of result → release BEFORE call + IdeviceHandle *raw = socket.release(); - if (IdeviceFfiError* e = ::core_device_proxy_new(raw, &out)) { - // socket is already consumed on error; do NOT touch it - err = FfiError(e); - return std::nullopt; - } - return CoreDeviceProxy::adopt(out); + FfiError e(::core_device_proxy_new(raw, &out)); + if (e) { + return Err(e); + } + return Ok(CoreDeviceProxy::adopt(out)); } // ---- IO ---- -bool CoreDeviceProxy::send(const uint8_t* data, size_t len, FfiError& err) { - if (IdeviceFfiError* e = ::core_device_proxy_send(handle_.get(), data, len)) { - err = FfiError(e); - return false; - } - return true; +Result CoreDeviceProxy::send(const uint8_t *data, size_t len) { + FfiError e(::core_device_proxy_send(handle_.get(), data, len)); + if (e) { + return Err(e); + } + return Ok(); } -bool CoreDeviceProxy::recv(std::vector& out, FfiError& err) { - if (out.empty()) - out.resize(4096); // a reasonable default; caller can pre-size - size_t actual = 0; - if (IdeviceFfiError* e = - ::core_device_proxy_recv(handle_.get(), out.data(), &actual, out.size())) { - err = FfiError(e); - return false; - } - out.resize(actual); - return true; +Result CoreDeviceProxy::recv(std::vector &out) { + if (out.empty()) + out.resize(4096); // a reasonable default; caller can pre-size + size_t actual = 0; + FfiError e( + ::core_device_proxy_recv(handle_.get(), out.data(), &actual, out.size())); + if (e) { + return Err(e); + } + out.resize(actual); + return Ok(); } // ---- Handshake ---- -std::optional CoreDeviceProxy::get_client_parameters(FfiError& err) const { - uint16_t mtu = 0; - char* addr_c = nullptr; - char* mask_c = nullptr; +Result +CoreDeviceProxy::get_client_parameters() const { + uint16_t mtu = 0; + char *addr_c = nullptr; + char *mask_c = nullptr; - if (IdeviceFfiError* e = - ::core_device_proxy_get_client_parameters(handle_.get(), &mtu, &addr_c, &mask_c)) { - err = FfiError(e); - return std::nullopt; - } + FfiError e(::core_device_proxy_get_client_parameters(handle_.get(), &mtu, + &addr_c, &mask_c)); + if (e) { + return Err(e); + } - CoreClientParams params; - params.mtu = mtu; - if (addr_c) { - params.address = addr_c; - ::idevice_string_free(addr_c); - } - if (mask_c) { - params.netmask = mask_c; - ::idevice_string_free(mask_c); - } - return params; + CoreClientParams params; + params.mtu = mtu; + if (addr_c) { + params.address = addr_c; + ::idevice_string_free(addr_c); + } + if (mask_c) { + params.netmask = mask_c; + ::idevice_string_free(mask_c); + } + return Ok(std::move(params)); } -std::optional CoreDeviceProxy::get_server_address(FfiError& err) const { - char* addr_c = nullptr; - if (IdeviceFfiError* e = ::core_device_proxy_get_server_address(handle_.get(), &addr_c)) { - err = FfiError(e); - return std::nullopt; - } - std::string s; - if (addr_c) { - s = addr_c; - ::idevice_string_free(addr_c); - } - return s; +Result CoreDeviceProxy::get_server_address() const { + char *addr_c = nullptr; + FfiError e(::core_device_proxy_get_server_address(handle_.get(), &addr_c)); + if (e) { + return Err(e); + } + std::string s; + if (addr_c) { + s = addr_c; + ::idevice_string_free(addr_c); + } + return Ok(s); } -std::optional CoreDeviceProxy::get_server_rsd_port(FfiError& err) const { - uint16_t port = 0; - if (IdeviceFfiError* e = ::core_device_proxy_get_server_rsd_port(handle_.get(), &port)) { - err = FfiError(e); - return std::nullopt; - } - return port; +Result CoreDeviceProxy::get_server_rsd_port() const { + uint16_t port = 0; + FfiError e(::core_device_proxy_get_server_rsd_port(handle_.get(), &port)); + if (e) { + return Err(e); + } + return Ok(port); } // ---- Adapter creation (consumes *this) ---- -std::optional CoreDeviceProxy::create_tcp_adapter(FfiError& err) && { - AdapterHandle* out = nullptr; +Result CoreDeviceProxy::create_tcp_adapter() && { + AdapterHandle *out = nullptr; - // Rust consumes the proxy regardless of result → release BEFORE call - CoreDeviceProxyHandle* raw = this->release(); + // Rust consumes the proxy regardless of result → release BEFORE call + CoreDeviceProxyHandle *raw = this->release(); - if (IdeviceFfiError* e = ::core_device_proxy_create_tcp_adapter(raw, &out)) { - err = FfiError(e); - return std::nullopt; - } - return Adapter::adopt(out); + FfiError e(::core_device_proxy_create_tcp_adapter(raw, &out)); + if (e) { + return Err(e); + } + return Ok(Adapter::adopt(out)); } } // namespace IdeviceFFI diff --git a/cpp/src/debug_proxy.cpp b/cpp/src/debug_proxy.cpp index f3143cb..9e8321d 100644 --- a/cpp/src/debug_proxy.cpp +++ b/cpp/src/debug_proxy.cpp @@ -6,136 +6,138 @@ namespace IdeviceFFI { // ---- helpers ---- -static std::optional take_cstring(char* p) { - if (!p) - return std::nullopt; - std::string s(p); - ::idevice_string_free(p); - return s; +static Option take_cstring(char *p) { + if (!p) + return None; + std::string s(p); + ::idevice_string_free(p); + return Some(s); } // ---- DebugCommand ---- -std::optional DebugCommand::make(const std::string& name, - const std::vector& argv) { - std::vector c_argv; - c_argv.reserve(argv.size()); - for (auto& a : argv) - c_argv.push_back(a.c_str()); +Option DebugCommand::make(const std::string &name, + const std::vector &argv) { + std::vector c_argv; + c_argv.reserve(argv.size()); + for (auto &a : argv) + c_argv.push_back(a.c_str()); - auto* h = ::debugserver_command_new( - name.c_str(), - c_argv.empty() ? nullptr : const_cast(c_argv.data()), - c_argv.size()); - if (!h) - return std::nullopt; - return DebugCommand(h); + auto *h = ::debugserver_command_new( + name.c_str(), + c_argv.empty() ? nullptr : const_cast(c_argv.data()), + c_argv.size()); + if (!h) + return None; + return Some(DebugCommand(h)); } // ---- DebugProxy factories ---- -std::optional -DebugProxy::connect_rsd(Adapter& adapter, RsdHandshake& rsd, FfiError& err) { - ::DebugProxyHandle* out = nullptr; - if (IdeviceFfiError* e = ::debug_proxy_connect_rsd(adapter.raw(), rsd.raw(), &out)) { - err = FfiError(e); - return std::nullopt; - } - return DebugProxy(out); +Result DebugProxy::connect_rsd(Adapter &adapter, + RsdHandshake &rsd) { + ::DebugProxyHandle *out = nullptr; + FfiError e(::debug_proxy_connect_rsd(adapter.raw(), rsd.raw(), &out)); + if (e) { + return Err(e); + } + return Ok(DebugProxy(out)); } -std::optional DebugProxy::from_readwrite_ptr(::ReadWriteOpaque* consumed, - FfiError& err) { - ::DebugProxyHandle* out = nullptr; - if (IdeviceFfiError* e = ::debug_proxy_new(consumed, &out)) { - err = FfiError(e); - return std::nullopt; - } - return DebugProxy(out); +Result +DebugProxy::from_readwrite_ptr(::ReadWriteOpaque *consumed) { + ::DebugProxyHandle *out = nullptr; + FfiError e(::debug_proxy_new(consumed, &out)); + if (e) { + return Err(e); + } + return Ok(DebugProxy(out)); } -std::optional DebugProxy::from_readwrite(ReadWrite&& rw, FfiError& err) { - // Rust consumes the pointer regardless of outcome; release before calling - return from_readwrite_ptr(rw.release(), err); +Result DebugProxy::from_readwrite(ReadWrite &&rw) { + // Rust consumes the pointer regardless of outcome; release before calling + return from_readwrite_ptr(rw.release()); } // ---- DebugProxy API ---- -std::optional DebugProxy::send_command(const std::string& name, - const std::vector& argv, - FfiError& err) { - auto cmd = DebugCommand::make(name, argv); - if (!cmd) { - // treat as invalid arg - err.code = -1; - err.message = "debugserver_command_new failed"; - return std::nullopt; - } +Result, FfiError> +DebugProxy::send_command(const std::string &name, + const std::vector &argv) { + auto cmdRes = DebugCommand::make(name, argv); + if (cmdRes.is_none()) { + // treat as invalid arg + FfiError err; + err.code = -1; + err.message = "debugserver_command_new failed"; + return Err(err); + } + auto cmd = std::move(cmdRes).unwrap(); - char* resp_c = nullptr; - if (IdeviceFfiError* e = ::debug_proxy_send_command(handle_, cmd->raw(), &resp_c)) { - err = FfiError(e); - return std::nullopt; - } - return take_cstring(resp_c); // may be null → std::nullopt + char *resp_c = nullptr; + FfiError e(::debug_proxy_send_command(handle_, cmd.raw(), &resp_c)); + if (e) { + return Err(e); + } + + return Ok(take_cstring(resp_c)); } -std::optional DebugProxy::read_response(FfiError& err) { - char* resp_c = nullptr; - if (IdeviceFfiError* e = ::debug_proxy_read_response(handle_, &resp_c)) { - err = FfiError(e); - return std::nullopt; - } - return take_cstring(resp_c); +Result, FfiError> DebugProxy::read_response() { + char *resp_c = nullptr; + FfiError e(::debug_proxy_read_response(handle_, &resp_c)); + if (e) { + return Err(e); + } + return Ok(take_cstring(resp_c)); } -bool DebugProxy::send_raw(const std::vector& data, FfiError& err) { - if (IdeviceFfiError* e = ::debug_proxy_send_raw(handle_, data.data(), data.size())) { - err = FfiError(e); - return false; - } - return true; +Result DebugProxy::send_raw(const std::vector &data) { + FfiError e(::debug_proxy_send_raw(handle_, data.data(), data.size())); + if (e) { + return Err(e); + } + return Ok(); } -std::optional DebugProxy::read(std::size_t len, FfiError& err) { - char* resp_c = nullptr; - if (IdeviceFfiError* e = ::debug_proxy_read(handle_, len, &resp_c)) { - err = FfiError(e); - return std::nullopt; - } - return take_cstring(resp_c); +Result, FfiError> DebugProxy::read(std::size_t len) { + char *resp_c = nullptr; + FfiError e(::debug_proxy_read(handle_, len, &resp_c)); + if (e) { + return Err(e); + } + return Ok(take_cstring(resp_c)); } -std::optional DebugProxy::set_argv(const std::vector& argv, - FfiError& err) { - std::vector c_argv; - c_argv.reserve(argv.size()); - for (auto& a : argv) - c_argv.push_back(a.c_str()); +Result, FfiError> +DebugProxy::set_argv(const std::vector &argv) { + std::vector c_argv; + c_argv.reserve(argv.size()); + for (auto &a : argv) + c_argv.push_back(a.c_str()); - char* resp_c = nullptr; - if (IdeviceFfiError* e = ::debug_proxy_set_argv( - handle_, - c_argv.empty() ? nullptr : const_cast(c_argv.data()), - c_argv.size(), - &resp_c)) { - err = FfiError(e); - return std::nullopt; - } - return take_cstring(resp_c); + char *resp_c = nullptr; + FfiError e(::debug_proxy_set_argv( + handle_, + c_argv.empty() ? nullptr : const_cast(c_argv.data()), + c_argv.size(), &resp_c)); + if (e) { + return Err(e); + } + return Ok(take_cstring(resp_c)); } -bool DebugProxy::send_ack(FfiError& err) { - if (IdeviceFfiError* e = ::debug_proxy_send_ack(handle_)) { - err = FfiError(e); - return false; - } - return true; +Result DebugProxy::send_ack() { + FfiError e(::debug_proxy_send_ack(handle_)); + if (e) { + return Err(e); + } + return Ok(); } -bool DebugProxy::send_nack(FfiError& err) { - if (IdeviceFfiError* e = ::debug_proxy_send_nack(handle_)) { - err = FfiError(e); - return false; - } - return true; +Result DebugProxy::send_nack() { + FfiError e(::debug_proxy_send_nack(handle_)); + if (e) { + return Err(e); + } + return Ok(); } } // namespace IdeviceFFI diff --git a/cpp/src/diagnosticsservice.cpp b/cpp/src/diagnosticsservice.cpp index f67d866..8e95cdb 100644 --- a/cpp/src/diagnosticsservice.cpp +++ b/cpp/src/diagnosticsservice.cpp @@ -1,35 +1,39 @@ // Jackson Coxson -#include #include +#include +#include namespace IdeviceFFI { // Local helper: take ownership of a C string and convert to std::string -static std::optional take_cstring(char* p) { - if (!p) - return std::nullopt; +static Option take_cstring(char* p) { + if (!p) { + return None; + } + std::string s(p); ::idevice_string_free(p); - return s; + return Some(std::move(s)); } // -------- SysdiagnoseStream -------- -std::optional> SysdiagnoseStream::next_chunk(FfiError& err) { - if (!h_) - return std::nullopt; +Result>, FfiError> SysdiagnoseStream::next_chunk() { + if (!h_) { + return Err(FfiError::NotConnected()); + } uint8_t* data = nullptr; std::size_t len = 0; - if (IdeviceFfiError* e = ::sysdiagnose_stream_next(h_, &data, &len)) { - err = FfiError(e); - return std::nullopt; + FfiError e(::sysdiagnose_stream_next(h_, &data, &len)); + if (e) { + return Err(e); } if (!data || len == 0) { // End of stream - return std::nullopt; + return Ok(Option>(None)); } // Copy into a C++ buffer @@ -38,52 +42,52 @@ std::optional> SysdiagnoseStream::next_chunk(FfiError& err) idevice_data_free(data, len); - return out; + return Ok(Some(out)); } // -------- DiagnosticsService -------- -std::optional -DiagnosticsService::connect_rsd(Adapter& adapter, RsdHandshake& rsd, FfiError& err) { +Result DiagnosticsService::connect_rsd(Adapter& adapter, + RsdHandshake& rsd) { ::DiagnosticsServiceHandle* out = nullptr; - if (IdeviceFfiError* e = ::diagnostics_service_connect_rsd(adapter.raw(), rsd.raw(), &out)) { - err = FfiError(e); - return std::nullopt; + FfiError e(::diagnostics_service_connect_rsd(adapter.raw(), rsd.raw(), &out)); + if (e) { + return Err(e); } - return DiagnosticsService(out); + return Ok(DiagnosticsService(out)); } -std::optional DiagnosticsService::from_stream_ptr(::ReadWriteOpaque* consumed, - FfiError& err) { +Result +DiagnosticsService::from_stream_ptr(::ReadWriteOpaque* consumed) { ::DiagnosticsServiceHandle* out = nullptr; - if (IdeviceFfiError* e = ::diagnostics_service_new(consumed, &out)) { - err = FfiError(e); - return std::nullopt; + FfiError e(::diagnostics_service_new(consumed, &out)); + if (e) { + return Err(e); } - return DiagnosticsService(out); + return Ok(DiagnosticsService(out)); } -std::optional DiagnosticsService::capture_sysdiagnose(bool dry_run, - FfiError& err) { - if (!h_) - return std::nullopt; +Result DiagnosticsService::capture_sysdiagnose(bool dry_run) { + if (!h_) { + return Err(FfiError::NotConnected()); + } char* filename_c = nullptr; std::size_t expected_len = 0; ::SysdiagnoseStreamHandle* stream_h = nullptr; - if (IdeviceFfiError* e = ::diagnostics_service_capture_sysdiagnose( - h_, dry_run ? true : false, &filename_c, &expected_len, &stream_h)) { - err = FfiError(e); - return std::nullopt; + FfiError e(::diagnostics_service_capture_sysdiagnose( + h_, dry_run ? true : false, &filename_c, &expected_len, &stream_h)); + if (e) { + return Err(e); } - auto fname = take_cstring(filename_c).value_or(std::string{}); + auto fname = take_cstring(filename_c).unwrap_or(std::string{}); SysdiagnoseStream stream(stream_h); SysdiagnoseCapture cap{/*preferred_filename*/ std::move(fname), /*expected_length*/ expected_len, /*stream*/ std::move(stream)}; - return cap; + return Ok(std::move(cap)); } } // namespace IdeviceFFI diff --git a/cpp/src/ffi.cpp b/cpp/src/ffi.cpp index fc95293..51be26f 100644 --- a/cpp/src/ffi.cpp +++ b/cpp/src/ffi.cpp @@ -2,6 +2,7 @@ #include #include +#include #include namespace IdeviceFFI { @@ -14,4 +15,18 @@ FfiError::FfiError(const IdeviceFfiError* err) FfiError::FfiError() : code(0), message("") { } + +FfiError FfiError::NotConnected() { + FfiError err; + err.code = -11; // from idevice/lib.rs + err.message = "No established socket connection"; + return err; +} +FfiError FfiError::InvalidArgument() { + FfiError err; + err.code = -57; // from idevice/lib.rs + err.message = "No established socket connection"; + return err; +} + } // namespace IdeviceFFI diff --git a/cpp/src/idevice.cpp b/cpp/src/idevice.cpp index 6899092..caa490b 100644 --- a/cpp/src/idevice.cpp +++ b/cpp/src/idevice.cpp @@ -4,53 +4,50 @@ namespace IdeviceFFI { -std::optional -Idevice::create(IdeviceSocketHandle* socket, const std::string& label, FfiError& err) { +Result Idevice::create(IdeviceSocketHandle* socket, const std::string& label) { IdeviceHandle* h = nullptr; - if (IdeviceFfiError* e = idevice_new(socket, label.c_str(), &h)) { - err = FfiError(e); - return std::nullopt; + FfiError e(idevice_new(socket, label.c_str(), &h)); + if (e) { + return Err(e); } - return Idevice(h); + return Ok(Idevice(h)); } -std::optional Idevice::create_tcp(const sockaddr* addr, - socklen_t addr_len, - const std::string& label, - FfiError& err) { +Result +Idevice::create_tcp(const sockaddr* addr, socklen_t addr_len, const std::string& label) { IdeviceHandle* h = nullptr; - if (IdeviceFfiError* e = idevice_new_tcp_socket(addr, addr_len, label.c_str(), &h)) { - err = FfiError(e); - return std::nullopt; + FfiError e(idevice_new_tcp_socket(addr, addr_len, label.c_str(), &h)); + if (e) { + return Err(e); } - return Idevice(h); + return Ok(Idevice(h)); } -std::optional Idevice::get_type(FfiError& err) const { - char* cstr = nullptr; - if (IdeviceFfiError* e = idevice_get_type(handle_.get(), &cstr)) { - err = FfiError(e); - return std::nullopt; +Result Idevice::get_type() const { + char* cstr = nullptr; + FfiError e(idevice_get_type(handle_.get(), &cstr)); + if (e) { + return Err(e); } std::string out(cstr); idevice_string_free(cstr); - return out; + return Ok(out); } -bool Idevice::rsd_checkin(FfiError& err) { - if (IdeviceFfiError* e = idevice_rsd_checkin(handle_.get())) { - err = FfiError(e); - return false; +Result Idevice::rsd_checkin() { + FfiError e(idevice_rsd_checkin(handle_.get())); + if (e) { + return Err(e); } - return true; + return Ok(); } -bool Idevice::start_session(const PairingFile& pairing_file, FfiError& err) { - if (IdeviceFfiError* e = idevice_start_session(handle_.get(), pairing_file.raw())) { - err = FfiError(e); - return false; +Result Idevice::start_session(const PairingFile& pairing_file) { + FfiError e(idevice_start_session(handle_.get(), pairing_file.raw())); + if (e) { + return Err(e); } - return true; + return Ok(); } } // namespace IdeviceFFI diff --git a/cpp/src/location_simulation.cpp b/cpp/src/location_simulation.cpp index c53eef6..99abb85 100644 --- a/cpp/src/location_simulation.cpp +++ b/cpp/src/location_simulation.cpp @@ -4,29 +4,29 @@ namespace IdeviceFFI { -std::optional LocationSimulation::create(RemoteServer& server, FfiError& err) { +Result LocationSimulation::create(RemoteServer& server) { LocationSimulationHandle* out = nullptr; - if (IdeviceFfiError* e = ::location_simulation_new(server.raw(), &out)) { - err = FfiError(e); - return std::nullopt; + FfiError e(::location_simulation_new(server.raw(), &out)); + if (e) { + return Err(e); } - return LocationSimulation::adopt(out); + return Ok(LocationSimulation::adopt(out)); } -bool LocationSimulation::clear(FfiError& err) { - if (IdeviceFfiError* e = ::location_simulation_clear(handle_.get())) { - err = FfiError(e); - return false; +Result LocationSimulation::clear() { + FfiError e(::location_simulation_clear(handle_.get())); + if (e) { + return Err(e); } - return true; + return Ok(); } -bool LocationSimulation::set(double latitude, double longitude, FfiError& err) { - if (IdeviceFfiError* e = ::location_simulation_set(handle_.get(), latitude, longitude)) { - err = FfiError(e); - return false; +Result LocationSimulation::set(double latitude, double longitude) { + FfiError e(::location_simulation_set(handle_.get(), latitude, longitude)); + if (e) { + return Err(e); } - return true; + return Ok(); } } // namespace IdeviceFFI diff --git a/cpp/src/lockdown.cpp b/cpp/src/lockdown.cpp index 107439e..2a798a1 100644 --- a/cpp/src/lockdown.cpp +++ b/cpp/src/lockdown.cpp @@ -7,60 +7,51 @@ namespace IdeviceFFI { -std::optional Lockdown::connect(Provider& provider, FfiError& err) { +Result Lockdown::connect(Provider& provider) { LockdowndClientHandle* out = nullptr; - - if (IdeviceFfiError* e = ::lockdownd_connect(provider.raw(), &out)) { - // Rust freed the provider on error -> abandon our ownership to avoid double free. + FfiError e(::lockdownd_connect(provider.raw(), &out)); + if (e) { provider.release(); - err = FfiError(e); - return std::nullopt; + return Err(e); } - // Success: provider is NOT consumed; keep ownership. - return Lockdown::adopt(out); + return Ok(Lockdown::adopt(out)); } -std::optional Lockdown::from_socket(Idevice&& socket, FfiError& err) { +Result Lockdown::from_socket(Idevice&& socket) { LockdowndClientHandle* out = nullptr; - - if (IdeviceFfiError* e = ::lockdownd_new(socket.raw(), &out)) { - // Error: Rust did NOT consume the socket (it returns early for invalid args), - // so keep ownership; report error. - err = FfiError(e); - return std::nullopt; + FfiError e(::lockdownd_new(socket.raw(), &out)); + if (e) { + return Err(e); } - // Success: Rust consumed the socket -> abandon our ownership. socket.release(); - return Lockdown::adopt(out); + return Ok(Lockdown::adopt(out)); } -bool Lockdown::start_session(const PairingFile& pf, FfiError& err) { - if (IdeviceFfiError* e = ::lockdownd_start_session(handle_.get(), pf.raw())) { - err = FfiError(e); - return false; +Result Lockdown::start_session(const PairingFile& pf) { + FfiError e(::lockdownd_start_session(handle_.get(), pf.raw())); + if (e) { + return Err(e); } - return true; + return Ok(); } -std::optional> Lockdown::start_service(const std::string& identifier, - FfiError& err) { +Result, FfiError> Lockdown::start_service(const std::string& identifier) { uint16_t port = 0; bool ssl = false; - if (IdeviceFfiError* e = - ::lockdownd_start_service(handle_.get(), identifier.c_str(), &port, &ssl)) { - err = FfiError(e); - return std::nullopt; + FfiError e(::lockdownd_start_service(handle_.get(), identifier.c_str(), &port, &ssl)); + if (e) { + return Err(e); } - return std::make_pair(port, ssl); + return Ok(std::make_pair(port, ssl)); } -std::optional Lockdown::get_value(const char* key, const char* domain, FfiError& err) { - plist_t out = nullptr; - if (IdeviceFfiError* e = ::lockdownd_get_value(handle_.get(), key, domain, &out)) { - err = FfiError(e); - return std::nullopt; +Result Lockdown::get_value(const char* key, const char* domain) { + plist_t out = nullptr; + FfiError e(::lockdownd_get_value(handle_.get(), key, domain, &out)); + if (e) { + return Err(e); } - return out; // caller now owns `out` and must free with the plist API + return Ok(out); } } // namespace IdeviceFFI diff --git a/cpp/src/pairing_file.cpp b/cpp/src/pairing_file.cpp index bc2f388..9ba8f40 100644 --- a/cpp/src/pairing_file.cpp +++ b/cpp/src/pairing_file.cpp @@ -8,46 +8,46 @@ namespace IdeviceFFI { // Deleter definition (out-of-line) void PairingFileDeleter::operator()(IdevicePairingFile* p) const noexcept { - if (p) + if (p) { idevice_pairing_file_free(p); + } } // Static member definitions -std::optional PairingFile::read(const std::string& path, FfiError& err) { +Result PairingFile::read(const std::string& path) { IdevicePairingFile* ptr = nullptr; - if (IdeviceFfiError* e = idevice_pairing_file_read(path.c_str(), &ptr)) { - err = FfiError(e); - return std::nullopt; + FfiError e(idevice_pairing_file_read(path.c_str(), &ptr)); + if (e) { + return Err(e); } - return PairingFile(ptr); + return Ok(PairingFile(ptr)); } -std::optional -PairingFile::from_bytes(const uint8_t* data, size_t size, FfiError& err) { +Result PairingFile::from_bytes(const uint8_t* data, size_t size) { IdevicePairingFile* raw = nullptr; - if (IdeviceFfiError* e = idevice_pairing_file_from_bytes(data, size, &raw)) { - err = FfiError(e); - return std::nullopt; + FfiError e(idevice_pairing_file_from_bytes(data, size, &raw)); + if (e) { + return Err(e); } - return PairingFile(raw); + return Ok(PairingFile(raw)); } -std::optional> PairingFile::serialize(FfiError& err) const { +Result, FfiError> PairingFile::serialize() const { if (!ptr_) { - return std::nullopt; + return Err(FfiError::InvalidArgument()); } uint8_t* data = nullptr; size_t size = 0; - if (IdeviceFfiError* e = idevice_pairing_file_serialize(ptr_.get(), &data, &size)) { - err = FfiError(e); - return std::nullopt; + FfiError e(idevice_pairing_file_serialize(ptr_.get(), &data, &size)); + if (e) { + return Err(e); } std::vector out(data, data + size); idevice_data_free(data, size); - return out; + return Ok(out); } } // namespace IdeviceFFI diff --git a/cpp/src/provider.cpp b/cpp/src/provider.cpp index 6a5f606..7c47d56 100644 --- a/cpp/src/provider.cpp +++ b/cpp/src/provider.cpp @@ -6,58 +6,53 @@ namespace IdeviceFFI { -std::optional Provider::tcp_new(const idevice_sockaddr* ip, - PairingFile&& pairing, - const std::string& label, - FfiError& err) { +Result +Provider::tcp_new(const idevice_sockaddr* ip, PairingFile&& pairing, const std::string& label) { IdeviceProviderHandle* out = nullptr; - // Call with exact types; do NOT cast to void* - if (IdeviceFfiError* e = idevice_tcp_provider_new( - ip, static_cast(pairing.raw()), label.c_str(), &out)) { - err = FfiError(e); - return std::nullopt; + FfiError e(idevice_tcp_provider_new( + ip, static_cast(pairing.raw()), label.c_str(), &out)); + if (e) { + return Err(e); } // Success: Rust consumed the pairing file -> abandon our ownership - pairing.release(); // implement as: ptr_.release() in PairingFile + pairing.release(); - return Provider::adopt(out); + return Ok(Provider::adopt(out)); } -std::optional Provider::usbmuxd_new(UsbmuxdAddr&& addr, - uint32_t tag, - const std::string& udid, - uint32_t device_id, - const std::string& label, - FfiError& err) { +Result Provider::usbmuxd_new(UsbmuxdAddr&& addr, + uint32_t tag, + const std::string& udid, + uint32_t device_id, + const std::string& label) { IdeviceProviderHandle* out = nullptr; - if (IdeviceFfiError* e = usbmuxd_provider_new(static_cast(addr.raw()), - tag, - udid.c_str(), - device_id, - label.c_str(), - &out)) { - err = FfiError(e); - return std::nullopt; + FfiError e(usbmuxd_provider_new(static_cast(addr.raw()), + tag, + udid.c_str(), + device_id, + label.c_str(), + &out)); + if (e) { + return Err(e); } // Success: Rust consumed the addr -> abandon our ownership - addr.release(); // implement as: ptr_.release() in UsbmuxdAddr - - return Provider::adopt(out); + addr.release(); + return Ok(Provider::adopt(out)); } -std::optional Provider::get_pairing_file(FfiError& err) { +Result Provider::get_pairing_file() { IdevicePairingFile* out = nullptr; - if (IdeviceFfiError* e = idevice_provider_get_pairing_file(handle_.get(), &out)) { - err = FfiError(e); - return std::nullopt; + FfiError e(idevice_provider_get_pairing_file(handle_.get(), &out)); + if (e) { + return Err(e); } - - return PairingFile(out); + + return Ok(PairingFile(out)); } } // namespace IdeviceFFI diff --git a/cpp/src/remote_server.cpp b/cpp/src/remote_server.cpp index 90e1b06..acfc736 100644 --- a/cpp/src/remote_server.cpp +++ b/cpp/src/remote_server.cpp @@ -4,27 +4,26 @@ namespace IdeviceFFI { -std::optional RemoteServer::from_socket(ReadWrite&& rw, FfiError& err) { +Result RemoteServer::from_socket(ReadWrite&& rw) { RemoteServerHandle* out = nullptr; // Rust consumes the stream regardless of result, release BEFORE the call ReadWriteOpaque* raw = rw.release(); - if (IdeviceFfiError* e = ::remote_server_new(raw, &out)) { - err = FfiError(e); - return std::nullopt; + FfiError e(::remote_server_new(raw, &out)); + if (e) { + return Err(e); } - return RemoteServer::adopt(out); + return Ok(RemoteServer::adopt(out)); } -std::optional -RemoteServer::connect_rsd(Adapter& adapter, RsdHandshake& rsd, FfiError& err) { +Result RemoteServer::connect_rsd(Adapter& adapter, RsdHandshake& rsd) { RemoteServerHandle* out = nullptr; - if (IdeviceFfiError* e = ::remote_server_connect_rsd(adapter.raw(), rsd.raw(), &out)) { - err = FfiError(e); - return std::nullopt; + FfiError e(::remote_server_connect_rsd(adapter.raw(), rsd.raw(), &out)); + if (e) { + return Err(e); } - return RemoteServer::adopt(out); + return Ok(RemoteServer::adopt(out)); } } // namespace IdeviceFFI diff --git a/cpp/src/rsd.cpp b/cpp/src/rsd.cpp index 0511c54..281b09d 100644 --- a/cpp/src/rsd.cpp +++ b/cpp/src/rsd.cpp @@ -7,10 +7,12 @@ namespace IdeviceFFI { // ---------- helpers to copy/free CRsdService ---------- static RsdService to_cpp_and_free(CRsdService* c) { RsdService s; - if (c->name) + if (c->name) { s.name = c->name; - if (c->entitlement) + } + if (c->entitlement) { s.entitlement = c->entitlement; + } s.port = c->port; s.uses_remote_xpc = c->uses_remote_xpc; s.service_version = c->service_version; @@ -20,8 +22,9 @@ static RsdService to_cpp_and_free(CRsdService* c) { auto** arr = c->features; s.features.reserve(c->features_count); for (size_t i = 0; i < c->features_count; ++i) { - if (arr[i]) + if (arr[i]) { s.features.emplace_back(arr[i]); + } } } @@ -33,8 +36,9 @@ static RsdService to_cpp_and_free(CRsdService* c) { static std::vector to_cpp_and_free(CRsdServiceArray* arr) { std::vector out; if (!arr || !arr->services || arr->count == 0) { - if (arr) + if (arr) { rsd_free_services(arr); + } return out; } out.reserve(arr->count); @@ -51,8 +55,9 @@ static std::vector to_cpp_and_free(CRsdServiceArray* arr) { auto** feats = begin[i].features; out.back().features.reserve(begin[i].features_count); for (size_t j = 0; j < begin[i].features_count; ++j) { - if (feats[j]) + if (feats[j]) { out.back().features.emplace_back(feats[j]); + } } } } @@ -80,68 +85,67 @@ RsdHandshake& RsdHandshake::operator=(const RsdHandshake& other) { } // ---------- factory ---------- -std::optional RsdHandshake::from_socket(ReadWrite&& rw, FfiError& err) { +Result RsdHandshake::from_socket(ReadWrite&& rw) { RsdHandshakeHandle* out = nullptr; // Rust consumes the socket regardless of result ⇒ release BEFORE call. ReadWriteOpaque* raw = rw.release(); - - if (IdeviceFfiError* e = rsd_handshake_new(raw, &out)) { - err = FfiError(e); - return std::nullopt; + FfiError e(rsd_handshake_new(raw, &out)); + if (e) { + return Err(e); } - return RsdHandshake::adopt(out); + return Ok(RsdHandshake::adopt(out)); } // ---------- queries ---------- -std::optional RsdHandshake::protocol_version(FfiError& err) const { - size_t v = 0; - if (IdeviceFfiError* e = rsd_get_protocol_version(handle_.get(), &v)) { - err = FfiError(e); - return std::nullopt; +Result RsdHandshake::protocol_version() const { + size_t v = 0; + FfiError e(rsd_get_protocol_version(handle_.get(), &v)); + if (e) { + return Err(e); } - return v; + return Ok(v); } -std::optional RsdHandshake::uuid(FfiError& err) const { - char* c = nullptr; - if (IdeviceFfiError* e = rsd_get_uuid(handle_.get(), &c)) { - err = FfiError(e); - return std::nullopt; +Result RsdHandshake::uuid() const { + char* c = nullptr; + FfiError e(rsd_get_uuid(handle_.get(), &c)); + if (e) { + return Err(e); } std::string out; if (c) { out = c; rsd_free_string(c); } - return out; + return Ok(out); } -std::optional> RsdHandshake::services(FfiError& err) const { +Result, FfiError> RsdHandshake::services() const { CRsdServiceArray* arr = nullptr; - if (IdeviceFfiError* e = rsd_get_services(handle_.get(), &arr)) { - err = FfiError(e); - return std::nullopt; + FfiError e(rsd_get_services(handle_.get(), &arr)); + if (e) { + return Err(e); } - return to_cpp_and_free(arr); + return Ok(to_cpp_and_free(arr)); } -std::optional RsdHandshake::service_available(const std::string& name, FfiError& err) const { - bool avail = false; - if (IdeviceFfiError* e = rsd_service_available(handle_.get(), name.c_str(), &avail)) { - err = FfiError(e); - return std::nullopt; +Result RsdHandshake::service_available(const std::string& name) const { + bool avail = false; + FfiError e(rsd_service_available(handle_.get(), name.c_str(), &avail)); + if (e) { + return Err(e); } - return avail; + return Ok(avail); } -std::optional RsdHandshake::service_info(const std::string& name, FfiError& err) const { +Result RsdHandshake::service_info(const std::string& name) const { CRsdService* svc = nullptr; - if (IdeviceFfiError* e = rsd_get_service_info(handle_.get(), name.c_str(), &svc)) { - err = FfiError(e); - return std::nullopt; + FfiError e(rsd_get_service_info(handle_.get(), name.c_str(), &svc)); + if (e) { + return Err(e); } - return to_cpp_and_free(svc); + return Ok(to_cpp_and_free(svc)); } } // namespace IdeviceFFI diff --git a/cpp/src/tcp_callback_feeder.cpp b/cpp/src/tcp_callback_feeder.cpp index 9967554..07c2e6b 100644 --- a/cpp/src/tcp_callback_feeder.cpp +++ b/cpp/src/tcp_callback_feeder.cpp @@ -28,16 +28,16 @@ bool TcpObjectStackEater::read(OwnedBuffer& out, FfiError& err) const { } // ---------- TcpStackFromCallback ---------- -std::optional -TcpObjectStack::create(const std::string& our_ip, const std::string& their_ip, FfiError& err) { +Result TcpObjectStack::create(const std::string& our_ip, + const std::string& their_ip) { ::TcpFeedObject* feeder_h = nullptr; ::TcpEatObject* eater_h = nullptr; ::AdapterHandle* adapter_h = nullptr; - if (IdeviceFfiError* e = ::idevice_tcp_stack_into_sync_objects( - our_ip.c_str(), their_ip.c_str(), &feeder_h, &eater_h, &adapter_h)) { - err = FfiError(e); - return std::nullopt; + FfiError e(::idevice_tcp_stack_into_sync_objects( + our_ip.c_str(), their_ip.c_str(), &feeder_h, &eater_h, &adapter_h)); + if (e) { + return Err(e); } auto impl = std::make_unique(); @@ -47,7 +47,7 @@ TcpObjectStack::create(const std::string& our_ip, const std::string& their_ip, F TcpObjectStack out; out.impl_ = std::move(impl); - return out; + return Ok(std::move(out)); } TcpObjectStackFeeder& TcpObjectStack::feeder() { @@ -65,49 +65,54 @@ const TcpObjectStackEater& TcpObjectStack::eater() const { } Adapter& TcpObjectStack::adapter() { - if (!impl_ || !impl_->adapter) { + if (!impl_ || impl_->adapter.is_some()) { static Adapter* never = nullptr; return *never; } - return *(impl_->adapter); + return (impl_->adapter.unwrap()); } const Adapter& TcpObjectStack::adapter() const { - if (!impl_ || !impl_->adapter) { + if (!impl_ || impl_->adapter.is_none()) { static Adapter* never = nullptr; return *never; } - return *(impl_->adapter); + return (impl_->adapter.unwrap()); } // ---------- Release APIs ---------- -std::optional TcpObjectStack::release_feeder() { - if (!impl_) - return std::nullopt; +Option TcpObjectStack::release_feeder() { + if (!impl_) { + return None; + } auto has = impl_->feeder.raw() != nullptr; - if (!has) - return std::nullopt; + if (!has) { + return None; + } TcpObjectStackFeeder out = std::move(impl_->feeder); // impl_->feeder is now empty (h_ == nullptr) thanks to move - return std::optional(std::move(out)); + return Some(std::move(out)); } -std::optional TcpObjectStack::release_eater() { - if (!impl_) - return std::nullopt; +Option TcpObjectStack::release_eater() { + if (!impl_) { + return None; + } auto has = impl_->eater.raw() != nullptr; - if (!has) - return std::nullopt; + if (!has) { + return None; + } TcpObjectStackEater out = std::move(impl_->eater); - return std::optional(std::move(out)); + return Some(std::move(out)); } -std::optional TcpObjectStack::release_adapter() { - if (!impl_ || !impl_->adapter) - return std::nullopt; +Option TcpObjectStack::release_adapter() { + if (!impl_ || impl_->adapter.is_none()) { + return None; + } // Move out and clear our optional - auto out = std::move(*(impl_->adapter)); + auto out = std::move((impl_->adapter.unwrap())); impl_->adapter.reset(); - return std::optional(std::move(out)); + return Some(std::move(out)); } } // namespace IdeviceFFI diff --git a/cpp/src/usbmuxd.cpp b/cpp/src/usbmuxd.cpp index 1ae2bba..5c4fb04 100644 --- a/cpp/src/usbmuxd.cpp +++ b/cpp/src/usbmuxd.cpp @@ -6,24 +6,23 @@ namespace IdeviceFFI { // ---------- UsbmuxdAddr ---------- -std::optional -UsbmuxdAddr::tcp_new(const sockaddr* addr, socklen_t addr_len, FfiError& err) { +Result UsbmuxdAddr::tcp_new(const sockaddr* addr, socklen_t addr_len) { UsbmuxdAddrHandle* h = nullptr; - if (IdeviceFfiError* e = idevice_usbmuxd_tcp_addr_new(addr, addr_len, &h)) { - err = FfiError(e); - return std::nullopt; + FfiError e(idevice_usbmuxd_tcp_addr_new(addr, addr_len, &h)); + if (e) { + return Err(e); } - return UsbmuxdAddr(h); + return Ok(UsbmuxdAddr(h)); } #if defined(__unix__) || defined(__APPLE__) -std::optional UsbmuxdAddr::unix_new(const std::string& path, FfiError& err) { +Result UsbmuxdAddr::unix_new(const std::string& path) { UsbmuxdAddrHandle* h = nullptr; - if (IdeviceFfiError* e = idevice_usbmuxd_unix_addr_new(path.c_str(), &h)) { - err = FfiError(e); - return std::nullopt; + FfiError e(idevice_usbmuxd_unix_addr_new(path.c_str(), &h)); + if (e) { + return Err(e); } - return UsbmuxdAddr(h); + return Ok(UsbmuxdAddr(h)); } #endif @@ -48,114 +47,109 @@ std::string UsbmuxdConnectionType::to_string() const { } // ---------- UsbmuxdDevice ---------- -std::optional UsbmuxdDevice::get_udid() const { +Option UsbmuxdDevice::get_udid() const { char* c = idevice_usbmuxd_device_get_udid(handle_.get()); - if (!c) - return std::nullopt; + if (!c) { + return None; + } std::string out(c); idevice_string_free(c); - return out; + return Some(out); } -std::optional UsbmuxdDevice::get_id() const { +Option UsbmuxdDevice::get_id() const { uint32_t id = idevice_usbmuxd_device_get_device_id(handle_.get()); - if (id == 0) - return std::nullopt; // adjust if 0 can be valid - return id; + if (id == 0) { + return None; + } + return Some(id); } -std::optional UsbmuxdDevice::get_connection_type() const { +Option UsbmuxdDevice::get_connection_type() const { uint8_t t = idevice_usbmuxd_device_get_connection_type(handle_.get()); - if (t == 0) - return std::nullopt; - return UsbmuxdConnectionType(t); + if (t == 0) { + return None; + } + return Some(UsbmuxdConnectionType(t)); } // ---------- UsbmuxdConnection ---------- -std::optional UsbmuxdConnection::tcp_new(const idevice_sockaddr* addr, - idevice_socklen_t addr_len, - uint32_t tag, - FfiError& err) { +Result +UsbmuxdConnection::tcp_new(const idevice_sockaddr* addr, idevice_socklen_t addr_len, uint32_t tag) { UsbmuxdConnectionHandle* h = nullptr; - if (IdeviceFfiError* e = idevice_usbmuxd_new_tcp_connection(addr, addr_len, tag, &h)) { - err = FfiError(e); - return std::nullopt; + FfiError e(idevice_usbmuxd_new_tcp_connection(addr, addr_len, tag, &h)); + if (e) { + return Err(e); } - return UsbmuxdConnection(h); + return Ok(UsbmuxdConnection(h)); } #if defined(__unix__) || defined(__APPLE__) -std::optional -UsbmuxdConnection::unix_new(const std::string& path, uint32_t tag, FfiError& err) { +Result UsbmuxdConnection::unix_new(const std::string& path, + uint32_t tag) { UsbmuxdConnectionHandle* h = nullptr; - if (IdeviceFfiError* e = idevice_usbmuxd_new_unix_socket_connection(path.c_str(), tag, &h)) { - err = FfiError(e); - return std::nullopt; + FfiError e(idevice_usbmuxd_new_unix_socket_connection(path.c_str(), tag, &h)); + if (e) { + return Err(e); } - return UsbmuxdConnection(h); + return Ok(UsbmuxdConnection(h)); } #endif -std::optional UsbmuxdConnection::default_new(uint32_t tag, FfiError& err) { +Result UsbmuxdConnection::default_new(uint32_t tag) { UsbmuxdConnectionHandle* h = nullptr; - if (IdeviceFfiError* e = idevice_usbmuxd_new_default_connection(tag, &h)) { - err = FfiError(e); - return std::nullopt; + FfiError e(idevice_usbmuxd_new_default_connection(tag, &h)); + if (e) { + return Err(e); } - return UsbmuxdConnection(h); + return Ok(UsbmuxdConnection(h)); } -std::optional> UsbmuxdConnection::get_devices(FfiError& err) const { +Result, FfiError> UsbmuxdConnection::get_devices() const { UsbmuxdDeviceHandle** list = nullptr; int count = 0; - if (IdeviceFfiError* e = idevice_usbmuxd_get_devices(handle_.get(), &list, &count)) { - err = FfiError(e); - return std::nullopt; + FfiError e(idevice_usbmuxd_get_devices(handle_.get(), &list, &count)); + if (e) { + return Err(e); } std::vector out; out.reserve(count); for (int i = 0; i < count; ++i) { out.emplace_back(UsbmuxdDevice::adopt(list[i])); } - return out; + return Ok(std::move(out)); } -std::optional UsbmuxdConnection::get_buid(FfiError& err) const { - char* c = nullptr; - if (IdeviceFfiError* e = idevice_usbmuxd_get_buid(handle_.get(), &c)) { - err = FfiError(e); - return std::nullopt; +Result UsbmuxdConnection::get_buid() const { + char* c = nullptr; + FfiError e(idevice_usbmuxd_get_buid(handle_.get(), &c)); + if (e) { + return Err(e); } std::string out(c); idevice_string_free(c); - return out; + return Ok(out); } -std::optional UsbmuxdConnection::get_pair_record(const std::string& udid, - FfiError& err) { +Result UsbmuxdConnection::get_pair_record(const std::string& udid) { IdevicePairingFile* pf = nullptr; - if (IdeviceFfiError* e = idevice_usbmuxd_get_pair_record(handle_.get(), udid.c_str(), &pf)) { - err = FfiError(e); - return std::nullopt; + FfiError e(idevice_usbmuxd_get_pair_record(handle_.get(), udid.c_str(), &pf)); + if (e) { + return Err(e); } - return PairingFile(pf); + return Ok(PairingFile(pf)); } -std::optional UsbmuxdConnection::connect_to_device(uint32_t device_id, - uint16_t port, - const std::string& path, - FfiError& err) && { +Result UsbmuxdConnection::connect_to_device(uint32_t device_id, + uint16_t port, + const std::string& path) && { UsbmuxdConnectionHandle* raw = handle_.release(); - IdeviceHandle* out = nullptr; - IdeviceFfiError* e = - idevice_usbmuxd_connect_to_device(raw, device_id, port, path.c_str(), &out); - + FfiError e(idevice_usbmuxd_connect_to_device(raw, device_id, port, path.c_str(), &out)); if (e) { - err = FfiError(e); - return std::nullopt; + return Err(e); } - return Idevice::adopt(out); + return Ok(Idevice::adopt(out)); } } // namespace IdeviceFFI