opendal/services/gdrive/
backend.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::fmt::Debug;
19use std::sync::Arc;
20
21use bytes::Buf;
22use chrono::Utc;
23use http::Response;
24use http::StatusCode;
25
26use super::core::GdriveCore;
27use super::core::GdriveFile;
28use super::delete::GdriveDeleter;
29use super::error::parse_error;
30use super::lister::GdriveLister;
31use super::writer::GdriveWriter;
32use crate::raw::*;
33use crate::*;
34
35#[derive(Clone, Debug)]
36pub struct GdriveBackend {
37    pub core: Arc<GdriveCore>,
38}
39
40impl Access for GdriveBackend {
41    type Reader = HttpBody;
42    type Writer = oio::OneShotWriter<GdriveWriter>;
43    type Lister = oio::PageLister<GdriveLister>;
44    type Deleter = oio::OneShotDeleter<GdriveDeleter>;
45    type BlockingReader = ();
46    type BlockingWriter = ();
47    type BlockingLister = ();
48    type BlockingDeleter = ();
49
50    fn info(&self) -> Arc<AccessorInfo> {
51        self.core.info.clone()
52    }
53
54    async fn create_dir(&self, path: &str, _args: OpCreateDir) -> Result<RpCreateDir> {
55        let path = build_abs_path(&self.core.root, path);
56        let _ = self.core.path_cache.ensure_dir(&path).await?;
57
58        Ok(RpCreateDir::default())
59    }
60
61    async fn stat(&self, path: &str, _args: OpStat) -> Result<RpStat> {
62        let resp = self.core.gdrive_stat(path).await?;
63
64        if resp.status() != StatusCode::OK {
65            return Err(parse_error(resp));
66        }
67
68        let bs = resp.into_body();
69        let gdrive_file: GdriveFile =
70            serde_json::from_reader(bs.reader()).map_err(new_json_deserialize_error)?;
71
72        let file_type = if gdrive_file.mime_type == "application/vnd.google-apps.folder" {
73            EntryMode::DIR
74        } else {
75            EntryMode::FILE
76        };
77        let mut meta = Metadata::new(file_type).with_content_type(gdrive_file.mime_type);
78        if let Some(v) = gdrive_file.size {
79            meta = meta.with_content_length(v.parse::<u64>().map_err(|e| {
80                Error::new(ErrorKind::Unexpected, "parse content length").set_source(e)
81            })?);
82        }
83        if let Some(v) = gdrive_file.modified_time {
84            meta = meta.with_last_modified(v.parse::<chrono::DateTime<Utc>>().map_err(|e| {
85                Error::new(ErrorKind::Unexpected, "parse last modified time").set_source(e)
86            })?);
87        }
88        Ok(RpStat::new(meta))
89    }
90
91    async fn read(&self, path: &str, args: OpRead) -> Result<(RpRead, Self::Reader)> {
92        let resp = self.core.gdrive_get(path, args.range()).await?;
93
94        let status = resp.status();
95        match status {
96            StatusCode::OK | StatusCode::PARTIAL_CONTENT => Ok((RpRead::new(), resp.into_body())),
97            _ => {
98                let (part, mut body) = resp.into_parts();
99                let buf = body.to_buffer().await?;
100                Err(parse_error(Response::from_parts(part, buf)))
101            }
102        }
103    }
104
105    async fn write(&self, path: &str, _: OpWrite) -> Result<(RpWrite, Self::Writer)> {
106        let path = build_abs_path(&self.core.root, path);
107
108        // As Google Drive allows files have the same name, we need to check if the file exists.
109        // If the file exists, we will keep its ID and update it.
110        let file_id = self.core.path_cache.get(&path).await?;
111
112        Ok((
113            RpWrite::default(),
114            oio::OneShotWriter::new(GdriveWriter::new(self.core.clone(), path, file_id)),
115        ))
116    }
117
118    async fn delete(&self) -> Result<(RpDelete, Self::Deleter)> {
119        Ok((
120            RpDelete::default(),
121            oio::OneShotDeleter::new(GdriveDeleter::new(self.core.clone())),
122        ))
123    }
124
125    async fn list(&self, path: &str, _args: OpList) -> Result<(RpList, Self::Lister)> {
126        let path = build_abs_path(&self.core.root, path);
127        let l = GdriveLister::new(path, self.core.clone());
128        Ok((RpList::default(), oio::PageLister::new(l)))
129    }
130
131    async fn copy(&self, from: &str, to: &str, _args: OpCopy) -> Result<RpCopy> {
132        let resp = self.core.gdrive_copy(from, to).await?;
133
134        match resp.status() {
135            StatusCode::OK => Ok(RpCopy::default()),
136            _ => Err(parse_error(resp)),
137        }
138    }
139
140    async fn rename(&self, from: &str, to: &str, _args: OpRename) -> Result<RpRename> {
141        let source = build_abs_path(&self.core.root, from);
142        let target = build_abs_path(&self.core.root, to);
143
144        // rename will overwrite `to`, delete it if exist
145        if let Some(id) = self.core.path_cache.get(&target).await? {
146            let resp = self.core.gdrive_trash(&id).await?;
147            let status = resp.status();
148            if status != StatusCode::OK {
149                return Err(parse_error(resp));
150            }
151
152            self.core.path_cache.remove(&target).await;
153        }
154
155        let resp = self
156            .core
157            .gdrive_patch_metadata_request(&source, &target)
158            .await?;
159
160        let status = resp.status();
161
162        match status {
163            StatusCode::OK => {
164                let body = resp.into_body();
165                let meta: GdriveFile =
166                    serde_json::from_reader(body.reader()).map_err(new_json_deserialize_error)?;
167
168                let cache = &self.core.path_cache;
169
170                cache.remove(&build_abs_path(&self.core.root, from)).await;
171                cache
172                    .insert(&build_abs_path(&self.core.root, to), &meta.id)
173                    .await;
174
175                Ok(RpRename::default())
176            }
177            _ => Err(parse_error(resp)),
178        }
179    }
180}