diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 8c81918e..9a24f835 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -47,7 +47,7 @@ jobs: - name: Rust Cache uses: swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1 - - uses: taiki-e/install-action@59012be0884e296ca2da49b530610e72c49039ad # v2.81.6 + - uses: taiki-e/install-action@15449e3094499af05d8d964a1c884208e4b8b595 # v2.81.11 with: tool: nextest,cargo-llvm-cov diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index ba55dd53..b65b99a3 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -11,17 +11,17 @@ repos: args: [--fix=lf] - repo: https://github.com/rbubley/mirrors-prettier - rev: 515f543f5718ebfd6ce22e16708bb32c68ff96e1 # frozen: v3.8.3 + rev: 06507ab9500d4a9919b5ebd76d9d5b7074f15c1f # frozen: v3.8.4 hooks: - id: prettier - repo: https://github.com/crate-ci/typos - rev: f8a58b6b53f2279f71eb605f03a4ae4d10608f45 # frozen: v1.47.0 + rev: 37bb98842b0d8c4ffebdb75301a13db0267cef89 # frozen: v1.47.2 hooks: - id: typos - repo: https://github.com/python-jsonschema/check-jsonschema - rev: f805888065fdb6162e1f800e50bb9460cbd223d6 # frozen: 0.37.2 + rev: 5030dca3047414c338091455ac41803200ec1f0f # frozen: 0.37.3 hooks: - id: check-github-workflows - id: check-renovate diff --git a/Cargo.lock b/Cargo.lock index 31bea058..734657cb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -93,6 +93,7 @@ dependencies = [ "parking_lot", "percent-encoding", "windows-sys 0.60.2", + "wl-clipboard-rs", "x11rb", ] @@ -279,9 +280,9 @@ checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "bitflags" -version = "2.11.1" +version = "2.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4512299f36f043ab09a583e57bceb5a5aab7a73db1805848e8fef3c9e8c78b3" +checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8" [[package]] name = "block2" @@ -331,9 +332,9 @@ checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" [[package]] name = "cc" -version = "1.2.62" +version = "1.2.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1dce859f0832a7d088c4f1119888ab94ef4b5d6795d1ce05afb7fe159d79f98" +checksum = "dad887fd958be91b5098c0248def011f4523ab786cd411be668777e55063501f" dependencies = [ "find-msvc-tools", "jobserver", @@ -531,9 +532,6 @@ name = "deranged" version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" -dependencies = [ - "powerfmt", -] [[package]] name = "dirs" @@ -592,6 +590,12 @@ version = "0.15.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b" +[[package]] +name = "downcast-rs" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" + [[package]] name = "dunce" version = "1.0.5" @@ -695,6 +699,12 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" +[[package]] +name = "fixedbitset" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99" + [[package]] name = "flate2" version = "1.1.9" @@ -905,9 +915,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.4.14" +version = "0.4.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "171fefbc92fe4a4de27e0698d6a5b392d6a0e333506bc49133760b3bcf948733" +checksum = "6cb093c84e8bd9b188d4c4a8cb6579fc016968d14c99882163cd3ff402a4f155" dependencies = [ "atomic-waker", "bytes", @@ -970,9 +980,9 @@ checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" [[package]] name = "http" -version = "1.4.1" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8be7462df143984c4598a256ef469b251d7d7f9e271135073e78fc535414f3d0" +checksum = "6970f50e31d6fc17d3fa27329444bfa74e196cf62e95052a3f6fee181dba6425" dependencies = [ "bytes", "itoa", @@ -1015,9 +1025,9 @@ checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] name = "hyper" -version = "1.10.0" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb92f162bf56536459fc83c79b974bb12837acfed43d6bc370a7916d0ae15ecc" +checksum = "55281c53a1894c864990125767da440a4e630446785086f52523b20033b74498" dependencies = [ "atomic-waker", "bytes", @@ -1325,13 +1335,12 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.99" +version = "0.3.102" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "142bc4740e452c1e57ade0cbc129f139c9093e354346f0872ef985f4f5cf5f11" +checksum = "03d04c30968dffe80775bd4d7fb676131cd04a1fb46d2686dbffbaec2d9dfd31" dependencies = [ "cfg-if", "futures-util", - "once_cell", "wasm-bindgen", ] @@ -1391,9 +1400,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.30" +version = "0.4.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "616ec5685824bcc94416c6d4a7a446eea774a31efd7062c8480ba6fd06d7a6e5" +checksum = "953f07c43838f8e6f9758cab68bf5bed85465e7587ebe0b823f1bcd81978ad3a" [[package]] name = "lru-slab" @@ -1403,21 +1412,23 @@ checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" [[package]] name = "mac-notification-sys" -version = "0.6.12" +version = "0.6.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29a16783dd1a47849b8c8133c9cd3eb2112cfbc6901670af3dba47c8bbfb07d3" +checksum = "04fd0110fd05744c3c904acf1a8ca624a721d75a35638f127cd5fb6b7ccfb1bf" dependencies = [ "cc", + "log", "objc2", "objc2-foundation", "time", + "uuid", ] [[package]] name = "memchr" -version = "2.8.1" +version = "2.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b947ae49db0d222b1dbc6b113ce7248a3fc3a6ca21b696717bfc000ba4484d8" +checksum = "88904434abc2901f197fe8cc55f0445e7ded921dba5911dad2e2b39b48e663c4" [[package]] name = "memoffset" @@ -1475,6 +1486,15 @@ dependencies = [ "pxfm", ] +[[package]] +name = "nom" +version = "8.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df9761775871bdef83bee530e60050f7e54b1105350d6884eb0fb4f46c2f9405" +dependencies = [ + "memchr", +] + [[package]] name = "notify-rust" version = "4.17.0" @@ -1632,6 +1652,16 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "os_pipe" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d8fae84b431384b68627d0f9b3b1245fcf9f46f6c0e3dc902e9dce64edd1967" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + [[package]] name = "parking" version = "2.2.1" @@ -1667,6 +1697,17 @@ version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" +[[package]] +name = "petgraph" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8701b58ea97060d5e5b155d383a69952a60943f0e6dfe30b04c287beb0b27455" +dependencies = [ + "fixedbitset", + "hashbrown 0.15.5", + "indexmap", +] + [[package]] name = "pin-project-lite" version = "0.2.17" @@ -1684,6 +1725,12 @@ dependencies = [ "futures-io", ] +[[package]] +name = "pkg-config" +version = "0.3.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e" + [[package]] name = "png" version = "0.18.1" @@ -1800,6 +1847,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "quick-xml" +version = "0.39.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdcc8dd4e2f670d309a5f0e83fe36dfdc05af317008fea29144da1a2ac858e5e" +dependencies = [ + "memchr", +] + [[package]] name = "quinn" version = "0.11.9" @@ -1928,9 +1984,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.12.3" +version = "1.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" +checksum = "f1292b7759ae1cb9ec195452d1390a074f0cd8541ab7a5a8c31cd6db45d4a6ba" dependencies = [ "aho-corasick", "memchr", @@ -1951,9 +2007,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.10" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" +checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4" [[package]] name = "reqwest" @@ -2054,9 +2110,9 @@ dependencies = [ [[package]] name = "rustls-native-certs" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "612460d5f7bea540c490b2b6395d8e34a953e52b491accd6c86c8164c5932a63" +checksum = "dab5152771c58876a2146916e53e35057e1a4dfa2b9df0f0305b07f611fdea4d" dependencies = [ "openssl-probe", "rustls-pki-types", @@ -2255,9 +2311,9 @@ dependencies = [ [[package]] name = "shlex" -version = "1.3.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" +checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" [[package]] name = "signal-hook-registry" @@ -2299,9 +2355,9 @@ checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" [[package]] name = "smallvec" -version = "1.15.1" +version = "1.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" +checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90" [[package]] name = "socket2" @@ -2368,7 +2424,7 @@ version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b1e66e07de489fe43a46678dd0b8df65e0c973909df1b60ba33874e297ba9b9" dependencies = [ - "quick-xml", + "quick-xml 0.37.5", "thiserror", "windows", "windows-version", @@ -2432,12 +2488,11 @@ dependencies = [ [[package]] name = "time" -version = "0.3.47" +version = "0.3.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c" +checksum = "711a53c2d47bbd818258c498c8dbfe186a2526c631495cfe7e078567f86b8469" dependencies = [ "deranged", - "itoa", "num-conv", "powerfmt", "serde_core", @@ -2447,15 +2502,15 @@ dependencies = [ [[package]] name = "time-core" -version = "0.1.8" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca" +checksum = "9e1c906769ad99c88eaa54e728060edef082f8e358ff32030cb7c7d315e81109" [[package]] name = "time-macros" -version = "0.2.27" +version = "0.2.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e70e4c5a0e0a8a4823ad65dfe1a6930e4f4d756dcd9dd7939022b5e8c501215" +checksum = "71c652a3727a9cbb9a02f707f530b618ce00d0ccd762009c8c23bd191df3c17d" dependencies = [ "num-conv", "time-core", @@ -2653,6 +2708,17 @@ dependencies = [ "tracing-core", ] +[[package]] +name = "tree_magic_mini" +version = "3.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8765b90061cba6c22b5831f675da109ae5561588290f9fa2317adab2714d5a6" +dependencies = [ + "memchr", + "nom", + "petgraph", +] + [[package]] name = "try-lock" version = "0.2.5" @@ -2726,10 +2792,11 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.23.1" +version = "1.23.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddd74a9687298c6858e9b88ec8935ec45d22e8fd5e6394fa1bd4e99a87789c76" +checksum = "144d6b123cef80b301b8f72a9e2ca4370ddec21950d0a103dd22c437006d2db7" dependencies = [ + "getrandom 0.4.2", "js-sys", "serde_core", "wasm-bindgen", @@ -2768,9 +2835,9 @@ checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] name = "wasip2" -version = "1.0.3+wasi-0.2.9" +version = "1.0.4+wasi-0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20064672db26d7cdc89c7798c48a0fdfac8213434a1186e5ef29fd560ae223d6" +checksum = "b67efb37e106e55ce722a510d6b5f9c17f083e5fc79afc2badeb12cc313d9487" dependencies = [ "wit-bindgen 0.57.1", ] @@ -2786,9 +2853,9 @@ dependencies = [ [[package]] name = "wasm-bindgen" -version = "0.2.122" +version = "0.2.125" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ed04576f974d2b2fba0f38c51dbc5518011e38c36bf1143164be765528fd409" +checksum = "8ddb3f79143bced6de84270411622a2699cee572fc0875aeaf1e7867cf9fca1a" dependencies = [ "cfg-if", "once_cell", @@ -2799,9 +2866,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.72" +version = "0.4.75" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9473dbd2991ae90b6291c3c32c30c6187ac49aa32f9905d1cce280ec1e110b0f" +checksum = "503b14d284f2c8dac03b819967e155ea753f573586193b2b2c95990cb5d69280" dependencies = [ "js-sys", "wasm-bindgen", @@ -2809,9 +2876,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.122" +version = "0.2.125" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "916151b09da36bd82f6615cbf3a419e2f0ba23a03c6160e8e92eb6bd4aa1dec6" +checksum = "4e21a184b13fb19e157296e2c46056aec9092264fab83e4ba59e68c61b323c3d" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -2819,9 +2886,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.122" +version = "0.2.125" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "299047362ccbfce148b67ab7e73349f77748e00c8296f9542adfad2ad82c5c5e" +checksum = "fecefd9c35bd935a20fc3fc344b5f29138961e4f47fb03297d88f2587afb5ebd" dependencies = [ "bumpalo", "proc-macro2", @@ -2832,9 +2899,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.122" +version = "0.2.125" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a929b2c61f11ba3e9bc35b50c1f25cb38e0e892c0c231ae2b8cf78d5dad4437" +checksum = "23939e44bb9a5d7576fa2b563dc2e136628f1224e88a8deed09e04858b77871f" dependencies = [ "unicode-ident", ] @@ -2873,11 +2940,81 @@ dependencies = [ "semver", ] +[[package]] +name = "wayland-backend" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2857dd20b54e916ec7253b3d6b4d5c4d7d4ca2c33c2e11c6c76a99bd8744755d" +dependencies = [ + "cc", + "downcast-rs", + "rustix", + "smallvec", + "wayland-sys", +] + +[[package]] +name = "wayland-client" +version = "0.31.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "645c7c96bb74690c3189b5c9cb4ca1627062bb23693a4fad9d8c3de958260144" +dependencies = [ + "bitflags", + "rustix", + "wayland-backend", + "wayland-scanner", +] + +[[package]] +name = "wayland-protocols" +version = "0.32.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "563a85523cade2429938e790815fd7319062103b9f4a2dc806e9b53b95982d8f" +dependencies = [ + "bitflags", + "wayland-backend", + "wayland-client", + "wayland-scanner", +] + +[[package]] +name = "wayland-protocols-wlr" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb04e52f7836d7c7976c78ca0250d61e33873c34156a2a1fc9474828ec268234" +dependencies = [ + "bitflags", + "wayland-backend", + "wayland-client", + "wayland-protocols", + "wayland-scanner", +] + +[[package]] +name = "wayland-scanner" +version = "0.31.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c324a910fd86ebdc364a3e61ec1f11737d3b1d6c273c0239ee8ff4bc0d24b4a" +dependencies = [ + "proc-macro2", + "quick-xml 0.39.4", + "quote", +] + +[[package]] +name = "wayland-sys" +version = "0.31.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8eab23fefc9e41f8e841df4a9c707e8a8c4ed26e944ef69297184de2785e3be" +dependencies = [ + "pkg-config", +] + [[package]] name = "web-sys" -version = "0.3.99" +version = "0.3.102" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d621441cfc37b84979402712047321980c178f299193a3589d05b99e8763436" +checksum = "a6430a72df5eb332242960fe84b3002a241163998241eb596d4f739b9757061d" dependencies = [ "js-sys", "wasm-bindgen", @@ -3325,6 +3462,24 @@ dependencies = [ "wasmparser", ] +[[package]] +name = "wl-clipboard-rs" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9651471a32e87d96ef3a127715382b2d11cc7c8bb9822ded8a7cc94072eb0a3" +dependencies = [ + "libc", + "log", + "os_pipe", + "rustix", + "thiserror", + "tree_magic_mini", + "wayland-backend", + "wayland-client", + "wayland-protocols", + "wayland-protocols-wlr", +] + [[package]] name = "writeable" version = "0.6.3" @@ -3350,9 +3505,9 @@ checksum = "ea6fc2961e4ef194dcbfe56bb845534d0dc8098940c7e5c012a258bfec6701bd" [[package]] name = "yoke" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abe8c5fda708d9ca3df187cae8bfb9ceda00dd96231bed36e445a1a48e66f9ca" +checksum = "709fe23a0424b6a435d82152b1bd3fdfb0833487d5fa90d05d42762a9891fef5" dependencies = [ "stable_deref_trait", "yoke-derive", @@ -3373,9 +3528,9 @@ dependencies = [ [[package]] name = "zbus" -version = "5.15.0" +version = "5.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3bcbf15c8708d7fc1be0c993622e0a5cbd5e8b52bfa40afa4c3e0cd8d724ac1" +checksum = "eee682d202a77e4a9f3b2c2bdf48a7b28af5c08c34ddf66f98c93e5e39464285" dependencies = [ "async-broadcast", "async-executor", @@ -3408,9 +3563,9 @@ dependencies = [ [[package]] name = "zbus_macros" -version = "5.15.0" +version = "5.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51fa5406ad9175a8c825a931f8cf347116b531b3634fcb0b627c290f1f2516ff" +checksum = "adf1bd45a81a103745b1757754762a26e8cd01e4532e4d6c8ec431624b80d1d6" dependencies = [ "proc-macro-crate", "proc-macro2", @@ -3434,18 +3589,18 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.8.49" +version = "0.8.52" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bce33a6288fa3f072a8c2c7d0f2fdbb90e28298f0135c1f99b96c3db2efcc60b" +checksum = "ce1022995ff5ff5d841ad7d994facc23098cd40152f2c1d11cd607c6f530653f" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.49" +version = "0.8.52" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fd425244944f4ab65ccff928e7323354c5a018c75838362fdce749dfad2ee1e" +checksum = "1ae7f38b72ec2a254e2b87ef277cf2cd4fb97cbebf944faa6f33354da0867930" dependencies = [ "proc-macro2", "quote", @@ -3475,9 +3630,9 @@ dependencies = [ [[package]] name = "zeroize" -version = "1.8.2" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" +checksum = "e13c156562582aa81c60cb29407084cdb54c4164760106ab78e6c5b0858cf64e" [[package]] name = "zerotrie" @@ -3535,9 +3690,9 @@ dependencies = [ [[package]] name = "zvariant" -version = "5.11.0" +version = "5.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c1567a6ec68df868cbbfde844cfc6d81649fe5109a62b116b19fabd53e618ee" +checksum = "a192a0bde63360d77a7523c833d4b4ce6070a927e2c53246e4c540b1a3e27be0" dependencies = [ "endi", "enumflags2", @@ -3549,9 +3704,9 @@ dependencies = [ [[package]] name = "zvariant_derive" -version = "5.11.0" +version = "5.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7d5b780599bbde114e39d9a0799577fad1ced5105d38515745f7b3099d8ceda" +checksum = "90bc6cde9c01c511074be97f7ccb6c19d0da89e3f8662e812e999dcfd4638737" dependencies = [ "proc-macro-crate", "proc-macro2", @@ -3562,9 +3717,9 @@ dependencies = [ [[package]] name = "zvariant_utils" -version = "3.3.1" +version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d464f5733ffa07a3164d656f18533caace9d0638596721355d73256a410d691" +checksum = "1e8535915cfa75547e559d8c68e8139909a4aeee076831e4ef7fc59d8172c4d6" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index 7cb37b50..892f56fe 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,7 +28,7 @@ fontdue = { version = "0.9" } image = { version = "0.25", default-features = false, features = ["png", "jpeg", "gif", "webp", "bmp", "tiff"] } # System integration -arboard = { version = "3.6", default-features = false } +arboard = { version = "3.6", default-features = false, features = ["wayland-data-control"] } notify-rust = { version = "4.17" } # Error handling & logging diff --git a/src/cli.rs b/src/cli.rs index 84e1da8f..bc36b467 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -32,7 +32,7 @@ pub struct Args { pub notify: bool, /// Disable copying the result to the clipboard. - #[arg(long = "no-clipboard")] + #[arg(long)] pub no_clipboard: bool, /// Path to .env file. Overrides default config path. @@ -40,7 +40,7 @@ pub struct Args { pub env_file: Option, /// Max concurrent uploads. - #[arg(short, long, default_value_t = NonZeroUsize::new(4).expect("nonzero"))] + #[arg(short, long, default_value_t = const { NonZeroUsize::new(4).expect("nonzero") })] pub jobs: NonZeroUsize, /// Increase verbosity (-v for info, -vv for debug). diff --git a/src/image.rs b/src/image.rs index 56928bdb..448a49d2 100644 --- a/src/image.rs +++ b/src/image.rs @@ -18,7 +18,7 @@ const FONT_DATA: &[u8] = include_bytes!("fonts/DejaVuSerif.ttf"); pub struct Font(FdFont); /// Detect image format from raw bytes. -pub fn get_image_ext(data: &[u8]) -> Result { +pub fn detect_format(data: &[u8]) -> Result { image::guess_format(data).context("unable to detect image format") } @@ -32,7 +32,7 @@ pub fn get_font() -> Font { /// Generate a JPEG thumbnail with a text caption showing dimensions, format, and file size. pub fn make_thumbnail(data: &[u8], font: &Font) -> Result> { - let format = get_image_ext(data)?; + let format = detect_format(data)?; let img = image::load_from_memory(data).context("failed to decode image")?; let (orig_w, orig_h) = (img.width(), img.height()); @@ -132,15 +132,15 @@ mod tests { use super::*; #[test] - fn test_get_image_ext_png() { + fn test_detect_format_png() { let data = create_test_png(); - let fmt = get_image_ext(&data).unwrap(); + let fmt = detect_format(&data).unwrap(); assert_eq!(fmt, ImageFormat::Png); } #[test] - fn test_get_image_ext_invalid() { - let result = get_image_ext(b"not an image"); + fn test_detect_format_invalid() { + let result = detect_format(b"not an image"); assert!(result.is_err()); } @@ -151,7 +151,7 @@ mod tests { let thumb = make_thumbnail(&data, &font).unwrap(); assert!(!thumb.is_empty()); - let fmt = get_image_ext(&thumb).unwrap(); + let fmt = detect_format(&thumb).unwrap(); assert_eq!(fmt, ImageFormat::Jpeg); } diff --git a/src/main.rs b/src/main.rs index 6a138976..aa483f26 100644 --- a/src/main.rs +++ b/src/main.rs @@ -27,14 +27,9 @@ async fn main() -> ExitCode { let args = Args::parse(); init_logging(args.verbose); - let default_env_path = util::get_config_path(); - let env_path = args.env_file.as_ref().or(default_env_path.as_ref()); - - if let Some(path) = env_path - && path.exists() - && let Err(e) = dotenvy::from_path(path) - { - error!("failed to load env file {}: {e}", path.display()); + if let Err(e) = load_env(&args) { + error!("{e:#}"); + return ExitCode::FAILURE; } match run(&args).await { @@ -46,6 +41,21 @@ async fn main() -> ExitCode { } } +/// Load env vars. +fn load_env(args: &Args) -> Result<()> { + if let Some(path) = &args.env_file { + return dotenvy::from_path(path) + .with_context(|| format!("failed to load env file {}", path.display())); + } + + if let Some(path) = util::get_config_path().filter(|p| p.exists()) + && let Err(e) = dotenvy::from_path(&path) + { + error!("failed to load env file {}: {e}", path.display()); + } + Ok(()) +} + /// Upload a single image (and its thumbnail if requested). async fn upload_single( client: &reqwest::Client, diff --git a/src/upload/beeimg.rs b/src/upload/beeimg.rs index 0a98565b..038cd5e5 100644 --- a/src/upload/beeimg.rs +++ b/src/upload/beeimg.rs @@ -4,7 +4,7 @@ use reqwest::multipart::{Form, Part}; use serde::Deserialize; use super::parse_json; -use crate::image::get_image_ext; +use crate::image::detect_format; pub const API_URL: &str = "https://beeimg.com/api/upload/file/json/"; @@ -22,7 +22,7 @@ struct Files { /// /// No authentication required. pub async fn upload(client: &Client, data: Vec, url: &str) -> Result { - let ext = get_image_ext(&data)?; + let ext = detect_format(&data)?; let name = format!("img.{}", ext.extensions_str()[0]); let content_type = ext.to_mime_type(); diff --git a/src/upload/cloudinary.rs b/src/upload/cloudinary.rs index 75dcae8c..1a777c6b 100644 --- a/src/upload/cloudinary.rs +++ b/src/upload/cloudinary.rs @@ -4,7 +4,7 @@ use reqwest::multipart::{Form, Part}; use serde::Deserialize; use super::parse_json; -use crate::image::get_image_ext; +use crate::image::detect_format; pub const API_URL: &str = "https://api.cloudinary.com/v1_1"; @@ -23,7 +23,7 @@ pub async fn upload( api_key: &str, api_secret: &str, ) -> Result { - let ext = get_image_ext(&data)?; + let ext = detect_format(&data)?; let ext_str = ext.extensions_str()[0]; let form = Form::new().part( diff --git a/src/upload/fastpic.rs b/src/upload/fastpic.rs index 51d56738..b1fb8609 100644 --- a/src/upload/fastpic.rs +++ b/src/upload/fastpic.rs @@ -1,4 +1,4 @@ -use anyhow::{Context, Result}; +use anyhow::{Context, Result, bail}; use reqwest::Client; use reqwest::multipart::{Form, Part}; use tracing::debug; @@ -26,10 +26,15 @@ pub async fn upload(client: &Client, data: Vec, url: &str) -> Result let body = response_text(resp, "fastpic").await?; - extract_tag(&body, "imagepath").ok_or_else(|| { - debug!("Response text:\n{body}"); - anyhow::anyhow!("image link not found in fastpic response") - }) + if let Some(link) = extract_tag(&body, "imagepath") { + return Ok(link); + } + + debug!("Response text:\n{body}"); + match extract_tag(&body, "error") { + Some(e) => bail!("fastpic error: {e}"), + None => bail!("image link not found in fastpic response"), + } } /// Extract trimmed text content of an XML tag. @@ -85,8 +90,9 @@ mod tests { .await .unwrap_err(); assert!( - err.to_string().contains("image link not found"), - "expected image link not found error, got: {err}" + err.to_string() + .contains("Sorry, uploading are not allowed."), + "expected server error message, got: {err}" ); } } diff --git a/src/upload/gofile.rs b/src/upload/gofile.rs index 03485ec5..c0c8346c 100644 --- a/src/upload/gofile.rs +++ b/src/upload/gofile.rs @@ -4,7 +4,7 @@ use reqwest::multipart::{Form, Part}; use serde::Deserialize; use super::parse_json; -use crate::image::get_image_ext; +use crate::image::detect_format; pub const API_URL: &str = "https://api.gofile.io/servers"; @@ -64,7 +64,7 @@ async fn get_server(client: &Client, servers_url: &str) -> Result { } async fn upload_to(client: &Client, data: Vec, upload_url: &str) -> Result { - let ext = get_image_ext(&data)?; + let ext = detect_format(&data)?; let ext_str = ext.extensions_str()[0]; let form = Form::new().part( diff --git a/src/upload/imagekit.rs b/src/upload/imagekit.rs index 6a0bbb28..0bd5da5a 100644 --- a/src/upload/imagekit.rs +++ b/src/upload/imagekit.rs @@ -4,7 +4,7 @@ use reqwest::multipart::{Form, Part}; use serde::Deserialize; use super::parse_json; -use crate::image::get_image_ext; +use crate::image::detect_format; pub const API_URL: &str = "https://upload.imagekit.io/api/v1/files/upload"; @@ -17,7 +17,7 @@ struct Response { /// /// Requires API key. pub async fn upload(client: &Client, data: Vec, url: &str, key: &str) -> Result { - let ext = get_image_ext(&data)?; + let ext = detect_format(&data)?; let ext_str = ext.extensions_str()[0]; let filename = format!("img.{ext_str}"); diff --git a/src/upload/imgbox.rs b/src/upload/imgbox.rs index 85433253..5b159e9a 100644 --- a/src/upload/imgbox.rs +++ b/src/upload/imgbox.rs @@ -1,11 +1,11 @@ -use anyhow::{Context, Result}; +use anyhow::{Context, Result, bail}; use reqwest::Client; use reqwest::multipart::{Form, Part}; use serde::Deserialize; use tracing::debug; use super::{parse_json, response_text}; -use crate::TIMEOUT; +use crate::{TIMEOUT, USER_AGENT}; pub const API_URL: &str = "https://imgbox.com"; @@ -33,6 +33,7 @@ struct UploadResponse { /// A local cookie-aware client maintains the session across the three steps. pub async fn upload(_client: &Client, data: Vec, url: &str) -> Result { let client = reqwest::Client::builder() + .user_agent(USER_AGENT) .cookie_store(true) .timeout(TIMEOUT) .build() @@ -96,7 +97,7 @@ fn extract_csrf_token(html: &str) -> Result { } } debug!("HTML:\n{html}"); - anyhow::bail!("csrf token not found in imgbox page") + bail!("csrf token not found in imgbox page") } async fn fetch_token(client: &Client, base_url: &str, csrf_token: &str) -> Result { diff --git a/src/upload/imgchest.rs b/src/upload/imgchest.rs index 489eb99e..f30ee4a9 100644 --- a/src/upload/imgchest.rs +++ b/src/upload/imgchest.rs @@ -4,7 +4,7 @@ use reqwest::multipart::{Form, Part}; use serde::Deserialize; use super::parse_json; -use crate::image::get_image_ext; +use crate::image::detect_format; pub const API_URL: &str = "https://api.imgchest.com/v1/post"; @@ -27,7 +27,7 @@ struct Image { /// /// Requires API key. pub async fn upload(client: &Client, data: Vec, url: &str, key: &str) -> Result { - let ext = get_image_ext(&data)?; + let ext = detect_format(&data)?; let ext_str = ext.extensions_str()[0]; let filename = format!("img.{ext_str}"); diff --git a/src/upload/imghippo.rs b/src/upload/imghippo.rs index c0d4f671..ba3239a1 100644 --- a/src/upload/imghippo.rs +++ b/src/upload/imghippo.rs @@ -4,7 +4,7 @@ use reqwest::multipart::{Form, Part}; use serde::Deserialize; use super::parse_json; -use crate::image::get_image_ext; +use crate::image::detect_format; pub const API_URL: &str = "https://api.imghippo.com/v1/upload"; @@ -22,7 +22,7 @@ struct Data { /// /// Requires API key. pub async fn upload(client: &Client, data: Vec, url: &str, key: &str) -> Result { - let ext = get_image_ext(&data)?; + let ext = detect_format(&data)?; let ext_str = ext.extensions_str()[0]; let form = Form::new().text("api_key", key.to_owned()).part( diff --git a/src/upload/mod.rs b/src/upload/mod.rs index 0ac79f96..2753c79d 100644 --- a/src/upload/mod.rs +++ b/src/upload/mod.rs @@ -52,7 +52,7 @@ pub(crate) async fn response_text(resp: reqwest::Response, provider: &str) -> Re .await .with_context(|| format!("failed to read {provider} response"))?; - anyhow::ensure!(status.is_success(), "{provider} returned {status}: {body}"); + ensure!(status.is_success(), "{provider} returned {status}: {body}"); Ok(body) } @@ -68,9 +68,9 @@ pub enum Hosting { Imageban, Imagekit, Imgbb, - Imghippo, Imgbox, Imgchest, + Imghippo, Imgur, Lensdump, Pixeldrain, diff --git a/src/upload/pixhost.rs b/src/upload/pixhost.rs index 5e9ec7a9..122dce28 100644 --- a/src/upload/pixhost.rs +++ b/src/upload/pixhost.rs @@ -1,4 +1,4 @@ -use anyhow::{Context, Result}; +use anyhow::{Context, Result, bail}; use reqwest::Client; use reqwest::multipart::{Form, Part}; use serde::Deserialize; @@ -61,7 +61,7 @@ async fn extract_direct_link(client: &Client, show_url: &str) -> Result } debug!("Response text:\n{page}"); - anyhow::bail!("direct link not found in pixhost show page") + bail!("direct link not found in pixhost show page") } #[cfg(test)] diff --git a/src/upload/postimages.rs b/src/upload/postimages.rs index 697ba3d8..d4eac2c3 100644 --- a/src/upload/postimages.rs +++ b/src/upload/postimages.rs @@ -1,4 +1,4 @@ -use anyhow::{Context, Result}; +use anyhow::{Context, Result, anyhow}; use base64::Engine as _; use reqwest::Client; use tracing::debug; @@ -40,7 +40,7 @@ fn extract_hotlink(xml: &str) -> Result { .inspect(|url| debug!("postimages hotlink: {url}")) .ok_or_else(|| { debug!("Response text:\n{xml}"); - anyhow::anyhow!("hotlink not found in postimages response") + anyhow!("hotlink not found in postimages response") }) } diff --git a/src/upload/sxcu.rs b/src/upload/sxcu.rs index a40eff2d..eb5af862 100644 --- a/src/upload/sxcu.rs +++ b/src/upload/sxcu.rs @@ -4,7 +4,7 @@ use reqwest::multipart::{Form, Part}; use serde::Deserialize; use super::parse_json; -use crate::image::get_image_ext; +use crate::image::detect_format; pub const API_URL: &str = "https://sxcu.net/api/files/create"; @@ -17,7 +17,7 @@ struct Response { /// /// No authentication required. pub async fn upload(client: &Client, data: Vec, url: &str) -> Result { - let ext = get_image_ext(&data)?; + let ext = detect_format(&data)?; let ext_str = ext.extensions_str()[0]; let form = Form::new().part("file", Part::bytes(data).file_name("image")); diff --git a/src/upload/tixte.rs b/src/upload/tixte.rs index f93e1bbf..a30c9c94 100644 --- a/src/upload/tixte.rs +++ b/src/upload/tixte.rs @@ -4,7 +4,7 @@ use reqwest::multipart::{Form, Part}; use serde::Deserialize; use super::parse_json; -use crate::image::get_image_ext; +use crate::image::detect_format; pub const API_URL: &str = "https://api.tixte.com/v1/upload"; @@ -22,7 +22,7 @@ struct Data { /// /// Requires API key. pub async fn upload(client: &Client, data: Vec, url: &str, key: &str) -> Result { - let ext = get_image_ext(&data)?; + let ext = detect_format(&data)?; let ext_str = ext.extensions_str()[0]; let filename = format!("img.{ext_str}"); diff --git a/src/upload/uplio.rs b/src/upload/uplio.rs index de3f65aa..6750f369 100644 --- a/src/upload/uplio.rs +++ b/src/upload/uplio.rs @@ -1,10 +1,10 @@ -use anyhow::{Context, Result}; +use anyhow::{Context, Result, bail}; use reqwest::Client; use reqwest::multipart::{Form, Part}; use tracing::debug; use super::response_text; -use crate::image::get_image_ext; +use crate::image::detect_format; pub const API_URL: &str = "https://upl.io"; @@ -12,7 +12,7 @@ pub const API_URL: &str = "https://upl.io"; /// /// Requires API key. pub async fn upload(client: &Client, data: Vec, url: &str, key: &str) -> Result { - let ext = get_image_ext(&data)?; + let ext = detect_format(&data)?; let ext_str = ext.extensions_str()[0]; let filename = format!("img.{ext_str}"); @@ -32,7 +32,7 @@ pub async fn upload(client: &Client, data: Vec, url: &str, key: &str) -> Res // Convert "https://upl.io/UID[.ext]" to "https://upl.io/i/UID.ext" let Some((host, uid)) = text.rsplit_once('/') else { debug!("Response text:\n{text}"); - anyhow::bail!("unexpected uplio response format"); + bail!("unexpected uplio response format"); }; let uid_base = uid.rsplit_once('.').map_or(uid, |(base, _)| base); Ok(format!("{host}/i/{uid_base}.{ext_str}")) diff --git a/src/upload/uploadcare.rs b/src/upload/uploadcare.rs index b682bdf6..88bb9520 100644 --- a/src/upload/uploadcare.rs +++ b/src/upload/uploadcare.rs @@ -4,7 +4,7 @@ use reqwest::multipart::{Form, Part}; use serde::Deserialize; use super::parse_json; -use crate::image::get_image_ext; +use crate::image::detect_format; pub const API_URL: &str = "https://upload.uploadcare.com/base/"; @@ -17,7 +17,7 @@ struct Response { /// /// Requires API key (public key). pub async fn upload(client: &Client, data: Vec, url: &str, key: &str) -> Result { - let ext = get_image_ext(&data)?; + let ext = detect_format(&data)?; let ext_str = ext.extensions_str()[0]; let form = Form::new() diff --git a/src/upload/vgy.rs b/src/upload/vgy.rs index a3ad05c5..cdb2ed28 100644 --- a/src/upload/vgy.rs +++ b/src/upload/vgy.rs @@ -4,7 +4,7 @@ use reqwest::multipart::{Form, Part}; use serde::Deserialize; use super::parse_json; -use crate::image::get_image_ext; +use crate::image::detect_format; pub const API_URL: &str = "https://vgy.me/upload"; @@ -17,7 +17,7 @@ struct Response { /// /// Requires API key. pub async fn upload(client: &Client, data: Vec, url: &str, key: &str) -> Result { - let ext = get_image_ext(&data)?; + let ext = detect_format(&data)?; let ext_str = ext.extensions_str()[0]; let filename = format!("img.{ext_str}"); diff --git a/tests/cli_test.rs b/tests/cli_test.rs index fff07290..4ee882c8 100644 --- a/tests/cli_test.rs +++ b/tests/cli_test.rs @@ -40,6 +40,17 @@ fn test_nonexistent_file_fails() { assert!(!output.status.success()); } +#[test] +fn test_missing_env_file_fails() { + let output = imgup() + .args(["--env-file", "/tmp/nonexistent_env_12345", "dummy.png"]) + .output() + .expect("failed to run"); + assert!(!output.status.success()); + let stderr = String::from_utf8_lossy(&output.stderr); + assert!(stderr.contains("env file")); +} + #[test] fn test_hosting_values() { // Verify invalid hosting value is rejected