Files
voidpin/crates/voidpin/src/main.rs
kjuulh f060e9d2ca
All checks were successful
continuous-integration/drone/push Build is passing
feat: pipe output
2025-08-03 14:32:52 +02:00

164 lines
4.6 KiB
Rust

use std::{io::Read, net::SocketAddr};
use anyhow::Context;
use clap::{Parser, Subcommand};
use copy::LocalCopierState;
use grpc::void_pin_server::VoidPinServer;
use grpc_server::GrpcServer;
use remote_copy::RemoteCopierState;
use state::State;
use tokio::io::AsyncWriteExt;
use tonic::transport;
use tracing_subscriber::EnvFilter;
mod grpc {
include!("gen/voidpin.v1.rs");
}
mod grpc_server;
mod copy;
mod remote_copy;
mod state;
#[derive(Parser)]
#[command(author, version, about, long_about = None, subcommand_required = true)]
struct Command {
#[command(subcommand)]
command: Option<Commands>,
}
#[derive(Subcommand)]
enum Commands {
Listen {
#[arg(long, env = "VOIDPIN_GRPC_HOST", default_value = "0.0.0.0:7900")]
grpc: SocketAddr,
},
Copy {},
Paste {},
Remote {
#[command(subcommand)]
command: RemoteCommands,
},
}
#[derive(Subcommand)]
enum RemoteCommands {
Copy {
#[arg(
long = "remote-host",
env = "VOIDPIN_REMOTE",
default_value = "http://0.0.0.0:7900"
)]
remote_host: String,
},
Paste {
#[arg(
long = "remote-host",
env = "VOIDPIN_REMOTE",
default_value = "http://0.0.0.0:7900"
)]
remote_host: String,
},
}
#[tokio::main]
async fn main() -> anyhow::Result<()> {
dotenv::dotenv().ok();
tracing_subscriber::fmt()
.with_env_filter(
EnvFilter::builder()
//.with_default_directive("error".parse().unwrap())
.from_env_lossy(),
)
.with_writer(std::io::stderr)
.init();
let cli = Command::parse();
tracing::debug!("Starting cli");
let state = State::new();
match cli.command.unwrap() {
Commands::Listen { grpc } => {
tracing::info!(grpc = grpc.to_string(), "starting listener");
transport::Server::builder()
.add_service(VoidPinServer::new(GrpcServer::new(state)))
.serve(grpc)
.await?;
}
Commands::Copy {} => {
if let Ok(remote_host) = std::env::var("VOIDPIN_REMOTE") {
let mut input = String::new();
std::io::stdin()
.read_to_string(&mut input)
.context("failed to read from stdin")?;
if input.is_empty() {
tracing::info!("no content to put in clipboard");
return Ok(());
}
tracing::debug!(content = &input, "found content");
state.remote_copier(&remote_host).copy(input).await?;
return Ok(());
}
let mut input = String::new();
std::io::stdin()
.read_to_string(&mut input)
.context("failed to read from stdin")?;
if input.is_empty() {
tracing::info!("no content to put in clipboard");
return Ok(());
}
tracing::debug!(content = &input, "found content");
state.local_copier().copy(input.as_bytes()).await?;
}
Commands::Paste {} => {
let mut stdout = tokio::io::stdout();
if let Ok(remote_host) = std::env::var("VOIDPIN_REMOTE") {
let output = state.remote_copier(&remote_host).paste().await?;
stdout.write_all(output.as_bytes()).await?;
stdout.flush().await?;
return Ok(());
}
let output = state.local_copier().paste().await?;
stdout.write_all(&output).await?;
stdout.flush().await?;
}
Commands::Remote { command } => match command {
RemoteCommands::Copy { remote_host } => {
let mut input = String::new();
std::io::stdin()
.read_to_string(&mut input)
.context("failed to read from stdin")?;
if input.is_empty() {
tracing::info!("no content to put in clipboard");
return Ok(());
}
tracing::debug!(content = &input, "found content");
state.remote_copier(&remote_host).copy(input).await?;
}
RemoteCommands::Paste { remote_host } => {
let output = state.remote_copier(&remote_host).paste().await?;
let mut stdout = tokio::io::stdout();
stdout.write_all(output.as_bytes()).await?;
stdout.flush().await?;
}
},
}
Ok(())
}