diff --git a/Cargo.lock b/Cargo.lock index 0f02d18b..f471a020 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -77,7 +77,7 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" dependencies = [ - "getrandom 0.2.7", + "getrandom 0.2.10", "once_cell", "version_check", ] @@ -147,16 +147,6 @@ dependencies = [ "futures-core", ] -[[package]] -name = "async-dup" -version = "1.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7427a12b8dc09291528cfb1da2447059adb4a257388c2acd6497a79d55cf6f7c" -dependencies = [ - "futures-io", - "simple-mutex", -] - [[package]] name = "async-executor" version = "1.4.1" @@ -186,23 +176,6 @@ dependencies = [ "once_cell", ] -[[package]] -name = "async-h1" -version = "2.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d1d1dae8cb2c4258a79d6ed088b7fb9b4763bf4e9b22d040779761e046a2971" -dependencies = [ - "async-channel", - "async-dup", - "async-global-executor", - "async-io", - "futures-lite", - "http-types", - "httparse", - "log", - "pin-project", -] - [[package]] name = "async-io" version = "1.13.0" @@ -283,19 +256,6 @@ version = "4.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "30696a84d817107fc028e049980e09d5e140e8da8f1caeb17e8e950658a3cea9" -[[package]] -name = "async-tls" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d85a97c4a0ecce878efd3f945f119c78a646d8975340bca0398f9bb05c30cc52" -dependencies = [ - "futures-core", - "futures-io", - "rustls", - "webpki", - "webpki-roots", -] - [[package]] name = "async-trait" version = "0.1.56" @@ -360,12 +320,6 @@ version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4cbbc9d0964165b47557570cce6c952866c2678457aca742aafc9fb771d30270" -[[package]] -name = "base64" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3441f0f7b02788e948e47f457ca01f1d7e6d92c693bc132c22b087d3141c03ff" - [[package]] name = "base64" version = "0.13.0" @@ -483,6 +437,12 @@ version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" +[[package]] +name = "bytes" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e4cec68f03f32e44924783795810fa50a7035d8c8ebe78580ad7e6c703fba38" + [[package]] name = "bytes" version = "1.4.0" @@ -503,9 +463,12 @@ checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53" [[package]] name = "cc" -version = "1.0.73" +version = "1.0.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" +checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +dependencies = [ + "libc", +] [[package]] name = "cfg-if" @@ -651,17 +614,6 @@ dependencies = [ "crossbeam-utils", ] -[[package]] -name = "config" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19b076e143e1d9538dde65da30f8481c2a6c44040edb8e02b9bf1351edb92ce3" -dependencies = [ - "lazy_static", - "nom 5.1.2", - "serde", -] - [[package]] name = "console" version = "0.15.0" @@ -907,6 +859,37 @@ dependencies = [ "cipher", ] +[[package]] +name = "curl" +version = "0.4.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "509bd11746c7ac09ebd19f0b17782eae80aadee26237658a6b4808afb5c11a22" +dependencies = [ + "curl-sys", + "libc", + "openssl-probe", + "openssl-sys", + "schannel", + "socket2", + "winapi", +] + +[[package]] +name = "curl-sys" +version = "0.4.68+curl-8.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4a0d18d88360e374b16b2273c832b5e57258ffc1d4aa4f96b108e0738d5752f" +dependencies = [ + "cc", + "libc", + "libnghttp2-sys", + "libz-sys", + "openssl-sys", + "pkg-config", + "vcpkg", + "windows-sys 0.48.0", +] + [[package]] name = "daemonize" version = "0.4.1" @@ -951,33 +934,6 @@ dependencies = [ "syn 1.0.96", ] -[[package]] -name = "dashmap" -version = "5.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" -dependencies = [ - "cfg-if 1.0.0", - "hashbrown 0.14.1", - "lock_api", - "once_cell", - "parking_lot_core 0.9.9", -] - -[[package]] -name = "deadpool" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d126179d86aee4556e54f5f3c6bf6d9884e7cc52cef82f77ee6f90a7747616d" -dependencies = [ - "async-trait", - "config", - "crossbeam-queue", - "num_cpus", - "serde", - "tokio", -] - [[package]] name = "deltae" version = "0.3.0" @@ -1293,6 +1249,17 @@ dependencies = [ "zellij-tile", ] +[[package]] +name = "flume" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bebadab126f8120d410b677ed95eee4ba6eb7c6dd8e34a5ec88a08050e26132" +dependencies = [ + "futures-core", + "futures-sink", + "spinning_top", +] + [[package]] name = "fnv" version = "1.0.7" @@ -1490,9 +1457,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.7" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6" +checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" dependencies = [ "cfg-if 1.0.0", "libc", @@ -1571,12 +1538,6 @@ dependencies = [ "ahash", ] -[[package]] -name = "hashbrown" -version = "0.14.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7dfda62a12f55daeae5015f81b0baea145391cb4520f86c248fc615d72640d12" - [[package]] name = "heck" version = "0.3.3" @@ -1639,23 +1600,29 @@ dependencies = [ "digest 0.9.0", ] +[[package]] +name = "http" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" +dependencies = [ + "bytes 1.4.0", + "fnv", + "itoa", +] + [[package]] name = "http-client" version = "6.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1947510dc91e2bf586ea5ffb412caad7673264e14bb39fb9078da114a94ce1a5" dependencies = [ - "async-h1", "async-std", - "async-tls", "async-trait", "cfg-if 1.0.0", - "dashmap", - "deadpool", - "futures", "http-types", + "isahc", "log", - "rustls", ] [[package]] @@ -1680,12 +1647,6 @@ dependencies = [ "url", ] -[[package]] -name = "httparse" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" - [[package]] name = "humantime" version = "2.1.0" @@ -1875,6 +1836,29 @@ version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "616cde7c720bb2bb5824a224687d8f77bfd38922027f01d825cd7453be5099fb" +[[package]] +name = "isahc" +version = "0.9.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2948a0ce43e2c2ef11d7edf6816508998d99e13badd1150be0914205df9388a" +dependencies = [ + "bytes 0.5.6", + "crossbeam-utils", + "curl", + "curl-sys", + "flume", + "futures-lite", + "http", + "log", + "once_cell", + "slab", + "sluice", + "tracing", + "tracing-futures", + "url", + "waker-fn", +] + [[package]] name = "itertools" version = "0.10.5" @@ -1964,23 +1948,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72d9d1bd215936bc8647ad92986bb56f3f216550b53c44ab785e3217ae33625e" [[package]] -name = "lexical-core" -version = "0.7.6" +name = "libc" +version = "0.2.149" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6607c62aa161d23d17a9072cc5da0be67cdfc89d3afb1e8d9c842bebc2525ffe" -dependencies = [ - "arrayvec 0.5.2", - "bitflags", - "cfg-if 1.0.0", - "ryu", - "static_assertions", -] +checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b" [[package]] -name = "libc" -version = "0.2.140" +name = "libnghttp2-sys" +version = "0.1.8+1.55.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99227334921fae1a979cf0bfdfcc6b3e5ce376ef57e16fb6fb3ea2ed6095f80c" +checksum = "4fae956c192dadcdb5dace96db71fa0b827333cce7c7b38dc71446f024d8a340" +dependencies = [ + "cc", + "libc", +] [[package]] name = "libssh2-sys" @@ -2273,7 +2254,6 @@ version = "5.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ffb4262d26ed83a1c0a33a38fe2bb15797329c85770da05e6b828ddb782627af" dependencies = [ - "lexical-core", "memchr", "version_check", ] @@ -2395,6 +2375,21 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + +[[package]] +name = "openssl-src" +version = "300.1.6+3.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439fac53e092cd7442a3660c85dde4643ab3b5bd39040912388dcdabf6b88085" +dependencies = [ + "cc", +] + [[package]] name = "openssl-sys" version = "0.9.93" @@ -2403,6 +2398,7 @@ checksum = "db4d56a4c0478783083cfafcc42493dd4a981d41669da64b4572a2a089b51b1d" dependencies = [ "cc", "libc", + "openssl-src", "pkg-config", "vcpkg", ] @@ -2793,7 +2789,7 @@ version = "0.11.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b82eaa1d779e9a4bc1c3217db8ffbeabaae1dca241bf70183242128d48681cd" dependencies = [ - "bytes", + "bytes 1.4.0", "prost-derive", ] @@ -2803,7 +2799,7 @@ version = "0.11.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "119533552c9a7ffacc21e099c24a0ac8bb19c2a2a3f363de84cd9b844feab270" dependencies = [ - "bytes", + "bytes 1.4.0", "heck 0.4.0", "itertools", "lazy_static", @@ -2941,7 +2937,7 @@ version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" dependencies = [ - "getrandom 0.2.7", + "getrandom 0.2.10", ] [[package]] @@ -3010,7 +3006,7 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" dependencies = [ - "getrandom 0.2.7", + "getrandom 0.2.10", "redox_syscall 0.2.13", "thiserror", ] @@ -3074,21 +3070,6 @@ dependencies = [ "bytecheck", ] -[[package]] -name = "ring" -version = "0.16.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" -dependencies = [ - "cc", - "libc", - "once_cell", - "spin", - "untrusted", - "web-sys", - "winapi", -] - [[package]] name = "rkyv" version = "0.7.39" @@ -3175,19 +3156,6 @@ dependencies = [ "windows-sys 0.45.0", ] -[[package]] -name = "rustls" -version = "0.18.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d1126dcf58e93cee7d098dbda643b5f92ed724f1f6a63007c1116eed6700c81" -dependencies = [ - "base64 0.12.3", - "log", - "ring", - "sct", - "webpki", -] - [[package]] name = "ryu" version = "1.0.10" @@ -3203,22 +3171,21 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "schannel" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c3733bf4cf7ea0880754e19cb5a462007c4a8c1914bff372ccc95b464f1df88" +dependencies = [ + "windows-sys 0.48.0", +] + [[package]] name = "scopeguard" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" -[[package]] -name = "sct" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b362b83898e0e69f38515b82ee15aa80636befe47c3b6d3d89a911e78fc228ce" -dependencies = [ - "ring", - "untrusted", -] - [[package]] name = "seahash" version = "4.1.0" @@ -3452,15 +3419,6 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2e24979f63a11545f5f2c60141afe249d4f19f84581ea2138065e400941d83d3" -[[package]] -name = "simple-mutex" -version = "1.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38aabbeafa6f6dead8cebf246fe9fae1f9215c8d29b3a69f93bd62a9e4a3dcd6" -dependencies = [ - "event-listener", -] - [[package]] name = "siphasher" version = "0.3.10" @@ -3498,6 +3456,17 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "826167069c09b99d56f31e9ae5c99049e932a98c9dc2dac47645b08dbbf76ba7" +[[package]] +name = "sluice" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d7400c0eff44aa2fcb5e31a5f24ba9716ed90138769e4977a2ba6014ae63eb5" +dependencies = [ + "async-channel", + "futures-core", + "futures-io", +] + [[package]] name = "smallvec" version = "1.8.0" @@ -3520,12 +3489,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "spin" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" - [[package]] name = "spinning" version = "0.1.0" @@ -3535,6 +3498,15 @@ dependencies = [ "lock_api", ] +[[package]] +name = "spinning_top" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b9eb1a2f4c41445a3a0ff9abc5221c5fcd28e1f13cd7c0397706f9ac938ddb0" +dependencies = [ + "lock_api", +] + [[package]] name = "ssh2" version = "0.9.3" @@ -3562,12 +3534,6 @@ dependencies = [ "version_check", ] -[[package]] -name = "static_assertions" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" - [[package]] name = "status-bar" version = "0.1.0" @@ -3736,14 +3702,13 @@ dependencies = [ "async-trait", "cfg-if 1.0.0", "futures-util", - "getrandom 0.2.7", + "getrandom 0.2.10", "http-client", "http-types", "log", "mime_guess", "once_cell", "pin-project-lite", - "rustls", "serde", "serde_json", ] @@ -4020,17 +3985,6 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c7c4ceeeca15c8384bbc3e011dbd8fccb7f068a440b752b7d9b32ceb0ca0e2e8" -[[package]] -name = "tokio" -version = "1.29.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "532826ff75199d5833b9d2c5fe410f29235e25704ee5f0ef599fb51c21f4a4da" -dependencies = [ - "autocfg", - "backtrace", - "pin-project-lite", -] - [[package]] name = "toml" version = "0.5.10" @@ -4073,6 +4027,16 @@ dependencies = [ "once_cell", ] +[[package]] +name = "tracing-futures" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97d095ae15e245a057c8e8451bab9b3ee1e1f68e9ba2b4fbc18d0ac5237835f2" +dependencies = [ + "pin-project", + "tracing", +] + [[package]] name = "typemap-ors" version = "1.0.0" @@ -4194,12 +4158,6 @@ dependencies = [ "destructure_traitobject", ] -[[package]] -name = "untrusted" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" - [[package]] name = "url" version = "2.2.2" @@ -4225,7 +4183,7 @@ version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "79daa5ed5740825c40b389c5e50312b9c86df53fccd33f281df655642b43869d" dependencies = [ - "getrandom 0.2.7", + "getrandom 0.2.10", "serde", ] @@ -4429,7 +4387,7 @@ version = "3.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "840af6d21701220cb805dc7201af301cb99e9b4f646f48a41befbc1d949f0f90" dependencies = [ - "bytes", + "bytes 1.4.0", "cfg-if 1.0.0", "indexmap", "js-sys", @@ -4590,7 +4548,7 @@ version = "3.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9bc4fe3b48ccc620901bdcdfac98d8a76ef3487412c221752814750c2e7db4c1" dependencies = [ - "bytes", + "bytes 1.4.0", "thiserror", "wasmer-vfs", ] @@ -4601,11 +4559,11 @@ version = "3.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e893ecd57c63db83b17dacfaee90f660e1d7f5b26d2f9d88ea6aa2e8c4bc301d" dependencies = [ - "bytes", + "bytes 1.4.0", "cfg-if 1.0.0", "derivative", "generational-arena", - "getrandom 0.2.7", + "getrandom 0.2.10", "libc", "thiserror", "tracing", @@ -4625,7 +4583,7 @@ version = "3.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd65882d8cee793848776f26e979b7ff3e139d927947d87304c940c89527a730" dependencies = [ - "bytes", + "bytes 1.4.0", "tracing", "wasmer-vfs", "wasmer-vnet", @@ -4752,25 +4710,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "webpki" -version = "0.21.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8e38c0608262c46d4a56202ebabdeb094cef7e560ca7a226c6bf055188aa4ea" -dependencies = [ - "ring", - "untrusted", -] - -[[package]] -name = "webpki-roots" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f20dea7535251981a9670857150d571846545088359b28e4951d350bdaf179f" -dependencies = [ - "webpki", -] - [[package]] name = "wepoll-ffi" version = "0.1.2" @@ -5193,6 +5132,7 @@ dependencies = [ "crossbeam", "directories", "expect-test", + "futures", "humantime", "include_dir", "insta", @@ -5206,6 +5146,7 @@ dependencies = [ "nix 0.23.1", "notify-debouncer-full", "once_cell", + "openssl-sys", "percent-encoding", "prost", "prost-build", diff --git a/src/main.rs b/src/main.rs index 3d1f37f3..72dce0d2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -46,6 +46,28 @@ fn main() { commands::send_action_to_session(command_cli_action, opts.session, config); std::process::exit(0); } + if let Some(Command::Sessions(Sessions::Plugin { + url, + floating, + in_place, + configuration, + })) = opts.command + { + let command_cli_action = CliAction::NewPane { + command: vec![], + plugin: Some(url), + direction: None, + cwd: None, + floating, + in_place, + name: None, + close_on_exit: false, + start_suspended: false, + configuration, + }; + commands::send_action_to_session(command_cli_action, opts.session, config); + std::process::exit(0); + } if let Some(Command::Sessions(Sessions::Edit { file, direction, diff --git a/zellij-server/src/plugins/wasm_bridge.rs b/zellij-server/src/plugins/wasm_bridge.rs index b2ddb992..260740f9 100644 --- a/zellij-server/src/plugins/wasm_bridge.rs +++ b/zellij-server/src/plugins/wasm_bridge.rs @@ -4,6 +4,7 @@ use crate::plugins::plugin_map::{AtomicEvent, PluginEnv, PluginMap, RunningPlugi use crate::plugins::plugin_worker::MessageToWorker; use crate::plugins::watch_filesystem::watch_filesystem; use crate::plugins::zellij_exports::{wasi_read_string, wasi_write_object}; +use highway::{HighwayHash, PortableHash}; use log::info; use std::{ collections::{HashMap, HashSet}, @@ -14,7 +15,10 @@ use std::{ use wasmer::{Module, Store, Value}; use zellij_utils::async_channel::Sender; use zellij_utils::async_std::task::{self, JoinHandle}; +use zellij_utils::consts::ZELLIJ_CACHE_DIR; use zellij_utils::data::{PermissionStatus, PermissionType}; +use zellij_utils::downloader::download::Download; +use zellij_utils::downloader::Downloader; use zellij_utils::input::permission::PermissionCache; use zellij_utils::notify_debouncer_full::{notify::RecommendedWatcher, Debouncer, FileIdMap}; use zellij_utils::plugin_api::event::ProtobufEvent; @@ -129,7 +133,7 @@ impl WasmBridge { let plugin_id = self.next_plugin_id; - let plugin = self + let mut plugin = self .plugins .get(run) .with_context(|| format!("failed to resolve plugin {run:?}")) @@ -158,6 +162,33 @@ impl WasmBridge { let _ = senders.send_to_background_jobs(BackgroundJob::AnimatePluginLoading(plugin_id)); let mut loading_indication = LoadingIndication::new(plugin_name.clone()); + + if let RunPluginLocation::Remote(url) = &plugin.location { + let download = Download::from(url); + + let hash: String = PortableHash::default() + .hash128(download.url.as_bytes()) + .iter() + .map(ToString::to_string) + .collect(); + + let plugin_directory = ZELLIJ_CACHE_DIR.join(hash); + + // The plugin path is determined by the hash of the plugin URL in the cache directory. + plugin.path = plugin_directory.join(&download.file_name); + + let downloader = Downloader::new(plugin_directory); + match downloader.fetch(&download).await { + Ok(_) => {}, + Err(e) => handle_plugin_loading_failure( + &senders, + plugin_id, + &mut loading_indication, + e, + ), + } + } + match PluginLoader::start_plugin( plugin_id, client_id, diff --git a/zellij-utils/Cargo.toml b/zellij-utils/Cargo.toml index 8845b64f..bb7d0308 100644 --- a/zellij-utils/Cargo.toml +++ b/zellij-utils/Cargo.toml @@ -54,7 +54,11 @@ interprocess = "1.2.1" async-std = { version = "1.3.0", features = ["unstable"] } notify-debouncer-full = "0.1.0" humantime = "2.1.0" -surf = { version = "2.3.2", default-features = false, features = ["h1-client-rustls"] } +futures = "0.3.28" +surf = { version = "2.3.2", default-features = false, features = [ + "curl-client", +] } +openssl-sys = { version = "0.9.93", features = ["vendored"] } [dev-dependencies] insta = { version = "1.6.0", features = ["backtrace"] } diff --git a/zellij-utils/src/cli.rs b/zellij-utils/src/cli.rs index 79aad994..c196c17d 100644 --- a/zellij-utils/src/cli.rs +++ b/zellij-utils/src/cli.rs @@ -217,6 +217,32 @@ pub enum Sessions { #[clap(short, long, value_parser, default_value("false"), takes_value(false))] start_suspended: bool, }, + /// Load a plugin + #[clap(visible_alias = "r")] + Plugin { + /// Plugin URL, can either start with http(s), file: or zellij: + #[clap(last(true), required(true))] + url: String, + + /// Plugin configuration + #[clap(short, long, value_parser)] + configuration: Option, + + /// Open the new pane in floating mode + #[clap(short, long, value_parser, default_value("false"), takes_value(false))] + floating: bool, + + /// Open the new pane in place of the current pane, temporarily suspending it + #[clap( + short, + long, + value_parser, + default_value("false"), + takes_value(false), + conflicts_with("floating") + )] + in_place: bool, + }, /// Edit file with default $EDITOR / $VISUAL #[clap(visible_alias = "e")] Edit { diff --git a/zellij-utils/src/downloader/download.rs b/zellij-utils/src/downloader/download.rs new file mode 100644 index 00000000..d665f7e5 --- /dev/null +++ b/zellij-utils/src/downloader/download.rs @@ -0,0 +1,49 @@ +use serde::{Deserialize, Serialize}; +use surf::Url; + +#[derive(Debug, Default, Clone, Deserialize, Serialize, PartialEq, Eq, Hash)] +pub struct Download { + pub url: String, + pub file_name: String, +} + +impl Download { + pub fn from(url: &str) -> Self { + match Url::parse(url) { + Ok(u) => u + .path_segments() + .map_or_else(Download::default, |segments| { + let file_name = segments.last().unwrap_or("").to_string(); + + Download { + url: url.to_string(), + file_name, + } + }), + Err(_) => Download::default(), + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_from_download() { + let download = Download::from("https://github.com/example/plugin.wasm"); + assert_eq!(download.url, "https://github.com/example/plugin.wasm"); + assert_eq!(download.file_name, "plugin.wasm"); + } + + #[test] + fn test_empty_download() { + let d1 = Download::from("https://example.com"); + assert_eq!(d1.url, "https://example.com"); + assert_eq!(d1.file_name, ""); + + let d2 = Download::from("github.com"); + assert_eq!(d2.url, ""); + assert_eq!(d2.file_name, ""); + } +} diff --git a/zellij-utils/src/downloader/mod.rs b/zellij-utils/src/downloader/mod.rs new file mode 100644 index 00000000..b0b2771d --- /dev/null +++ b/zellij-utils/src/downloader/mod.rs @@ -0,0 +1,147 @@ +pub mod download; + +use async_std::{ + fs::{create_dir_all, File}, + io::{ReadExt, WriteExt}, + stream, task, +}; +use futures::{StreamExt, TryStreamExt}; +use std::path::PathBuf; +use surf::Client; +use thiserror::Error; + +use self::download::Download; + +#[derive(Error, Debug)] +pub enum DownloaderError { + #[error("RequestError: {0}")] + Request(surf::Error), + #[error("StatusError: {0}, StatusCode: {1}")] + Status(String, surf::StatusCode), + #[error("IoError: {0}")] + Io(#[source] std::io::Error), + #[error("IoPathError: {0}, File: {1}")] + IoPath(std::io::Error, PathBuf), +} + +#[derive(Default, Debug)] +pub struct Downloader { + client: Client, + directory: PathBuf, +} + +impl Downloader { + pub fn new(directory: PathBuf) -> Self { + Self { + client: surf::client().with(surf::middleware::Redirect::default()), + directory, + } + } + + pub fn set_directory(&mut self, directory: PathBuf) { + self.directory = directory; + } + + pub fn download(&self, downloads: &[Download]) -> Vec> { + task::block_on(async { + stream::from_iter(downloads) + .map(|download| self.fetch(download)) + .buffer_unordered(4) + .collect::>() + .await + }) + } + + pub async fn fetch(&self, download: &Download) -> Result<(), DownloaderError> { + let mut file_size: usize = 0; + + let file_path = self.directory.join(&download.file_name); + + if file_path.exists() { + file_size = match file_path.metadata() { + Ok(metadata) => metadata.len() as usize, + Err(e) => return Err(DownloaderError::IoPath(e, file_path)), + } + } + + let response = self + .client + .get(&download.url) + .await + .map_err(|e| DownloaderError::Request(e))?; + let status = response.status(); + + if status.is_client_error() || status.is_server_error() { + return Err(DownloaderError::Status( + status.canonical_reason().to_string(), + status, + )); + } + + let length = response.len().unwrap_or(0); + if length > 0 && length == file_size { + return Ok(()); + } + + let mut dest = { + create_dir_all(&self.directory) + .await + .map_err(|e| DownloaderError::IoPath(e, self.directory.clone()))?; + File::create(&file_path) + .await + .map_err(|e| DownloaderError::IoPath(e, file_path))? + }; + + let mut bytes = response.bytes(); + while let Some(byte) = bytes.try_next().await.map_err(DownloaderError::Io)? { + dest.write_all(&[byte]).await.map_err(DownloaderError::Io)?; + } + + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + use tempfile::tempdir; + + #[test] + #[ignore] + fn test_fetch_plugin() { + let dir = tempdir().expect("could not get temp dir"); + let dir_path = dir.path(); + + let downloader = Downloader::new(dir_path.to_path_buf()); + let dl = Download::from( + "https://github.com/imsnif/monocle/releases/download/0.37.2/monocle.wasm", + ); + + let result = task::block_on(downloader.fetch(&dl)); + + assert!(result.is_ok()); + } + + #[test] + #[ignore] + fn test_download_plugins() { + let dir = tempdir().expect("could not get temp dir"); + let dir_path = dir.path(); + + let downloader = Downloader::new(dir_path.to_path_buf()); + let downloads = vec![ + Download::from( + "https://github.com/imsnif/monocle/releases/download/0.37.2/monocle.wasm", + ), + Download::from( + "https://github.com/imsnif/multitask/releases/download/0.38.2/multitask.wasm", + ), + ]; + + let results = downloader.download(&downloads); + for result in results { + assert!(result.is_ok()) + } + } +} diff --git a/zellij-utils/src/input/layout.rs b/zellij-utils/src/input/layout.rs index ace471f8..312286ec 100644 --- a/zellij-utils/src/input/layout.rs +++ b/zellij-utils/src/input/layout.rs @@ -287,6 +287,7 @@ impl FromStr for PluginUserConfiguration { pub enum RunPluginLocation { File(PathBuf), Zellij(PluginTag), + Remote(String), } impl Default for RunPluginLocation { @@ -332,6 +333,7 @@ impl RunPluginLocation { }; Ok(Self::File(path)) }, + "https" | "http" => Ok(Self::Remote(url.as_str().to_owned())), _ => Err(PluginsConfigError::InvalidUrlScheme(url)), } } @@ -339,6 +341,7 @@ impl RunPluginLocation { match self { RunPluginLocation::File(pathbuf) => format!("file:{}", pathbuf.display()), RunPluginLocation::Zellij(plugin_tag) => format!("zellij:{}", plugin_tag), + RunPluginLocation::Remote(url) => format!("remote:{}", url), } } } @@ -351,6 +354,7 @@ impl From<&RunPluginLocation> for Url { path.clone().into_os_string().into_string().unwrap() ), RunPluginLocation::Zellij(tag) => format!("zellij:{}", tag), + RunPluginLocation::Remote(url) => format!("remote:{}", url), }; Self::parse(&url).unwrap() } @@ -364,8 +368,8 @@ impl fmt::Display for RunPluginLocation { "{}", path.clone().into_os_string().into_string().unwrap() ), - Self::Zellij(tag) => write!(f, "{}", tag), + Self::Remote(url) => write!(f, "{}", url), } } } diff --git a/zellij-utils/src/input/plugins.rs b/zellij-utils/src/input/plugins.rs index f12aba2a..2a262222 100644 --- a/zellij-utils/src/input/plugins.rs +++ b/zellij-utils/src/input/plugins.rs @@ -55,6 +55,13 @@ impl PluginsConfig { userspace_configuration: run.configuration.clone(), ..plugin }), + RunPluginLocation::Remote(_) => Some(PluginConfig { + path: PathBuf::new(), + run: PluginType::Pane(None), + _allow_exec_host_cmd: run._allow_exec_host_cmd, + location: run.location.clone(), + userspace_configuration: run.configuration.clone(), + }), } } @@ -231,7 +238,7 @@ pub enum PluginsConfigError { DuplicatePlugins(PluginTag), #[error("Failed to parse url: {0:?}")] InvalidUrl(#[from] url::ParseError), - #[error("Only 'file:' and 'zellij:' url schemes are supported for plugin lookup. '{0}' does not match either.")] + #[error("Only 'file:', 'http(s):' and 'zellij:' url schemes are supported for plugin lookup. '{0}' does not match either.")] InvalidUrlScheme(Url), #[error("Could not find plugin at the path: '{0:?}'")] InvalidPluginLocation(PathBuf), diff --git a/zellij-utils/src/lib.rs b/zellij-utils/src/lib.rs index 5438f323..05752e21 100644 --- a/zellij-utils/src/lib.rs +++ b/zellij-utils/src/lib.rs @@ -17,6 +17,8 @@ pub mod shared; #[cfg(not(target_family = "wasm"))] pub mod channels; // Requires async_std #[cfg(not(target_family = "wasm"))] +pub mod downloader; // Requires async_std +#[cfg(not(target_family = "wasm"))] pub mod ipc; // Requires interprocess #[cfg(not(target_family = "wasm"))] pub mod logging; // Requires log4rs