summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--ios/MullvadREST/ApiHandlers/RESTAPIProxy.swift14
-rw-r--r--ios/MullvadREST/Extensions/String+UnsafePointer.swift8
-rw-r--r--ios/MullvadREST/MullvadAPI/APIHandlers/MullvadAPIProxy.swift2
-rw-r--r--ios/MullvadREST/MullvadAPI/MullvadApiRequestFactory.swift38
-rw-r--r--ios/MullvadREST/Rust/RustProblemReportRequest.swift57
-rw-r--r--ios/MullvadRustRuntime/MullvadApiCancellable.swift5
-rw-r--r--ios/MullvadRustRuntime/include/mullvad_rust_runtime.h17
-rw-r--r--ios/MullvadVPN.xcodeproj/project.pbxproj16
-rw-r--r--ios/MullvadVPN/View controllers/ProblemReport/ProblemReportInteractor.swift2
-rw-r--r--mullvad-api/src/lib.rs3
-rw-r--r--mullvad-ios/src/api_client/response.rs1
-rw-r--r--mullvad-ios/src/api_client/send_problem_report.rs108
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();
+ }
+ }
+}