This is an automated email from the ASF dual-hosted git repository.

xuanwo pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/incubator-opendal.git


The following commit(s) were added to refs/heads/main by this push:
     new 9cce4d31 feat(services/sftp): support copy and rename for sftp (#2263)
9cce4d31 is described below

commit 9cce4d31418567ed2fc8e7178f11a48ac9cbdede
Author: silver-ymz <[email protected]>
AuthorDate: Wed May 17 16:01:26 2023 +0800

    feat(services/sftp): support copy and rename for sftp (#2263)
    
    * feat(services/sftp): support copy and rename for sftp
    
    Signed-off-by: silver-ymz <[email protected]>
    
    * change copy and rename to partially support
    
    Signed-off-by: silver-ymz <[email protected]>
    
    * rename create dir for destination
    
    Signed-off-by: silver-ymz <[email protected]>
    
    * remove explanation of partial support
    
    Signed-off-by: silver-ymz <[email protected]>
    
    * remove read_can_seek
    
    Signed-off-by: silver-ymz <[email protected]>
    
    ---------
    
    Signed-off-by: silver-ymz <[email protected]>
---
 core/Cargo.toml                   |  7 +-----
 core/src/services/sftp/backend.rs | 49 ++++++++++++++++++++++++++++++++-------
 2 files changed, 41 insertions(+), 15 deletions(-)

diff --git a/core/Cargo.toml b/core/Cargo.toml
index 3817a64b..6d1f0e99 100644
--- a/core/Cargo.toml
+++ b/core/Cargo.toml
@@ -135,12 +135,7 @@ services-s3 = [
   "reqsign?/services-aws",
   "reqsign?/reqwest_request",
 ]
-services-sftp = [
-  "dep:openssh",
-  "dep:openssh-sftp-client",
-  "dep:dirs",
-  "futures/executor",
-]
+services-sftp = ["dep:openssh", "dep:openssh-sftp-client", "dep:dirs"]
 services-sled = ["dep:sled"]
 services-supabase = []
 services-vercel-artifacts = []
diff --git a/core/src/services/sftp/backend.rs 
b/core/src/services/sftp/backend.rs
index 8f9b7be0..08c9d2cb 100644
--- a/core/src/services/sftp/backend.rs
+++ b/core/src/services/sftp/backend.rs
@@ -56,7 +56,7 @@ use crate::*;
 /// - [x] create_dir
 /// - [x] delete
 /// - [ ] copy
-/// - [ ] rename
+/// - [x] rename
 /// - [x] list
 /// - [ ] ~~scan~~
 /// - [ ] ~~presign~~
@@ -277,8 +277,10 @@ impl Accessor for SftpBackend {
                 stat: true,
 
                 read: true,
+                read_with_range: true,
 
                 write: true,
+                write_without_content_length: true,
                 create_dir: true,
                 delete: true,
 
@@ -286,6 +288,8 @@ impl Accessor for SftpBackend {
                 list_with_limit: true,
                 list_with_delimiter_slash: true,
 
+                rename: true,
+
                 ..Default::default()
             });
 
@@ -353,14 +357,7 @@ impl Accessor for SftpBackend {
         Ok((RpRead::new(end - start), r))
     }
 
-    async fn write(&self, path: &str, args: OpWrite) -> Result<(RpWrite, 
Self::Writer)> {
-        if args.content_length().is_none() {
-            return Err(Error::new(
-                ErrorKind::Unsupported,
-                "write without content length is not supported",
-            ));
-        }
-
+    async fn write(&self, path: &str, _: OpWrite) -> Result<(RpWrite, 
Self::Writer)> {
         if let Some((dir, _)) = path.rsplit_once('/') {
             self.create_dir(dir, OpCreateDir::default()).await?;
         }
@@ -376,6 +373,40 @@ impl Accessor for SftpBackend {
         Ok((RpWrite::new(), SftpWriter::new(file)))
     }
 
+    async fn copy(&self, from: &str, to: &str, _: OpCopy) -> Result<RpCopy> {
+        let client = self.connect().await?;
+
+        let mut fs = client.fs();
+        fs.set_cwd(&self.root);
+
+        if let Some((dir, _)) = to.rsplit_once('/') {
+            self.create_dir(dir, OpCreateDir::default()).await?;
+        }
+
+        let src = fs.canonicalize(from).await?;
+        let dst = fs.canonicalize(to).await?;
+        let mut src_file = client.open(&src).await?;
+        let mut dst_file = client.create(dst).await?;
+
+        src_file.copy_all_to(&mut dst_file).await?;
+
+        Ok(RpCopy::default())
+    }
+
+    async fn rename(&self, from: &str, to: &str, _: OpRename) -> 
Result<RpRename> {
+        let client = self.connect().await?;
+
+        let mut fs = client.fs();
+        fs.set_cwd(&self.root);
+
+        if let Some((dir, _)) = to.rsplit_once('/') {
+            self.create_dir(dir, OpCreateDir::default()).await?;
+        }
+        fs.rename(from, to).await?;
+
+        Ok(RpRename::default())
+    }
+
     async fn stat(&self, path: &str, _: OpStat) -> Result<RpStat> {
         let client = self.connect().await?;
         let mut fs = client.fs();

Reply via email to