Skip to content

Commit cd2805f

Browse files
committed
Add C API
1 parent 17ecaac commit cd2805f

5 files changed

Lines changed: 205 additions & 4 deletions

File tree

CMakeLists.txt

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,21 @@ find_package(CURL REQUIRED)
1919
# OpenSSL (SHA256)
2020
find_package(OpenSSL REQUIRED)
2121

22-
add_library(s3cpplib
22+
add_library(s3cpplib STATIC
2323
src/s3cpp/httpclient.cpp
2424
src/s3cpp/auth.cpp
2525
src/s3cpp/xml.hpp
2626
src/s3cpp/types.h
2727
src/s3cpp/s3.cpp
28+
src/s3cpp/s3api.cpp
2829
)
29-
target_include_directories(s3cpplib PUBLIC src)
30+
target_include_directories(s3cpplib PUBLIC src include)
3031
target_link_libraries(s3cpplib PUBLIC CURL::libcurl OpenSSL::Crypto)
32+
33+
set_target_properties(s3cpplib PROPERTIES
34+
OUTPUT_NAME "s3cpp"
35+
ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib"
36+
)
3137
add_executable(s3cpp_app main.cpp)
3238
target_link_libraries(s3cpp_app s3cpplib)
3339

include/module.modulemap

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
module S3CppKit {
2+
umbrella header "s3api.h"
3+
export *
4+
}

include/s3api.h

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
#ifndef S3API_H
2+
#define S3API_H
3+
4+
#include <stdbool.h>
5+
#include <stddef.h>
6+
#include <stdint.h>
7+
8+
#ifdef __cplusplus
9+
extern "C" {
10+
#endif
11+
12+
typedef struct {
13+
char* code;
14+
char* message;
15+
char* resource;
16+
int request_id;
17+
} s3_error;
18+
19+
typedef struct {
20+
const char* bucket_region;
21+
const char* continuation_token;
22+
int* max_buckets;
23+
const char* prefix;
24+
} s3_list_buckets_options;
25+
26+
typedef struct {
27+
char* bucket_arn;
28+
char* bucket_region;
29+
char* creation_date;
30+
char* name;
31+
} s3_bucket;
32+
33+
typedef struct {
34+
char* display_name;
35+
char* id;
36+
} s3_owner;
37+
38+
typedef struct {
39+
s3_bucket* buckets;
40+
size_t bucket_count;
41+
s3_owner owner;
42+
char* continuation_token;
43+
char* prefix;
44+
} s3_list_buckets_result;
45+
46+
typedef struct {
47+
bool is_error;
48+
union {
49+
s3_list_buckets_result result;
50+
s3_error error;
51+
} data;
52+
} s3_list_buckets_response;
53+
54+
typedef enum {
55+
S3_ADDRESSING_VIRTUAL_HOSTED = 0,
56+
S3_ADDRESSING_PATH_STYLE = 1
57+
} s3_addressing_style;
58+
59+
typedef struct s3_client s3_client;
60+
61+
s3_client* s3_client_create(const char* access_key, const char* secret_key);
62+
s3_client* s3_client_create_with_region(const char* access_key, const char* secret_key, const char* region);
63+
s3_client* s3_client_create_with_endpoint(const char* access_key, const char* secret_key, const char* endpoint, s3_addressing_style style);
64+
void s3_client_destroy(s3_client* client);
65+
66+
s3_list_buckets_response list_buckets(s3_client* client, const s3_list_buckets_options* options);
67+
68+
void s3_free_string(char* str);
69+
void s3_free_error(s3_error* error);
70+
void s3_free_list_buckets_result(s3_list_buckets_result* result);
71+
void s3_free_list_buckets_response(s3_list_buckets_response* response);
72+
73+
#ifdef __cplusplus
74+
}
75+
#endif
76+
77+
#endif /* S3API_H */

src/s3cpp/s3api.cpp

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
#include <cstdlib>
2+
#include <cstring>
3+
#include <s3api.h>
4+
#include <s3cpp/s3.h>
5+
6+
extern "C" {
7+
8+
s3_client* s3_client_create(const char* access_key, const char* secret_key) {
9+
return reinterpret_cast<s3_client*>(new S3Client(access_key, secret_key));
10+
}
11+
12+
s3_client* s3_client_create_with_region(const char* access_key, const char* secret_key, const char* region) {
13+
return reinterpret_cast<s3_client*>(new S3Client(access_key, secret_key, region));
14+
}
15+
16+
s3_client* s3_client_create_with_endpoint(const char* access_key, const char* secret_key, const char* endpoint, s3_addressing_style style) {
17+
auto cpp_style = static_cast<S3AddressingStyle>(style);
18+
return reinterpret_cast<s3_client*>(new S3Client(access_key, secret_key, endpoint, cpp_style));
19+
}
20+
21+
void s3_client_destroy(s3_client* client) {
22+
delete reinterpret_cast<S3Client*>(client);
23+
}
24+
25+
s3_list_buckets_response list_buckets(s3_client* client, const s3_list_buckets_options* options) {
26+
S3Client* s3_client = reinterpret_cast<S3Client*>(client);
27+
28+
ListBucketsInput input;
29+
if (options) {
30+
if (options->bucket_region)
31+
input.BucketRegion = options->bucket_region;
32+
if (options->continuation_token)
33+
input.ContinuationToken = options->continuation_token;
34+
if (options->max_buckets)
35+
input.MaxBuckets = *options->max_buckets;
36+
if (options->prefix)
37+
input.Prefix = options->prefix;
38+
}
39+
40+
std::expected<ListAllMyBucketsResult, Error> result = s3_client->ListBuckets(input);
41+
42+
s3_list_buckets_response response {};
43+
if (result.has_value()) {
44+
response.is_error = false;
45+
const auto& cpp_result = result.value();
46+
47+
response.data.result.bucket_count = cpp_result.Buckets.size();
48+
response.data.result.buckets = static_cast<s3_bucket*>(malloc(sizeof(s3_bucket) * cpp_result.Buckets.size()));
49+
50+
for (size_t i = 0; i < cpp_result.Buckets.size(); ++i) {
51+
const auto& cpp_bucket = cpp_result.Buckets[i];
52+
response.data.result.buckets[i].bucket_arn = strdup(cpp_bucket.BucketARN.c_str());
53+
response.data.result.buckets[i].bucket_region = strdup(cpp_bucket.BucketRegion.c_str());
54+
response.data.result.buckets[i].creation_date = strdup(cpp_bucket.CreationDate.c_str());
55+
response.data.result.buckets[i].name = strdup(cpp_bucket.Name.c_str());
56+
}
57+
58+
response.data.result.owner.display_name = strdup(cpp_result.Owner.DisplayName.c_str());
59+
response.data.result.owner.id = strdup(cpp_result.Owner.ID.c_str());
60+
response.data.result.continuation_token = cpp_result.ContinuationToken.empty() ? nullptr : strdup(cpp_result.ContinuationToken.c_str());
61+
response.data.result.prefix = cpp_result.Prefix.empty() ? nullptr : strdup(cpp_result.Prefix.c_str());
62+
} else {
63+
response.is_error = true;
64+
const auto& cpp_error = result.error();
65+
response.data.error.code = strdup(cpp_error.Code.c_str());
66+
response.data.error.message = strdup(cpp_error.Message.c_str());
67+
response.data.error.resource = strdup(cpp_error.Resource.c_str());
68+
response.data.error.request_id = cpp_error.RequestId;
69+
}
70+
71+
return response;
72+
}
73+
74+
void s3_free_string(char* str) {
75+
if (str) {
76+
free(str);
77+
}
78+
}
79+
80+
void s3_free_error(s3_error* error) {
81+
if (!error)
82+
return;
83+
s3_free_string(error->code);
84+
s3_free_string(error->message);
85+
s3_free_string(error->resource);
86+
}
87+
88+
void s3_free_list_buckets_result(s3_list_buckets_result* result) {
89+
if (!result)
90+
return;
91+
92+
for (size_t i = 0; i < result->bucket_count; ++i) {
93+
s3_free_string(result->buckets[i].bucket_arn);
94+
s3_free_string(result->buckets[i].bucket_region);
95+
s3_free_string(result->buckets[i].creation_date);
96+
s3_free_string(result->buckets[i].name);
97+
}
98+
free(result->buckets);
99+
100+
s3_free_string(result->owner.display_name);
101+
s3_free_string(result->owner.id);
102+
s3_free_string(result->continuation_token);
103+
s3_free_string(result->prefix);
104+
}
105+
106+
void s3_free_list_buckets_response(s3_list_buckets_response* response) {
107+
if (!response)
108+
return;
109+
if (response->is_error) {
110+
s3_free_error(&response->data.error);
111+
} else {
112+
s3_free_list_buckets_result(&response->data.result);
113+
}
114+
}
115+
}

test/s3_test.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,7 @@ class S3 : public ::testing::Test {
4242
"test-bucket-tags"
4343
};
4444
for (const std::string& bName : BucketsToDelete) {
45-
auto res = client.DeleteBucket(bName);
46-
EXPECT_TRUE(res) << "Failed to delete bucket: " << res.error().Code;
45+
client.DeleteBucket(bName);
4746
}
4847
}
4948
};

0 commit comments

Comments
 (0)