Skip to main content

datafusion_ffi/config/
mod.rs

1// Licensed to the Apache Software Foundation (ASF) under one
2// or more contributor license agreements.  See the NOTICE file
3// distributed with this work for additional information
4// regarding copyright ownership.  The ASF licenses this file
5// to you under the Apache License, Version 2.0 (the
6// "License"); you may not use this file except in compliance
7// with the License.  You may obtain a copy of the License at
8//
9//   http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing,
12// software distributed under the License is distributed on an
13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14// KIND, either express or implied.  See the License for the
15// specific language governing permissions and limitations
16// under the License.
17
18pub mod extension_options;
19
20use abi_stable::StableAbi;
21use abi_stable::std_types::{RHashMap, RString};
22use datafusion_common::config::{
23    ConfigExtension, ConfigOptions, ExtensionOptions, TableOptions,
24};
25use datafusion_common::{DataFusionError, Result};
26
27use crate::config::extension_options::FFI_ExtensionOptions;
28
29/// A stable struct for sharing [`ConfigOptions`] across FFI boundaries.
30///
31/// Accessing FFI extension options require a slightly different pattern
32/// than local extensions. The trait [`ExtensionOptionsFFIProvider`] can
33/// be used to simplify accessing FFI extensions.
34#[repr(C)]
35#[derive(Debug, Clone, StableAbi)]
36pub struct FFI_ConfigOptions {
37    base_options: RHashMap<RString, RString>,
38
39    extensions: FFI_ExtensionOptions,
40}
41
42impl From<&ConfigOptions> for FFI_ConfigOptions {
43    fn from(options: &ConfigOptions) -> Self {
44        let base_options: RHashMap<RString, RString> = options
45            .entries()
46            .into_iter()
47            .filter_map(|entry| entry.value.map(|value| (entry.key, value)))
48            .map(|(key, value)| (key.into(), value.into()))
49            .collect();
50
51        let mut extensions = FFI_ExtensionOptions::default();
52        for (extension_name, extension) in options.extensions.iter() {
53            for entry in extension.entries().iter() {
54                if let Some(value) = entry.value.as_ref() {
55                    extensions
56                        .set(format!("{extension_name}.{}", entry.key).as_str(), value)
57                        .expect("FFI_ExtensionOptions set should always return Ok");
58                }
59            }
60        }
61
62        Self {
63            base_options,
64            extensions,
65        }
66    }
67}
68
69impl TryFrom<FFI_ConfigOptions> for ConfigOptions {
70    type Error = DataFusionError;
71    fn try_from(ffi_options: FFI_ConfigOptions) -> Result<Self, Self::Error> {
72        let mut options = ConfigOptions::default();
73        options.extensions.insert(ffi_options.extensions);
74
75        for kv_tuple in ffi_options.base_options.iter() {
76            options.set(kv_tuple.0.as_str(), kv_tuple.1.as_str())?;
77        }
78
79        Ok(options)
80    }
81}
82
83pub trait ExtensionOptionsFFIProvider {
84    /// Extract a [`ConfigExtension`]. This method should attempt to first extract
85    /// the extension from the local options when possible. Should that fail, it
86    /// should attempt to extract the FFI options and then convert them to the
87    /// desired [`ConfigExtension`].
88    fn local_or_ffi_extension<C: ConfigExtension + Clone + Default>(&self) -> Option<C>;
89}
90
91impl ExtensionOptionsFFIProvider for ConfigOptions {
92    fn local_or_ffi_extension<C: ConfigExtension + Clone + Default>(&self) -> Option<C> {
93        self.extensions
94            .get::<C>()
95            .map(|v| v.to_owned())
96            .or_else(|| {
97                self.extensions
98                    .get::<FFI_ExtensionOptions>()
99                    .and_then(|ffi_ext| ffi_ext.to_extension().ok())
100            })
101    }
102}
103
104impl ExtensionOptionsFFIProvider for TableOptions {
105    fn local_or_ffi_extension<C: ConfigExtension + Clone + Default>(&self) -> Option<C> {
106        self.extensions
107            .get::<C>()
108            .map(|v| v.to_owned())
109            .or_else(|| {
110                self.extensions
111                    .get::<FFI_ExtensionOptions>()
112                    .and_then(|ffi_ext| ffi_ext.to_extension().ok())
113            })
114    }
115}
116
117/// A stable struct for sharing [`TableOptions`] across FFI boundaries.
118///
119/// Accessing FFI extension options require a slightly different pattern
120/// than local extensions. The trait [`ExtensionOptionsFFIProvider`] can
121/// be used to simplify accessing FFI extensions.
122#[repr(C)]
123#[derive(Debug, Clone, StableAbi)]
124pub struct FFI_TableOptions {
125    base_options: RHashMap<RString, RString>,
126
127    extensions: FFI_ExtensionOptions,
128}
129
130impl From<&TableOptions> for FFI_TableOptions {
131    fn from(options: &TableOptions) -> Self {
132        let base_options: RHashMap<RString, RString> = options
133            .entries()
134            .into_iter()
135            .filter_map(|entry| entry.value.map(|value| (entry.key, value)))
136            .map(|(key, value)| (key.into(), value.into()))
137            .collect();
138
139        let mut extensions = FFI_ExtensionOptions::default();
140        for (extension_name, extension) in options.extensions.iter() {
141            for entry in extension.entries().iter() {
142                if let Some(value) = entry.value.as_ref() {
143                    extensions
144                        .set(format!("{extension_name}.{}", entry.key).as_str(), value)
145                        .expect("FFI_ExtensionOptions set should always return Ok");
146                }
147            }
148        }
149
150        Self {
151            base_options,
152            extensions,
153        }
154    }
155}
156
157impl TryFrom<FFI_TableOptions> for TableOptions {
158    type Error = DataFusionError;
159    fn try_from(ffi_options: FFI_TableOptions) -> Result<Self, Self::Error> {
160        let mut options = TableOptions::default();
161        options.extensions.insert(ffi_options.extensions);
162
163        for kv_tuple in ffi_options.base_options.iter() {
164            options.set(kv_tuple.0.as_str(), kv_tuple.1.as_str())?;
165        }
166
167        Ok(options)
168    }
169}