feat: added the ability to edit files remotely
This commit is contained in:
@@ -15,4 +15,5 @@ rmp-serde = "1.1.2"
|
||||
serde = { version = "1.0.188", features = ["derive"] }
|
||||
sparse-05-common = { version = "0.1.0", path = "../sparse-05-common" }
|
||||
structopt = { version = "0.3.26", features = ["paw"] }
|
||||
tempfile = "3.8.0"
|
||||
tokio = { version = "1.32.0", features = ["io-std", "net", "fs", "macros", "rt"] }
|
||||
|
||||
@@ -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?;
|
||||
|
||||
|
||||
@@ -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(""),
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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:?}");
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user