blob: 09b06230a054216f17e7575ae530530250fffa6b [file] [log] [blame]
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#import "ios/chrome/browser/shared/model/utils/rust_unzipper.h"
#import "base/apple/foundation_util.h"
#import "base/check.h"
#import "base/containers/span_rust.h"
#import "base/functional/bind.h"
#import "base/strings/sys_string_conversions.h"
#import "base/task/sequenced_task_runner.h"
#import "base/task/thread_pool.h"
#import "ios/chrome/browser/shared/model/utils/zip_ffi_glue.rs.h"
using rust_zip::RustFileData;
using rust_zip::unzip_archive_in_memory;
using rust_zip::UnzipResult;
namespace {
NSString* const kRustUnzipperErrorDomain = @"rust_unzipper_error_domain";
constexpr char kEmptyDataErrorDescription[] = "Input NSData is empty.";
constexpr NSInteger kDefaultErrorCode = -1;
// Creates a standard error object from the unzip failure details.
NSError* ParseUnzipErrorAsNSError(const std::string& message,
NSInteger error_code = kDefaultErrorCode) {
NSString* error_message =
message.empty()
? [NSString stringWithFormat:@"Rust unzipping failed with code: %d "
@"(no message provided)",
static_cast<int>(error_code)]
: base::SysUTF8ToNSString(message);
NSDictionary* user_info = @{NSLocalizedDescriptionKey : error_message};
return [NSError errorWithDomain:kRustUnzipperErrorDomain
code:error_code
userInfo:user_info];
}
// Synchronously unzips the provided data, returning the contained files or an
// error.
UnzipResultData UnzipDataSync(NSData* data) {
if (data.length == 0) {
return {.error = ParseUnzipErrorAsNSError(kEmptyDataErrorDescription)};
}
std::vector<RustFileData> file_contents;
std::string error_string;
if (UnzipResult result_code = unzip_archive_in_memory(
SpanToRustSlice(base::apple::NSDataToSpan(data)), file_contents,
error_string);
result_code != UnzipResult::Success) {
return {.error = ParseUnzipErrorAsNSError(
error_string, static_cast<NSInteger>(result_code))};
}
NSMutableArray<NSData*>* results_array =
[NSMutableArray arrayWithCapacity:file_contents.size()];
for (const auto& single_file_contents : file_contents) {
NSData* ns_data_entry =
[NSData dataWithBytes:single_file_contents.data.data()
length:single_file_contents.data.size()];
[results_array addObject:ns_data_entry];
}
return {.unzipped_files = results_array};
}
} // namespace
void UnzipData(NSData* data,
base::OnceCallback<void(UnzipResultData)> callback) {
base::ThreadPool::PostTaskAndReplyWithResult(
FROM_HERE, {base::MayBlock()}, base::BindOnce(&UnzipDataSync, data),
std::move(callback));
}