commit 952f62ae824a50845a0b527f449f391a67b5da0c
parent 89cf19042172c9c8475c6334079edeb01d329238
Author: Asher Morgan <59518073+ashermorgan@users.noreply.github.com>
Date: Mon, 1 Jul 2024 17:07:44 -0700
Implement ssh::stage_file function
Diffstat:
| M | src/ssh.rs | | | 105 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--- |
1 file changed, 102 insertions(+), 3 deletions(-)
diff --git a/src/ssh.rs b/src/ssh.rs
@@ -1,4 +1,46 @@
+use shellexpand::tilde_with_context;
+use std::path::{MAIN_SEPARATOR_STR, PathBuf};
use std::process::Command;
+use super::local::copy_file;
+
+/// Copy a file to an SCP staging directory
+///
+/// The destination directory structure will be recreated in the staging
+/// directory under either the home or root subdirectories. This staging system
+/// allows for many files to transferred at once and for missing directories to
+/// be created automatically on the remote machine.
+#[allow(dead_code)]
+fn stage_file(src: &str, dst: &str, staging_dir: &str) -> Result<(), String> {
+ let _staging_dir = PathBuf::from(staging_dir);
+ let home_dir = _staging_dir.join("home");
+ let root_dir = _staging_dir.join("root");
+ let get_home_dir = || {
+ Some::<String>(home_dir.to_string_lossy().into())
+ };
+
+ // Resolve ~/... paths to home staging directory:
+ let mut _dst: PathBuf = (&tilde_with_context(dst, get_home_dir).to_mut())
+ .into();
+
+ // Resolve relative paths to home staging directory:
+ _dst = home_dir.join(_dst);
+
+ // Resolve other absolute paths to root staging directory:
+ if !_dst.starts_with(home_dir) {
+ // Root should be / and C:\ on Unix and Windows respectively, but
+ // iter().next() will return / and C:, so we must manually add another
+ // path separator. (Duplicate slashes are ignored on Unix).
+ let root = PathBuf::from(match _dst.iter().next() {
+ Some(x) => Ok(x),
+ None => Err(String::from("Destination path does not have root")),
+ }?).join(MAIN_SEPARATOR_STR);
+ _dst = root_dir.join(_dst.strip_prefix(root)
+ .map_err(|why| why.to_string())?);
+ }
+
+ copy_file(src, _dst.to_string_lossy().to_mut())
+ .map_err(|why| why.to_string())
+}
/// Recursively copy a directory to another machine via SCP
#[allow(dead_code)]
@@ -19,14 +61,16 @@ fn send_dir(src: &str, dst: &str, host: &str) -> Result<(), String> {
}
#[cfg(test)]
-#[cfg(target_family = "unix")]
mod tests {
+ #![allow(unused_imports)]
+
use super::*;
use crate::common::{SSH_HOST, read_file, setup_integration, write_file};
use std::fs;
#[test]
+ #[cfg(target_family = "unix")]
fn test_send_dir_basic() {
let tmp = setup_integration("test_send_dir_basic");
@@ -35,7 +79,7 @@ mod tests {
let result = send_dir(&tmp.local.to_str().unwrap(), "~/", SSH_HOST);
- assert_eq!(result.is_ok(), true);
+ assert_eq!(result, Ok(()));
let contents1 = read_file(&tmp.ssh.join("foo"));
assert_eq!(contents1, "contents of foo");
let contents2 = read_file(&tmp.ssh.join("bar"));
@@ -43,6 +87,7 @@ mod tests {
}
#[test]
+ #[cfg(target_family = "unix")]
fn test_send_dir_nested() {
let tmp = setup_integration("test_send_dir_nested");
@@ -52,10 +97,64 @@ mod tests {
let result = send_dir(tmp.local.to_str().unwrap(), "~/", SSH_HOST);
- assert_eq!(result.is_ok(), true);
+ assert_eq!(result, Ok(()));
let contents1 = read_file(&tmp.ssh.join("foo"));
assert_eq!(contents1, "contents of foo");
let contents2 = read_file(&tmp.ssh.join("dir").join("bar"));
assert_eq!(contents2, "contents of bar");
}
+
+ #[test]
+ fn test_stage_file_tilde() {
+ let tmp = setup_integration("test_stage_file_tilde");
+
+ let src = tmp.local.join("foo");
+ let dst = "~/dir/bar";
+ let dst_real = tmp.local.join("home").join("dir").join("bar");
+ let staging = &tmp.local;
+ write_file(&src, "contents of foo");
+
+ let result = stage_file(src.to_str().unwrap(), dst,
+ staging.to_str().unwrap());
+
+ assert_eq!(result, Ok(()));
+ assert_eq!(dst_real.exists(), true);
+ assert_eq!(read_file(&dst_real), "contents of foo");
+ }
+
+ #[test]
+ fn test_stage_file_relative() {
+ let tmp = setup_integration("test_stage_file_relative");
+
+ let src = tmp.local.join("foo");
+ let dst = "dir/bar";
+ let dst_real = tmp.local.join("home").join("dir").join("bar");
+ let staging = &tmp.local;
+ write_file(&src, "contents of foo");
+
+ let result = stage_file(src.to_str().unwrap(), dst,
+ staging.to_str().unwrap());
+
+ assert_eq!(result, Ok(()));
+ assert_eq!(dst_real.exists(), true);
+ assert_eq!(read_file(&dst_real), "contents of foo");
+ }
+
+ #[test]
+ fn test_stage_file_absolute() {
+ let tmp = setup_integration("test_stage_file_absolute");
+
+ let src = tmp.local.join("foo");
+ let dst = "/dir/bar";
+ let dst_real = tmp.local.join("root").join("dir").join("bar");
+ let staging = &tmp.local;
+ write_file(&src, "contents of foo");
+
+ let result = stage_file(src.to_str().unwrap(), dst,
+ staging.to_str().unwrap());
+
+ assert_eq!(result, Ok(()));
+ assert_eq!(dst_real.exists(), true);
+ assert_eq!(read_file(&dst_real), "contents of foo");
+ }
}