diff options
| author | mojganii <mojgan.jelodar@codic.se> | 2025-03-26 15:24:08 +0100 |
|---|---|---|
| committer | mojganii <mojgan.jelodar@codic.se> | 2025-04-15 15:38:45 +0200 |
| commit | 4f52ef7f5f4072ff53d1ddc1f358cbf3e35a6d14 (patch) | |
| tree | 610ce7ffe5e27b92bd4621e63e0e000a453ff802 | |
| parent | f8aeeb42fa5cc7f1cc428bb4b34cb8d5de09bb51 (diff) | |
| download | mullvadvpn-4f52ef7f5f4072ff53d1ddc1f358cbf3e35a6d14.tar.xz mullvadvpn-4f52ef7f5f4072ff53d1ddc1f358cbf3e35a6d14.zip | |
Add metadata support to send problem report
| -rw-r--r-- | ios/MullvadREST/ApiHandlers/RESTAPIProxy.swift | 14 | ||||
| -rw-r--r-- | ios/MullvadREST/Extensions/String+UnsafePointer.swift | 8 | ||||
| -rw-r--r-- | ios/MullvadREST/MullvadAPI/APIHandlers/MullvadAPIProxy.swift | 2 | ||||
| -rw-r--r-- | ios/MullvadREST/MullvadAPI/MullvadApiRequestFactory.swift | 38 | ||||
| -rw-r--r-- | ios/MullvadREST/Rust/RustProblemReportRequest.swift | 57 | ||||
| -rw-r--r-- | ios/MullvadRustRuntime/MullvadApiCancellable.swift | 5 | ||||
| -rw-r--r-- | ios/MullvadRustRuntime/include/mullvad_rust_runtime.h | 17 | ||||
| -rw-r--r-- | ios/MullvadVPN.xcodeproj/project.pbxproj | 16 | ||||
| -rw-r--r-- | ios/MullvadVPN/View controllers/ProblemReport/ProblemReportInteractor.swift | 2 | ||||
| -rw-r--r-- | mullvad-api/src/lib.rs | 3 | ||||
| -rw-r--r-- | mullvad-ios/src/api_client/response.rs | 1 | ||||
| -rw-r--r-- | mullvad-ios/src/api_client/send_problem_report.rs | 108 |
12 files changed, 206 insertions, 65 deletions
diff --git a/ios/MullvadREST/ApiHandlers/RESTAPIProxy.swift b/ios/MullvadREST/ApiHandlers/RESTAPIProxy.swift index 39585a88b7..d45745446e 100644 --- a/ios/MullvadREST/ApiHandlers/RESTAPIProxy.swift +++ b/ios/MullvadREST/ApiHandlers/RESTAPIProxy.swift @@ -263,20 +263,6 @@ extension REST { let newExpiry: Date } - public struct ProblemReportRequest: Codable, Sendable { - public let address: String - public let message: String - public let log: String - public let metadata: [String: String] - - public init(address: String, message: String, log: String, metadata: [String: String]) { - self.address = address - self.message = message - self.log = log - self.metadata = metadata - } - } - private struct SubmitVoucherRequest: Encodable, Sendable { let voucherCode: String } diff --git a/ios/MullvadREST/Extensions/String+UnsafePointer.swift b/ios/MullvadREST/Extensions/String+UnsafePointer.swift index 4d60a1c77e..bd699bd2a8 100644 --- a/ios/MullvadREST/Extensions/String+UnsafePointer.swift +++ b/ios/MullvadREST/Extensions/String+UnsafePointer.swift @@ -7,10 +7,14 @@ // import Foundation extension String { - func toUnsafePointer() -> UnsafePointer<UInt8>? { + /// Converts the `String` to an `UnsafePointer<UInt8>`. The memory allocated for the pointer + /// is not null-terminated, so the caller must ensure that they manage the memory properly. + func toUnsafePointer() -> (pointer: UnsafePointer<UInt8>?, size: UInt)? { guard let data = self.data(using: .utf8) else { return nil } + let buffer = UnsafeMutablePointer<UInt8>.allocate(capacity: data.count) data.copyBytes(to: buffer, count: data.count) - return UnsafePointer(buffer) + + return (UnsafePointer(buffer), UInt(data.count)) } } diff --git a/ios/MullvadREST/MullvadAPI/APIHandlers/MullvadAPIProxy.swift b/ios/MullvadREST/MullvadAPI/APIHandlers/MullvadAPIProxy.swift index 5d2555f07f..4350161a3a 100644 --- a/ios/MullvadREST/MullvadAPI/APIHandlers/MullvadAPIProxy.swift +++ b/ios/MullvadREST/MullvadAPI/APIHandlers/MullvadAPIProxy.swift @@ -201,7 +201,7 @@ extension REST { let newExpiry: Date } - public struct ProblemReportRequest: Encodable, Sendable { + public struct ProblemReportRequest: Codable, Sendable { public let address: String public let message: String public let log: String diff --git a/ios/MullvadREST/MullvadAPI/MullvadApiRequestFactory.swift b/ios/MullvadREST/MullvadAPI/MullvadApiRequestFactory.swift index 970f70ef76..647b1b71be 100644 --- a/ios/MullvadREST/MullvadAPI/MullvadApiRequestFactory.swift +++ b/ios/MullvadREST/MullvadAPI/MullvadApiRequestFactory.swift @@ -24,43 +24,44 @@ public struct MullvadApiRequestFactory: Sendable { let rawCompletionPointer = Unmanaged.passRetained(completionPointer).toOpaque() - return switch request { + switch request { case let .getAddressList(retryStrategy): - MullvadApiCancellable(handle: mullvad_api_get_addresses( + return MullvadApiCancellable(handle: mullvad_api_get_addresses( apiContext.context, rawCompletionPointer, retryStrategy.toRustStrategy() )) case let .getRelayList(retryStrategy, etag: etag): - MullvadApiCancellable(handle: mullvad_api_get_relays( + return MullvadApiCancellable(handle: mullvad_api_get_relays( apiContext.context, rawCompletionPointer, retryStrategy.toRustStrategy(), etag )) case let .sendProblemReport(retryStrategy, problemReportRequest): - MullvadApiCancellable(handle: mullvad_api_send_problem_report( + let rustRequest = RustProblemReportRequest(from: problemReportRequest) + return MullvadApiCancellable(handle: mullvad_api_send_problem_report( apiContext.context, rawCompletionPointer, retryStrategy.toRustStrategy(), - problemReportRequest.toRust() + rustRequest.toRust() )) case let .getAccount(retryStrategy, accountNumber: accountNumber): - MullvadApiCancellable(handle: mullvad_api_get_account( + return MullvadApiCancellable(handle: mullvad_api_get_account( apiContext.context, rawCompletionPointer, retryStrategy.toRustStrategy(), accountNumber )) case let .createAccount(retryStrategy): - MullvadApiCancellable(handle: mullvad_api_create_account( + return MullvadApiCancellable(handle: mullvad_api_create_account( apiContext.context, rawCompletionPointer, retryStrategy.toRustStrategy() )) case let .deleteAccount(retryStrategy, accountNumber: accountNumber): - MullvadApiCancellable(handle: mullvad_api_delete_account( + return MullvadApiCancellable(handle: mullvad_api_delete_account( apiContext.context, rawCompletionPointer, retryStrategy.toRustStrategy(), @@ -74,24 +75,3 @@ public struct MullvadApiRequestFactory: Sendable { extension REST { public typealias MullvadApiRequestHandler = (((MullvadApiResponse) throws -> Void)?) -> MullvadApiCancellable } - -private extension REST.ProblemReportRequest { - func toRust() -> UnsafePointer<SwiftProblemReportRequest> { - let structPointer = UnsafeMutablePointer<SwiftProblemReportRequest>.allocate(capacity: 1) - - let addressPointer = address.toUnsafePointer() - let messagePointer = message.toUnsafePointer() - let logPointer = log.toUnsafePointer() - - structPointer.initialize(to: SwiftProblemReportRequest( - address: addressPointer, - address_len: UInt(address.utf8.count), - message: messagePointer, - message_len: UInt(message.utf8.count), - log: logPointer, - log_len: UInt(log.utf8.count) - )) - - return UnsafePointer(structPointer) - } -} diff --git a/ios/MullvadREST/Rust/RustProblemReportRequest.swift b/ios/MullvadREST/Rust/RustProblemReportRequest.swift new file mode 100644 index 0000000000..8bd8a4490f --- /dev/null +++ b/ios/MullvadREST/Rust/RustProblemReportRequest.swift @@ -0,0 +1,57 @@ +// +// RustProblemReportRequest.swift +// MullvadVPN +// +// Created by Mojgan on 2025-03-21. +// Copyright © 2025 Mullvad VPN AB. All rights reserved. +// +import MullvadLogging +import MullvadRustRuntime + +final public class RustProblemReportRequest { + typealias StringPointer = (pointer: UnsafePointer<UInt8>?, size: UInt)? + private let logger = Logger(label: "RustProblemReportRequest") + private let problemReportMetaData: ProblemReportMetadata + private let addressPointer: StringPointer + private let messagePointer: StringPointer + private let logPointer: StringPointer + public init(from request: REST.ProblemReportRequest) { + addressPointer = request.address.toUnsafePointer() + messagePointer = request.message.toUnsafePointer() + logPointer = request.log.toUnsafePointer() + + self.problemReportMetaData = swift_problem_report_meta_data_new() + + for (key, value) in request.metadata { + key.withCString { keyPtr in + value.withCString { valuePtr in + if swift_problem_report_meta_data_add(problemReportMetaData, keyPtr, valuePtr) == false { + logger + .error( + "Failed to add metadata. Key: '\(key)' might be invalid or contain unsupported characters." + ) + } + } + } + } + } + + public func toRust() -> SwiftProblemReportRequest { + return SwiftProblemReportRequest( + address: addressPointer?.pointer ?? nil, + address_len: addressPointer?.size ?? 0, + message: messagePointer?.pointer ?? nil, + message_len: messagePointer?.size ?? 0, + log: logPointer?.pointer ?? nil, + log_len: logPointer?.size ?? 0, + meta_data: problemReportMetaData + ) + } + + public func release() { + swift_problem_report_meta_data_free(problemReportMetaData) + addressPointer?.pointer?.deallocate() + messagePointer?.pointer?.deallocate() + logPointer?.pointer?.deallocate() + } +} diff --git a/ios/MullvadRustRuntime/MullvadApiCancellable.swift b/ios/MullvadRustRuntime/MullvadApiCancellable.swift index 6c76bc3c14..3c7c184096 100644 --- a/ios/MullvadRustRuntime/MullvadApiCancellable.swift +++ b/ios/MullvadRustRuntime/MullvadApiCancellable.swift @@ -10,12 +10,15 @@ import MullvadTypes public class MullvadApiCancellable: Cancellable { private let handle: SwiftCancelHandle + private let deinitializer: (() -> Void)? - public init(handle: consuming SwiftCancelHandle) { + public init(handle: consuming SwiftCancelHandle, deinitializer: (() -> Void)? = nil) { self.handle = handle + self.deinitializer = deinitializer } deinit { + deinitializer?() mullvad_api_cancel_task_drop(handle) } diff --git a/ios/MullvadRustRuntime/include/mullvad_rust_runtime.h b/ios/MullvadRustRuntime/include/mullvad_rust_runtime.h index 77dac6b4da..958144c802 100644 --- a/ios/MullvadRustRuntime/include/mullvad_rust_runtime.h +++ b/ios/MullvadRustRuntime/include/mullvad_rust_runtime.h @@ -24,6 +24,8 @@ typedef struct EncryptedDnsProxyState EncryptedDnsProxyState; typedef struct ExchangeCancelToken ExchangeCancelToken; +typedef struct Map Map; + typedef struct RequestCancelHandle RequestCancelHandle; typedef struct RetryStrategy RetryStrategy; @@ -54,6 +56,10 @@ typedef struct CompletionCookie { void *inner; } CompletionCookie; +typedef struct ProblemReportMetadata { + struct Map *inner; +} ProblemReportMetadata; + typedef struct SwiftProblemReportRequest { const uint8_t *address; uintptr_t address_len; @@ -61,6 +67,7 @@ typedef struct SwiftProblemReportRequest { uintptr_t message_len; const uint8_t *log; uintptr_t log_len; + struct ProblemReportMetadata meta_data; } SwiftProblemReportRequest; typedef struct ProxyHandle { @@ -270,7 +277,15 @@ struct SwiftRetryStrategy mullvad_api_retry_strategy_exponential(uintptr_t max_r struct SwiftCancelHandle mullvad_api_send_problem_report(struct SwiftApiContext api_context, void *completion_cookie, struct SwiftRetryStrategy retry_strategy, - const struct SwiftProblemReportRequest *request); + struct SwiftProblemReportRequest request); + +struct ProblemReportMetadata swift_problem_report_meta_data_new(void); + +bool swift_problem_report_meta_data_add(struct ProblemReportMetadata map, + const char *key, + const char *value); + +void swift_problem_report_meta_data_free(struct ProblemReportMetadata map); /** * Initializes a valid pointer to an instance of `EncryptedDnsProxyState`. diff --git a/ios/MullvadVPN.xcodeproj/project.pbxproj b/ios/MullvadVPN.xcodeproj/project.pbxproj index 8c64f72340..7adfaf5e2d 100644 --- a/ios/MullvadVPN.xcodeproj/project.pbxproj +++ b/ios/MullvadVPN.xcodeproj/project.pbxproj @@ -1081,6 +1081,7 @@ F0E8E4C32A602E0D00ED26A3 /* AccountDeletionViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = F0E8E4C22A602E0D00ED26A3 /* AccountDeletionViewModel.swift */; }; F0E8E4C52A60499100ED26A3 /* AccountDeletionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F0E8E4C42A60499100ED26A3 /* AccountDeletionViewController.swift */; }; F0E8E4C92A604E7400ED26A3 /* AccountDeletionInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = F0E8E4C82A604E7400ED26A3 /* AccountDeletionInteractor.swift */; }; + F0EEFBA22D8D61A9007FE4B3 /* RustProblemReportRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = F0EEFB9E2D8D60E1007FE4B3 /* RustProblemReportRequest.swift */; }; F0EF50D52A949F8E0031E8DF /* ChangeLogViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = F0EF50D42A949F8E0031E8DF /* ChangeLogViewModel.swift */; }; F0F316192BF3572B0078DBCF /* RelaySelectorResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = F0F316182BF3572B0078DBCF /* RelaySelectorResult.swift */; }; F0F3161B2BF358590078DBCF /* NoRelaysSatisfyingConstraintsError.swift in Sources */ = {isa = PBXBuildFile; fileRef = F0F3161A2BF358590078DBCF /* NoRelaysSatisfyingConstraintsError.swift */; }; @@ -2481,6 +2482,7 @@ F0E8E4C22A602E0D00ED26A3 /* AccountDeletionViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountDeletionViewModel.swift; sourceTree = "<group>"; }; F0E8E4C42A60499100ED26A3 /* AccountDeletionViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountDeletionViewController.swift; sourceTree = "<group>"; }; F0E8E4C82A604E7400ED26A3 /* AccountDeletionInteractor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountDeletionInteractor.swift; sourceTree = "<group>"; }; + F0EEFB9E2D8D60E1007FE4B3 /* RustProblemReportRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RustProblemReportRequest.swift; sourceTree = "<group>"; }; F0EF50D42A949F8E0031E8DF /* ChangeLogViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChangeLogViewModel.swift; sourceTree = "<group>"; }; F0F1EF8C2BE8FF0A00CED01D /* LaunchArguments.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LaunchArguments.swift; sourceTree = "<group>"; }; F0F316182BF3572B0078DBCF /* RelaySelectorResult.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RelaySelectorResult.swift; sourceTree = "<group>"; }; @@ -2700,6 +2702,7 @@ F0DC779F2B2222D20087F09D /* Relay */, 06FAE67B28F83CA50033DD93 /* REST.swift */, F0DC77A12B2313330087F09D /* RetryStrategy */, + F0EEFBA02D8D6133007FE4B3 /* Rust */, F0DC77A02B2223290087F09D /* Transport */, ); path = MullvadREST; @@ -4513,16 +4516,16 @@ A992DA1E2C24709F00DE7CE5 /* MullvadRustRuntime */ = { isa = PBXGroup; children = ( - A9D9A4D32C36E1EA004088DD /* mullvad_rust_runtime.h */, - A992DA1F2C24709F00DE7CE5 /* MullvadRustRuntime.h */, 014449942CA293B100C0C2F2 /* EncryptedDNSProxy.swift */, A948809A2BC9308D0090A44C /* EphemeralPeerExchangeActor.swift */, A9EB4F9C2B7FAB21002A2D7A /* EphemeralPeerNegotiator.swift */, A9A557F42B7E3E5C0017ADA8 /* EphemeralPeerReceiver.swift */, + A9D9A4D32C36E1EA004088DD /* mullvad_rust_runtime.h */, 7A99D3702D56220E00891FF7 /* MullvadApiCancellable.swift */, 7A3215702D392F0B005DF395 /* MullvadApiCompletion.swift */, 7AB931232D43C2C2005FCEBA /* MullvadApiContext.swift */, 7AB931252D43D222005FCEBA /* MullvadApiResponse.swift */, + A992DA1F2C24709F00DE7CE5 /* MullvadRustRuntime.h */, F0DDE40F2B220458006B57A7 /* ShadowSocksProxy.swift */, 584023212A406BF5007B27AC /* TunnelObfuscator.swift */, ); @@ -4798,6 +4801,14 @@ path = AccountDeletion; sourceTree = "<group>"; }; + F0EEFBA02D8D6133007FE4B3 /* Rust */ = { + isa = PBXGroup; + children = ( + F0EEFB9E2D8D60E1007FE4B3 /* RustProblemReportRequest.swift */, + ); + path = Rust; + sourceTree = "<group>"; + }; F0EF50D12A8FA47E0031E8DF /* ChangeLog */ = { isa = PBXGroup; children = ( @@ -5797,6 +5808,7 @@ F0B894EF2BF751C500817A42 /* RelayWithLocation.swift in Sources */, F0DDE42C2B220A15006B57A7 /* Midpoint.swift in Sources */, A90763C72B2858DC0045ADF0 /* CancellableChain.swift in Sources */, + F0EEFBA22D8D61A9007FE4B3 /* RustProblemReportRequest.swift in Sources */, 7AB9312F2D4A5D0A005FCEBA /* MullvadApiNetworkOperation.swift in Sources */, 06799AF128F98E4800ACD94E /* RESTAPIProxy.swift in Sources */, F0DDE42A2B220A15006B57A7 /* Haversine.swift in Sources */, diff --git a/ios/MullvadVPN/View controllers/ProblemReport/ProblemReportInteractor.swift b/ios/MullvadVPN/View controllers/ProblemReport/ProblemReportInteractor.swift index 55fc1df7d3..18bf3c88a5 100644 --- a/ios/MullvadVPN/View controllers/ProblemReport/ProblemReportInteractor.swift +++ b/ios/MullvadVPN/View controllers/ProblemReport/ProblemReportInteractor.swift @@ -80,7 +80,7 @@ final class ProblemReportInteractor: @unchecked Sendable { metadata: metadataDict ) - _ = self.apiProxy.mullvadSendProblemReport(request, retryStrategy: .default, completionHandler: { result in + _ = self.apiProxy.sendProblemReport(request, retryStrategy: .default, completionHandler: { result in DispatchQueue.main.async { completion(result) } diff --git a/mullvad-api/src/lib.rs b/mullvad-api/src/lib.rs index eb3589ac67..8c2a473925 100644 --- a/mullvad-api/src/lib.rs +++ b/mullvad-api/src/lib.rs @@ -724,7 +724,8 @@ impl ProblemReportProxy { let request = factory .post_json(&format!("{APP_URL_PREFIX}/problem-report"), &report)? .expected_status(&[StatusCode::NO_CONTENT]); - service.request(request).await + service.request(request).await?; + Ok(()) } } } diff --git a/mullvad-ios/src/api_client/response.rs b/mullvad-ios/src/api_client/response.rs index 3b017e37c6..829c945e8f 100644 --- a/mullvad-ios/src/api_client/response.rs +++ b/mullvad-ios/src/api_client/response.rs @@ -3,6 +3,7 @@ use std::{ ptr::{self, null_mut}, }; +use mullvad_api::StatusCode; use mullvad_api::{ rest::{self, Response}, RelayListProxy, StatusCode, diff --git a/mullvad-ios/src/api_client/send_problem_report.rs b/mullvad-ios/src/api_client/send_problem_report.rs index 1d2547298f..bb1c8f5f43 100644 --- a/mullvad-ios/src/api_client/send_problem_report.rs +++ b/mullvad-ios/src/api_client/send_problem_report.rs @@ -2,6 +2,8 @@ use mullvad_api::{ rest::{self, MullvadRestHandle}, ProblemReportProxy, }; +use std::ffi::CStr; +use std::os::raw::c_char; use talpid_future::retry::retry_future; use super::{ @@ -22,7 +24,7 @@ pub unsafe extern "C" fn mullvad_api_send_problem_report( api_context: SwiftApiContext, completion_cookie: *mut libc::c_void, retry_strategy: SwiftRetryStrategy, - request: *const SwiftProblemReportRequest, + request: SwiftProblemReportRequest, ) -> SwiftCancelHandle { let completion_handler = SwiftCompletionHandler::new(CompletionCookie(completion_cookie)); let completion = completion_handler.clone(); @@ -75,14 +77,13 @@ async fn mullvad_api_send_problem_report_inner( problem_report_request: ProblemReportRequest, ) -> Result<SwiftMullvadApiResponse, rest::Error> { let api = ProblemReportProxy::new(rest_client); - let empty_metadata: BTreeMap<String, String> = BTreeMap::new(); let future_factory = || { - api.porblem_report_response( + api.problem_report( &problem_report_request.address, &problem_report_request.message, &(String::from_utf8_lossy(&problem_report_request.log)), - &empty_metadata, + &problem_report_request.meta_data, ) }; @@ -91,8 +92,8 @@ async fn mullvad_api_send_problem_report_inner( Ok(_) => false, }; - let response = retry_future(future_factory, should_retry, retry_strategy.delays()).await?; - SwiftMullvadApiResponse::with_body(response).await + retry_future(future_factory, should_retry, retry_strategy.delays()).await?; + SwiftMullvadApiResponse::ok().await } #[repr(C)] @@ -103,24 +104,20 @@ pub struct SwiftProblemReportRequest { message_len: usize, log: *const u8, log_len: usize, + meta_data: ProblemReportMetadata, } struct ProblemReportRequest { address: String, message: String, log: Vec<u8>, + meta_data: BTreeMap<String, String>, } unsafe impl Send for SwiftProblemReportRequest {} impl ProblemReportRequest { - unsafe fn from_swift_parameters(request: *const SwiftProblemReportRequest) -> Option<Self> { - if request.is_null() { - return None; - } - - let request = &*request; // Dereference the pointer - + unsafe fn from_swift_parameters(request: SwiftProblemReportRequest) -> Option<Self> { let address_slice = slice::from_raw_parts(request.address, request.address_len); let message_slice = slice::from_raw_parts(request.message, request.message_len); let log_slice = slice::from_raw_parts(request.log, request.log_len); @@ -129,10 +126,95 @@ impl ProblemReportRequest { let message = String::from_utf8(message_slice.to_vec()).ok()?; let log = log_slice.to_vec(); + let meta_data = if request.meta_data.inner.is_null() { + BTreeMap::new() + } else { + let swift_map = &request.meta_data; + let mut converted_map = BTreeMap::new(); + + if let Some(inner) = swift_map.inner.as_ref() { + for (key, value) in &inner.0 { + converted_map.insert(key.clone(), value.clone()); + } + } + converted_map + }; + Some(Self { address, message, log, + meta_data, }) } } + +#[repr(C)] +pub struct ProblemReportMetadata { + inner: *mut Map, +} + +struct Map(BTreeMap<String, String>); + +impl Map { + fn new() -> Self { + Map(BTreeMap::new()) + } + + unsafe fn add(&mut self, key: *const c_char, value: *const c_char) -> bool { + if key.is_null() || value.is_null() { + log::error!("Failed to add metadata: key or value is NULL."); + return false; + } + let key = unsafe { CStr::from_ptr(key) }; + let value = unsafe { CStr::from_ptr(value) }; + + match key.to_str() { + Ok(key_str) => match value.to_str() { + Ok(value_str) => { + self.0.insert(key_str.to_owned(), value_str.to_owned()); + true + } + Err(err) => { + log::error!("{err:?}"); + false + } + }, + Err(err) => { + log::error!("{err:?}"); + false + } + } + } +} + +#[no_mangle] +pub extern "C" fn swift_problem_report_meta_data_new() -> ProblemReportMetadata { + let map = Box::new(Map::new()); + ProblemReportMetadata { + inner: Box::into_raw(map), + } +} + +#[no_mangle] +pub extern "C" fn swift_problem_report_meta_data_add( + map: ProblemReportMetadata, + key: *const c_char, + value: *const c_char, +) -> bool { + if let Some(inner) = unsafe { map.inner.as_mut() } { + unsafe { inner.add(key, value) } + } else { + false + } +} + +#[no_mangle] +pub extern "C" fn swift_problem_report_meta_data_free(mut map: ProblemReportMetadata) { + if !map.inner.is_null() { + unsafe { + drop(Box::from_raw(map.inner)); + map.inner = std::ptr::null_mut(); + } + } +} |
