diff --git a/build.rs b/build.rs index ed759f1..b33bf68 100644 --- a/build.rs +++ b/build.rs @@ -52,9 +52,8 @@ fn emit_git_commit() { eprintln!("`git rev-parse --short HEAD` failed, stderr: {stderr}"); panic!("`git rev-parse --short HEAD` failed"); } - let rev = std::str::from_utf8(&output.stdout) - .expect("git did not output utf8") - .trim(); + let rev = + std::str::from_utf8(&output.stdout).expect("git did not output utf8"); println!("cargo::rustc-env=REPO_REV={rev}"); } @@ -87,20 +86,13 @@ fn emit_git_dirty() { // If there *was* any output, parse it and tell cargo to re-run if any of // these files changed. (Maybe they get reverted! Then the repo status // will change.) - let mut split = output.split('\x00'); - while let Some(field) = split.next() { - if field.is_empty() { - continue; - } - let prefix = &field[0..3]; - println!("cargo::rerun-if-changed={}", &field[3..]); - let b = prefix.as_bytes(); - if b[0] == b'R' || b[1] == b'R' || b[0] == b'C' || b[1] == b'C' { - if let Some(additional) = split.next() { - println!("cargo::rerun-if-changed={additional}"); - } - } + for line in output.lines() { + let fields: Vec<_> = line.split('\x00').collect(); + let parts: Vec<_> = fields[0].split(' ').collect(); + let path = parts[1]; + println!("cargo::rerun-if-changed={path}"); } + // Emit the repository status. let dirty = if output.trim().is_empty() { "" diff --git a/src/client/config.rs b/src/client/config.rs index 3d57e73..c7f0bea 100644 --- a/src/client/config.rs +++ b/src/client/config.rs @@ -20,11 +20,6 @@ impl ServerConfig { ServerConfig { auto: true, ports: HashMap::new() } } - #[cfg(test)] - pub fn insert(&mut self, port: u16, config: PortConfig) { - self.ports.insert(port, config); - } - pub fn contains_key(&self, port: u16) -> bool { self.ports.contains_key(&port) } diff --git a/src/client/mod.rs b/src/client/mod.rs index 94cb208..18bd3fc 100644 --- a/src/client/mod.rs +++ b/src/client/mod.rs @@ -1,6 +1,7 @@ use crate::message::{Message, MessageReader, MessageWriter}; use anyhow::{bail, Result}; use bytes::BytesMut; +use copypasta::{ClipboardContext, ClipboardProvider}; use log::LevelFilter; use log::{debug, error, info, warn}; use std::collections::HashMap; @@ -212,11 +213,16 @@ async fn client_handle_messages( } ClipStart(id) => { + info!("Starting clip op {id}"); clipboard_messages.insert(id, Vec::new()); } ClipData(id, mut data) => match clipboard_messages.get_mut(&id) { Some(bytes) => { + info!( + "Received data for clip op {id} ({len} bytes)", + len = data.len() + ); if bytes.len() < MAX_CLIPBOARD_SIZE { bytes.append(&mut data); } @@ -237,10 +243,9 @@ async fn client_handle_messages( continue; }; - if let Err(e) = - events.send(ui::UIEvent::SetClipboard(data)).await - { - error!("Error sending clipboard request: {:?}", e); + let mut ctx = ClipboardContext::new().unwrap(); + if let Err(e) = ctx.set_contents(data) { + error!("Unable to set clipboard data for op {id}: {e:?}"); } } diff --git a/src/client/ui.rs b/src/client/ui.rs index 50daac3..a20a8e1 100644 --- a/src/client/ui.rs +++ b/src/client/ui.rs @@ -1,7 +1,6 @@ use super::{client_listen, config::ServerConfig}; use crate::message::PortDesc; use anyhow::Result; -use copypasta::{ClipboardContext, ClipboardProvider}; use crossterm::{ event::{Event, EventStream, KeyCode, KeyEvent, KeyModifiers}, execute, @@ -34,7 +33,6 @@ pub enum UIEvent { ServerLine(String), LogLine(log::Level, String), Ports(Vec), - SetClipboard(String), } pub enum UIReturn { @@ -168,6 +166,7 @@ impl Listener { } } +#[derive(Debug)] pub struct UI { events: mpsc::Receiver, ports: HashMap, @@ -180,7 +179,6 @@ pub struct UI { show_help: bool, alternate_screen: bool, raw_mode: bool, - clipboard: ClipboardContext, } impl UI { @@ -197,8 +195,6 @@ impl UI { config, alternate_screen: false, raw_mode: false, - clipboard: ClipboardContext::new() - .expect("Unable to initialize clipboard context"), } } @@ -571,7 +567,7 @@ impl UI { // Grab the selected port let selected_port = self.get_selected_port(); - for mut port_desc in p.into_iter() { + for port_desc in p.into_iter() { leftover_ports.remove(&port_desc.port); if let Some(listener) = self.ports.get_mut(&port_desc.port) { @@ -579,8 +575,6 @@ impl UI { } else { let config = self.config.get(port_desc.port); info!("Port config {port_desc:?} -> {config:?}"); - port_desc.desc = - config.description.unwrap_or(port_desc.desc); self.ports.insert( port_desc.port, @@ -627,14 +621,6 @@ impl UI { } self.lines.push_back(format!("[CLIENT] {line}")); } - Some(UIEvent::SetClipboard(contents)) => { - let length = contents.len(); - if let Err(e) = self.clipboard.set_contents(contents) { - error!("Error setting clipboard contents: {e:#}"); - } else { - info!("Received clipboard contents ({length} bytes)"); - } - } None => { self.running = false; } @@ -674,8 +660,6 @@ fn centered_rect(width_chars: u16, height_chars: u16, r: Rect) -> Rect { #[cfg(test)] mod tests { - use crate::client::config::PortConfig; - use super::*; use assert_matches::assert_matches; @@ -940,56 +924,4 @@ mod tests { assert_eq!(centered.width, 10); assert_eq!(centered.height, 10); } - - #[test] - fn port_config_description_respected() { - let (sender, receiver) = mpsc::channel(64); - let mut config = ServerConfig::default(); - config.insert( - 8080, - PortConfig { - enabled: true, - description: Some("override".to_string()), - }, - ); - - let mut ui = UI::new(receiver, config); - - // There are ports... - ui.handle_internal_event(Some(UIEvent::Ports(vec![PortDesc { - port: 8080, - desc: "my-service".to_string(), - }]))); - - let description = ui.ports.get(&8080).unwrap().desc.as_ref().unwrap(); - assert_eq!(description.desc, "override"); - - drop(sender); - } - - #[test] - fn port_config_enabled_respected() { - let (sender, receiver) = mpsc::channel(64); - let mut config = ServerConfig::default(); - config.insert( - 8080, - PortConfig { - enabled: false, - description: Some("override".to_string()), - }, - ); - - let mut ui = UI::new(receiver, config); - - // There are ports... - ui.handle_internal_event(Some(UIEvent::Ports(vec![PortDesc { - port: 8080, - desc: "my-service".to_string(), - }]))); - - let state = ui.ports.get(&8080).unwrap().state(); - assert_eq!(state, State::Disabled); - - drop(sender); - } } diff --git a/src/lib.rs b/src/lib.rs index 6920aa0..a1e99a7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,10 +3,6 @@ mod message; mod reverse; mod server; -pub const VERSION: &str = env!("CARGO_PKG_VERSION"); -pub const REV: &str = env!("REPO_REV"); -pub const DIRTY: &str = env!("REPO_DIRTY"); - pub use client::run_client; pub use reverse::browse_url; pub use reverse::clip_file; diff --git a/src/main.rs b/src/main.rs index 7871d50..6862124 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,10 @@ // TODO: An actual proper command line parsing use indoc::indoc; +const VERSION: &str = env!("CARGO_PKG_VERSION"); +const REV: &str = env!("REPO_REV"); +const DIRTY: &str = env!("REPO_DIRTY"); + fn usage() { println!(indoc! {" usage: fwd [options] ( | browse | clip []) @@ -117,7 +121,7 @@ fn parse_args(args: Vec) -> Args { async fn browse_url(url: &str) { if let Err(e) = fwd::browse_url(url).await { eprintln!("Unable to open {url}"); - eprintln!("{:#}", e); + eprintln!("{}", e); std::process::exit(1); } } @@ -125,7 +129,7 @@ async fn browse_url(url: &str) { async fn clip_file(file: String) { if let Err(e) = fwd::clip_file(&file).await { eprintln!("Unable to copy to the clipboard"); - eprintln!("{:#}", e); + eprintln!("{}", e); std::process::exit(1); } } @@ -137,7 +141,7 @@ async fn main() { usage(); } Args::Version => { - println!("fwd {} (rev {}{})", fwd::VERSION, fwd::REV, fwd::DIRTY); + println!("fwd {VERSION} (rev {REV}{DIRTY})"); } Args::Server => { fwd::run_server().await; diff --git a/src/reverse.rs b/src/reverse.rs index f717dd9..a7d8534 100644 --- a/src/reverse.rs +++ b/src/reverse.rs @@ -6,28 +6,16 @@ use tokio::io::{AsyncRead, AsyncReadExt}; mod unix; #[cfg(target_family = "unix")] -pub use unix::{handle_reverse_connections, ReverseConnection}; +pub use unix::{handle_reverse_connections, send_reverse_message}; use crate::message::Message; #[cfg(not(target_family = "unix"))] -pub struct ReverseConnection {} - -#[cfg(not(target_family = "unix"))] -impl ReverseConnection { - pub async fn new() -> Result { - use anyhow::anyhow; - Err(anyhow!( - "Server-side operations are not supported on this platform" - )) - } - - pub async fn send(&mut self, message: Message) -> Result<()> { - use anyhow::anyhow; - Err(anyhow!( - "Server-side operations are not supported on this platform" - )) - } +pub async fn send_reverse_message(_message: Message) -> Result<()> { + use anyhow::anyhow; + Err(anyhow!( + "Server-side operations are not supported on this platform" + )) } #[cfg(not(target_family = "unix"))] @@ -39,16 +27,12 @@ pub async fn handle_reverse_connections( #[inline] pub async fn browse_url(url: &str) -> Result<()> { - ReverseConnection::new() - .await? - .send(Message::Browse(url.to_string())) - .await + send_reverse_message(Message::Browse(url.to_string())).await } async fn clip_reader(reader: &mut T) -> Result<()> { - let mut connection = ReverseConnection::new().await?; let clip_id: u64 = random(); - connection.send(Message::ClipStart(clip_id)).await?; + send_reverse_message(Message::ClipStart(clip_id)).await?; let mut count = 0; let mut buf = vec![0; 1024]; @@ -59,7 +43,7 @@ async fn clip_reader(reader: &mut T) -> Result<()> { } count += read; if count == buf.len() { - connection.send(Message::ClipData(clip_id, buf)).await?; + send_reverse_message(Message::ClipData(clip_id, buf)).await?; buf = vec![0; 1024]; count = 0; } @@ -67,10 +51,10 @@ async fn clip_reader(reader: &mut T) -> Result<()> { if count > 0 { buf.resize(count, 0); - connection.send(Message::ClipData(clip_id, buf)).await?; + send_reverse_message(Message::ClipData(clip_id, buf)).await?; } - connection.send(Message::ClipEnd(clip_id)).await?; + send_reverse_message(Message::ClipEnd(clip_id)).await?; Ok(()) } diff --git a/src/reverse/unix.rs b/src/reverse/unix.rs index d3cc801..92025ce 100644 --- a/src/reverse/unix.rs +++ b/src/reverse/unix.rs @@ -9,27 +9,32 @@ use tokio::sync::mpsc; use crate::message::{Message, MessageReader, MessageWriter}; -pub struct ReverseConnection { - writer: MessageWriter, +pub async fn send_reverse_message(message: Message) -> Result<()> { + let path = socket_path().context("Error getting socket path")?; + let stream = match UnixStream::connect(&path).await { + Ok(s) => s, + Err(e) => bail!( + "Error connecting to socket: {e} (is fwd actually connected here?)" + ), + }; + let mut writer = MessageWriter::new(stream); + writer + .write(message) + .await + .context("Error sending browse message")?; + Ok(()) } -impl ReverseConnection { - pub async fn new() -> Result { - let path = socket_path().context("Error getting socket path")?; - let stream = match UnixStream::connect(&path).await { - Ok(s) => s, - Err(e) => bail!("Error connecting to socket: {e} (is fwd actually connected here?)"), - }; - - Ok(ReverseConnection { writer: MessageWriter::new(stream) }) - } - - pub async fn send(&mut self, message: Message) -> Result<()> { - self.writer - .write(message) - .await - .context("Error sending reverse message")?; - Ok(()) +fn socket_directory() -> Result { + let base_directories = xdg::BaseDirectories::new() + .context("Error creating BaseDirectories")?; + match base_directories.place_runtime_file("fwd") { + Ok(path) => Ok(path), + Err(_) => { + let mut path = std::env::temp_dir(); + path.push(format!("fwd{}", users::get_current_uid())); + Ok(path) + } } } @@ -48,19 +53,6 @@ pub fn socket_path() -> Result { Ok(socket_path) } -fn socket_directory() -> Result { - let base_directories = xdg::BaseDirectories::new() - .context("Error creating BaseDirectories")?; - match base_directories.place_runtime_file("fwd") { - Ok(path) => Ok(path), - Err(_) => { - let mut path = std::env::temp_dir(); - path.push(format!("fwd{}", users::get_current_uid())); - Ok(path) - } - } -} - pub async fn handle_reverse_connections( messages: mpsc::Sender, ) -> Result<()> { @@ -95,18 +87,10 @@ async fn handle_connection( sender: mpsc::Sender, ) -> Result<()> { let mut reader = MessageReader::new(socket); - while let Ok(message) = reader.read().await { - match message { - Message::Browse(url) => sender.send(Message::Browse(url)).await?, - Message::ClipStart(id) => { - sender.send(Message::ClipStart(id)).await? - } - Message::ClipData(id, data) => { - sender.send(Message::ClipData(id, data)).await? - } - Message::ClipEnd(id) => sender.send(Message::ClipEnd(id)).await?, - _ => bail!("Unsupported message: {:?}", message), - } + let message = reader.read().await.context("Error reading message")?; + match message { + Message::Browse(url) => sender.send(Message::Browse(url)).await?, + _ => bail!("Unsupported message: {:?}", message), } Ok(()) diff --git a/src/server/mod.rs b/src/server/mod.rs index 804d877..1188c98 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -26,26 +26,12 @@ async fn server_loop( ) -> Result<()> { // The first message we send must be an announcement. writer.send(Message::Hello(0, 2, vec![])).await?; - let mut version_reported = false; + loop { use Message::*; match reader.read().await? { Ping => (), Refresh => { - // Just log the version, if we haven't yet. We do this extra - // work to avoid spamming the log, but we wait until we - // receive the first message to be sure that the client is in - // a place to display our logging properly. - if !version_reported { - eprintln!( - "fwd server {} (rev {}{})", - crate::VERSION, - crate::REV, - crate::DIRTY - ); - version_reported = true; - } - let ports = match refresh::get_entries().await { Ok(ports) => ports, Err(e) => {