blob: fe0b4254e2a58b1366d66ee38f4e851190854845 [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 ::opendal as core;
use std::collections::HashMap;
use std::ffi::{c_char, c_void, CString};
use std::ptr;
/// \brief A user metadata key-value pair.
#[repr(C)]
pub struct opendal_metadata_user_metadata_pair {
/// The key of the user metadata.
pub key: *const c_char,
/// The value of the user metadata.
pub value: *const c_char,
}
struct opendal_metadata_user_metadata_inner {
pairs: Vec<opendal_metadata_user_metadata_pair>,
#[allow(dead_code)]
keys: Vec<CString>,
#[allow(dead_code)]
values: Vec<CString>,
}
impl opendal_metadata_user_metadata_inner {
fn new(user_metadata: &HashMap<String, String>) -> Self {
let mut keys = Vec::with_capacity(user_metadata.len());
let mut values = Vec::with_capacity(user_metadata.len());
let mut pairs = Vec::with_capacity(user_metadata.len());
for (key, value) in user_metadata {
keys.push(
CString::new(key.as_str()).expect("user metadata key should not contain nul"),
);
values.push(
CString::new(value.as_str()).expect("user metadata value should not contain nul"),
);
pairs.push(opendal_metadata_user_metadata_pair {
key: keys.last().expect("key has just been pushed").as_ptr(),
value: values.last().expect("value has just been pushed").as_ptr(),
});
}
Self {
pairs,
keys,
values,
}
}
}
/// \brief User metadata associated with a **path**.
#[repr(C)]
pub struct opendal_metadata_user_metadata {
/// The pointer to the user metadata in the Rust code.
/// Only touch this on judging whether it is NULL.
inner: *mut c_void,
}
/// \brief Carries all metadata associated with a **path**.
///
/// The metadata of the "thing" under a path. Please **only** use the opendal_metadata
/// with our provided API, e.g. opendal_metadata_content_length().
///
/// \note The metadata is also heap-allocated, please call opendal_metadata_free() on this
/// to free the heap memory.
///
/// @see opendal_metadata_free
#[repr(C)]
pub struct opendal_metadata {
/// The pointer to the opendal::Metadata in the Rust code.
/// Only touch this on judging whether it is NULL.
inner: *mut c_void,
}
impl opendal_metadata {
fn deref(&self) -> &core::Metadata {
// Safety: the inner should never be null once constructed
// The use-after-free is undefined behavior
unsafe { &*(self.inner as *mut core::Metadata) }
}
}
impl opendal_metadata {
/// Convert a Rust core [`od::Metadata`] into a heap allocated C-compatible
/// [`opendal_metadata`]
pub(crate) fn new(m: core::Metadata) -> Self {
Self {
inner: Box::into_raw(Box::new(m)) as _,
}
}
/// \brief Free the heap-allocated metadata used by opendal_metadata
#[no_mangle]
pub unsafe extern "C" fn opendal_metadata_free(ptr: *mut opendal_metadata) {
unsafe {
if !ptr.is_null() {
drop(Box::from_raw((*ptr).inner as *mut core::Metadata));
drop(Box::from_raw(ptr));
}
}
}
fn optional_str(v: Option<&str>) -> *mut c_char {
match v {
Some(v) => CString::new(v)
.expect("metadata string should not contain nul")
.into_raw(),
None => ptr::null_mut(),
}
}
/// \brief Return mode of the metadata: 0 for unknown, 1 for file, and 2 for dir.
#[no_mangle]
pub extern "C" fn opendal_metadata_mode(&self) -> u8 {
match self.deref().mode() {
core::EntryMode::Unknown => 0,
core::EntryMode::FILE => 1,
core::EntryMode::DIR => 2,
}
}
/// \brief Return the content_length of the metadata
#[no_mangle]
pub extern "C" fn opendal_metadata_content_length(&self) -> u64 {
self.deref().content_length()
}
/// \brief Return whether the path represents a file
#[no_mangle]
pub extern "C" fn opendal_metadata_is_file(&self) -> bool {
self.deref().is_file()
}
/// \brief Return whether the path represents a directory
#[no_mangle]
pub extern "C" fn opendal_metadata_is_dir(&self) -> bool {
self.deref().is_dir()
}
/// \brief Return whether this metadata is current.
///
/// Returns 1 for current, 0 for not current, and 2 if unknown.
#[no_mangle]
pub extern "C" fn opendal_metadata_is_current(&self) -> u8 {
match self.deref().is_current() {
Some(true) => 1,
Some(false) => 0,
None => 2,
}
}
/// \brief Return whether this metadata is deleted.
#[no_mangle]
pub extern "C" fn opendal_metadata_is_deleted(&self) -> bool {
self.deref().is_deleted()
}
/// \brief Return the cache control of the metadata.
///
/// \note: The string is on heap, free it with opendal_string_free().
#[no_mangle]
pub extern "C" fn opendal_metadata_cache_control(&self) -> *mut c_char {
Self::optional_str(self.deref().cache_control())
}
/// \brief Return the content disposition of the metadata.
///
/// \note: The string is on heap, free it with opendal_string_free().
#[no_mangle]
pub extern "C" fn opendal_metadata_content_disposition(&self) -> *mut c_char {
Self::optional_str(self.deref().content_disposition())
}
/// \brief Return the content md5 of the metadata.
///
/// \note: The string is on heap, free it with opendal_string_free().
#[no_mangle]
pub extern "C" fn opendal_metadata_content_md5(&self) -> *mut c_char {
Self::optional_str(self.deref().content_md5())
}
/// \brief Return the content type of the metadata.
///
/// \note: The string is on heap, free it with opendal_string_free().
#[no_mangle]
pub extern "C" fn opendal_metadata_content_type(&self) -> *mut c_char {
Self::optional_str(self.deref().content_type())
}
/// \brief Return the content encoding of the metadata.
///
/// \note: The string is on heap, free it with opendal_string_free().
#[no_mangle]
pub extern "C" fn opendal_metadata_content_encoding(&self) -> *mut c_char {
Self::optional_str(self.deref().content_encoding())
}
/// \brief Return the etag of the metadata.
///
/// \note: The string is on heap, free it with opendal_string_free().
#[no_mangle]
pub extern "C" fn opendal_metadata_etag(&self) -> *mut c_char {
Self::optional_str(self.deref().etag())
}
/// \brief Return the version of the metadata.
///
/// \note: The string is on heap, free it with opendal_string_free().
#[no_mangle]
pub extern "C" fn opendal_metadata_version(&self) -> *mut c_char {
Self::optional_str(self.deref().version())
}
/// \brief Return the user metadata of the metadata.
///
/// \note: The returned user metadata is on heap, free it with opendal_metadata_user_metadata_free().
#[no_mangle]
pub extern "C" fn opendal_metadata_get_user_metadata(
&self,
) -> *mut opendal_metadata_user_metadata {
match self.deref().user_metadata() {
Some(user_metadata) => Box::into_raw(Box::new(opendal_metadata_user_metadata {
inner: Box::into_raw(Box::new(opendal_metadata_user_metadata_inner::new(
user_metadata,
))) as _,
})),
None => ptr::null_mut(),
}
}
/// \brief Return the last_modified of the metadata, in milliseconds
///
/// # Example
/// ```C
/// // ... previously you wrote "Hello, World!" to path "/testpath"
/// opendal_result_stat s = opendal_operator_stat(op, "/testpath");
/// assert(s.error == NULL);
///
/// opendal_metadata *meta = s.meta;
/// assert(opendal_metadata_last_modified_ms(meta) != -1);
/// ```
#[no_mangle]
pub extern "C" fn opendal_metadata_last_modified_ms(&self) -> i64 {
let mtime = self.deref().last_modified();
match mtime {
None => -1,
Some(time) => time.into_inner().as_millisecond(),
}
}
}
impl opendal_metadata_user_metadata {
/// \brief Return the key-value pairs of the user metadata.
#[no_mangle]
pub unsafe extern "C" fn opendal_metadata_user_metadata_pairs(
metadata: *const Self,
) -> *const opendal_metadata_user_metadata_pair {
if metadata.is_null() {
return ptr::null();
}
let inner = unsafe { &*((*metadata).inner as *mut opendal_metadata_user_metadata_inner) };
inner.pairs.as_ptr()
}
/// \brief Return the number of key-value pairs in the user metadata.
#[no_mangle]
pub unsafe extern "C" fn opendal_metadata_user_metadata_len(metadata: *const Self) -> usize {
if metadata.is_null() {
return 0;
}
let inner = unsafe { &*((*metadata).inner as *mut opendal_metadata_user_metadata_inner) };
inner.pairs.len()
}
/// \brief Free the user metadata returned by opendal_metadata_user_metadata.
#[no_mangle]
pub unsafe extern "C" fn opendal_metadata_user_metadata_free(metadata: *mut Self) {
if !metadata.is_null() {
drop(unsafe {
Box::from_raw((*metadata).inner as *mut opendal_metadata_user_metadata_inner)
});
drop(unsafe { Box::from_raw(metadata) });
}
}
}