blob: 3648f8a93c38f9acfd710b05f7a3fe86e9c23469 [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::collections::HashMap;
use opendal::raw::Timestamp;
use crate::error::OpenDALError;
use crate::validators::prelude::{
validate_list_limit, validate_read_chunk, validate_read_concurrent, validate_read_gap,
validate_read_range_end, validate_write_chunk, validate_write_concurrent,
};
fn parse_bool(values: &HashMap<String, String>, key: &str) -> Result<Option<bool>, OpenDALError> {
let Some(raw) = values.get(key) else {
return Ok(None);
};
match raw.to_ascii_lowercase().as_str() {
"true" | "1" => Ok(Some(true)),
"false" | "0" => Ok(Some(false)),
_ => Err(crate::utils::config_invalid_error(format!(
"invalid boolean value for {key}: {raw}"
))),
}
}
fn parse_usize(values: &HashMap<String, String>, key: &str) -> Result<Option<usize>, OpenDALError> {
let Some(raw) = values.get(key) else {
return Ok(None);
};
raw.parse::<usize>().map(Some).map_err(|err| {
crate::utils::config_invalid_error(format!("invalid usize value for {key}: {raw}, {err}"))
})
}
fn parse_u64(values: &HashMap<String, String>, key: &str) -> Result<Option<u64>, OpenDALError> {
let Some(raw) = values.get(key) else {
return Ok(None);
};
raw.parse::<u64>().map(Some).map_err(|err| {
crate::utils::config_invalid_error(format!("invalid u64 value for {key}: {raw}, {err}"))
})
}
fn parse_timestamp(
values: &HashMap<String, String>,
key: &str,
) -> Result<Option<Timestamp>, OpenDALError> {
let Some(raw) = values.get(key) else {
return Ok(None);
};
let millis = raw.parse::<i64>().map_err(|err| {
crate::utils::config_invalid_error(format!(
"invalid timestamp milliseconds for {key}: {raw}, {err}"
))
})?;
Timestamp::from_millisecond(millis)
.map(Some)
.map_err(OpenDALError::from_opendal_error)
}
fn parse_string(values: &HashMap<String, String>, key: &str) -> Option<String> {
values.get(key).cloned()
}
pub fn parse_read_options(
values: &HashMap<String, String>,
) -> Result<opendal::options::ReadOptions, OpenDALError> {
let mut options = opendal::options::ReadOptions::default();
let offset = parse_u64(values, "offset")?.unwrap_or_default();
let length = parse_u64(values, "length")?;
if offset > 0 || length.is_some() {
options.range = match validate_read_range_end(offset, length)? {
Some(end) => (offset..end).into(),
None => (offset..).into(),
};
}
options.version = parse_string(values, "version");
options.if_match = parse_string(values, "if_match");
options.if_none_match = parse_string(values, "if_none_match");
options.if_modified_since = parse_timestamp(values, "if_modified_since")?;
options.if_unmodified_since = parse_timestamp(values, "if_unmodified_since")?;
if let Some(concurrent) = parse_usize(values, "concurrent")? {
validate_read_concurrent(concurrent)?;
options.concurrent = concurrent;
}
if let Some(chunk) = parse_usize(values, "chunk")? {
validate_read_chunk(chunk)?;
options.chunk = Some(chunk);
}
if let Some(gap) = parse_usize(values, "gap")? {
validate_read_gap(gap)?;
options.gap = Some(gap);
}
options.override_content_type = parse_string(values, "override_content_type");
options.override_cache_control = parse_string(values, "override_cache_control");
options.override_content_disposition = parse_string(values, "override_content_disposition");
Ok(options)
}
pub fn parse_write_options(
values: &HashMap<String, String>,
) -> Result<opendal::options::WriteOptions, OpenDALError> {
let mut options = opendal::options::WriteOptions::default();
if let Some(append) = parse_bool(values, "append")? {
options.append = append;
}
options.cache_control = parse_string(values, "cache_control");
options.content_type = parse_string(values, "content_type");
options.content_disposition = parse_string(values, "content_disposition");
options.content_encoding = parse_string(values, "content_encoding");
options.if_match = parse_string(values, "if_match");
options.if_none_match = parse_string(values, "if_none_match");
if let Some(if_not_exists) = parse_bool(values, "if_not_exists")? {
options.if_not_exists = if_not_exists;
}
if let Some(concurrent) = parse_usize(values, "concurrent")? {
validate_write_concurrent(concurrent)?;
options.concurrent = concurrent;
}
if let Some(chunk) = parse_usize(values, "chunk")? {
validate_write_chunk(chunk)?;
options.chunk = Some(chunk);
}
let user_metadata = values
.iter()
.filter_map(|(key, value)| {
key.strip_prefix("user_metadata.")
.map(|k| (k.to_string(), value.to_string()))
})
.collect::<HashMap<_, _>>();
if !user_metadata.is_empty() {
options.user_metadata = Some(user_metadata);
}
Ok(options)
}
pub fn parse_stat_options(
values: &HashMap<String, String>,
) -> Result<opendal::options::StatOptions, OpenDALError> {
Ok(opendal::options::StatOptions {
version: parse_string(values, "version"),
if_match: parse_string(values, "if_match"),
if_none_match: parse_string(values, "if_none_match"),
if_modified_since: parse_timestamp(values, "if_modified_since")?,
if_unmodified_since: parse_timestamp(values, "if_unmodified_since")?,
override_content_type: parse_string(values, "override_content_type"),
override_cache_control: parse_string(values, "override_cache_control"),
override_content_disposition: parse_string(values, "override_content_disposition"),
})
}
pub fn parse_list_options(
values: &HashMap<String, String>,
) -> Result<opendal::options::ListOptions, OpenDALError> {
let mut options = opendal::options::ListOptions::default();
if let Some(recursive) = parse_bool(values, "recursive")? {
options.recursive = recursive;
}
if let Some(limit) = parse_usize(values, "limit")? {
validate_list_limit(limit)?;
options.limit = Some(limit);
}
options.start_after = parse_string(values, "start_after");
if let Some(versions) = parse_bool(values, "versions")? {
options.versions = versions;
}
if let Some(deleted) = parse_bool(values, "deleted")? {
options.deleted = deleted;
}
Ok(options)
}