Skip to main content

object_store_opendal/service/
core.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
18use crate::{datetime_to_timestamp, timestamp_to_datetime};
19use object_store::{
20    Attribute, AttributeValue, GetOptions, GetRange, ObjectMeta, PutOptions, PutResult,
21};
22use opendal::raw::*;
23use opendal::*;
24use std::borrow::Cow;
25
26/// Parse OpStat arguments to object_store GetOptions for head requests
27pub fn parse_op_stat(args: &OpStat) -> Result<GetOptions> {
28    let mut options = GetOptions {
29        head: true, // This is a head request
30        ..Default::default()
31    };
32
33    if let Some(version) = args.version() {
34        options.version = Some(version.to_string());
35    }
36
37    if let Some(if_match) = args.if_match() {
38        options.if_match = Some(if_match.to_string());
39    }
40
41    if let Some(if_none_match) = args.if_none_match() {
42        options.if_none_match = Some(if_none_match.to_string());
43    }
44
45    if let Some(if_modified_since) = args.if_modified_since() {
46        options.if_modified_since = timestamp_to_datetime(if_modified_since);
47    }
48
49    if let Some(if_unmodified_since) = args.if_unmodified_since() {
50        options.if_unmodified_since = timestamp_to_datetime(if_unmodified_since);
51    }
52
53    Ok(options)
54}
55
56/// Parse OpRead arguments to object_store GetOptions
57pub fn parse_op_read(args: &OpRead, range: BytesRange) -> Result<GetOptions> {
58    let mut options = GetOptions::default();
59
60    if let Some(version) = args.version() {
61        options.version = Some(version.to_string());
62    }
63
64    if let Some(if_match) = args.if_match() {
65        options.if_match = Some(if_match.to_string());
66    }
67
68    if let Some(if_none_match) = args.if_none_match() {
69        options.if_none_match = Some(if_none_match.to_string());
70    }
71
72    if let Some(if_modified_since) = args.if_modified_since() {
73        options.if_modified_since = timestamp_to_datetime(if_modified_since);
74    }
75
76    if let Some(if_unmodified_since) = args.if_unmodified_since() {
77        options.if_unmodified_since = timestamp_to_datetime(if_unmodified_since);
78    }
79
80    match range {
81        BytesRange::Range {
82            offset: 0,
83            size: None,
84        } => {}
85        BytesRange::Range { offset, size } => match size {
86            Some(size) => {
87                let end = offset.checked_add(size).ok_or_else(|| {
88                    Error::new(ErrorKind::RangeNotSatisfied, "range exceeds content length")
89                        .with_context("offset", offset)
90                        .with_context("size", size)
91                })?;
92                options.range = Some(GetRange::Bounded(offset..end));
93            }
94            None => {
95                options.range = Some(GetRange::Offset(offset));
96            }
97        },
98        BytesRange::Suffix { size } => {
99            options.range = Some(GetRange::Suffix(size));
100        }
101    }
102
103    Ok(options)
104}
105
106/// Parse OpWrite arguments to object_store PutOptions
107pub fn parse_op_write(args: &OpWrite) -> Result<PutOptions> {
108    let mut opts = PutOptions::default();
109
110    if let Some(content_type) = args.content_type() {
111        opts.attributes.insert(
112            Attribute::ContentType,
113            AttributeValue::from(content_type.to_string()),
114        );
115    }
116
117    if let Some(content_disposition) = args.content_disposition() {
118        opts.attributes.insert(
119            Attribute::ContentDisposition,
120            AttributeValue::from(content_disposition.to_string()),
121        );
122    }
123
124    if let Some(cache_control) = args.cache_control() {
125        opts.attributes.insert(
126            Attribute::CacheControl,
127            AttributeValue::from(cache_control.to_string()),
128        );
129    }
130
131    if let Some(user_metadata) = args.user_metadata() {
132        for (key, value) in user_metadata {
133            opts.attributes.insert(
134                Attribute::Metadata(Cow::from(key.to_string())),
135                AttributeValue::from(value.to_string()),
136            );
137        }
138    }
139    Ok(opts)
140}
141
142/// Convert PutOptions to PutMultipartOptions
143pub fn format_put_multipart_options(opts: PutOptions) -> object_store::PutMultipartOptions {
144    object_store::PutMultipartOptions {
145        attributes: opts.attributes,
146        ..Default::default()
147    }
148}
149
150/// Format PutResult to OpenDAL Metadata
151pub fn format_put_result(result: PutResult) -> Metadata {
152    let mut metadata = Metadata::new(EntryMode::FILE);
153    if let Some(etag) = &result.e_tag {
154        metadata.set_etag(etag);
155    }
156    if let Some(version) = &result.version {
157        metadata.set_version(version);
158    }
159    metadata
160}
161
162/// Format `object_store::ObjectMeta` to `opendal::Metadata`.
163pub fn format_metadata(meta: &ObjectMeta) -> Metadata {
164    let mut metadata = Metadata::new(EntryMode::FILE);
165    metadata.set_content_length(meta.size);
166    if let Some(last_modified) = datetime_to_timestamp(meta.last_modified) {
167        metadata.set_last_modified(last_modified);
168    }
169    if let Some(etag) = &meta.e_tag {
170        metadata.set_etag(etag);
171    }
172    if let Some(version) = &meta.version {
173        metadata.set_version(version);
174    }
175    metadata
176}
177
178#[cfg(test)]
179mod tests {
180    use super::*;
181
182    #[test]
183    fn test_parse_op_read_suffix_range() -> Result<()> {
184        let opts = parse_op_read(&OpRead::new(), BytesRange::suffix(8))?;
185
186        assert_eq!(opts.range, Some(GetRange::Suffix(8)));
187        Ok(())
188    }
189}