bindings/haskell/src/lib.rs (377 lines of code) (raw):

// Licensed to the Apache Software Foundation (ASF) under one // or more contributor license agreements. See the NOTICE file // distributed with this work for additional information // regarding copyright ownership. The ASF licenses this file // to you under the Apache License, Version 2.0 (the // "License"); you may not use this file except in compliance // with the License. You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, // software distributed under the License is distributed on an // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. mod logger; mod result; mod types; use std::collections::HashMap; use std::ffi::CStr; use std::mem; use std::os::raw::c_char; use std::str::FromStr; use ::opendal as od; use logger::HsLogger; use od::layers::LoggingLayer; use od::layers::RetryLayer; use od::BlockingLister; use result::FFIResult; use types::ByteSlice; use types::Metadata; /// # Safety /// /// * The `keys`, `values`, `len` are valid from `HashMap`. /// * The memory pointed to by `scheme` contain a valid nul terminator at the end of /// the string. /// * The `result` is a valid pointer, and has available memory to write to. /// /// # Panics /// /// * If `keys` or `values` are not valid pointers. /// * If `len` is not the same for `keys` and `values`. /// * If `log_level` is not a valid value passed by haskell. /// * If `callback` is not a valid function pointer. /// * If `result` is not a valid pointer. #[no_mangle] pub unsafe extern "C" fn via_map_ffi( scheme: *const c_char, keys: *const *const c_char, values: *const *const c_char, len: usize, callback: Option<extern "C" fn(u32, *const c_char)>, result: *mut FFIResult<od::BlockingOperator>, ) { let scheme_str = match CStr::from_ptr(scheme).to_str() { Ok(s) => s, Err(_) => { *result = FFIResult::err("Failed to convert scheme to string"); return; } }; let scheme = match od::Scheme::from_str(scheme_str) { Ok(s) => s, Err(_) => { *result = FFIResult::err("Failed to parse scheme"); return; } }; let keys_vec = std::slice::from_raw_parts(keys, len); let values_vec = std::slice::from_raw_parts(values, len); let map = keys_vec .iter() .zip(values_vec.iter()) .map(|(&k, &v)| { ( CStr::from_ptr(k).to_string_lossy().into_owned(), CStr::from_ptr(v).to_string_lossy().into_owned(), ) }) .collect::<HashMap<String, String>>(); if let Some(callback) = callback { if let Err(e) = log::set_boxed_logger(Box::new(HsLogger { callback })) .map(|()| log::set_max_level(log::LevelFilter::Debug)) { *result = FFIResult::err_with_source( "Failed to register logger", od::Error::new(od::ErrorKind::Unexpected, e.to_string().as_str()), ); return; } } let res = match od::Operator::via_map(scheme, map) { Ok(mut operator) => { operator = operator.layer(RetryLayer::new()); if callback.is_some() { operator = operator.layer(LoggingLayer::default()); } FFIResult::ok(operator.blocking()) } Err(e) => FFIResult::err_with_source("Failed to create Operator", e), }; *result = res; } /// # Safety /// /// * `operator` is a valid pointer to a `BlockingOperator`. /// /// # Panics /// /// * If `operator` is not a valid pointer. #[no_mangle] pub unsafe extern "C" fn free_operator(operator: *mut od::BlockingOperator) { if !operator.is_null() { drop(Box::from_raw(operator)); } } /// # Safety /// /// * `op` is a valid pointer to a `BlockingOperator`. /// * `path` is a valid pointer to a nul terminated string. /// * `result` is a valid pointer, and has available memory to write to /// /// # Panics /// /// * If `op` is not a valid pointer. /// * If `result` is not a valid pointer, or does not have available memory to write to. #[no_mangle] pub unsafe extern "C" fn blocking_read( op: *mut od::BlockingOperator, path: *const c_char, result: *mut FFIResult<ByteSlice>, ) { let op = if op.is_null() { *result = FFIResult::err("Operator is null"); return; } else { &mut *op }; let path_str = match CStr::from_ptr(path).to_str() { Ok(s) => s, Err(_) => { *result = FFIResult::err("Failed to convert path to string"); return; } }; let res = match op.read(path_str) { Ok(bytes) => FFIResult::ok(ByteSlice::from_vec(bytes)), Err(e) => FFIResult::err_with_source("Failed to read", e), }; *result = res; } /// # Safety /// /// * `op` is a valid pointer to a `BlockingOperator`. /// * `path` is a valid pointer to a nul terminated string. /// * `bytes` is a valid pointer to a byte array. /// * `len` is the length of `bytes`. /// * `result` is a valid pointer, and has available memory to write to /// /// # Panics /// /// * If `op` is not a valid pointer. /// * If `bytes` is not a valid pointer, or `len` is more than the length of `bytes`. /// * If `result` is not a valid pointer, or does not have available memory to write to. #[no_mangle] pub unsafe extern "C" fn blocking_write( op: *mut od::BlockingOperator, path: *const c_char, bytes: *const c_char, len: usize, result: *mut FFIResult<()>, ) { let op = if op.is_null() { *result = FFIResult::err("Operator is null"); return; } else { &mut *op }; let path_str = match CStr::from_ptr(path).to_str() { Ok(s) => s, Err(_) => { *result = FFIResult::err("Failed to convert path to string"); return; } }; let bytes = Vec::from_raw_parts(bytes as *mut u8, len, len); let res = match op.write(path_str, bytes.clone()) { Ok(()) => FFIResult::ok(()), Err(e) => FFIResult::err_with_source("Failed to write", e), }; *result = res; // bytes memory is controlled by Haskell, we can't drop it here mem::forget(bytes); } /// # Safety /// /// * `op` is a valid pointer to a `BlockingOperator`. /// * `path` is a valid pointer to a nul terminated string. /// * `result` is a valid pointer, and has available memory to write to /// /// # Panics /// /// * If `op` is not a valid pointer. /// * If `result` is not a valid pointer, or does not have available memory to write to. #[no_mangle] pub unsafe extern "C" fn blocking_is_exist( op: *mut od::BlockingOperator, path: *const c_char, result: *mut FFIResult<bool>, ) { let op = if op.is_null() { *result = FFIResult::err("Operator is null"); return; } else { &mut *op }; let path_str = match CStr::from_ptr(path).to_str() { Ok(s) => s, Err(_) => { *result = FFIResult::err("Failed to convert path to string"); return; } }; let res = match op.is_exist(path_str) { Ok(exist) => FFIResult::ok(exist), Err(e) => FFIResult::err_with_source("Failed to check if path exists", e), }; *result = res; } /// # Safety /// /// * `op` is a valid pointer to a `BlockingOperator`. /// * `path` is a valid pointer to a nul terminated string. /// * `result` is a valid pointer, and has available memory to write to /// /// # Panics /// /// * If `op` is not a valid pointer. /// * If `result` is not a valid pointer, or does not have available memory to write to. #[no_mangle] pub unsafe extern "C" fn blocking_create_dir( op: *mut od::BlockingOperator, path: *const c_char, result: *mut FFIResult<()>, ) { let op = if op.is_null() { *result = FFIResult::err("Operator is null"); return; } else { &mut *op }; let path_str = match CStr::from_ptr(path).to_str() { Ok(s) => s, Err(_) => { *result = FFIResult::err("Failed to convert path to string"); return; } }; let res = match op.create_dir(path_str) { Ok(()) => FFIResult::ok(()), Err(e) => FFIResult::err_with_source("Failed to create directory", e), }; *result = res; } /// # Safety /// /// * `op` is a valid pointer to a `BlockingOperator`. /// * `path_from` is a valid pointer to a nul terminated string. /// * `path_to` is a valid pointer to a nul terminated string. /// * `result` is a valid pointer, and has available memory to write to /// /// # Panics /// /// * If `op` is not a valid pointer. /// * If `result` is not a valid pointer, or does not have available memory to write to. #[no_mangle] pub unsafe extern "C" fn blocking_copy( op: *mut od::BlockingOperator, path_from: *const c_char, path_to: *const c_char, result: *mut FFIResult<()>, ) { let op = if op.is_null() { *result = FFIResult::err("Operator is null"); return; } else { &mut *op }; let path_from_str = match CStr::from_ptr(path_from).to_str() { Ok(s) => s, Err(_) => { *result = FFIResult::err("Failed to convert source path to string"); return; } }; let path_to_str = match CStr::from_ptr(path_to).to_str() { Ok(s) => s, Err(_) => { *result = FFIResult::err("Failed to convert destination path to string"); return; } }; let res = match op.copy(path_from_str, path_to_str) { Ok(()) => FFIResult::ok(()), Err(e) => FFIResult::err_with_source("Failed to copy", e), }; *result = res; } /// # Safety /// /// * `op` is a valid pointer to a `BlockingOperator`. /// * `path_from` is a valid pointer to a nul terminated string. /// * `path_to` is a valid pointer to a nul terminated string. /// * `result` is a valid pointer, and has available memory to write to /// /// # Panics /// /// * If `op` is not a valid pointer. /// * If `result` is not a valid pointer, or does not have available memory to write to. #[no_mangle] pub unsafe extern "C" fn blocking_rename( op: *mut od::BlockingOperator, path_from: *const c_char, path_to: *const c_char, result: *mut FFIResult<()>, ) { let op = if op.is_null() { *result = FFIResult::err("Operator is null"); return; } else { &mut *op }; let path_from_str = match CStr::from_ptr(path_from).to_str() { Ok(s) => s, Err(_) => { *result = FFIResult::err("Failed to convert source path to string"); return; } }; let path_to_str = match CStr::from_ptr(path_to).to_str() { Ok(s) => s, Err(_) => { *result = FFIResult::err("Failed to convert destination path to string"); return; } }; let res = match op.rename(path_from_str, path_to_str) { Ok(()) => FFIResult::ok(()), Err(e) => FFIResult::err_with_source("Failed to rename", e), }; *result = res; } /// # Safety /// /// * `op` is a valid pointer to a `BlockingOperator`. /// * `path` is a valid pointer to a nul terminated string. /// * `result` is a valid pointer, and has available memory to write to /// /// # Panics /// /// * If `op` is not a valid pointer. /// * If `result` is not a valid pointer, or does not have available memory to write to. #[no_mangle] pub unsafe extern "C" fn blocking_delete( op: *mut od::BlockingOperator, path: *const c_char, result: *mut FFIResult<()>, ) { let op = if op.is_null() { *result = FFIResult::err("Operator is null"); return; } else { &mut *op }; let path_str = match CStr::from_ptr(path).to_str() { Ok(s) => s, Err(_) => { *result = FFIResult::err("Failed to convert path to string"); return; } }; let res = match op.delete(path_str) { Ok(()) => FFIResult::ok(()), Err(e) => FFIResult::err_with_source("Failed to delete", e), }; *result = res; } /// # Safety /// /// * `op` is a valid pointer to a `BlockingOperator`. /// * `path` is a valid pointer to a nul terminated string. /// * `result` is a valid pointer, and has available memory to write to /// /// # Panics /// /// * If `op` is not a valid pointer. /// * If `result` is not a valid pointer, or does not have available memory to write to. #[no_mangle] pub unsafe extern "C" fn blocking_stat( op: *mut od::BlockingOperator, path: *const c_char, result: *mut FFIResult<Metadata>, ) { let op = if op.is_null() { *result = FFIResult::err("Operator is null"); return; } else { &mut *op }; let path_str = match CStr::from_ptr(path).to_str() { Ok(s) => s, Err(_) => { *result = FFIResult::err("Failed to convert path to string"); return; } }; let res = match op.stat(path_str) { Ok(meta) => FFIResult::ok(meta.into()), Err(e) => FFIResult::err_with_source("Failed to stat", e), }; *result = res; } /// # Safety /// /// * `op` is a valid pointer to a `BlockingOperator`. /// * `path` is a valid pointer to a nul terminated string. /// * `result` is a valid pointer, and has available memory to write to /// /// # Panics /// /// * If `op` is not a valid pointer. /// * If `result` is not a valid pointer, or does not have available memory to write to. #[no_mangle] pub unsafe extern "C" fn blocking_list( op: *mut od::BlockingOperator, path: *const c_char, result: *mut FFIResult<*mut BlockingLister>, ) { let op = if op.is_null() { *result = FFIResult::err("Operator is null"); return; } else { &mut *op }; let path_str = match CStr::from_ptr(path).to_str() { Ok(s) => s, Err(_) => { *result = FFIResult::err("Failed to convert path to string"); return; } }; let res = match op.list(path_str) { Ok(lister) => FFIResult::ok(Box::into_raw(Box::new(lister))), Err(e) => FFIResult::err_with_source("Failed to list", e), }; *result = res; } /// # Safety /// /// * `op` is a valid pointer to a `BlockingOperator`. /// * `path` is a valid pointer to a nul terminated string. /// * `result` is a valid pointer, and has available memory to write to /// /// # Panics /// /// * If `op` is not a valid pointer. /// * If `result` is not a valid pointer, or does not have available memory to write to. #[no_mangle] pub unsafe extern "C" fn blocking_scan( op: *mut od::BlockingOperator, path: *const c_char, result: *mut FFIResult<*mut BlockingLister>, ) { let op = if op.is_null() { *result = FFIResult::err("Operator is null"); return; } else { &mut *op }; let path_str = match CStr::from_ptr(path).to_str() { Ok(s) => s, Err(_) => { *result = FFIResult::err("Failed to convert path to string"); return; } }; let res = match op.scan(path_str) { Ok(lister) => FFIResult::ok(Box::into_raw(Box::new(lister))), Err(e) => FFIResult::err_with_source("Failed to scan", e), }; *result = res; } /// # Safety /// /// * `lister` is a valid pointer to a `BlockingLister`. /// * `result` is a valid pointer, and has available memory to write to /// /// # Panics /// /// * If `lister` is not a valid pointer. /// * If `result` is not a valid pointer, or does not have available memory to write to. #[no_mangle] pub unsafe extern "C" fn lister_next( lister: *mut BlockingLister, result: *mut FFIResult<*const c_char>, ) { let lister = if lister.is_null() { *result = FFIResult::err("Lister is null"); return; } else { &mut *lister }; let res = match lister.next() { Some(Ok(item)) => { let res = types::leak_str(item.path()); FFIResult::ok(res) } Some(Err(e)) => FFIResult::err_with_source("Failed to get next item", e), None => FFIResult::ok(std::ptr::null()), }; *result = res; } /// # Safety /// /// * `lister` is a valid pointer to a `BlockingLister`. /// /// # Panics /// /// * If `lister` is not a valid pointer. #[no_mangle] pub unsafe extern "C" fn free_lister(lister: *mut BlockingLister) { if !lister.is_null() { drop(Box::from_raw(lister)); } }