From 435c87c958d7b9741cceb53cf50e4fcadb49ca68 Mon Sep 17 00:00:00 2001 From: Steppy Date: Fri, 13 Dec 2024 12:50:05 +0100 Subject: [PATCH] Add OsStringBuilder and remove debug usages --- src/main.rs | 62 ++++++++++---------- src/os_string_builder.rs | 121 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 152 insertions(+), 31 deletions(-) create mode 100644 src/os_string_builder.rs diff --git a/src/main.rs b/src/main.rs index d408c2c..9e18302 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,6 @@ mod action; mod file; +mod os_string_builder; mod server; use crate::action::{Action, FileAction, ServerActions}; @@ -9,7 +10,6 @@ use lazy_regex::{lazy_regex, Lazy, Regex}; use server::{Server, ServerReference}; use std::cell::LazyCell; use std::env; -use std::ffi::OsString; use std::hash::Hash; use std::io::Write; use std::iter::once; @@ -119,13 +119,9 @@ fn main() -> Result<(), String> { Ok(ServerActions { server, actions: { - //TODO don't use debug values here, use joiner let output = ShellCmd::new("ssh") .arg(&server.ssh_name) - .arg(format!( - "cd {:?}; ls {:?}", - server.server_directory_path, upload_directory - )) + .arg(osf!("ls ") + server.server_directory_path.join(&upload_directory)) .stdout(Stdio::piped()) .output() .map_err(|e| format!("failed to query files via ssh: {e}"))?; @@ -206,47 +202,43 @@ fn main() -> Result<(), String> { for file_action in server_actions.actions { match file_action.kind { Action::Add | Action::Replace => { - //TODO replace with joiner - let mut destination = OsString::from(&server.ssh_name); - destination.push(":"); - destination.push(&server.server_directory_path); - if !server - .server_directory_path - .to_string_lossy() - .ends_with("/") - { - destination.push("/"); - } - destination.push(&upload_directory); ShellCmd::new("scp") .arg(file.clone()) - .arg(destination) + .arg( + osf!(&server.ssh_name) + + ":" + + server.server_directory_path.join(&upload_directory), + ) .spawn() .map_err(|e| format!("failed to upload file: {e}"))? .wait() .map_err(|e| format!("failed to wait for upload: {e}"))?; } Action::Delete => { - //TODO don't use debug values here, use joiner ShellCmd::new("ssh") .arg(&server.ssh_name) - .arg(format!( - "cd {:?}; cd {upload_directory:?}; rm {:?}", - server.server_directory_path, file_action.file - )) + .arg( + osf!("cd ") + + server.server_directory_path.join(&upload_directory) + + "; rm " + + &file_action.file, + ) .spawn() .map_err(|e| format!("failed to send delete command: {e}"))? .wait() .map_err(|e| format!("failed to wait for delete command: {e}"))?; } Action::Rename { new_name } => { - //TODO don't use debug values, use joiner ShellCmd::new("ssh") .arg(&server.ssh_name) - .arg(format!( - "cd {:?}; cd {upload_directory:?}; mv {:?} {new_name:?}", - server.server_directory_path, file_action.file - )) + .arg( + osf!("cd ") + + server.server_directory_path.join(&upload_directory) + + "; mv " + + &file_action.file + + " " + + new_name, + ) .spawn() .map_err(|e| format!("failed to send rename command: {e}"))? .wait() @@ -262,10 +254,9 @@ fn main() -> Result<(), String> { start_ssh_agent()?; for server in servers { println!("Running command on '{}'...", server.ssh_name); - //TODO don't use debug values, use joiner ShellCmd::new("ssh") .arg(server.ssh_name) - .arg(format!("cd {:?}; {command}", server.server_directory_path)) + .arg(osf!("cd ") + server.server_directory_path + "; " + &command) .spawn() .map_err(|_| "failed to start ssh command".to_string())? .wait() @@ -343,4 +334,13 @@ mod test { servers ); } + + #[test] + fn path_experiment() { + let server_dir = PathBuf::from("steptech"); + let upload_dir = PathBuf::from("/home"); //absolute path + + let joined = server_dir.join(upload_dir); + assert_eq!(PathBuf::from("/home"), joined); + } } diff --git a/src/os_string_builder.rs b/src/os_string_builder.rs new file mode 100644 index 0000000..a00f3a1 --- /dev/null +++ b/src/os_string_builder.rs @@ -0,0 +1,121 @@ +use std::ffi::{OsStr, OsString}; +use std::fmt::{Debug, Formatter}; +use std::hash::{Hash, Hasher}; +use std::ops::{Add, AddAssign}; +use std::os::unix::ffi::OsStrExt; + +#[derive(Clone, Default, Eq)] +pub struct OsStringBuilder { + result: OsString, +} + +impl OsStringBuilder { + pub fn from(s: impl AsRef) -> Self { + Self { + result: s.as_ref().to_os_string(), + } + } + + pub fn build(&self) -> OsString { + self.result.clone() + } +} + +impl From for OsString { + fn from(value: OsStringBuilder) -> Self { + value.result + } +} + +impl Debug for OsStringBuilder { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + Debug::fmt(&self.result, f) + } +} + +impl AsRef for OsStringBuilder { + fn as_ref(&self) -> &OsStr { + self.result.as_ref() + } +} + +impl

Add

for OsStringBuilder +where + P: AsRef, +{ + type Output = Self; + + fn add(mut self, rhs: P) -> Self::Output { + self.result.push(rhs); + self + } +} + +impl

AddAssign

for OsStringBuilder +where + P: AsRef, +{ + fn add_assign(&mut self, rhs: P) { + self.result.push(rhs); + } +} + +impl

PartialEq

for OsStringBuilder +where + P: AsRef, +{ + fn eq(&self, other: &P) -> bool { + self.result == other.as_ref() + } +} + +impl Hash for OsStringBuilder { + fn hash(&self, state: &mut H) { + self.result.hash(state); + } +} + +pub fn ensure_trailing_slash(path: impl AsRef) -> OsString { + let mut str = path.as_ref().to_os_string(); + if !str.as_bytes().ends_with(b"/") { + str.push("/") + } + str +} + +#[macro_export] +macro_rules! dir { + ($path:expr) => {{ + use $crate::os_string_builder::ensure_trailing_slash; + ensure_trailing_slash($path) + }}; +} + +#[macro_export] +macro_rules! osf { + () => {{ + use $crate::os_string_builder::OsStringBuilder; + OsStringBuilder::default() + }}; + ($s:expr) => { + osf!() + $s + }; +} + +#[cfg(test)] +mod test { + use std::path::PathBuf; + + #[test] + fn test_build() { + assert_eq!(osf!("foo") + "Bar", "fooBar"); + } + + #[test] + fn test_add_dir() { + let foo = PathBuf::from("foo"); + let bar = PathBuf::from("bar"); + + assert_eq!(osf!("cd ") + dir!(foo) + dir!(bar), "cd foo/bar/"); + } +}