remowt-client 0.1.5

russh-based client connection to a remowt agent
Documentation
use std::sync::Arc;

use remowt_link_shared::editor::{EditorBackend, Error};
use russh::client::Handle;
use tokio::net::UnixListener;
use tracing::error;

use crate::SshHandler;

pub struct SshEditor {
	pub sess: Arc<Handle<SshHandler>>,
}
impl EditorBackend for SshEditor {
	async fn open_editor(&self, socket_path: String) -> Result<(), Error> {
		let local = std::env::temp_dir().join(format!("remowt-nvim-{}.sock", uuid::Uuid::new_v4()));
		let _ = std::fs::remove_file(&local);
		let listener = UnixListener::bind(&local).map_err(|e| Error::Failed(e.to_string()))?;

		let sess = self.sess.clone();
		let forward = tokio::spawn(async move {
			loop {
				let Ok((mut stream, _)) = listener.accept().await else {
					break;
				};
				let sess = sess.clone();
				let remote = socket_path.clone();
				tokio::spawn(async move {
					match sess.channel_open_direct_streamlocal(remote).await {
						Ok(ch) => {
							let mut remote = ch.into_stream();
							let _ = tokio::io::copy_bidirectional(&mut stream, &mut remote).await;
						}
						Err(e) => error!("opening direct-streamlocal to nvim failed: {e}"),
					}
				});
			}
		});

		let status = tokio::process::Command::new("neovide")
			.arg("--no-fork")
			.arg("--server")
			.arg(&local)
			.status()
			.await
			.map_err(|e| Error::Failed(format!("spawning neovide: {e}")));

		forward.abort();
		let _ = std::fs::remove_file(&local);

		match status? {
			s if s.success() => Ok(()),
			s => Err(Error::Failed(format!("neovide exited with {s}"))),
		}
	}
}