Compare commits
No commits in common. "main" and "docs/add-usage" have entirely different histories.
main
...
docs/add-u
20
CHANGELOG.md
20
CHANGELOG.md
@ -1,20 +0,0 @@
|
||||
# Changelog
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [0.0.1] - 2025-01-10
|
||||
|
||||
### Added
|
||||
- add initial copy command
|
||||
|
||||
### Docs
|
||||
- add example of call
|
||||
- add a small readme with intent
|
||||
|
||||
### Other
|
||||
- add stub for remote copier
|
||||
- extract local copier to struct
|
829
Cargo.lock
generated
829
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@ -3,7 +3,7 @@ members = ["crates/*"]
|
||||
resolver = "2"
|
||||
|
||||
[workspace.package]
|
||||
version = "0.0.1"
|
||||
version = "0.1.0"
|
||||
|
||||
[workspace.dependencies]
|
||||
voidpin = { path = "crates/voidpin" }
|
||||
|
18
README.md
18
README.md
@ -4,25 +4,9 @@ Voidpin allows sending copy/paste commands across the wire. It is specifically i
|
||||
|
||||
Voidpin sends clipboard content to a local service, that then puts content in a local clipboard. It can also go the other way, but the primary intent is remote -> local.
|
||||
|
||||
## Demo
|
||||
|
||||
![demo](assets/demo.gif)
|
||||
|
||||
## Usage
|
||||
|
||||
```bash
|
||||
# Local
|
||||
voidpin copy
|
||||
|
||||
# Remote
|
||||
ip a # 10.0.9.1
|
||||
voidpin listen & # make voidpin listener run in background
|
||||
|
||||
# login to remote
|
||||
ssh remote@remote
|
||||
export VOIDPIN_REMOTE=http://10.0.9.1:7900
|
||||
echo "some content" | voidpin remote copy
|
||||
|
||||
# In a browser for example
|
||||
# Ctrl+v or Command+v
|
||||
VOIDPIN_REMOTE=10.0.9.1:19191 voidpin remote copy
|
||||
```
|
||||
|
175
assets/demo.cast
175
assets/demo.cast
@ -1,175 +0,0 @@
|
||||
{"version": 2, "width": 130, "height": 40, "timestamp": 1736839984, "env": {"SHELL": "/bin/zsh", "TERM": "xterm-256color"}}
|
||||
[0.479638, "o", "\u001b[1m\u001b[7m%\u001b[27m\u001b[1m\u001b[0m \r \r"]
|
||||
[0.586074, "o", "\r\u001b[0m\u001b[27m\u001b[24m\u001b[J\r\n\u001b[38;2;255;153;102mvoidpin\u001b[0m \u001b[90mHEAD\u001b[1;32m(153edb1)\u001b[0m \u001b[38;2;255;153;102m \u001b[0mis \u001b[1;38;5;208m📦 v0.0.1\u001b[0m \u001b[1;31mrs \u001b[0mwith \u001b[1;34m🐃 v1.49.0 \u001b[0m\r\n\u001b[38;2;255;153;102m❯\u001b[0m \u001b[K"]
|
||||
[0.587071, "o", "\u001b[6 q"]
|
||||
[0.587964, "o", "\u001b[6 q"]
|
||||
[0.588147, "o", "\u001b[?2004h"]
|
||||
[3.167878, "o", "v"]
|
||||
[3.191237, "o", "\b\u001b[1m\u001b[31mv\u001b[0m\u001b[39m"]
|
||||
[3.232572, "o", "\b\u001b[1m\u001b[31mv\u001b[0m\u001b[39m\u001b[90moidpin listen\u001b[39m\u001b[13D"]
|
||||
[3.300942, "o", "\b\u001b[1m\u001b[31mv\u001b[1m\u001b[31mo\u001b[0m\u001b[39m"]
|
||||
[3.430551, "o", "\b\b\u001b[1m\u001b[31mv\u001b[1m\u001b[31mo\u001b[1m\u001b[31mi\u001b[0m\u001b[39m"]
|
||||
[3.544553, "o", "\b\u001b[1m\u001b[31mi\u001b[1m\u001b[31md\u001b[0m\u001b[39m"]
|
||||
[3.689365, "o", "\b\u001b[1m\u001b[31md\u001b[1m\u001b[31mp\u001b[0m\u001b[39m"]
|
||||
[3.797695, "o", "\b\u001b[1m\u001b[31mp\u001b[1m\u001b[31mi\u001b[0m\u001b[39m"]
|
||||
[3.97905, "o", "\b\u001b[1m\u001b[31mi\u001b[1m\u001b[31mn\u001b[0m\u001b[39m"]
|
||||
[3.980916, "o", "\b\b\b\b\b\b\b\u001b[0m\u001b[32mv\u001b[0m\u001b[32mo\u001b[0m\u001b[32mi\u001b[0m\u001b[32md\u001b[0m\u001b[32mp\u001b[0m\u001b[32mi\u001b[0m\u001b[32mn\u001b[39m"]
|
||||
[4.066146, "o", "\b\u001b[32mn\u001b[32m \u001b[39m"]
|
||||
[4.068196, "o", "\b\b\u001b[32mn\u001b[39m\u001b[39m "]
|
||||
[4.336382, "o", "\u001b[39mr\u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \b\b\b\b\b"]
|
||||
[4.342211, "o", "\b\u001b[4mr\u001b[24m"]
|
||||
[4.422896, "o", "\b\u001b[4mr\u001b[4me\u001b[24m"]
|
||||
[4.452272, "o", "\b\u001b[4me\u001b[4mm\u001b[24m"]
|
||||
[4.45517, "o", "\b\b\b\u001b[24mr\u001b[24me\u001b[24mm"]
|
||||
[4.583628, "o", "o"]
|
||||
[4.666593, "o", "t"]
|
||||
[4.759127, "o", "e"]
|
||||
[4.900598, "o", " "]
|
||||
[5.746797, "o", "&"]
|
||||
[6.624085, "o", "\b \b"]
|
||||
[6.74323, "o", "\b"]
|
||||
[6.896869, "o", "\b \b"]
|
||||
[7.033699, "o", "\b \b"]
|
||||
[7.174094, "o", "\b \b"]
|
||||
[7.309328, "o", "\b \b"]
|
||||
[7.313831, "o", "\b\b\u001b[4mr\u001b[4me\u001b[24m"]
|
||||
[7.443394, "o", "\b\b\u001b[4mr\u001b[24m\u001b[24m \b"]
|
||||
[7.573529, "o", "\b\u001b[24m \b"]
|
||||
[7.600586, "o", "\u001b[90mlisten\u001b[39m\b\b\b\b\b\b"]
|
||||
[7.789065, "o", "\u001b[39ml"]
|
||||
[7.957613, "o", "\u001b[39mi"]
|
||||
[8.041393, "o", "\u001b[39ms"]
|
||||
[8.137227, "o", "\u001b[39mt"]
|
||||
[8.196626, "o", "\u001b[39me"]
|
||||
[8.263729, "o", "\u001b[39mn"]
|
||||
[8.401872, "o", " "]
|
||||
[8.5879, "o", "&"]
|
||||
[8.95472, "o", "\u001b[?1l\u001b>"]
|
||||
[8.954912, "o", "\u001b[?2004l"]
|
||||
[8.960125, "o", "\u001b[0 q"]
|
||||
[8.960423, "o", "\r\r\n"]
|
||||
[8.986315, "o", "[1] 36816\r\n"]
|
||||
[8.98642, "o", "\u001b[1m\u001b[7m%\u001b[27m\u001b[1m\u001b[0m \r \r"]
|
||||
[8.995202, "o", "\u001b[2m2025-01-14T07:33:13.136349Z\u001b[0m \u001b[32m INFO\u001b[0m \u001b[2mvoidpin\u001b[0m\u001b[2m:\u001b[0m starting listener \u001b[3mgrpc\u001b[0m\u001b[2m=\u001b[0m\"0.0.0.0:7900\"\r\n"]
|
||||
[9.060819, "o", "\r\u001b[0m\u001b[27m\u001b[24m\u001b[J\r\n\u001b[38;2;255;153;102mvoidpin\u001b[0m \u001b[90mHEAD\u001b[1;32m(153edb1)\u001b[0m \u001b[38;2;255;153;102m \u001b[0mis \u001b[1;38;5;208m📦 v0.0.1\u001b[0m \u001b[1;31mrs \u001b[0mwith \u001b[1;34m🐃 v1.49.0 \u001b[0m\r\n\u001b[1;34m✦\u001b[0m \u001b[38;2;255;153;102m❯\u001b[0m \u001b[K"]
|
||||
[9.061906, "o", "\u001b[6 q"]
|
||||
[9.062876, "o", "\u001b[6 q"]
|
||||
[9.063056, "o", "\u001b[?2004h"]
|
||||
[10.265829, "o", "s"]
|
||||
[10.272815, "o", "\b\u001b[1m\u001b[31ms\u001b[0m\u001b[39m"]
|
||||
[10.34456, "o", "\b\u001b[1m\u001b[31ms\u001b[0m\u001b[39m\u001b[90msh nef_remote\u001b[39m\u001b[13D"]
|
||||
[10.409989, "o", "\b\u001b[1m\u001b[31ms\u001b[1m\u001b[31ms\u001b[0m\u001b[39m"]
|
||||
[10.531573, "o", "\b\b\u001b[1m\u001b[31ms\u001b[1m\u001b[31ms\u001b[1m\u001b[31mh\u001b[0m\u001b[39m"]
|
||||
[10.534938, "o", "\b\b\b\u001b[0m\u001b[32ms\u001b[0m\u001b[32ms\u001b[0m\u001b[32mh\u001b[39m"]
|
||||
[10.650848, "o", "\b\u001b[32mh\u001b[32m \u001b[39m"]
|
||||
[10.654699, "o", "\b\b\u001b[32mh\u001b[39m\u001b[39m "]
|
||||
[12.060573, "o", "\u001b[39mn"]
|
||||
[12.153753, "o", "\u001b[39me"]
|
||||
[12.238632, "o", "\u001b[39mf"]
|
||||
[12.536018, "o", "\u001b[39m_"]
|
||||
[12.700909, "o", "\u001b[39mr"]
|
||||
[12.776911, "o", "\u001b[39me"]
|
||||
[12.839538, "o", "\u001b[39mm"]
|
||||
[12.97043, "o", "\u001b[39mo"]
|
||||
[13.030559, "o", "\u001b[39mt"]
|
||||
[13.111063, "o", "\u001b[39me"]
|
||||
[13.319003, "o", "\u001b[?1l\u001b>"]
|
||||
[13.319486, "o", "\u001b[?2004l"]
|
||||
[13.325988, "o", "\u001b[0 q"]
|
||||
[13.326164, "o", "\r\r\n"]
|
||||
[14.748903, "o", "Last login: Tue Jan 14 08:27:03 2025 from 10.0.9.20\r\r\n"]
|
||||
[14.80584, "o", "\u001b[1m\u001b[7m%\u001b[27m\u001b[1m\u001b[0m \r \r"]
|
||||
[14.840843, "o", "\r\u001b[0m\u001b[27m\u001b[24m\u001b[J\r\n\u001b[1;33mkjuulh\u001b[0m in \u001b[1;2;32m🌐 nefarious\u001b[0m in \u001b[38;2;255;153;102m~\u001b[0m on \u001b[1;33m☁️ (eu-west-1) \u001b[0m\r\n\u001b[38;2;255;153;102m❯\u001b[0m \u001b[K\u001b[6 q\u001b[6 q\u001b[?2004h"]
|
||||
[15.45051, "o", "e"]
|
||||
[15.456172, "o", "\b\u001b[1m\u001b[31me\u001b[0m\u001b[39m"]
|
||||
[15.461708, "o", "\b\u001b[1m\u001b[31me\u001b[0m\u001b[39m\u001b[90mcho \"mystuff\" | voidpin remote copy\u001b[39m\u001b[35D"]
|
||||
[15.623151, "o", "\b\u001b[1m\u001b[31me\u001b[1m\u001b[31mx\u001b[0m\u001b[39m\u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[34D"]
|
||||
[15.644024, "o", "\u001b[90mport VOIDPIN_REMOTE=http://10.0.9.20:7900\u001b[39m\u001b[41D"]
|
||||
[15.743819, "o", "\b\b\u001b[1m\u001b[31me\u001b[1m\u001b[31mx\u001b[1m\u001b[31mp\u001b[0m\u001b[39m"]
|
||||
[15.908543, "o", "\b\u001b[1m\u001b[31mp\u001b[1m\u001b[31mo\u001b[0m\u001b[39m"]
|
||||
[16.003031, "o", "\b\u001b[1m\u001b[31mo\u001b[1m\u001b[31mr\u001b[0m\u001b[39m"]
|
||||
[16.265633, "o", "\b\u001b[1m\u001b[31mr\u001b[1m\u001b[31mt\u001b[0m\u001b[39m\b\b\b\b\b\b\u001b[0m\u001b[33me\u001b[0m\u001b[33mx\u001b[0m\u001b[33mp\u001b[0m\u001b[33mo\u001b[0m\u001b[33mr\u001b[0m\u001b[33mt\u001b[39m"]
|
||||
[16.276286, "o", "\b\u001b[33mt\u001b[33m \u001b[39m\b\b\u001b[33mt\u001b[39m\u001b[39m "]
|
||||
[17.117006, "o", "\u001b[39mV"]
|
||||
[17.118576, "o", "\b\u001b[4mV\u001b[24m"]
|
||||
[17.334112, "o", "\b\u001b[4mV\u001b[39m\u001b[4mO\u001b[24m\b\b\u001b[24mV\u001b[24mO"]
|
||||
[17.363757, "o", "\u001b[39mI"]
|
||||
[17.604581, "o", "\u001b[39mD"]
|
||||
[17.931075, "o", "\u001b[39mP\u001b[39mI\u001b[39mN\u001b[39m_\u001b[39mR\u001b[39mE\u001b[39mM\u001b[39mO\u001b[39mT\u001b[39mE\u001b[39m=\u001b[39mh\u001b[39mt\u001b[39mt\u001b[39mp\u001b[39m:\u001b[39m/\u001b[39m/\u001b[39m1\u001b[39m0\u001b[39m.\u001b[39m0\u001b[39m.\u001b[39m9\u001b[39m.\u001b[39m2\u001b[39m0\u001b[39m:\u001b[39m7\u001b[39m9\u001b[39m0\u001b[39m0"]
|
||||
[18.721578, "o", "\u001b[0 q\u001b[?2004l\r\r\n"]
|
||||
[18.729848, "o", "\u001b[1m\u001b[7m%\u001b[27m\u001b[1m\u001b[0m \r \r"]
|
||||
[18.745102, "o", "\r\u001b[0m\u001b[27m\u001b[24m\u001b[J\r\n\u001b[1;33mkjuulh\u001b[0m in \u001b[1;2;32m🌐 nefarious\u001b[0m in \u001b[38;2;255;153;102m~\u001b[0m on \u001b[1;33m☁️ (eu-west-1) \u001b[0m\r\n\u001b[38;2;255;153;102m❯\u001b[0m \u001b[K\u001b[6 q\u001b[6 q\u001b[?2004h"]
|
||||
[19.768372, "o", "v\b\u001b[4mv\u001b[24m"]
|
||||
[19.787679, "o", "\b\u001b[4mv\u001b[24m\u001b[90mirtualenv venv\u001b[39m\u001b[14D"]
|
||||
[19.948701, "o", "\b\u001b[4mv\u001b[39m\u001b[4mo\u001b[24m\u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[13D"]
|
||||
[19.950166, "o", "\b\b\u001b[24m\u001b[1m\u001b[31mv\u001b[24m\u001b[1m\u001b[31mo\u001b[0m\u001b[39m"]
|
||||
[19.998976, "o", "\b\b\u001b[1m\u001b[31mv\u001b[1m\u001b[31mo\u001b[1m\u001b[31mi\u001b[0m\u001b[39m"]
|
||||
[20.399333, "o", "\b\b\b\u001b[1m\u001b[31mv\u001b[1m\u001b[31mo\u001b[0m\u001b[39m\u001b[0m\u001b[39m \b"]
|
||||
[20.536472, "o", "\b\b\u001b[1m\u001b[31mv\u001b[0m\u001b[39m\u001b[0m\u001b[39m \b\b\u001b[0m\u001b[39m\u001b[4mv\u001b[24m"]
|
||||
[20.54648, "o", "\b\u001b[4mv\u001b[24m\u001b[90mirtualenv venv\u001b[39m\u001b[14D"]
|
||||
[20.659047, "o", "\b\u001b[24m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[15D"]
|
||||
[21.004154, "o", "e\b\u001b[1m\u001b[31me\u001b[0m\u001b[39m"]
|
||||
[21.010045, "o", "\b\u001b[1m\u001b[31me\u001b[0m\u001b[39m\u001b[90mxport VOIDPIN_REMOTE=http://10.0.9.20:7900\u001b[39m\u001b[42D"]
|
||||
[21.21325, "o", "\b\u001b[1m\u001b[31me\u001b[1m\u001b[31mc\u001b[0m\u001b[39m\u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[41D"]
|
||||
[21.222033, "o", "\u001b[90mho \"mystuff\" | voidpin remote copy\u001b[39m\u001b[34D"]
|
||||
[21.294992, "o", "\b\b\u001b[1m\u001b[31me\u001b[1m\u001b[31mc\u001b[1m\u001b[31mh\u001b[0m\u001b[39m"]
|
||||
[21.419667, "o", "\b\u001b[1m\u001b[31mh\u001b[1m\u001b[31mo\u001b[0m\u001b[39m"]
|
||||
[21.42291, "o", "\b\b\b\b\u001b[0m\u001b[32me\u001b[0m\u001b[32mc\u001b[0m\u001b[32mh\u001b[0m\u001b[32mo\u001b[39m"]
|
||||
[21.505433, "o", "\b\u001b[32mo\u001b[32m \u001b[39m\b\b\u001b[32mo\u001b[39m\u001b[39m "]
|
||||
[21.988846, "o", "\u001b[39m\"\u001b[39mm\u001b[39my\u001b[39ms\u001b[39mt\u001b[39mu\u001b[39mf\u001b[39mf\u001b[39m\"\u001b[39m \u001b[39m|\u001b[39m \u001b[39mv\u001b[39mo\u001b[39mi\u001b[39md\u001b[39mp\u001b[39mi\u001b[39mn\u001b[39m \u001b[39mr\u001b[39me\u001b[39mm\u001b[39mo\u001b[39mt\u001b[39me\u001b[39m \u001b[39mc\u001b[39mo\u001b[39mp\u001b[39my"]
|
||||
[21.993557, "o", "\u001b[31D\u001b[33m\"\u001b[33mm\u001b[33my\u001b[33ms\u001b[33mt\u001b[33mu\u001b[33mf\u001b[33mf\u001b[33m\"\u001b[39m\u001b[3C\u001b[32mv\u001b[32mo\u001b[32mi\u001b[32md\u001b[32mp\u001b[32mi\u001b[32mn\u001b[39m\u001b[12C"]
|
||||
[23.489379, "o", "\u001b[0 q"]
|
||||
[23.490939, "o", "\u001b[?2004l\r\r\n"]
|
||||
[23.569732, "o", "\u001b[2m2025-01-14T07:33:20.444640Z\u001b[0m \u001b[32m INFO\u001b[0m \u001b[2mvoidpin::remote_copy\u001b[0m\u001b[2m:\u001b[0m sending copy request\r\n"]
|
||||
[23.595738, "o", "\u001b[2m2025-01-14T07:33:27.736730Z\u001b[0m \u001b[32m INFO\u001b[0m \u001b[2mvoidpin::copy\u001b[0m\u001b[2m:\u001b[0m copy process ended with status: ExitStatus(unix_wait_status(0))\n"]
|
||||
[23.677319, "o", "\u001b[2m2025-01-14T07:33:20.549068Z\u001b[0m \u001b[32m INFO\u001b[0m \u001b[2mvoidpin::remote_copy\u001b[0m\u001b[2m:\u001b[0m sent copy request\r\n"]
|
||||
[23.677877, "o", "\u001b[1m\u001b[7m%\u001b[27m\u001b[1m\u001b[0m \r \r"]
|
||||
[23.682634, "o", "\r\u001b[0m\u001b[27m\u001b[24m\u001b[J\r\n\u001b[1;33mkjuulh\u001b[0m in \u001b[1;2;32m🌐 nefarious\u001b[0m in \u001b[38;2;255;153;102m~\u001b[0m on \u001b[1;33m☁️ (eu-west-1) \u001b[0m\r\n\u001b[38;2;255;153;102m❯\u001b[0m \u001b[K"]
|
||||
[23.684943, "o", "\u001b[6 q\u001b[6 q"]
|
||||
[23.685144, "o", "\u001b[?2004h"]
|
||||
[26.039094, "o", "e\b\u001b[1m\u001b[31me\u001b[0m\u001b[39m"]
|
||||
[26.058901, "o", "\b\u001b[1m\u001b[31me\u001b[0m\u001b[39m\u001b[90mcho \"mystuff\" | voidpin remote copy\u001b[39m\u001b[35D"]
|
||||
[26.200566, "o", "\b\u001b[1m\u001b[31me\u001b[1m\u001b[31mx\u001b[0m\u001b[39m\u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[34D"]
|
||||
[26.224342, "o", "\b\b\u001b[1m\u001b[31me\u001b[1m\u001b[31mx\u001b[1m\u001b[31mi\u001b[0m\u001b[39m"]
|
||||
[26.236469, "o", "\u001b[90mt\u001b[39m\b"]
|
||||
[26.338963, "o", "\b\u001b[1m\u001b[31mi\u001b[1m\u001b[31mt\u001b[0m\u001b[39m"]
|
||||
[26.347595, "o", "\b\b\b\b\u001b[0m\u001b[32me\u001b[0m\u001b[32mx\u001b[0m\u001b[32mi\u001b[0m\u001b[32mt\u001b[39m"]
|
||||
[27.917748, "o", "\u001b[0 q\u001b[?2004l\r\r\n"]
|
||||
[27.92436, "o", "Connection to 10.0.9.18 closed.\r\r\n"]
|
||||
[27.927279, "o", "\u001b[1m\u001b[7m%\u001b[27m\u001b[1m\u001b[0m \r \r"]
|
||||
[28.005869, "o", "\r\u001b[0m\u001b[27m\u001b[24m\u001b[J\r\n\u001b[38;2;255;153;102mvoidpin\u001b[0m \u001b[90mHEAD\u001b[1;32m(153edb1)\u001b[0m \u001b[38;2;255;153;102m \u001b[0mis \u001b[1;38;5;208m📦 v0.0.1\u001b[0m \u001b[1;31mrs \u001b[0mwith \u001b[1;34m🐃 v1.49.0 \u001b[0m\u001b[33m14s\u001b[0m \r\n\u001b[1;34m✦\u001b[0m \u001b[38;2;255;153;102m❯\u001b[0m \u001b[K"]
|
||||
[28.006941, "o", "\u001b[6 q"]
|
||||
[28.007819, "o", "\u001b[6 q"]
|
||||
[28.007998, "o", "\u001b[?2004h"]
|
||||
[29.624611, "o", "p"]
|
||||
[29.632533, "o", "\b\u001b[1m\u001b[31mp\u001b[0m\u001b[39m"]
|
||||
[29.674006, "o", "\b\u001b[1m\u001b[31mp\u001b[0m\u001b[39m\u001b[90msql -h localhost -p 5432 -U postgres -d service\u001b[39m\u001b[47D"]
|
||||
[29.861312, "o", "\b\u001b[1m\u001b[31mp\u001b[1m\u001b[31mb\u001b[0m\u001b[39m\u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[46D"]
|
||||
[29.896875, "o", "\u001b[90mcopy\u001b[39m\b\b\b\b"]
|
||||
[30.455748, "o", "\b\b\u001b[1m\u001b[31mp\u001b[1m\u001b[31mb\u001b[1m\u001b[31mp\u001b[0m\u001b[39m\u001b[39m \u001b[39m \u001b[39m \b\b\b"]
|
||||
[30.492269, "o", "\u001b[90maste\u001b[39m\b\b\b\b"]
|
||||
[30.511887, "o", "\b\u001b[1m\u001b[31mp\u001b[1m\u001b[31ma\u001b[0m\u001b[39m"]
|
||||
[30.640428, "o", "\b\u001b[1m\u001b[31ma\u001b[1m\u001b[31ms\u001b[0m\u001b[39m"]
|
||||
[30.900008, "o", "\b\u001b[1m\u001b[31ms\u001b[1m\u001b[31mt\u001b[0m\u001b[39m"]
|
||||
[31.00196, "o", "\b\u001b[1m\u001b[31mt\u001b[1m\u001b[31me\u001b[0m\u001b[39m"]
|
||||
[31.004152, "o", "\b\b\b\b\b\b\b\u001b[0m\u001b[32mp\u001b[0m\u001b[32mb\u001b[0m\u001b[32mp\u001b[0m\u001b[32ma\u001b[0m\u001b[32ms\u001b[0m\u001b[32mt\u001b[0m\u001b[32me\u001b[39m"]
|
||||
[31.436893, "o", "\u001b[?1l\u001b>"]
|
||||
[31.437147, "o", "\u001b[?2004l"]
|
||||
[31.440483, "o", "\u001b[0 q"]
|
||||
[31.440758, "o", "\r\r\n"]
|
||||
[31.506519, "o", "mystuff\r\n"]
|
||||
[31.507028, "o", "\u001b[1m\u001b[7m%\u001b[27m\u001b[1m\u001b[0m \r \r"]
|
||||
[31.578124, "o", "\r\u001b[0m\u001b[27m\u001b[24m\u001b[J\r\n\u001b[38;2;255;153;102mvoidpin\u001b[0m \u001b[90mHEAD\u001b[1;32m(153edb1)\u001b[0m \u001b[38;2;255;153;102m \u001b[0mis \u001b[1;38;5;208m📦 v0.0.1\u001b[0m \u001b[1;31mrs \u001b[0mwith \u001b[1;34m🐃 v1.49.0 \u001b[0m\r\n\u001b[1;34m✦\u001b[0m \u001b[38;2;255;153;102m❯\u001b[0m \u001b[K"]
|
||||
[31.579154, "o", "\u001b[6 q"]
|
||||
[31.580021, "o", "\u001b[6 q"]
|
||||
[31.580178, "o", "\u001b[?2004h"]
|
||||
[33.751349, "o", "\u001b[?2004l\r\r\n"]
|
||||
[33.75158, "o", "zsh: you have running jobs.\r\n"]
|
||||
[33.751697, "o", "\u001b[1m\u001b[7m%\u001b[27m\u001b[1m\u001b[0m \r \r"]
|
||||
[33.839059, "o", "\r\u001b[0m\u001b[27m\u001b[24m\u001b[J\r\n\u001b[38;2;255;153;102mvoidpin\u001b[0m \u001b[90mHEAD\u001b[1;32m(153edb1)\u001b[0m \u001b[38;2;255;153;102m \u001b[0mis \u001b[1;38;5;208m📦 v0.0.1\u001b[0m \u001b[1;31mrs \u001b[0mwith \u001b[1;34m🐃 v1.49.0 \u001b[0m\r\n\u001b[1;34m✦\u001b[0m \u001b[38;2;255;153;102m❯\u001b[0m \u001b[K"]
|
||||
[33.840399, "o", "\u001b[6 q"]
|
||||
[33.841412, "o", "\u001b[6 q"]
|
||||
[33.84158, "o", "\u001b[?2004h"]
|
||||
[39.131276, "o", "\u001b[?2004l\r\r\n"]
|
||||
[39.13278, "o", "zsh: warning: 1 jobs SIGHUPed\r\n"]
|
||||
[39.13415, "o", "[1] + hangup voidpin listen\r\n"]
|
BIN
assets/demo.gif
BIN
assets/demo.gif
Binary file not shown.
Before Width: | Height: | Size: 197 KiB |
10
buf.gen.yaml
10
buf.gen.yaml
@ -1,10 +0,0 @@
|
||||
version: v2
|
||||
managed:
|
||||
enabled: true
|
||||
plugins:
|
||||
- remote: buf.build/community/neoeinstein-prost
|
||||
out: crates/voidpin/src/gen
|
||||
- remote: buf.build/community/neoeinstein-tonic:v0.4.0
|
||||
out: crates/voidpin/src/gen
|
||||
inputs:
|
||||
- directory: crates/voidpin/proto
|
4
buf.yaml
4
buf.yaml
@ -1,4 +0,0 @@
|
||||
version: v2
|
||||
modules:
|
||||
- path: proto
|
||||
name: buf.build/noschemaplz/voidpin
|
@ -14,8 +14,3 @@ dotenv.workspace = true
|
||||
|
||||
serde = { version = "1.0.197", features = ["derive"] }
|
||||
uuid = { version = "1.7.0", features = ["v4"] }
|
||||
tonic = { version = "0.12.3", features = ["tls", "tls-roots"] }
|
||||
prost = "0.13.4"
|
||||
prost-types = "0.13.4"
|
||||
bytes = "1.9.0"
|
||||
async-trait = "0.1.85"
|
||||
|
@ -1,13 +0,0 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package voidpin.v1;
|
||||
|
||||
service VoidPin {
|
||||
rpc Copy(CopyRequest) returns (CopyResponse);
|
||||
}
|
||||
|
||||
message CopyRequest {
|
||||
bytes content = 1;
|
||||
}
|
||||
|
||||
message CopyResponse {}
|
@ -1,14 +0,0 @@
|
||||
// @generated
|
||||
// This file is @generated by prost-build.
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct CopyRequest {
|
||||
#[prost(bytes="vec", tag="1")]
|
||||
pub content: ::prost::alloc::vec::Vec<u8>,
|
||||
}
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, Copy, PartialEq, ::prost::Message)]
|
||||
pub struct CopyResponse {
|
||||
}
|
||||
include!("voidpin.v1.tonic.rs");
|
||||
// @@protoc_insertion_point(module)
|
@ -1,287 +0,0 @@
|
||||
// @generated
|
||||
/// Generated client implementations.
|
||||
pub mod void_pin_client {
|
||||
#![allow(unused_variables, dead_code, missing_docs, clippy::let_unit_value)]
|
||||
use tonic::codegen::*;
|
||||
use tonic::codegen::http::Uri;
|
||||
///
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct VoidPinClient<T> {
|
||||
inner: tonic::client::Grpc<T>,
|
||||
}
|
||||
impl VoidPinClient<tonic::transport::Channel> {
|
||||
/// Attempt to create a new client by connecting to a given endpoint.
|
||||
pub async fn connect<D>(dst: D) -> Result<Self, tonic::transport::Error>
|
||||
where
|
||||
D: TryInto<tonic::transport::Endpoint>,
|
||||
D::Error: Into<StdError>,
|
||||
{
|
||||
let conn = tonic::transport::Endpoint::new(dst)?.connect().await?;
|
||||
Ok(Self::new(conn))
|
||||
}
|
||||
}
|
||||
impl<T> VoidPinClient<T>
|
||||
where
|
||||
T: tonic::client::GrpcService<tonic::body::BoxBody>,
|
||||
T::Error: Into<StdError>,
|
||||
T::ResponseBody: Body<Data = Bytes> + Send + 'static,
|
||||
<T::ResponseBody as Body>::Error: Into<StdError> + Send,
|
||||
{
|
||||
pub fn new(inner: T) -> Self {
|
||||
let inner = tonic::client::Grpc::new(inner);
|
||||
Self { inner }
|
||||
}
|
||||
pub fn with_origin(inner: T, origin: Uri) -> Self {
|
||||
let inner = tonic::client::Grpc::with_origin(inner, origin);
|
||||
Self { inner }
|
||||
}
|
||||
pub fn with_interceptor<F>(
|
||||
inner: T,
|
||||
interceptor: F,
|
||||
) -> VoidPinClient<InterceptedService<T, F>>
|
||||
where
|
||||
F: tonic::service::Interceptor,
|
||||
T::ResponseBody: Default,
|
||||
T: tonic::codegen::Service<
|
||||
http::Request<tonic::body::BoxBody>,
|
||||
Response = http::Response<
|
||||
<T as tonic::client::GrpcService<tonic::body::BoxBody>>::ResponseBody,
|
||||
>,
|
||||
>,
|
||||
<T as tonic::codegen::Service<
|
||||
http::Request<tonic::body::BoxBody>,
|
||||
>>::Error: Into<StdError> + Send + Sync,
|
||||
{
|
||||
VoidPinClient::new(InterceptedService::new(inner, interceptor))
|
||||
}
|
||||
/// Compress requests with the given encoding.
|
||||
///
|
||||
/// This requires the server to support it otherwise it might respond with an
|
||||
/// error.
|
||||
#[must_use]
|
||||
pub fn send_compressed(mut self, encoding: CompressionEncoding) -> Self {
|
||||
self.inner = self.inner.send_compressed(encoding);
|
||||
self
|
||||
}
|
||||
/// Enable decompressing responses.
|
||||
#[must_use]
|
||||
pub fn accept_compressed(mut self, encoding: CompressionEncoding) -> Self {
|
||||
self.inner = self.inner.accept_compressed(encoding);
|
||||
self
|
||||
}
|
||||
/// Limits the maximum size of a decoded message.
|
||||
///
|
||||
/// Default: `4MB`
|
||||
#[must_use]
|
||||
pub fn max_decoding_message_size(mut self, limit: usize) -> Self {
|
||||
self.inner = self.inner.max_decoding_message_size(limit);
|
||||
self
|
||||
}
|
||||
/// Limits the maximum size of an encoded message.
|
||||
///
|
||||
/// Default: `usize::MAX`
|
||||
#[must_use]
|
||||
pub fn max_encoding_message_size(mut self, limit: usize) -> Self {
|
||||
self.inner = self.inner.max_encoding_message_size(limit);
|
||||
self
|
||||
}
|
||||
///
|
||||
pub async fn copy(
|
||||
&mut self,
|
||||
request: impl tonic::IntoRequest<super::CopyRequest>,
|
||||
) -> std::result::Result<tonic::Response<super::CopyResponse>, tonic::Status> {
|
||||
self.inner
|
||||
.ready()
|
||||
.await
|
||||
.map_err(|e| {
|
||||
tonic::Status::new(
|
||||
tonic::Code::Unknown,
|
||||
format!("Service was not ready: {}", e.into()),
|
||||
)
|
||||
})?;
|
||||
let codec = tonic::codec::ProstCodec::default();
|
||||
let path = http::uri::PathAndQuery::from_static("/voidpin.v1.VoidPin/Copy");
|
||||
let mut req = request.into_request();
|
||||
req.extensions_mut().insert(GrpcMethod::new("voidpin.v1.VoidPin", "Copy"));
|
||||
self.inner.unary(req, path, codec).await
|
||||
}
|
||||
}
|
||||
}
|
||||
/// Generated server implementations.
|
||||
pub mod void_pin_server {
|
||||
#![allow(unused_variables, dead_code, missing_docs, clippy::let_unit_value)]
|
||||
use tonic::codegen::*;
|
||||
/// Generated trait containing gRPC methods that should be implemented for use with VoidPinServer.
|
||||
#[async_trait]
|
||||
pub trait VoidPin: Send + Sync + 'static {
|
||||
///
|
||||
async fn copy(
|
||||
&self,
|
||||
request: tonic::Request<super::CopyRequest>,
|
||||
) -> std::result::Result<tonic::Response<super::CopyResponse>, tonic::Status>;
|
||||
}
|
||||
///
|
||||
#[derive(Debug)]
|
||||
pub struct VoidPinServer<T: VoidPin> {
|
||||
inner: _Inner<T>,
|
||||
accept_compression_encodings: EnabledCompressionEncodings,
|
||||
send_compression_encodings: EnabledCompressionEncodings,
|
||||
max_decoding_message_size: Option<usize>,
|
||||
max_encoding_message_size: Option<usize>,
|
||||
}
|
||||
struct _Inner<T>(Arc<T>);
|
||||
impl<T: VoidPin> VoidPinServer<T> {
|
||||
pub fn new(inner: T) -> Self {
|
||||
Self::from_arc(Arc::new(inner))
|
||||
}
|
||||
pub fn from_arc(inner: Arc<T>) -> Self {
|
||||
let inner = _Inner(inner);
|
||||
Self {
|
||||
inner,
|
||||
accept_compression_encodings: Default::default(),
|
||||
send_compression_encodings: Default::default(),
|
||||
max_decoding_message_size: None,
|
||||
max_encoding_message_size: None,
|
||||
}
|
||||
}
|
||||
pub fn with_interceptor<F>(
|
||||
inner: T,
|
||||
interceptor: F,
|
||||
) -> InterceptedService<Self, F>
|
||||
where
|
||||
F: tonic::service::Interceptor,
|
||||
{
|
||||
InterceptedService::new(Self::new(inner), interceptor)
|
||||
}
|
||||
/// Enable decompressing requests with the given encoding.
|
||||
#[must_use]
|
||||
pub fn accept_compressed(mut self, encoding: CompressionEncoding) -> Self {
|
||||
self.accept_compression_encodings.enable(encoding);
|
||||
self
|
||||
}
|
||||
/// Compress responses with the given encoding, if the client supports it.
|
||||
#[must_use]
|
||||
pub fn send_compressed(mut self, encoding: CompressionEncoding) -> Self {
|
||||
self.send_compression_encodings.enable(encoding);
|
||||
self
|
||||
}
|
||||
/// Limits the maximum size of a decoded message.
|
||||
///
|
||||
/// Default: `4MB`
|
||||
#[must_use]
|
||||
pub fn max_decoding_message_size(mut self, limit: usize) -> Self {
|
||||
self.max_decoding_message_size = Some(limit);
|
||||
self
|
||||
}
|
||||
/// Limits the maximum size of an encoded message.
|
||||
///
|
||||
/// Default: `usize::MAX`
|
||||
#[must_use]
|
||||
pub fn max_encoding_message_size(mut self, limit: usize) -> Self {
|
||||
self.max_encoding_message_size = Some(limit);
|
||||
self
|
||||
}
|
||||
}
|
||||
impl<T, B> tonic::codegen::Service<http::Request<B>> for VoidPinServer<T>
|
||||
where
|
||||
T: VoidPin,
|
||||
B: Body + Send + 'static,
|
||||
B::Error: Into<StdError> + Send + 'static,
|
||||
{
|
||||
type Response = http::Response<tonic::body::BoxBody>;
|
||||
type Error = std::convert::Infallible;
|
||||
type Future = BoxFuture<Self::Response, Self::Error>;
|
||||
fn poll_ready(
|
||||
&mut self,
|
||||
_cx: &mut Context<'_>,
|
||||
) -> Poll<std::result::Result<(), Self::Error>> {
|
||||
Poll::Ready(Ok(()))
|
||||
}
|
||||
fn call(&mut self, req: http::Request<B>) -> Self::Future {
|
||||
let inner = self.inner.clone();
|
||||
match req.uri().path() {
|
||||
"/voidpin.v1.VoidPin/Copy" => {
|
||||
#[allow(non_camel_case_types)]
|
||||
struct CopySvc<T: VoidPin>(pub Arc<T>);
|
||||
impl<T: VoidPin> tonic::server::UnaryService<super::CopyRequest>
|
||||
for CopySvc<T> {
|
||||
type Response = super::CopyResponse;
|
||||
type Future = BoxFuture<
|
||||
tonic::Response<Self::Response>,
|
||||
tonic::Status,
|
||||
>;
|
||||
fn call(
|
||||
&mut self,
|
||||
request: tonic::Request<super::CopyRequest>,
|
||||
) -> Self::Future {
|
||||
let inner = Arc::clone(&self.0);
|
||||
let fut = async move {
|
||||
<T as VoidPin>::copy(&inner, request).await
|
||||
};
|
||||
Box::pin(fut)
|
||||
}
|
||||
}
|
||||
let accept_compression_encodings = self.accept_compression_encodings;
|
||||
let send_compression_encodings = self.send_compression_encodings;
|
||||
let max_decoding_message_size = self.max_decoding_message_size;
|
||||
let max_encoding_message_size = self.max_encoding_message_size;
|
||||
let inner = self.inner.clone();
|
||||
let fut = async move {
|
||||
let inner = inner.0;
|
||||
let method = CopySvc(inner);
|
||||
let codec = tonic::codec::ProstCodec::default();
|
||||
let mut grpc = tonic::server::Grpc::new(codec)
|
||||
.apply_compression_config(
|
||||
accept_compression_encodings,
|
||||
send_compression_encodings,
|
||||
)
|
||||
.apply_max_message_size_config(
|
||||
max_decoding_message_size,
|
||||
max_encoding_message_size,
|
||||
);
|
||||
let res = grpc.unary(method, req).await;
|
||||
Ok(res)
|
||||
};
|
||||
Box::pin(fut)
|
||||
}
|
||||
_ => {
|
||||
Box::pin(async move {
|
||||
Ok(
|
||||
http::Response::builder()
|
||||
.status(200)
|
||||
.header("grpc-status", "12")
|
||||
.header("content-type", "application/grpc")
|
||||
.body(empty_body())
|
||||
.unwrap(),
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
impl<T: VoidPin> Clone for VoidPinServer<T> {
|
||||
fn clone(&self) -> Self {
|
||||
let inner = self.inner.clone();
|
||||
Self {
|
||||
inner,
|
||||
accept_compression_encodings: self.accept_compression_encodings,
|
||||
send_compression_encodings: self.send_compression_encodings,
|
||||
max_decoding_message_size: self.max_decoding_message_size,
|
||||
max_encoding_message_size: self.max_encoding_message_size,
|
||||
}
|
||||
}
|
||||
}
|
||||
impl<T: VoidPin> Clone for _Inner<T> {
|
||||
fn clone(&self) -> Self {
|
||||
Self(Arc::clone(&self.0))
|
||||
}
|
||||
}
|
||||
impl<T: std::fmt::Debug> std::fmt::Debug for _Inner<T> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{:?}", self.0)
|
||||
}
|
||||
}
|
||||
impl<T: VoidPin> tonic::server::NamedService for VoidPinServer<T> {
|
||||
const NAME: &'static str = "voidpin.v1.VoidPin";
|
||||
}
|
||||
}
|
@ -1,30 +0,0 @@
|
||||
use crate::{copy::LocalCopierState, state::State};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct GrpcServer {
|
||||
state: State,
|
||||
}
|
||||
|
||||
impl GrpcServer {
|
||||
pub fn new(state: State) -> Self {
|
||||
Self { state }
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl crate::grpc::void_pin_server::VoidPin for GrpcServer {
|
||||
async fn copy(
|
||||
&self,
|
||||
req: tonic::Request<crate::grpc::CopyRequest>,
|
||||
) -> Result<tonic::Response<crate::grpc::CopyResponse>, tonic::Status> {
|
||||
let req = req.into_inner();
|
||||
|
||||
self.state
|
||||
.local_copier()
|
||||
.copy(&req.content)
|
||||
.await
|
||||
.map_err(|e| tonic::Status::internal(e.to_string()))?;
|
||||
|
||||
Ok(tonic::Response::new(crate::grpc::CopyResponse {}))
|
||||
}
|
||||
}
|
@ -1,22 +1,11 @@
|
||||
use std::{io::Read, net::SocketAddr};
|
||||
use std::io::Read;
|
||||
|
||||
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 tonic::transport;
|
||||
|
||||
mod grpc {
|
||||
include!("gen/voidpin.v1.rs");
|
||||
}
|
||||
|
||||
mod grpc_server;
|
||||
|
||||
mod copy;
|
||||
mod remote_copy;
|
||||
mod state;
|
||||
|
||||
#[derive(Parser)]
|
||||
@ -28,10 +17,7 @@ struct Command {
|
||||
|
||||
#[derive(Subcommand)]
|
||||
enum Commands {
|
||||
Listen {
|
||||
#[arg(long, env = "VOIDPIN_GRPC_HOST", default_value = "0.0.0.0:7900")]
|
||||
grpc: SocketAddr,
|
||||
},
|
||||
Listen {},
|
||||
Copy {},
|
||||
Remote {
|
||||
#[command(subcommand)]
|
||||
@ -41,14 +27,7 @@ enum Commands {
|
||||
|
||||
#[derive(Subcommand)]
|
||||
enum RemoteCommands {
|
||||
Copy {
|
||||
#[arg(
|
||||
long = "remote-host",
|
||||
env = "VOIDPIN_REMOTE",
|
||||
default_value = "http://0.0.0.0:7900"
|
||||
)]
|
||||
remote_host: String,
|
||||
},
|
||||
Copy {},
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
@ -62,13 +41,7 @@ async fn main() -> anyhow::Result<()> {
|
||||
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::Listen {} => {}
|
||||
Commands::Copy {} => {
|
||||
let mut input = String::new();
|
||||
std::io::stdin()
|
||||
@ -84,23 +57,7 @@ async fn main() -> anyhow::Result<()> {
|
||||
state.local_copier().copy(input.as_bytes()).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.as_bytes())
|
||||
.await?;
|
||||
}
|
||||
RemoteCommands::Copy {} => todo!(),
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
|
@ -1,50 +0,0 @@
|
||||
use tonic::transport::{Channel, ClientTlsConfig};
|
||||
|
||||
use crate::{grpc::CopyRequest, state::State};
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct RemoteCopier {
|
||||
host: String,
|
||||
}
|
||||
|
||||
impl RemoteCopier {
|
||||
pub fn new(remote_host: impl Into<String>) -> Self {
|
||||
Self {
|
||||
host: remote_host.into(),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn copy(&self, input: &[u8]) -> anyhow::Result<()> {
|
||||
let tls = ClientTlsConfig::new();
|
||||
let channel = Channel::from_shared(self.host.clone())?
|
||||
.tls_config(if self.host.starts_with("https") {
|
||||
tls.with_native_roots()
|
||||
} else {
|
||||
tls
|
||||
})?
|
||||
.connect()
|
||||
.await?;
|
||||
|
||||
tracing::debug!("establishing connection to remote");
|
||||
let mut client = crate::grpc::void_pin_client::VoidPinClient::new(channel);
|
||||
tracing::info!("sending copy request");
|
||||
client
|
||||
.copy(CopyRequest {
|
||||
content: input.into(),
|
||||
})
|
||||
.await?;
|
||||
tracing::info!("sent copy request");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub trait RemoteCopierState {
|
||||
fn remote_copier(&self, host: impl Into<String>) -> RemoteCopier;
|
||||
}
|
||||
|
||||
impl RemoteCopierState for State {
|
||||
fn remote_copier(&self, host: impl Into<String>) -> RemoteCopier {
|
||||
RemoteCopier::new(host)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user