Skip to content

Commit

Permalink
feat: export c interfaces
Browse files Browse the repository at this point in the history
  • Loading branch information
Zvicii committed Dec 28, 2023
1 parent bf907db commit b60b725
Show file tree
Hide file tree
Showing 4 changed files with 149 additions and 24 deletions.
76 changes: 76 additions & 0 deletions resources/ne_s3.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
extern "C" {
#ifdef _WIN32
#define NE_S3_IMPORT __declspec(dllimport)
#else
#define NE_S3_IMPORT
#endif
/// The callback function when upload/download finished
/// # Arguments
/// - `success` - Whether upload/download success
/// - `message` - The message of upload/download
/// - `user_data` - The user data passed to upload/download function
typedef void (*result_callback)(bool success,
const char* message,
const char* user_data);

/// The callback function when upload/download progress changed
/// # Arguments
/// - `progress` - The progress of upload/download
/// - `user_data` - The user data passed to upload/download function
typedef void (*progress_callback)(float progress, const char* user_data);

/// init sdk
/// run this function before any other functions
/// # Arguments
/// - `params` - The params of init, in json format
/// - `log_path` - The log path, use stdout if not given
NE_S3_IMPORT void __cdecl ne_s3_init(const char* params);

/// uninit sdk
/// run this function to shutdown
NE_S3_IMPORT void __cdecl ne_s3_uninit();

/// Upload file to s3
/// # Arguments
/// - `params` - The params of upload, json format
/// - `bucket` - The bucket name
/// - `object` - The object key
/// - `access_key_id` - The access key id
/// - `secret_access_key` - The secret access key
/// - `session_token` - The session token
/// - `security_token` - The security token
/// - `file_path` - The file path
/// - `region` - The region
/// - `tries` - The max retry times
/// - `endpoint` - The endpoint, use default if not set
/// - `ca_cert_path` - The ca certs path, use system certs if not set
/// - `result_callback` - The callback function when upload finished
/// - `progress_callback` - The callback function when upload progress changed
/// - `user_data` - The user data, will be passed to callbacks, lifetime must be
/// longer than callbacks
NE_S3_IMPORT void __cdecl ne_s3_upload(const char* params,
result_callback result,
progress_callback progress,
const char* user_data);

/// download file from s3
/// # Arguments
/// - `params` - The params of download, json format
/// - `bucket` - The bucket name
/// - `object` - The object key
/// - `access_key_id` - The access key id
/// - `secret_access_key` - The secret access key
/// - `session_token` - The session token
/// - `security_token` - The security token
/// - `file_path` - The file path
/// - `region` - The region
/// - `tries` - The max retry times
/// - `endpoint` - The endpoint, use default if not set
/// - `ca_cert_path` - The ca certs path, use system certs if not set
/// - `result_callback` - The callback function when download finished
/// - `user_data` - The user data, will be passed to callbacks, lifetime must be
/// longer than callbacks
NE_S3_IMPORT void __cdecl ne_s3_download(const char* params,
result_callback result,
const char* user_data);
}
20 changes: 13 additions & 7 deletions src/basic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,14 @@ use std::{
task::{Context, Poll},
};

/// result callback
/// - `success` - true if success, false if failed
/// - `message` - error message if failed
pub type ResultCallback = Box<dyn Fn(bool, String) + Send + Sync>;
pub type ProgressCallback = Arc<Mutex<dyn Fn(f64) + Send + Sync>>;

/// progress callback
/// - `progress` - progress of upload or download, in percentage
pub type ProgressCallback = Arc<Mutex<dyn Fn(f32) + Send + Sync>>;

#[derive(Debug)]
pub struct NeS3Credential {
Expand Down Expand Up @@ -68,7 +74,7 @@ impl ProgressTracker {
fn track(&mut self, len: u64) {
let mut bytes_written = self.bytes_written.lock().unwrap();
*bytes_written += len;
let progress = *bytes_written as f64 / self.content_length as f64 * 100.0;
let progress = *bytes_written as f32 / self.content_length as f32 * 100.0;
let progress_callback = self.progress_callback.lock().unwrap();
if std::time::Instant::now() - self.last_callback_time
< std::time::Duration::from_millis(500)
Expand Down Expand Up @@ -159,12 +165,12 @@ pub struct S3Params {
pub(crate) access_key_id: String,
pub(crate) secret_access_key: String,
pub(crate) session_token: String,
pub(crate) file_path: String,
pub(crate) security_token: String,
pub(crate) ca_certs_path: Option<String>,
pub(crate) file_path: String,
pub(crate) region: Option<String>,
pub(crate) tries: Option<u32>,
pub(crate) endpoint: Option<String>,
pub(crate) ca_cert_path: Option<String>,
}

fn load_ca_cert(path: &String) -> Result<RootCertStore, Box<dyn std::error::Error>> {
Expand Down Expand Up @@ -193,12 +199,12 @@ pub fn create_s3_client(params: &S3Params) -> Result<Client, Box<dyn std::error:
// Set max attempts.
// If tries is 1, there are no retries.
.retry_config(RetryConfig::standard().with_max_attempts(params.tries.unwrap_or(1)));
if params.ca_certs_path.is_some() {
if params.ca_cert_path.is_some() {
info!(
"use custom ca certs, path: {}",
params.ca_certs_path.as_ref().unwrap()
params.ca_cert_path.as_ref().unwrap()
);
let root_store = load_ca_cert(params.ca_certs_path.as_ref().unwrap())?;
let root_store = load_ca_cert(params.ca_cert_path.as_ref().unwrap())?;
let config = rustls::ClientConfig::builder()
.with_safe_defaults()
.with_root_certificates(root_store)
Expand Down
73 changes: 58 additions & 15 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use flexi_logger::{with_thread, Age, Cleanup, Criterion, FileSpec, Logger, Namin
use log::{error, info};
use serde::{Deserialize, Serialize};
use serde_json::json;
use std::{sync::Mutex, path::Path};
use std::{sync::{Mutex, Arc}, path::Path};
use sysinfo::System;
use urlencoding::decode;
pub use basic::S3Params;
Expand All @@ -17,23 +17,23 @@ struct InitParams {
}

static mut RUNTIME: Mutex<Option<tokio::runtime::Runtime>> = Mutex::new(None);
/// init tokio runtime
/// init sdk
/// run this function before any other functions
/// # Arguments
/// - `params` - The params of init, json format
/// - `log_path` - The log path, use stdout if not set
pub fn init(params_str: String) {
pub fn init(params: String) {
let mut runtime = unsafe { RUNTIME.lock().unwrap() };
if !runtime.is_none() {
return;
}
let params = match serde_json::from_str::<InitParams>(&params_str) {
Ok(params) => params,
let parsed_params = match serde_json::from_str::<InitParams>(&params) {
Ok(result) => result,
Err(err) => {
panic!("parse init params failed: {}", err);
}
};
let log_path = params.log_path.as_ref();
let log_path = parsed_params.log_path.as_ref();
if log_path.is_some_and(|path| Path::new(path).exists()) {
let log_path = log_path.unwrap();
let _logger = Logger::try_with_str("info")
Expand All @@ -55,7 +55,7 @@ pub fn init(params_str: String) {
.start()
.unwrap();
}
info!("init params: {}", params_str);
info!("init params: {}", params);
let mut system_info = System::new_all();
system_info.refresh_all();
let system_info_json = json!({
Expand All @@ -74,8 +74,14 @@ pub fn init(params_str: String) {
.unwrap();
*runtime = Some(rt);
}
#[no_mangle]
pub extern "cdecl" fn ne_s3_init(params: *const std::os::raw::c_char) {
let params = unsafe { std::ffi::CStr::from_ptr(params) };
let params = params.to_str().unwrap();
init(params.to_string());
}

/// uninit tokio runtime
/// uninit sdk
/// run this function to shutdown
pub fn uninit() {
let mut runtime = unsafe { RUNTIME.lock().unwrap() };
Expand All @@ -86,6 +92,10 @@ pub fn uninit() {
rt.shutdown_background();
}
}
#[no_mangle]
pub extern "cdecl" fn ne_s3_uninit() {
uninit();
}

/// Upload file to s3
/// # Arguments
Expand All @@ -94,16 +104,15 @@ pub fn uninit() {
/// - `object` - The object key
/// - `access_key_id` - The access key id
/// - `secret_access_key` - The secret access key
/// - `file_path` - The file path
/// - `session_token` - The session token
/// - `security_token` - The security token
/// - `file_path` - The file path
/// - `region` - The region
/// - `tries` - The max retry times
/// - `endpoint` - The endpoint, use default if not set
/// - `ca_cert_path` - The ca certs path, use system certs if not set
/// - `result_callback` - The callback function when upload finished
/// - `success` - The upload succeeded or not
/// - `message` - The error message if upload failed
/// - `progress_callback` - The callback function when upload progress changed
/// - `progress` - The progress of upload, in percentage
pub fn upload(
params: String,
result_callback: basic::ResultCallback,
Expand Down Expand Up @@ -147,6 +156,25 @@ pub fn upload(
}
});
}
#[no_mangle]
pub extern "cdecl" fn ne_s3_upload(
params: *const std::os::raw::c_char,
result_callback: extern "C" fn(success: bool, message: *const std::os::raw::c_char, user_data: *const std::os::raw::c_char),
progress_callback: extern "C" fn(progress: f32, user_data: *const std::os::raw::c_char),
user_data: *const std::os::raw::c_char,
) {
let params = unsafe { std::ffi::CStr::from_ptr(params as *mut _) };
let params = params.to_str().unwrap().to_string();
let user_data = unsafe { std::ffi::CStr::from_ptr(user_data) };
let result_callback = move |success: bool, message: String| {
let message = std::ffi::CString::new(message).unwrap();
result_callback(success, message.as_ptr(), user_data.as_ptr());
};
let progress_callback = move |progress: f32| {
progress_callback(progress, user_data.as_ptr());
};
upload(params.to_string(), Box::new(result_callback), Arc::new(Mutex::new(progress_callback)));
}

/// download file from s3
/// # Arguments
Expand All @@ -155,14 +183,14 @@ pub fn upload(
/// - `object` - The object key
/// - `access_key_id` - The access key id
/// - `secret_access_key` - The secret access key
/// - `file_path` - The file path
/// - `session_token` - The session token
/// - `security_token` - The security token
/// - `file_path` - The file path
/// - `region` - The region
/// - `tries` - The max retry times
/// - `endpoint` - The endpoint, use default if not set
/// - `ca_cert_path` - The ca certs path, use system certs if not set
/// - `result_callback` - The callback function when download finished
/// - `success` - The download succeeded or not
/// - `message` - The error message if download failed
pub fn download(params: String, result_callback: basic::ResultCallback) {
info!("download params: {}", params);
let runtime = unsafe { RUNTIME.lock().unwrap() };
Expand Down Expand Up @@ -201,4 +229,19 @@ pub fn download(params: String, result_callback: basic::ResultCallback) {
result_callback(false, error_descrption);
}
});
}
#[no_mangle]
pub extern "cdecl" fn ne_s3_download(
params: *const std::os::raw::c_char,
result_callback: extern "C" fn(success: bool, message: *const std::os::raw::c_char, user_data: *const std::os::raw::c_char),
user_data: *const std::os::raw::c_char,
) {
let params = unsafe { std::ffi::CStr::from_ptr(params as *mut _) };
let params = params.to_str().unwrap().to_string();
let user_data = unsafe { std::ffi::CStr::from_ptr(user_data) };
let result_callback = move |success: bool, message: String| {
let message = std::ffi::CString::new(message).unwrap();
result_callback(success, message.as_ptr(), user_data.as_ptr());
};
download(params.to_string(), Box::new(result_callback));
}
4 changes: 2 additions & 2 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ struct Args {
#[arg(long)]
security_token: String,
#[arg(long, default_value_t = String::new())]
ca_certs_path: String,
ca_cert_path: String,
#[arg(long, default_value_t = String::new())]
region: String,
#[arg(long, default_value_t = 3)]
Expand Down Expand Up @@ -58,7 +58,7 @@ fn main() {
*lock = true;
ccond.notify_one();
};
let progress_callback = |progress: f64| {
let progress_callback = |progress: f32| {
info!("put object progress: {:.2}%", progress);
};
upload(
Expand Down

0 comments on commit b60b725

Please sign in to comment.