opendal/services/swift/
lister.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 std::sync::Arc;
19
20use bytes::Buf;
21
22use super::core::*;
23use super::error::parse_error;
24use crate::raw::*;
25use crate::*;
26
27pub struct SwiftLister {
28    core: Arc<SwiftCore>,
29    path: String,
30    delimiter: &'static str,
31    limit: Option<usize>,
32}
33
34impl SwiftLister {
35    pub fn new(core: Arc<SwiftCore>, path: String, recursive: bool, limit: Option<usize>) -> Self {
36        let delimiter = if recursive { "" } else { "/" };
37        Self {
38            core,
39            path,
40            delimiter,
41            limit,
42        }
43    }
44}
45
46impl oio::PageList for SwiftLister {
47    async fn next_page(&self, ctx: &mut oio::PageContext) -> Result<()> {
48        let response = self
49            .core
50            .swift_list(&self.path, self.delimiter, self.limit, &ctx.token)
51            .await?;
52
53        let status_code = response.status();
54
55        if !status_code.is_success() {
56            let error = parse_error(response);
57            return Err(error);
58        }
59
60        let bytes = response.into_body();
61        let decoded_response: Vec<ListOpResponse> =
62            serde_json::from_reader(bytes.reader()).map_err(new_json_deserialize_error)?;
63
64        // Update token and done based on resp.
65        if let Some(entry) = decoded_response.last() {
66            let path = match entry {
67                ListOpResponse::Subdir { subdir } => subdir,
68                ListOpResponse::FileInfo { name, .. } => name,
69            };
70            ctx.token.clone_from(path);
71        } else {
72            ctx.done = true;
73        }
74
75        for status in decoded_response {
76            let entry: oio::Entry = match status {
77                ListOpResponse::Subdir { subdir } => {
78                    let mut path = build_rel_path(self.core.root.as_str(), subdir.as_str());
79                    if path.is_empty() {
80                        path = "/".to_string();
81                    }
82                    let meta = Metadata::new(EntryMode::DIR);
83                    oio::Entry::with(path, meta)
84                }
85                ListOpResponse::FileInfo {
86                    bytes,
87                    hash,
88                    name,
89                    content_type,
90                    mut last_modified,
91                } => {
92                    let mut path = build_rel_path(self.core.root.as_str(), name.as_str());
93                    if path.is_empty() {
94                        path = "/".to_string();
95                    }
96                    let mut meta = Metadata::new(EntryMode::from_path(path.as_str()));
97                    meta.set_content_length(bytes);
98                    meta.set_content_md5(hash.as_str());
99
100                    // OpenStack Swift returns time without 'Z' at the end,
101                    // which causes an error in parse_datetime_from_rfc3339.
102                    // we'll change "2023-10-28T19:18:11.682610" to "2023-10-28T19:18:11.682610Z".
103                    if !last_modified.ends_with('Z') {
104                        last_modified.push('Z');
105                    }
106                    meta.set_last_modified(parse_datetime_from_rfc3339(last_modified.as_str())?);
107
108                    if let Some(content_type) = content_type {
109                        meta.set_content_type(content_type.as_str());
110                    }
111
112                    oio::Entry::with(path, meta)
113                }
114            };
115            ctx.entries.push_back(entry);
116        }
117        Ok(())
118    }
119}