Don't use shell command on local machine

This commit is contained in:
Leonard Steppy 2025-02-03 17:55:34 +01:00
parent 0109bf6c6f
commit faf4e47cac
2 changed files with 65 additions and 38 deletions

View File

@ -2,6 +2,7 @@ use std::error::Error;
use std::fmt::{Display, Formatter}; use std::fmt::{Display, Formatter};
use std::path::PathBuf; use std::path::PathBuf;
//TODO this whole structure should probably use OsString instead of String
#[derive(Debug, Clone, Eq, PartialEq, Hash)] #[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub struct FileNameInfo { pub struct FileNameInfo {
pub name: String, pub name: String,
@ -82,6 +83,7 @@ impl Display for FileInfoError {
impl Error for FileInfoError {} impl Error for FileInfoError {}
//TODO this structure should probably work with OsString instead of String
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct FileMatcher { pub struct FileMatcher {
name: String, name: String,
@ -108,7 +110,7 @@ impl FileMatcher {
..self ..self
} }
} }
pub fn matches(&self, file_name: &str) -> bool { pub fn matches(&self, file_name: &str) -> bool {
file_name.starts_with(&self.name) file_name.starts_with(&self.name)
&& self && self

View File

@ -15,7 +15,7 @@ use clap::{Parser, Subcommand, ValueEnum};
use lazy_regex::{lazy_regex, Lazy, Regex}; use lazy_regex::{lazy_regex, Lazy, Regex};
use server::{Server, ServerReference}; use server::{Server, ServerReference};
use std::cell::LazyCell; use std::cell::LazyCell;
use std::ffi::OsStr; use std::ffi::{OsStr, OsString};
use std::hash::Hash; use std::hash::Hash;
use std::io::Write; use std::io::Write;
use std::iter::once; use std::iter::once;
@ -292,23 +292,35 @@ fn main() -> Result<(), String> {
Ok(ServerActions { Ok(ServerActions {
server, server,
actions: { actions: {
let mut ls_command = match &server.address { let present_file_names: Vec<OsString> = match &server.address {
ServerAddress::Ssh { ssh_address } => { ServerAddress::Ssh { ssh_address } => ShellCmd::new("ls")
let mut cmd = ShellCmd::new("ssh"); .arg(ssh_address)
cmd.arg(ssh_address).arg(osf!("ls ") + &working_directory); .arg(osf!("ls ") + &working_directory)
cmd .collect_output()
} .map_err(|e| {
ServerAddress::Localhost => { format!(
//TODO don't use shell command on localhost, this will fail on windows "Failed to query present files on server {}: {e}",
let mut cmd = ShellCmd::new("ls"); server.get_name()
cmd.arg(&working_directory); )
cmd })?
} .stdout
.split(|&b| b == b'\n')
.map(|bytes| OsStr::from_bytes(bytes).to_os_string())
.collect(),
ServerAddress::Localhost => fs::read_dir(&working_directory)
.map_err(|e| format!("Failed to get files in working directory: {e}"))?
.map(|entry| entry.map_err(|e| format!("Failed to access directory entry: {e}")))
.collect::<Result<Vec<_>, _>>()?
.into_iter()
.filter_map(|entry| {
if entry.path().is_file() {
Some(entry.file_name())
} else {
None
}
})
.collect(),
}; };
let ls_output = ls_command
.collect_output()
.map_err(|e| format!("failed to query files: {e}"))?;
let ls_output = String::from_utf8_lossy(&ls_output.stdout);
file_details file_details
.iter() .iter()
@ -319,43 +331,56 @@ fn main() -> Result<(), String> {
file_matcher = file_matcher.and_extension(extension); file_matcher = file_matcher.and_extension(extension);
} }
let file_name = file_name_info.to_full_file_name(); let file_name = OsString::from(file_name_info.to_full_file_name());
let add_action = FileAction::new(file, Action::Add).expect("path points to file"); let add_action = FileAction::new(file, Action::Add).expect("path points to file");
let mut ls_lines = ls_output.lines(); if pure && present_file_names.iter().any(|file| *file == file_name) {
log!(
if pure && ls_lines.clone().any(|file| file == file_name) { logger,
log!(logger, debug, "file is already present on {}: {}", server.get_name(), file_name); debug,
"file is already present on {}: {}",
server.get_name(),
file_name.to_string_lossy()
);
return vec![]; //ignore that file, since it is already present return vec![]; //ignore that file, since it is already present
} }
match old_version_policy { match old_version_policy {
OldVersionPolicy::Ignore => { OldVersionPolicy::Ignore => {
if !ls_lines.any(|file| file == file_name) { if !present_file_names.iter().any(|file| *file == file_name) {
vec![add_action] //file doesn't exist yet vec![add_action] //file doesn't exist yet
} else { } else {
vec![FileAction::new(&file_name, Action::Replace) vec![FileAction::new(&file_name, Action::Replace)
.expect("path points to file")] .expect("path points to file")]
} }
} }
OldVersionPolicy::Archive => ls_lines OldVersionPolicy::Archive => {
.filter(|file| file_matcher.matches(file)) //TODO avoid lossy match
.map(|file| { present_file_names
FileAction::new( .iter()
file, .filter(|file| file_matcher.matches(&file.to_string_lossy()))
Action::rename(format!("{file}{}", file.chars().last().unwrap_or('1'))), .map(|file| {
) FileAction::new(
.expect("path points to file") file,
}) Action::rename(format!(
.chain(once(add_action)) "{}{}",
.collect(), file.to_string_lossy(),
file.to_string_lossy().chars().last().unwrap_or('1')
)),
)
.expect("path points to file")
})
.chain(once(add_action))
.collect()
},
OldVersionPolicy::Delete => { OldVersionPolicy::Delete => {
let mut actions = ls_lines //TODO avoid lossy match
.filter(|file| file_matcher.matches(file)) let mut actions = present_file_names.iter()
.filter(|file| file_matcher.matches(&file.to_string_lossy()))
.map(|file| { .map(|file| {
//special case -> file has the same name as current file, then we just need to replace it //special case -> file has the same name as current file, then we just need to replace it
if file == file_name { if *file == file_name {
FileAction::new(file, Action::Replace).expect("path points to file") FileAction::new(file, Action::Replace).expect("path points to file")
} else { } else {
FileAction::new(file, Action::Delete).expect("path points to file") FileAction::new(file, Action::Delete).expect("path points to file")