blob: 36dd7cf4ca919e0e643d5676de297f9860d4bc40 [file]
// 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.
use std::ffi::c_char;
use crate::utils::into_string_ptr;
#[repr(C)]
/// FFI representation of OpenDAL metadata.
pub struct OpendalMetadata {
pub mode: i32,
pub content_length: u64,
pub content_disposition: *mut c_char,
pub content_md5: *mut c_char,
pub content_type: *mut c_char,
pub content_encoding: *mut c_char,
pub cache_control: *mut c_char,
pub etag: *mut c_char,
pub last_modified_has_value: u8,
pub last_modified_second: i64,
pub last_modified_nanosecond: i32,
pub version: *mut c_char,
}
impl OpendalMetadata {
pub fn from_metadata(metadata: opendal::Metadata) -> Self {
let mode = match metadata.mode() {
opendal::EntryMode::FILE => 0,
opendal::EntryMode::DIR => 1,
opendal::EntryMode::Unknown => 2,
};
let (last_modified_has_value, last_modified_second, last_modified_nanosecond) =
if let Some(last_modified) = metadata.last_modified() {
(
1,
last_modified.into_inner().as_second(),
last_modified.into_inner().subsec_nanosecond(),
)
} else {
(0, 0, 0)
};
Self {
mode,
content_length: metadata.content_length(),
content_disposition: optional_string_to_ptr(metadata.content_disposition()),
content_md5: optional_string_to_ptr(metadata.content_md5()),
content_type: optional_string_to_ptr(metadata.content_type()),
content_encoding: optional_string_to_ptr(metadata.content_encoding()),
cache_control: optional_string_to_ptr(metadata.cache_control()),
etag: optional_string_to_ptr(metadata.etag()),
last_modified_has_value,
last_modified_second,
last_modified_nanosecond,
version: optional_string_to_ptr(metadata.version()),
}
}
}
/// Convert an optional Rust string into an owned UTF-8 C string pointer.
///
/// Returns null when the option is `None`.
fn optional_string_to_ptr(value: Option<&str>) -> *mut c_char {
value
.map(|v| into_string_ptr(v.to_string()))
.unwrap_or(std::ptr::null_mut())
}
/// # Safety
///
/// - `metadata` must be null or a pointer returned by Rust for `OpendalMetadata`.
/// - Must be called at most once for the same pointer.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn metadata_free(metadata: *mut OpendalMetadata) {
if metadata.is_null() {
return;
}
unsafe {
let metadata = Box::from_raw(metadata);
if !metadata.content_disposition.is_null() {
drop(std::ffi::CString::from_raw(metadata.content_disposition));
}
if !metadata.content_md5.is_null() {
drop(std::ffi::CString::from_raw(metadata.content_md5));
}
if !metadata.content_type.is_null() {
drop(std::ffi::CString::from_raw(metadata.content_type));
}
if !metadata.content_encoding.is_null() {
drop(std::ffi::CString::from_raw(metadata.content_encoding));
}
if !metadata.cache_control.is_null() {
drop(std::ffi::CString::from_raw(metadata.cache_control));
}
if !metadata.etag.is_null() {
drop(std::ffi::CString::from_raw(metadata.etag));
}
if !metadata.version.is_null() {
drop(std::ffi::CString::from_raw(metadata.version));
}
}
}