use std::sync::Arc;
use arrow::datatypes::{DataType, Field};
use arrow::ffi::FFI_ArrowSchema;
use arrow_schema::FieldRef;
use stabby::vec::Vec as SVec;
use crate::arrow_wrappers::WrappedSchema;
pub use crate::ffi_option::{FFI_Option, FFI_Result};
#[macro_export]
macro_rules! df_result {
( $x:expr ) => {
match Into::<::std::result::Result<_, _>>::into($x) {
Ok(v) => Ok(v),
Err(err) => {
datafusion_common::ffi_err!("{err}")
}
}
};
}
#[macro_export]
macro_rules! sresult {
( $x:expr ) => {
match $x {
Ok(v) => $crate::ffi_option::FFI_Result::Ok(v),
Err(e) => $crate::ffi_option::FFI_Result::Err(stabby::string::String::from(
e.to_string().as_str(),
)),
}
};
}
#[macro_export]
macro_rules! sresult_return {
( $x:expr ) => {
match $x {
Ok(v) => v,
Err(e) => {
return $crate::ffi_option::FFI_Result::Err(stabby::string::String::from(
e.to_string().as_str(),
))
}
}
};
}
pub fn vec_fieldref_to_rvec_wrapped(
fields: &[FieldRef],
) -> Result<SVec<WrappedSchema>, arrow::error::ArrowError> {
Ok(fields
.iter()
.map(FFI_ArrowSchema::try_from)
.collect::<Result<Vec<_>, arrow::error::ArrowError>>()?
.into_iter()
.map(WrappedSchema)
.collect())
}
pub fn rvec_wrapped_to_vec_fieldref(
fields: &SVec<WrappedSchema>,
) -> Result<Vec<FieldRef>, arrow::error::ArrowError> {
fields
.iter()
.map(|d| Field::try_from(&d.0).map(Arc::new))
.collect()
}
pub fn vec_datatype_to_rvec_wrapped(
data_types: &[DataType],
) -> Result<SVec<WrappedSchema>, arrow::error::ArrowError> {
Ok(data_types
.iter()
.map(FFI_ArrowSchema::try_from)
.collect::<Result<Vec<_>, arrow::error::ArrowError>>()?
.into_iter()
.map(WrappedSchema)
.collect())
}
pub fn rvec_wrapped_to_vec_datatype(
data_types: &SVec<WrappedSchema>,
) -> Result<Vec<DataType>, arrow::error::ArrowError> {
data_types
.iter()
.map(|d| DataType::try_from(&d.0))
.collect()
}
#[cfg(test)]
pub(crate) mod tests {
use std::sync::Arc;
use datafusion::error::DataFusionError;
use datafusion::prelude::SessionContext;
use datafusion_execution::TaskContextProvider;
use stabby::string::String as SString;
use crate::execution::FFI_TaskContextProvider;
use crate::ffi_option::FFI_Result;
pub(crate) fn test_session_and_ctx() -> (Arc<SessionContext>, FFI_TaskContextProvider)
{
let ctx = Arc::new(SessionContext::new());
let task_ctx_provider = Arc::clone(&ctx) as Arc<dyn TaskContextProvider>;
let task_ctx_provider = FFI_TaskContextProvider::from(&task_ctx_provider);
(ctx, task_ctx_provider)
}
fn wrap_result(result: Result<String, DataFusionError>) -> FFI_Result<String> {
FFI_Result::Ok(sresult_return!(result))
}
#[test]
fn test_conversion() {
const VALID_VALUE: &str = "valid_value";
const ERROR_VALUE: &str = "error_value";
let ok_r_result: FFI_Result<SString> = FFI_Result::Ok(SString::from(VALID_VALUE));
let err_r_result: FFI_Result<SString> =
FFI_Result::Err(SString::from(ERROR_VALUE));
let returned_ok_result = df_result!(ok_r_result);
assert!(returned_ok_result.is_ok());
assert!(*returned_ok_result.unwrap() == *VALID_VALUE);
let returned_err_result = df_result!(err_r_result);
assert!(returned_err_result.is_err());
assert!(
returned_err_result.unwrap_err().strip_backtrace()
== format!("FFI error: {ERROR_VALUE}")
);
let ok_result: Result<String, DataFusionError> = Ok(VALID_VALUE.to_string());
let err_result: Result<String, DataFusionError> =
datafusion_common::ffi_err!("{ERROR_VALUE}");
let returned_ok_r_result = wrap_result(ok_result);
let std_result: Result<String, SString> = returned_ok_r_result.into();
assert!(std_result == Ok(VALID_VALUE.into()));
let returned_err_r_result = wrap_result(err_result);
let std_result: Result<String, SString> = returned_err_r_result.into();
assert!(std_result.is_err());
assert!(
std_result
.unwrap_err()
.as_str()
.starts_with(format!("FFI error: {ERROR_VALUE}").as_str())
);
}
}