feat: added the ability to edit files remotely

This commit is contained in:
Andrew Rioux
2023-09-08 22:48:02 -04:00
parent eb5e86067b
commit ae24c2e0ad
8 changed files with 146 additions and 24 deletions

View File

@@ -14,6 +14,7 @@ pub async fn download_file(
let mut target_file = fs::OpenOptions::new()
.write(true)
.create(true)
.truncate(true)
.open(&local_path)
.await?;

View File

@@ -1,5 +1,4 @@
use ansi_term::{Color, Style};
use structopt::StructOpt;
use ansi_term::Style;
use crate::commands::connect::shell::SparseCommands;
@@ -30,14 +29,22 @@ pub fn print_help(arg: Option<SparseCommands>) {
\n\
Uploads the file at the local file path, overwriting the file at the remote file path"
),
Some(SparseCommands::EditFile { .. }) => println!(
"#edit [remote_file_path]\n\
\n\
downloads the file specified to edit locally with the editor specified by the EDITOR environment variable"
),
None => println!(
"\n{}{}\n\
\n\
The following shell commands are available:\n\
\n\
- #sysinfo\t\tprint information about the system you are connecting to
- #help\t\tprints this help page, or alternatively prints info about a command passed as an argument\n\
- #exit\t\texit from the shell and disconnect from the remote server\n\
- #sysinfo\t\t\t\tprint information about the system you are connecting to\n\
- #help\t\t\t\t\tprints this help page, or alternatively prints info about a command passed as an argument\n\
- #upload [local_file] [remote_file]\tuploads the file located at the local path specified to the remote path\n\
- #download [remote_file] [local_file]\tdownloads the file from the server at the path specified and saves it to the local path\n\
- #edit [remote_file]\t\t\tdownloads the remote file specified and opens it locally in an editor\n\
- #exit\t\t\t\t\texit from the shell and disconnect from the remote server\n\
",
Style::new().bold().paint("SHELL COMMANDS"),
Style::new().paint(""),

View File

@@ -1,9 +1,6 @@
use std::{path::PathBuf, sync::Arc};
use tokio::{
fs,
io::{self, AsyncReadExt},
};
use tokio::{fs, io::AsyncReadExt};
use sparse_05_common::messages::{
Command, Response, FILE_BUFFER_BUFFER_SIZE, FILE_TRANSFER_PACKET_SIZE,

View File

@@ -2,12 +2,16 @@ use std::{
io::{self, Read, Write},
os::fd::AsRawFd,
path::PathBuf,
sync::Arc,
sync::{
atomic::{AtomicBool, Ordering},
Arc,
},
thread,
};
use sparse_05_common::messages::{Capabilities, Command, Response};
use structopt::StructOpt;
use tempfile::NamedTempFile;
use tokio::{
io::{stderr, stdout, AsyncWriteExt},
runtime::Handle,
@@ -118,6 +122,36 @@ async fn run_command(
Ok(())
}
async fn edit_file(connection: Arc<Connection>, remote_path: PathBuf) -> anyhow::Result<()> {
let local_path = tempfile::NamedTempFile::new()?.into_temp_path();
commands::download::download_file(
Arc::clone(&connection),
remote_path.clone(),
local_path.to_owned(),
)
.await?;
let cmd = format!(
"{} {local_path:?}",
std::env::var("VISUAL")
.or(std::env::var("EDITOR"))
.unwrap_or("vim".to_owned())
);
tokio::process::Command::new("sh")
.arg("-c")
.arg(cmd)
.spawn()?
.wait()
.await?;
commands::upload::upload_file(Arc::clone(&connection), local_path.to_owned(), remote_path)
.await?;
Ok(())
}
pub(super) async fn shell(
connection: Arc<Connection>,
capabilities: Capabilities,
@@ -135,14 +169,24 @@ pub(super) async fn shell(
let (stdin_sender, mut stdin_receiver) = channel(64);
let pause = Arc::new(AtomicBool::new(false));
let pause_2 = Arc::clone(&pause);
let handle = Handle::current();
thread::spawn(move || {
let mut stdin_buf = [0u8; 1024];
loop {
if pause_2.load(Ordering::Relaxed) {
std::thread::sleep(std::time::Duration::from_millis(250));
continue;
}
let mut stdin = std::io::stdin();
let Ok(amount) = stdin.read(&mut stdin_buf) else {
continue;
};
if pause_2.load(Ordering::Relaxed) {
continue;
}
let stdin_buf = stdin_buf[..amount].to_vec();
let stdin_sender = stdin_sender.clone();
handle.spawn(async move {
@@ -229,17 +273,25 @@ pub(super) async fn shell(
eprintln!("{e:?}")
}
}
(Ok(SparseCommands::EditFile { remote_path }), _) => {}
_ => {
if let Err(e) = run_command(
&mut stdin_receiver,
input.to_string(),
Arc::clone(&connection),
)
.await
{
(Ok(SparseCommands::EditFile { remote_path }), _) => {
pause.store(true, Ordering::SeqCst);
if let Err(e) = edit_file(Arc::clone(&connection), remote_path).await {
eprintln!("{e:?}");
};
}
pause.store(false, Ordering::Relaxed);
}
_ => {
if !input.to_string().trim().is_empty() && !pause.load(Ordering::Relaxed) {
if let Err(e) = run_command(
&mut stdin_receiver,
input.to_string(),
Arc::clone(&connection),
)
.await
{
eprintln!("{e:?}");
};
}
}
}
}