From 1b3b773f80a1299c9ffa2178a8f237d8cb4fb6f2 Mon Sep 17 00:00:00 2001 From: jane400 Date: Wed, 22 Jan 2025 01:53:32 +0100 Subject: [PATCH] aaaa: commit worktree Features: - meson - (almost) all strings are localized - relm4_icons no longer used - default.nix updated but not tested - updated dependencies - unused for now: paket-utils, paket/locker - build-aux/checks.sh now enforced - fixups in libpaket/tracking parsing - libpaket: RegTokenDecodeError instead of generic DecodeError - moving dependency versions to workspace Cargo.toml - default.nix adjusted - README.md added - dependency: relm4 version pinned to include fixes --- Cargo.lock | 1543 +++++++++-------- Cargo.toml | 70 +- README.md | 29 + assets/copy-symbolic.svg | 2 + .../four-arrows-pointing-outward-symbolic.svg | 2 + assets/loupe-large-symbolic.svg | 4 + assets/mail-symbolic.svg | 2 + assets/minus-symbolic.svg | 2 + assets/package-x-generic-symbolic.svg | 8 + assets/parcel-locker-symbolic.svg | 15 + assets/person-symbolic.svg | 39 + assets/plus-symbolic.svg | 2 + assets/qr-code-scanner-symbolic.svg | 2 + build-aux/checks.sh | 459 +++++ build-aux/dist-vendor.sh | 13 + build.rs | 115 ++ default.nix | 53 +- icons.toml | 3 - libpaket/Cargo.toml | 33 +- libpaket/README.md | 1 + libpaket/src/advices/mod.rs | 4 +- libpaket/src/advices/www.rs | 39 +- libpaket/src/constants.rs | 9 +- libpaket/src/lib.rs | 7 +- libpaket/src/locker/command.rs | 88 +- libpaket/src/locker/crypto.rs | 11 +- libpaket/src/locker/register_base.rs | 1 - libpaket/src/locker/register_regtoken.rs | 48 +- libpaket/src/locker/regtoken.rs | 32 +- libpaket/src/locker/types.rs | 13 +- libpaket/src/login/constants.rs | 15 +- libpaket/src/login/openid_token.rs | 7 +- libpaket/src/login/utils.rs | 2 +- libpaket/src/stammdaten.rs | 1 - libpaket/src/tracking.rs | 18 +- libpaket/src/utils.rs | 10 +- meson.build | 83 + meson_options.txt | 12 + paket-utils/Cargo.toml | 9 + paket-utils/src/lib.rs | 65 + paket.gresource.xml | 17 + paket/Cargo.toml | 25 - paket/src/constants.rs | 1 - paket/src/lib.rs | 17 - po/POTFILES.in | 10 + po/meson.build | 15 + {paket/src => src}/account.rs | 19 +- {paket/src => src}/advice.rs | 52 +- {paket/src => src}/advices.rs | 79 +- paket/src/bin/paket.rs => src/app.rs | 38 +- src/bin/locker_scanner.rs | 81 + src/bin/paket.rs | 9 + src/bin/qr_test.rs | 83 + src/i18n.rs | 215 +++ {paket/src => src}/keyring.rs | 8 +- src/lib.rs | 47 + src/lockers.rs | 273 +++ {paket/src => src}/login.rs | 14 +- src/meson.build | 49 + {paket/src => src}/packstation.rs | 33 +- {paket/src => src}/ready.rs | 37 +- {paket/src => src}/scanner.rs | 58 +- {paket/src => src}/tracking.rs | 276 ++- src/utils.rs | 46 + 64 files changed, 3138 insertions(+), 1235 deletions(-) create mode 100644 README.md create mode 100644 assets/copy-symbolic.svg create mode 100644 assets/four-arrows-pointing-outward-symbolic.svg create mode 100644 assets/loupe-large-symbolic.svg create mode 100644 assets/mail-symbolic.svg create mode 100644 assets/minus-symbolic.svg create mode 100644 assets/package-x-generic-symbolic.svg create mode 100644 assets/parcel-locker-symbolic.svg create mode 100644 assets/person-symbolic.svg create mode 100644 assets/plus-symbolic.svg create mode 100644 assets/qr-code-scanner-symbolic.svg create mode 100755 build-aux/checks.sh create mode 100755 build-aux/dist-vendor.sh create mode 100644 build.rs delete mode 100644 icons.toml create mode 100644 meson.build create mode 100644 meson_options.txt create mode 100644 paket-utils/Cargo.toml create mode 100644 paket-utils/src/lib.rs create mode 100644 paket.gresource.xml delete mode 100644 paket/Cargo.toml delete mode 100644 paket/src/constants.rs delete mode 100644 paket/src/lib.rs create mode 100644 po/POTFILES.in create mode 100644 po/meson.build rename {paket/src => src}/account.rs (93%) rename {paket/src => src}/advice.rs (67%) rename {paket/src => src}/advices.rs (79%) rename paket/src/bin/paket.rs => src/app.rs (85%) create mode 100644 src/bin/locker_scanner.rs create mode 100644 src/bin/paket.rs create mode 100644 src/bin/qr_test.rs create mode 100644 src/i18n.rs rename {paket/src => src}/keyring.rs (96%) create mode 100644 src/lib.rs create mode 100644 src/lockers.rs rename {paket/src => src}/login.rs (97%) create mode 100644 src/meson.build rename {paket/src => src}/packstation.rs (87%) rename {paket/src => src}/ready.rs (89%) rename {paket/src => src}/scanner.rs (80%) rename {paket/src => src}/tracking.rs (57%) create mode 100644 src/utils.rs diff --git a/Cargo.lock b/Cargo.lock index a10171c..452767c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,12 +1,12 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "addr2line" -version = "0.24.1" +version = "0.24.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5fb1d8e4442bd405fdfd1dacb42792696b0cf9cb15882e5d097b742a676d375" +checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" dependencies = [ "gimli", ] @@ -54,47 +54,47 @@ dependencies = [ ] [[package]] -name = "android-tzdata" -version = "0.1.1" +name = "aho-corasick" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" - -[[package]] -name = "android_system_properties" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" dependencies = [ - "libc", + "memchr", ] [[package]] -name = "anyhow" -version = "1.0.89" +name = "allocator-api2" +version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86fdf8605db99b54d3cd748a44c6d04df638eb5dafb219b135d0149bd0db01f6" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" + +[[package]] +name = "anyhow" +version = "1.0.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04" [[package]] name = "aperture" -version = "0.7.0" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c3771ab803f9ce7c7b3e624ccc0abc6a920bc23340d045cd2b8909ded9fb4bd" +checksum = "e24b73e1afbdcb01ffaf0ebedf3cfbb48f401943c0e1d0e694e7252e94d94f1e" dependencies = [ "futures-channel", - "gst-plugin-gtk4", "gstreamer", + "gstreamer-base", "gstreamer-pbutils", "gstreamer-video", "gtk4", "log", - "once_cell", + "rqrr", ] [[package]] name = "async-broadcast" -version = "0.7.1" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20cd0e2e25ea8e5f7e9df04578dc6cf5c83577fd09b1a46aaf5c85e1c33f2a7e" +checksum = "435a87a52755b8f27fcf321ac4f04b2802e337c8c4872923137471ec39c37532" dependencies = [ "event-listener", "event-listener-strategy", @@ -116,9 +116,9 @@ dependencies = [ [[package]] name = "async-compression" -version = "0.4.13" +version = "0.4.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e614738943d3f68c628ae3dbce7c3daffb196665f82f8c8ea6b65de73c79429" +checksum = "df895a515f70646414f4b45c0b79082783b80552b373a68283012928df56f522" dependencies = [ "flate2", "futures-core", @@ -135,7 +135,7 @@ checksum = "30ca9a001c1e8ba5149f91a74362376cc6bc5b919d92d988668657bd570bdcec" dependencies = [ "async-task", "concurrent-queue", - "fastrand 2.1.1", + "fastrand 2.3.0", "futures-lite", "slab", ] @@ -153,9 +153,9 @@ dependencies = [ [[package]] name = "async-io" -version = "2.3.4" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "444b0228950ee6501b3568d3c93bf1176a1fdbc3b758dcd9475046d30f4dc7e8" +checksum = "43a2b323ccce0a1d90b449fd71f2a06ca7faa7c54c2751f06c9bd851fc061059" dependencies = [ "async-lock", "cfg-if", @@ -248,9 +248,9 @@ checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" [[package]] name = "async-trait" -version = "0.1.83" +version = "0.1.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd" +checksum = "3f934833b4b7233644e5848f235df3f57ed8c80f1528a26c3dfa13d2147fa056" dependencies = [ "proc-macro2", "quote", @@ -310,9 +310,15 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.6.0" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" +checksum = "8f68f53c83ab957f72c32642f3868eec03eb974d1fb82e453128456482613d36" + +[[package]] +name = "block" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" [[package]] name = "block-buffer" @@ -353,9 +359,9 @@ checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" [[package]] name = "bytemuck" -version = "1.18.0" +version = "1.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94bbb0ad554ad961ddc5da507a12a29b14e4ae5bda06b19f575a3e6079d2e2ae" +checksum = "ef657dfab802224e671f5818e9a4935f9b1957ed18e58292690cc39e7a4092a3" [[package]] name = "byteorder" @@ -365,17 +371,17 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.7.2" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "428d9aa8fbc0670b7b8d6030a7fadd0f86151cae55e4dbbece15f3780a3dfaf3" +checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b" [[package]] name = "cairo-rs" -version = "0.20.1" +version = "0.20.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8a0ea147c94108c9613235388f540e4d14c327f7081c9e471fc8ee8a2533e69" +checksum = "ae50b5510d86cf96ac2370e66d8dc960882f3df179d6a5a1e52bd94a1416c0f7" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "cairo-sys-rs", "glib", "libc", @@ -383,9 +389,9 @@ dependencies = [ [[package]] name = "cairo-sys-rs" -version = "0.20.0" +version = "0.20.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "428290f914b9b86089f60f5d8a9f6e440508e1bcff23b25afd51502b0a2da88f" +checksum = "f18b6bb8e43c7eb0f2aac7976afe0c61b6f5fc2ab7bc4c139537ea56c92290df" dependencies = [ "glib-sys", "libc", @@ -403,9 +409,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.1.24" +version = "1.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "812acba72f0a070b003d3697490d2b55b837230ae7c6c6497f05cc2ddbb8d938" +checksum = "13208fcbb66eaeffe09b99fffbe1af420f00a7b35aa99ad683dfc1aa76145229" dependencies = [ "jobserver", "libc", @@ -414,9 +420,9 @@ dependencies = [ [[package]] name = "cfg-expr" -version = "0.17.0" +version = "0.17.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0890061c4d3223e7267f3bad2ec40b997d64faac1c2815a4a9d95018e2b9e9c" +checksum = "8d4ba6e40bd1184518716a6e1a781bf9160e286d219ccdb8ab2612e74cfe4789" dependencies = [ "smallvec", "target-lexicon", @@ -434,18 +440,6 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" -[[package]] -name = "chrono" -version = "0.4.38" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" -dependencies = [ - "android-tzdata", - "iana-time-zone", - "num-traits", - "windows-targets", -] - [[package]] name = "cipher" version = "0.4.4" @@ -485,12 +479,13 @@ dependencies = [ [[package]] name = "cookie_store" -version = "0.21.0" +version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4934e6b7e8419148b6ef56950d277af8561060b56afd59e2aadf98b59fce6baa" +checksum = "2eac901828f88a5241ee0600950ab981148a18f2f756900ffba1b125ca6a3ef9" dependencies = [ "cookie", - "idna 0.5.0", + "document-features", + "idna", "log", "publicsuffix", "serde", @@ -518,9 +513,9 @@ checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] name = "cpufeatures" -version = "0.2.14" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0" +checksum = "16b80225097f2e5ae4e7179dd2266824648f3e2f49d9134d584b76389d31c4c3" dependencies = [ "libc", ] @@ -536,9 +531,9 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.20" +version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" [[package]] name = "crypto-common" @@ -617,6 +612,17 @@ dependencies = [ "subtle", ] +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "dlib" version = "0.5.2" @@ -626,6 +632,15 @@ dependencies = [ "libloading", ] +[[package]] +name = "document-features" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb6969eaabd2421f8a2775cfd2471a2b634372b4a25d41e3bd647b79912850a0" +dependencies = [ + "litrs", +] + [[package]] name = "dunce" version = "1.0.5" @@ -664,9 +679,9 @@ checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" [[package]] name = "encoding_rs" -version = "0.8.34" +version = "0.8.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59" +checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" dependencies = [ "cfg-if", ] @@ -679,9 +694,9 @@ checksum = "a3d8a32ae18130a3c84dd492d4215c3d913c3b07c6b63c2eb3eb7ff1101ab7bf" [[package]] name = "enumflags2" -version = "0.7.10" +version = "0.7.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d232db7f5956f3f14313dc2f87985c58bd2c695ce124c8cdd984e08e15ac133d" +checksum = "ba2f4b465f5318854c6f8dd686ede6c0a9dc67d4b1ac241cf0eb51521a309147" dependencies = [ "enumflags2_derive", "serde", @@ -689,9 +704,9 @@ dependencies = [ [[package]] name = "enumflags2_derive" -version = "0.7.10" +version = "0.7.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de0d48a183585823424a4ce1aa132d174a6a81bd540895822eb4c8373a8e49e8" +checksum = "fc4caf64a58d7a6d65ab00639b046ff54399a39f5f2554728895ace4b297cd79" dependencies = [ "proc-macro2", "quote", @@ -700,18 +715,18 @@ dependencies = [ [[package]] name = "env_filter" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f2c92ceda6ceec50f43169f9ee8424fe2db276791afde7b2cd8bc084cb376ab" +checksum = "186e05a59d4c50738528153b83b0b0194d3a29507dfec16eccd4b342903397d0" dependencies = [ "log", ] [[package]] name = "env_logger" -version = "0.11.5" +version = "0.11.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13fa619b91fb2381732789fc5de83b45675e882f66623b7d8cb4f643017018d" +checksum = "dcaee3d8e3cfc3fd92428d477bc97fc29ec8716d180c0d74c643bb26166660e0" dependencies = [ "env_filter", "humantime", @@ -726,19 +741,19 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "errno" -version = "0.3.9" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] name = "event-listener" -version = "5.3.1" +version = "5.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6032be9bd27023a771701cc49f9f053c751055f71efb2e0ae5c15809093675ba" +checksum = "3492acde4c3fc54c845eaab3eed8bd00c7a7d881f78bfc801e43a93dec1331ae" dependencies = [ "concurrent-queue", "parking", @@ -747,9 +762,9 @@ dependencies = [ [[package]] name = "event-listener-strategy" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f214dc438f977e6d4e3500aaa277f5ad94ca83fbbd9b1a15713ce2344ccc5a1" +checksum = "3c3e4e0dd3673c1139bf041f3008816d9cf2946bbfac2945c09e523b8d7b05b2" dependencies = [ "event-listener", "pin-project-lite", @@ -766,9 +781,9 @@ dependencies = [ [[package]] name = "fastrand" -version = "2.1.1" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" [[package]] name = "fiat-crypto" @@ -788,9 +803,9 @@ dependencies = [ [[package]] name = "flate2" -version = "1.0.34" +version = "1.0.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1b589b4dc103969ad3cf85c950899926ec64300a1a46d76c03a6072957036f0" +checksum = "c936bfdafb507ebbf50b8074c54fa31c5be9a1e7e5f467dd659697041407d07c" dependencies = [ "crc32fast", "miniz_oxide", @@ -798,9 +813,9 @@ dependencies = [ [[package]] name = "flume" -version = "0.11.0" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55ac459de2512911e4b674ce33cf20befaba382d05b62b008afc1c8b57cbf181" +checksum = "da0e4dd2a88388a1f4ccc7c9ce104604dab68d9f408dc34cd45823d5a9069095" dependencies = [ "futures-core", "futures-sink", @@ -814,6 +829,12 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foldhash" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0d2fde1f7b3d48b8395d5f2de76c18a528bd6a9cdde438df747bfcba3e05d6f" + [[package]] name = "foreign-types" version = "0.3.2" @@ -873,9 +894,9 @@ checksum = "6c2141d6d6c8512188a7891b4b01590a45f6dac67afb4f255c4124dbb86d4eaa" [[package]] name = "futures" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" +checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" dependencies = [ "futures-channel", "futures-core", @@ -888,9 +909,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" dependencies = [ "futures-core", "futures-sink", @@ -898,15 +919,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" [[package]] name = "futures-executor" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" +checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" dependencies = [ "futures-core", "futures-task", @@ -915,17 +936,17 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" [[package]] name = "futures-lite" -version = "2.3.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52527eb5074e35e9339c6b4e8d12600c7128b68fb25dcb9fa9dec18f7c25f3a5" +checksum = "f5edaec856126859abb19ed65f39e90fea3a9574b9707f13539acf4abf7eb532" dependencies = [ - "fastrand 2.1.1", + "fastrand 2.3.0", "futures-core", "futures-io", "parking", @@ -934,9 +955,9 @@ dependencies = [ [[package]] name = "futures-macro" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", @@ -945,15 +966,15 @@ dependencies = [ [[package]] name = "futures-sink" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" [[package]] name = "futures-task" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" [[package]] name = "futures-timer" @@ -963,9 +984,9 @@ checksum = "f288b0a4f20f9a56b5d1da57e2227c661b7b16168e2f72365f57b63326e29b24" [[package]] name = "futures-util" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" dependencies = [ "futures-channel", "futures-core", @@ -980,10 +1001,38 @@ dependencies = [ ] [[package]] -name = "gdk-pixbuf" -version = "0.20.4" +name = "g2gen" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4c29071a9e92337d8270a85cb0510cda4ac478be26d09ad027cc1d081911b19" +checksum = "dc3e32f911a41e073b8492473c3595a043e1369ab319a2dbf8c89b1fea06457c" +dependencies = [ + "g2poly", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "g2p" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a9afa6efed9af3a5a68ba066429c1497c299d4eafbd948fe630df47a8f2d29f" +dependencies = [ + "g2gen", + "g2poly", +] + +[[package]] +name = "g2poly" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fd8b261ccf00df8c5cc60c082bb7d7aa64c33a433cfcc091ca244326c924b2c" + +[[package]] +name = "gdk-pixbuf" +version = "0.20.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6efc7705f7863d37b12ad6974cbb310d35d054f5108cdc1e69037742f573c4c" dependencies = [ "gdk-pixbuf-sys", "gio", @@ -993,9 +1042,9 @@ dependencies = [ [[package]] name = "gdk-pixbuf-sys" -version = "0.20.4" +version = "0.20.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "687343b059b91df5f3fbd87b4307038fa9e647fcc0461d0d3f93e94fee20bf3d" +checksum = "67f2587c9202bf997476bbba6aaed4f78a11538a2567df002a5f57f5331d0b5c" dependencies = [ "gio-sys", "glib-sys", @@ -1006,9 +1055,9 @@ dependencies = [ [[package]] name = "gdk4" -version = "0.9.2" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c121aeeb0cf7545877ae615dac6bfd088b739d8abee4d55e7143b06927d16a31" +checksum = "d0196720118f880f71fe7da971eff58cc43a89c9cf73f46076b7cb1e60889b15" dependencies = [ "cairo-rs", "gdk-pixbuf", @@ -1021,9 +1070,9 @@ dependencies = [ [[package]] name = "gdk4-sys" -version = "0.9.2" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d3c03d1ea9d5199f14f060890fde68a3b5ec5699144773d1fa6abf337bfbc9c" +checksum = "60b0e1340bd15e7a78810cf39fed9e5d85f0a8f80b1d999d384ca17dcc452b60" dependencies = [ "cairo-sys-rs", "gdk-pixbuf-sys", @@ -1036,80 +1085,6 @@ dependencies = [ "system-deps", ] -[[package]] -name = "gdk4-wayland" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e10c2aa5fc14c4abd210607febc81147ec1fb711212ef423adf073650ce9d9e9" -dependencies = [ - "gdk4", - "gdk4-wayland-sys", - "gio", - "glib", - "libc", -] - -[[package]] -name = "gdk4-wayland-sys" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bb4cdcb5008122f273926b54d0004a5b0ade76f1995671218db12304476cefc" -dependencies = [ - "glib-sys", - "libc", - "system-deps", -] - -[[package]] -name = "gdk4-win32" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "275e0a5470f8da4ec00cf1856d4ebae4cd022b9abbb5eb2dbd57f511ca35fd22" -dependencies = [ - "gdk4", - "gdk4-win32-sys", - "gio", - "glib", - "libc", -] - -[[package]] -name = "gdk4-win32-sys" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7e8c93bb66b07216f516a94c997b13c41b26fc0fda1866542e3894e508c120a" -dependencies = [ - "gdk4-sys", - "glib-sys", - "libc", - "system-deps", -] - -[[package]] -name = "gdk4-x11" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a84f16ccd1e7fad79c1613411a041ba154371d1fa68ab15cd973fe889879efa5" -dependencies = [ - "gdk4", - "gdk4-x11-sys", - "gio", - "glib", - "libc", -] - -[[package]] -name = "gdk4-x11-sys" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e91004338e548c4774ec7aa32100557ee905e6ba8114151414a5c34644790380" -dependencies = [ - "gdk4-sys", - "glib-sys", - "libc", - "system-deps", -] - [[package]] name = "generic-array" version = "0.14.7" @@ -1133,6 +1108,26 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "gettext-rs" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a44e92f7dc08430aca7ed55de161253a22276dfd69c5526e5c5e95d1f7cf338a" +dependencies = [ + "gettext-sys", + "locale_config", +] + +[[package]] +name = "gettext-sys" +version = "0.22.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb45773f5b8945f12aecd04558f545964f943dacda1b1155b3d738f5469ef661" +dependencies = [ + "cc", + "temp-dir", +] + [[package]] name = "ghash" version = "0.5.1" @@ -1145,15 +1140,15 @@ dependencies = [ [[package]] name = "gimli" -version = "0.31.0" +version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32085ea23f3234fc7846555e85283ba4de91e21016dc0455a16286d87a292d64" +checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" [[package]] name = "gio" -version = "0.20.4" +version = "0.20.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8d999e8fb09583e96080867e364bc1e701284ad206c76a5af480d63833ad43c" +checksum = "a517657589a174be9f60c667f1fec8b7ac82ed5db4ebf56cf073a3b5955d8e2e" dependencies = [ "futures-channel", "futures-core", @@ -1168,24 +1163,24 @@ dependencies = [ [[package]] name = "gio-sys" -version = "0.20.4" +version = "0.20.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f7efc368de04755344f0084104835b6bb71df2c1d41e37d863947392a894779" +checksum = "8446d9b475730ebef81802c1738d972db42fde1c5a36a627ebc4d665fc87db04" dependencies = [ "glib-sys", "gobject-sys", "libc", "system-deps", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] name = "glib" -version = "0.20.4" +version = "0.20.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adcf1ec6d3650bf9fdbc6cee242d4fcebc6f6bfd9bea5b929b6a8b7344eb85ff" +checksum = "f969edf089188d821a30cde713b6f9eb08b20c63fc2e584aba2892a7984a8cc0" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "futures-channel", "futures-core", "futures-executor", @@ -1201,10 +1196,19 @@ dependencies = [ ] [[package]] -name = "glib-macros" -version = "0.20.4" +name = "glib-build-tools" +version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6bf88f70cd5720a6197639dcabcb378dd528d0cb68cb1f45e3b358bcb841cd7" +checksum = "7029c2651d9b5d5a3eea93ec8a1995665c6d3a69ce9bf6042ad9064d134736d8" +dependencies = [ + "gio", +] + +[[package]] +name = "glib-macros" +version = "0.20.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "715601f8f02e71baef9c1f94a657a9a77c192aea6097cf9ae7e5e177cd8cde68" dependencies = [ "heck", "proc-macro-crate", @@ -1215,9 +1219,9 @@ dependencies = [ [[package]] name = "glib-sys" -version = "0.20.4" +version = "0.20.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f9eca5d88cfa6a453b00d203287c34a2b7cac3a7831779aa2bb0b3c7233752b" +checksum = "b360ff0f90d71de99095f79c526a5888c9c92fc9ee1b19da06c6f5e75f0c2a53" dependencies = [ "libc", "system-deps", @@ -1225,9 +1229,9 @@ dependencies = [ [[package]] name = "glycin" -version = "2.0.1" +version = "2.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99ceb553ed23c012e4e16ba9212b495eb458ad831eb5f892b6b44406f0966286" +checksum = "a0c0c43ba80d02ea8cd540163e7cb49eced263fe3100c91c505acf5f9399ccb5" dependencies = [ "async-fs", "async-io", @@ -1249,7 +1253,7 @@ dependencies = [ "memmap2", "nix", "static_assertions", - "thiserror", + "thiserror 1.0.69", "tracing", "yeslogic-fontconfig-sys", "zbus", @@ -1257,9 +1261,9 @@ dependencies = [ [[package]] name = "glycin-utils" -version = "2.0.0" +version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c6132ef2817b05110f3b1f98c53dbddaac74ea8366472354ab486d02442f08f" +checksum = "2d4ea3c2b8d5aa43d87d60bb7f944c58aaade41d870e4258e69e204bbcd73a09" dependencies = [ "env_logger", "gufo-common", @@ -1271,15 +1275,15 @@ dependencies = [ "paste", "rmp-serde", "serde", - "thiserror", + "thiserror 1.0.69", "zbus", ] [[package]] name = "gobject-sys" -version = "0.20.4" +version = "0.20.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4c674d2ff8478cf0ec29d2be730ed779fef54415a2fb4b565c52def62696462" +checksum = "67a56235e971a63bfd75abb13ef70064e1346388723422a68580d8a6fbac6423" dependencies = [ "glib-sys", "libc", @@ -1288,9 +1292,9 @@ dependencies = [ [[package]] name = "graphene-rs" -version = "0.20.4" +version = "0.20.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f53144c7fe78292705ff23935f1477d511366fb2f73c43d63b37be89076d2fe" +checksum = "f39d3bcd2e24fd9c2874a56f277b72c03e728de9bdc95a8d4ef4c962f10ced98" dependencies = [ "glib", "graphene-sys", @@ -1299,9 +1303,9 @@ dependencies = [ [[package]] name = "graphene-sys" -version = "0.20.4" +version = "0.20.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e741797dc5081e59877a4d72c442c72d61efdd99161a0b1c1b29b6b988934b99" +checksum = "11a68d39515bf340e879b72cecd4a25c1332557757ada6e8aba8654b4b81d23a" dependencies = [ "glib-sys", "libc", @@ -1311,9 +1315,9 @@ dependencies = [ [[package]] name = "gsk4" -version = "0.9.2" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa21a2f7c51ee1c6cc1242c2faf3aae2b7566138f182696759987bde8219e922" +checksum = "32b9188db0a6219e708b6b6e7225718e459def664023dbddb8395ca1486d8102" dependencies = [ "cairo-rs", "gdk4", @@ -1326,9 +1330,9 @@ dependencies = [ [[package]] name = "gsk4-sys" -version = "0.9.2" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f9fb607554f9f4e8829eb7ea301b0fde051e1dbfd5d16b143a8a9c2fac6c01b" +checksum = "bca10fc65d68528a548efa3d8747934adcbe7058b73695c9a7f43a25352fce14" dependencies = [ "cairo-sys-rs", "gdk4-sys", @@ -1340,45 +1344,11 @@ dependencies = [ "system-deps", ] -[[package]] -name = "gst-plugin-gtk4" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44b3fe6a92ebf2e62366d9f2d8633cc634187bb7c40fe4db05edfb809e928197" -dependencies = [ - "async-channel", - "gdk4-wayland", - "gdk4-win32", - "gdk4-x11", - "gst-plugin-version-helper", - "gstreamer", - "gstreamer-allocators", - "gstreamer-base", - "gstreamer-gl", - "gstreamer-gl-egl", - "gstreamer-gl-wayland", - "gstreamer-gl-x11", - "gstreamer-video", - "gtk4", - "once_cell", - "windows-sys 0.52.0", -] - -[[package]] -name = "gst-plugin-version-helper" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e5e874f1660252fd2ec81c602066df3633b3a6fcbe2b196f7f93c27cf069b2a" -dependencies = [ - "chrono", - "toml_edit", -] - [[package]] name = "gstreamer" -version = "0.23.2" +version = "0.23.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49ecf3bcfc2ceb82ce02437f53ff2fcaee5e7d45ae697ab64a018408749779b9" +checksum = "700cb1b2e86dda424f85eb728102a111602317e40b4dd71cf1c0dc04e0cc5d95" dependencies = [ "cfg-if", "futures-channel", @@ -1396,40 +1366,14 @@ dependencies = [ "paste", "pin-project-lite", "smallvec", - "thiserror", -] - -[[package]] -name = "gstreamer-allocators" -version = "0.23.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edb98199904472782fc426827e3ada0bd5b92b7625f97c51649ec83b2f060019" -dependencies = [ - "glib", - "gstreamer", - "gstreamer-allocators-sys", - "libc", - "once_cell", -] - -[[package]] -name = "gstreamer-allocators-sys" -version = "0.23.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "286b83db9cf0aa2322993302e2439e8f9cbe42f34b81573ecf758b024b9e0f7f" -dependencies = [ - "glib-sys", - "gobject-sys", - "gstreamer-sys", - "libc", - "system-deps", + "thiserror 2.0.11", ] [[package]] name = "gstreamer-audio" -version = "0.23.2" +version = "0.23.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36d39b07213f83055fc705a384fa32ad581776b8e5b04c86f3a419ec5dfc0f81" +checksum = "52a6009b5c9c942cab1089956a501bd63778e65a3e69310949d173e90e2cdda2" dependencies = [ "cfg-if", "glib", @@ -1443,9 +1387,9 @@ dependencies = [ [[package]] name = "gstreamer-audio-sys" -version = "0.23.2" +version = "0.23.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d84744e7ac8f8bc0cf76b7be40f2d5be12e6cf197e4c6ca9d3438109c21e2f51" +checksum = "ef70a3d80e51ef9a45749a844cb8579d4cabe5ff59cb43a65d6f3a377943262f" dependencies = [ "glib-sys", "gobject-sys", @@ -1457,9 +1401,9 @@ dependencies = [ [[package]] name = "gstreamer-base" -version = "0.23.2" +version = "0.23.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46ce7330d2995138a77192ea20961422ddee1578e1a47480acb820c43ceb0e2d" +checksum = "d152db7983f98d5950cf64e53805286548063475fb61a5e5450fba4cec05899b" dependencies = [ "atomic_refcell", "cfg-if", @@ -1471,9 +1415,9 @@ dependencies = [ [[package]] name = "gstreamer-base-sys" -version = "0.23.2" +version = "0.23.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7796e694c21c215447811c9cff694dce1fc6e02b0bbafb75cd8583b6aefe9e5f" +checksum = "d47cc2d15f2a3d5eb129e5dacbbeec9600432b706805c15dff57b6aa11b2791c" dependencies = [ "glib-sys", "gobject-sys", @@ -1482,116 +1426,11 @@ dependencies = [ "system-deps", ] -[[package]] -name = "gstreamer-gl" -version = "0.23.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee43a7573ace4dc3dd2256a4285d814452c29f6824ea286be8e5c8934ecd124f" -dependencies = [ - "glib", - "gstreamer", - "gstreamer-base", - "gstreamer-gl-sys", - "gstreamer-video", - "libc", - "once_cell", -] - -[[package]] -name = "gstreamer-gl-egl" -version = "0.23.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d52c181df435ee0dcbeea7756070bd0c422fda7f42b77a1a4542b255128cd12" -dependencies = [ - "glib", - "gstreamer", - "gstreamer-gl", - "gstreamer-gl-egl-sys", - "libc", -] - -[[package]] -name = "gstreamer-gl-egl-sys" -version = "0.23.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58d6e6f284f5bbb3b9920450fdea1ac03b63c6dae29580aeb0ad9368998b0bf5" -dependencies = [ - "glib-sys", - "gstreamer-gl-sys", - "libc", - "system-deps", -] - -[[package]] -name = "gstreamer-gl-sys" -version = "0.23.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09e17ab40526ed24a769342b3ebbd5a2c17d34944399b59e8b1a254e40356e11" -dependencies = [ - "glib-sys", - "gobject-sys", - "gstreamer-base-sys", - "gstreamer-sys", - "gstreamer-video-sys", - "libc", - "system-deps", -] - -[[package]] -name = "gstreamer-gl-wayland" -version = "0.23.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eaeed2f0d804ae3dcbe00373e6de409322cccc2bc88cad54dfbc7f72d5c78486" -dependencies = [ - "glib", - "gstreamer", - "gstreamer-gl", - "gstreamer-gl-wayland-sys", - "libc", -] - -[[package]] -name = "gstreamer-gl-wayland-sys" -version = "0.23.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0caffd81a4c82079ba30757f82c4cf665e5547d56b36cfa420f85069c7910310" -dependencies = [ - "glib-sys", - "gstreamer-gl-sys", - "libc", - "system-deps", -] - -[[package]] -name = "gstreamer-gl-x11" -version = "0.23.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3aabc9164e4e5ae04a2a9e83a2aa9a83f5bce4c51e96efe570bee9bb23ed5ab2" -dependencies = [ - "glib", - "gstreamer", - "gstreamer-gl", - "gstreamer-gl-x11-sys", - "libc", -] - -[[package]] -name = "gstreamer-gl-x11-sys" -version = "0.23.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "137438be38e0518baca8ff382f36ec8812c4417f4b7b1895c3816ff49a6f8b5b" -dependencies = [ - "glib-sys", - "gstreamer-gl-sys", - "libc", - "system-deps", -] - [[package]] name = "gstreamer-pbutils" -version = "0.23.2" +version = "0.23.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bc96a1ef43542ba68159e440adc7211e1f03824d91fd8f4e05b84f84002d33c" +checksum = "d18b3a69ff575dd9fa65c9e813815f9e83e4bab8b89bf6118adada58be343e74" dependencies = [ "glib", "gstreamer", @@ -1599,14 +1438,14 @@ dependencies = [ "gstreamer-pbutils-sys", "gstreamer-video", "libc", - "thiserror", + "thiserror 2.0.11", ] [[package]] name = "gstreamer-pbutils-sys" -version = "0.23.2" +version = "0.23.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ba8e30cb601e11aa8b61d40bef1891ba51744d108406807932d0afc58992205" +checksum = "7002daec504ca76daa9b49b2c51757d81a11d57f119e1d8bc6ef65fe732684a1" dependencies = [ "glib-sys", "gobject-sys", @@ -1619,9 +1458,9 @@ dependencies = [ [[package]] name = "gstreamer-sys" -version = "0.23.2" +version = "0.23.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb3859929db32f26a35818d0d9ed82f0887c9221ca402ddefaea2bb99833d535" +checksum = "16cf1ae0a869aa7066ce3c685b76053b4b4f48f364a5b18c4b1f36ef57469719" dependencies = [ "glib-sys", "gobject-sys", @@ -1631,9 +1470,9 @@ dependencies = [ [[package]] name = "gstreamer-video" -version = "0.23.2" +version = "0.23.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "696ac49ef4c635230b4e4bdebb72cdc31b7a3e8f4fd3f2c1b5009bb10d4ec922" +checksum = "8fa41e40319e923236e96f0b691711d1504746ab9c89607d77d22aa84777f33f" dependencies = [ "cfg-if", "futures-channel", @@ -1643,14 +1482,14 @@ dependencies = [ "gstreamer-video-sys", "libc", "once_cell", - "thiserror", + "thiserror 2.0.11", ] [[package]] name = "gstreamer-video-sys" -version = "0.23.2" +version = "0.23.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "303620a75f22015110ead435c08577bfb7f7425a13b0165e1d03628725742f14" +checksum = "31dc0f49c117f4867b0f98c712aa55ebf25580151d794be8f9179ec2d877fd14" dependencies = [ "glib-sys", "gobject-sys", @@ -1662,9 +1501,9 @@ dependencies = [ [[package]] name = "gtk4" -version = "0.9.2" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31e2d105ce672f5cdcb5af2602e91c2901e91c72da15ab76f613ad57ecf04c6d" +checksum = "b697ff938136625f6acf75f01951220f47a45adcf0060ee55b4671cf734dac44" dependencies = [ "cairo-rs", "field-offset", @@ -1683,9 +1522,9 @@ dependencies = [ [[package]] name = "gtk4-macros" -version = "0.9.1" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9e7b362c8fccd2712297903717d65d30defdab2b509bc9d209cbe5ffb9fabaf" +checksum = "0ed1786c4703dd196baf7e103525ce0cf579b3a63a0570fe653b7ee6bac33999" dependencies = [ "proc-macro-crate", "proc-macro2", @@ -1695,9 +1534,9 @@ dependencies = [ [[package]] name = "gtk4-sys" -version = "0.9.2" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbe4325908b1c1642dbb48e9f49c07a73185babf43e8b2065b0f881a589f55b8" +checksum = "3af4b680cee5d2f786a2f91f1c77e95ecf2254522f0ca4edf3a2dce6cb35cecf" dependencies = [ "cairo-sys-rs", "gdk-pixbuf-sys", @@ -1730,31 +1569,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f4066b9e0375d2b44da9e7f0282c2bd65136fe5e7d033259b2181f499e312e1b" dependencies = [ "gufo-common", - "thiserror", + "thiserror 1.0.69", "tracing", ] -[[package]] -name = "gvdb" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acfa8cd2aa58da96876eb6276afe617c414d5846d1229274a821c1f8e8aa9e02" -dependencies = [ - "byteorder", - "flate2", - "quick-xml", - "safe-transmute", - "serde", - "serde_json", - "walkdir", - "zvariant", -] - [[package]] name = "h2" -version = "0.4.6" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "524e8ac6999421f49a846c2d4411f337e53497d8ec55d67753beffa43c5d9205" +checksum = "ccae279728d634d083c00f6099cb58f01cc99c145b84b8be2f6c74618d79922e" dependencies = [ "atomic-waker", "bytes", @@ -1771,9 +1594,14 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.15.0" +version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb" +checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" +dependencies = [ + "allocator-api2", + "equivalent", + "foldhash", +] [[package]] name = "heck" @@ -1781,12 +1609,6 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" -[[package]] -name = "hermit-abi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" - [[package]] name = "hermit-abi" version = "0.4.0" @@ -1819,9 +1641,9 @@ dependencies = [ [[package]] name = "http" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" +checksum = "f16ca2af56261c99fba8bac40a10251ce8188205a4c448fbb745a2e4daa76fea" dependencies = [ "bytes", "fnv", @@ -1865,9 +1687,9 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "hyper" -version = "1.4.1" +version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50dfd22e0e76d0f662d429a5f80fcaf3855009297eab6a0a9f8543834744ba05" +checksum = "256fb8d4bd6413123cc9d91832d78325c48ff41677595be797d90f42969beae0" dependencies = [ "bytes", "futures-channel", @@ -1885,9 +1707,9 @@ dependencies = [ [[package]] name = "hyper-rustls" -version = "0.27.3" +version = "0.27.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08afdbb5c31130e3034af566421053ab03787c640246a446327f550d11bcb333" +checksum = "2d191583f3da1305256f22463b9bb0471acad48a4e534a5218b9963e9c1f59b2" dependencies = [ "futures-util", "http", @@ -1918,9 +1740,9 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.9" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41296eb09f183ac68eec06e03cdbea2e759633d4067b2f6552fc2e009bcad08b" +checksum = "df2dcfbe0677734ab2f3ffa7fa7bfd4706bfdc1ef393f2ee30184aed67e631b4" dependencies = [ "bytes", "futures-channel", @@ -1936,53 +1758,149 @@ dependencies = [ ] [[package]] -name = "iana-time-zone" -version = "0.1.61" +name = "icu_collections" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220" +checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" dependencies = [ - "android_system_properties", - "core-foundation-sys", - "iana-time-zone-haiku", - "js-sys", - "wasm-bindgen", - "windows-core", + "displaydoc", + "yoke", + "zerofrom", + "zerovec", ] [[package]] -name = "iana-time-zone-haiku" -version = "0.1.2" +name = "icu_locid" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" dependencies = [ - "cc", + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_locid_transform" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_locid_transform_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_locid_transform_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" + +[[package]] +name = "icu_normalizer" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "utf16_iter", + "utf8_iter", + "write16", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" + +[[package]] +name = "icu_properties" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_locid_transform", + "icu_properties_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" + +[[package]] +name = "icu_provider" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_provider_macros", + "stable_deref_trait", + "tinystr", + "writeable", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_provider_macros" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" +dependencies = [ + "proc-macro2", + "quote", + "syn", ] [[package]] name = "idna" -version = "0.3.0" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" +checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" dependencies = [ - "unicode-bidi", - "unicode-normalization", + "idna_adapter", + "smallvec", + "utf8_iter", ] [[package]] -name = "idna" -version = "0.5.0" +name = "idna_adapter" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" dependencies = [ - "unicode-bidi", - "unicode-normalization", + "icu_normalizer", + "icu_properties", ] [[package]] name = "indexmap" -version = "2.6.0" +version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" +checksum = "8c9c992b02b5b4c94ea26e32fe5bccb7aa7d9f390ab5c1221ff895bc7ea8b652" dependencies = [ "equivalent", "hashbrown", @@ -2009,9 +1927,9 @@ dependencies = [ [[package]] name = "ipnet" -version = "2.10.0" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "187674a687eed5fe42285b40c6291f9a01517d415fad1c3cbc6a9f778af7fcd4" +checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" [[package]] name = "itertools" @@ -2024,9 +1942,9 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.11" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" +checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" [[package]] name = "javascriptcore6" @@ -2062,10 +1980,11 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.70" +version = "0.3.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1868808506b929d7b0cfa8f75951347aa71bb21144b7791bae35d9bccfcfe37a" +checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" dependencies = [ + "once_cell", "wasm-bindgen", ] @@ -2103,9 +2022,9 @@ dependencies = [ [[package]] name = "libadwaita" -version = "0.7.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ff9c222b5c783729de45185f07b2fec2d43a7f9c63961e777d3667e20443878" +checksum = "8611ee9fb85e7606c362b513afcaf5b59853f79e4d98caaaf581d99465014247" dependencies = [ "gdk4", "gio", @@ -2118,9 +2037,9 @@ dependencies = [ [[package]] name = "libadwaita-sys" -version = "0.7.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c44d8bdbad31d6639e1f20cc9c1424f1a8e02d751fc28d44659bf743fb9eca6" +checksum = "b099a223560118d4d4fa04b6d23f3ea5b7171fe1d83dfb7e6b45b54cdfc83af9" dependencies = [ "gdk4-sys", "gio-sys", @@ -2134,15 +2053,15 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.159" +version = "0.2.169" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "561d97a539a36e26a9a5fad1ea11a3039a67714694aaa379433e580854bc3dc5" +checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" [[package]] name = "libloading" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4" +checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34" dependencies = [ "cfg-if", "windows-targets", @@ -2150,9 +2069,9 @@ dependencies = [ [[package]] name = "libm" -version = "0.2.8" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" +checksum = "8355be11b20d696c8f18f6cc018c4e372165b1fa8126cef092399c9951984ffa" [[package]] name = "libpaket" @@ -2173,7 +2092,7 @@ dependencies = [ "serde_newtype", "serde_repr", "sha2", - "thiserror", + "thiserror 1.0.69", "url", "urlencoding", "uuid", @@ -2199,9 +2118,34 @@ checksum = "9a7cbbd4ad467251987c6e5b47d53b11a5a05add08f2447a9e2d70aef1e0d138" [[package]] name = "linux-raw-sys" -version = "0.4.14" +version = "0.4.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" +checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" + +[[package]] +name = "litemap" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ee93343901ab17bd981295f2cf0026d4ad018c7c31ba84549a4ddbb47a45104" + +[[package]] +name = "litrs" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4ce301924b7887e9d637144fdade93f9dfff9b60981d4ac161db09720d39aa5" + +[[package]] +name = "locale_config" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d2c35b16f4483f6c26f0e4e9550717a2f6575bcd6f12a53ff0c490a94a6934" +dependencies = [ + "lazy_static", + "objc", + "objc-foundation", + "regex", + "winapi", +] [[package]] name = "lock_api" @@ -2215,9 +2159,27 @@ dependencies = [ [[package]] name = "log" -version = "0.4.22" +version = "0.4.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" +checksum = "04cbf5b083de1c7e0222a7a51dbfdba1cbe1c6ab0b15e29fff3f6c077fd9cd9f" + +[[package]] +name = "lru" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "234cf4f4a04dc1f57e24b96cc0cd600cf2af460d4161ac5ecdd0af8e1f3b2a38" +dependencies = [ + "hashbrown", +] + +[[package]] +name = "malloc_buf" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" +dependencies = [ + "libc", +] [[package]] name = "md-5" @@ -2270,20 +2232,19 @@ checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" [[package]] name = "miniz_oxide" -version = "0.8.0" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" +checksum = "b8402cab7aefae129c6977bb0ff1b8fd9a04eb5b51efc50a70bea51cda0c7924" dependencies = [ "adler2", ] [[package]] name = "mio" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" +checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" dependencies = [ - "hermit-abi 0.3.9", "libc", "wasi", "windows-sys 0.52.0", @@ -2327,7 +2288,7 @@ version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "cfg-if", "cfg_aliases", "libc", @@ -2453,22 +2414,48 @@ dependencies = [ ] [[package]] -name = "object" -version = "0.36.4" +name = "objc" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "084f1a5821ac4c651660a94a7153d27ac9d8a53736203f58b31945ded098070a" +checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" +dependencies = [ + "malloc_buf", +] + +[[package]] +name = "objc-foundation" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1add1b659e36c9607c7aab864a76c7a4c2760cd0cd2e120f3fb8b952c7e22bf9" +dependencies = [ + "block", + "objc", + "objc_id", +] + +[[package]] +name = "objc_id" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c92d4ddb4bd7b50d730c215ff871754d0da6b2178849f8a2a2ab69712d0c073b" +dependencies = [ + "objc", +] + +[[package]] +name = "object" +version = "0.36.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" dependencies = [ "memchr", ] [[package]] name = "once_cell" -version = "1.20.1" +version = "1.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82881c4be219ab5faaf2ad5e5e5ecdff8c66bd7402ca3160975c93b24961afd1" -dependencies = [ - "portable-atomic", -] +checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" [[package]] name = "oo7" @@ -2511,11 +2498,11 @@ checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" [[package]] name = "openssl" -version = "0.10.66" +version = "0.10.68" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9529f4786b70a3e8c61e11179af17ab6188ad8d0ded78c5529441ed39d4bd9c1" +checksum = "6174bc48f102d208783c2c84bf931bb75927a617866870de8a4ea85597f871f5" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "cfg-if", "foreign-types 0.3.2", "libc", @@ -2543,9 +2530,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-sys" -version = "0.9.103" +version = "0.9.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f9e8deee91df40a943c71b917e5874b951d32a802526c85721ce3b776c929d6" +checksum = "45abf306cbf99debc8195b66b7346498d7b10c210de50418b5ccd7ceba08c741" dependencies = [ "cc", "libc", @@ -2578,13 +2565,15 @@ version = "0.1.0" dependencies = [ "aperture", "futures", + "gettext-rs", + "glib-build-tools", "glycin", "gtk4", "libadwaita", "libpaket", "oo7", + "regex", "relm4", - "relm4-icons", "reqwest", "secrecy", "serde", @@ -2594,11 +2583,18 @@ dependencies = [ "webkit6", ] +[[package]] +name = "paket-utils" +version = "0.1.0" +dependencies = [ + "glib", +] + [[package]] name = "pango" -version = "0.20.4" +version = "0.20.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa26aa54b11094d72141a754901cd71d9356432bb8147f9cace8d9c7ba95f356" +checksum = "9e89bd74250a03a05cec047b43465469102af803be2bf5e5a1088f8b8455e087" dependencies = [ "gio", "glib", @@ -2608,9 +2604,9 @@ dependencies = [ [[package]] name = "pango-sys" -version = "0.20.4" +version = "0.20.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84fd65917bf12f06544ae2bbc200abf9fc0a513a5a88a0fa81013893aef2b838" +checksum = "71787e0019b499a5eda889279e4adb455a4f3fdd6870cd5ab7f4a5aa25df6699" dependencies = [ "glib-sys", "gobject-sys", @@ -2648,9 +2644,9 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pin-project-lite" -version = "0.2.14" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" [[package]] name = "pin-utils" @@ -2665,7 +2661,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96c8c490f422ef9a4efd2cb5b42b76c8613d7e7dfc1caf667b8a3350a5acc066" dependencies = [ "atomic-waker", - "fastrand 2.1.1", + "fastrand 2.3.0", "futures-io", ] @@ -2687,13 +2683,13 @@ checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" [[package]] name = "polling" -version = "3.7.3" +version = "3.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc2790cd301dec6cd3b7a025e4815cf825724a51c98dccfe6a3e55f05ffb6511" +checksum = "a604568c3202727d1507653cb121dbd627a58684eb09a820fd746bee38b4442f" dependencies = [ "cfg-if", "concurrent-queue", - "hermit-abi 0.4.0", + "hermit-abi", "pin-project-lite", "rustix", "tracing", @@ -2712,12 +2708,6 @@ dependencies = [ "universal-hash", ] -[[package]] -name = "portable-atomic" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc9c68a3f6da06753e9335d63e27f6b9754dd1920d941135b7ea8224f141adb2" - [[package]] name = "powerfmt" version = "0.2.0" @@ -2744,9 +2734,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.86" +version = "1.0.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99" dependencies = [ "unicode-ident", ] @@ -2759,29 +2749,19 @@ checksum = "33cb294fe86a74cbcf50d4445b37da762029549ebeea341421c7c70370f86cac" [[package]] name = "publicsuffix" -version = "2.2.3" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96a8c1bda5ae1af7f99a2962e49df150414a43d62404644d98dd5c3a93d07457" +checksum = "6f42ea446cab60335f76979ec15e12619a2165b5ae2c12166bef27d283a9fadf" dependencies = [ - "idna 0.3.0", + "idna", "psl-types", ] -[[package]] -name = "quick-xml" -version = "0.31.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1004a344b30a54e2ee58d66a71b32d2db2feb0a31f9a2d302bf0536f15de2a33" -dependencies = [ - "memchr", - "serde", -] - [[package]] name = "quote" -version = "1.0.37" +version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" dependencies = [ "proc-macro2", ] @@ -2825,10 +2805,40 @@ dependencies = [ "fastrand 1.9.0", ] +[[package]] +name = "regex" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" + [[package]] name = "relm4" -version = "0.9.0" -source = "git+https://github.com/Relm4/Relm4.git#13b198eecb8e6d57ba9959c628aaabad318fef36" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30837553c1a8cfea1a404c83ec387c5c8ff9358e1060b057c274c5daa5035ad1" dependencies = [ "flume", "fragile", @@ -2845,24 +2855,14 @@ dependencies = [ [[package]] name = "relm4-css" version = "0.9.0" -source = "git+https://github.com/Relm4/Relm4.git#13b198eecb8e6d57ba9959c628aaabad318fef36" - -[[package]] -name = "relm4-icons" -version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15242978b4e65431be4ccd5cf539d3c142518e114b7ecfa72ffd02cd6432e6e3" -dependencies = [ - "gtk4", - "gvdb", - "serde", - "toml", -] +checksum = "1d3b924557df1cddc687b60b313c4b76620fdbf0e463afa4b29f67193ccf37f9" [[package]] name = "relm4-macros" -version = "0.9.0" -source = "git+https://github.com/Relm4/Relm4.git#13b198eecb8e6d57ba9959c628aaabad318fef36" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a895a7455441a857d100ca679bd24a92f91d28b5e3df63296792ac1af2eddde" dependencies = [ "proc-macro2", "quote", @@ -2871,9 +2871,9 @@ dependencies = [ [[package]] name = "reqwest" -version = "0.12.8" +version = "0.12.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f713147fbe92361e52392c73b8c9e48c04c6625bce969ef54dc901e58e042a7b" +checksum = "43e734407157c3c2034e0258f5e4473ddb361b1e85f95a66690d67264d7cd1da" dependencies = [ "async-compression", "base64", @@ -2908,6 +2908,7 @@ dependencies = [ "tokio", "tokio-native-tls", "tokio-util", + "tower", "tower-service", "url", "wasm-bindgen", @@ -2953,6 +2954,16 @@ dependencies = [ "serde", ] +[[package]] +name = "rqrr" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f126a9b02152815d84315316e7a759ee18a216d057095d56d19cec68a428b385" +dependencies = [ + "g2p", + "lru", +] + [[package]] name = "rustc-demangle" version = "0.1.24" @@ -2970,22 +2981,22 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.37" +version = "0.38.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8acb788b847c24f28525660c4d7758620a7210875711f79e7f663cc152726811" +checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "errno", "libc", "linux-raw-sys", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] name = "rustls" -version = "0.23.13" +version = "0.23.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2dabaac7466917e566adb06783a81ca48944c6898a1b08b9374106dd671f4c8" +checksum = "8f287924602bf649d949c63dc8ac8b235fa5387d394020705b80c4eb597ce5b8" dependencies = [ "once_cell", "rustls-pki-types", @@ -3005,9 +3016,9 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.9.0" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e696e35370c65c9c541198af4543ccd580cf17fc25d8e05c5a242b202488c55" +checksum = "d2bf47e6ff922db3825eb750c4e2ff784c6ff8fb9e13046ef6a1d1c5401b0b37" [[package]] name = "rustls-webpki" @@ -3020,32 +3031,23 @@ dependencies = [ "untrusted", ] +[[package]] +name = "rustversion" +version = "1.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7c45b9784283f1b2e7fb61b42047c2fd678ef0960d4f6f1eba131594cc369d4" + [[package]] name = "ryu" version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" -[[package]] -name = "safe-transmute" -version = "0.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3944826ff8fa8093089aba3acb4ef44b9446a99a16f3bf4e74af3f77d340ab7d" - -[[package]] -name = "same-file" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" -dependencies = [ - "winapi-util", -] - [[package]] name = "schannel" -version = "0.1.24" +version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9aaafd5a2b6e3d657ff009d82fbd630b6bd54dd4eb06f21693925cdf80f9b8b" +checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d" dependencies = [ "windows-sys 0.59.0", ] @@ -3058,9 +3060,9 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "secrecy" -version = "0.10.2" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba079fa568d52545cd70b334b2ce6f88f62b8fc2bda9290f48a0578388a49331" +checksum = "e891af845473308773346dc847b2c23ee78fe442e0472ac50e22a18a93d3ae5a" dependencies = [ "serde", "zeroize", @@ -3072,7 +3074,7 @@ version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "core-foundation", "core-foundation-sys", "libc", @@ -3081,9 +3083,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.12.0" +version = "2.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea4a292869320c0272d7bc55a5a6aafaff59b4f63404a003887b679a2e05b4b6" +checksum = "49db231d56a190491cb4aeda9527f1ad45345af50b0851622a7adb8c03b01c32" dependencies = [ "core-foundation-sys", "libc", @@ -3091,24 +3093,24 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.23" +version = "1.0.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" +checksum = "f79dfe2d285b0488816f30e700a7438c5a73d816b5b7d3ac72fbc48b0d185e03" [[package]] name = "serde" -version = "1.0.210" +version = "1.0.217" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" +checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.210" +version = "1.0.217" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" +checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" dependencies = [ "proc-macro2", "quote", @@ -3126,9 +3128,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.128" +version = "1.0.137" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8" +checksum = "930cfb6e6abf99298aaad7d29abbef7a9999a9a8806a40088f55f0dcec03146b" dependencies = [ "itoa", "memchr", @@ -3241,9 +3243,9 @@ checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" [[package]] name = "socket2" -version = "0.5.7" +version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" +checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8" dependencies = [ "libc", "windows-sys 0.52.0", @@ -3294,6 +3296,12 @@ dependencies = [ "der", ] +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + [[package]] name = "static_assertions" version = "1.1.0" @@ -3308,9 +3316,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" -version = "2.0.79" +version = "2.0.96" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590" +checksum = "d5d0adab1ae378d7f53bdebc67a39f1f151407ef230f0ce2883572f5d8985c80" dependencies = [ "proc-macro2", "quote", @@ -3319,20 +3327,31 @@ dependencies = [ [[package]] name = "sync_wrapper" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" dependencies = [ "futures-core", ] +[[package]] +name = "synstructure" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "system-configuration" version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "core-foundation", "system-configuration-sys", ] @@ -3367,13 +3386,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" [[package]] -name = "tempfile" -version = "3.13.0" +name = "temp-dir" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0f2c9fc62d0beef6951ccffd757e241266a2c833136efbe35af6cd2567dca5b" +checksum = "bc1ee6eef34f12f765cb94725905c6312b6610ab2b0940889cfe58dae7bc3c72" + +[[package]] +name = "tempfile" +version = "3.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8a559c81686f576e8cd0290cd2a24a2a9ad80c98b3478856500fcbd7acd704" dependencies = [ "cfg-if", - "fastrand 2.1.1", + "fastrand 2.3.0", + "getrandom", "once_cell", "rustix", "windows-sys 0.59.0", @@ -3381,18 +3407,38 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.64" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d50af8abc119fb8bb6dbabcfa89656f46f84aa0ac7688088608076ad2b459a84" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" dependencies = [ - "thiserror-impl", + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d452f284b73e6d76dd36758a0c8684b1d5be31f92b89d07fd5822175732206fc" +dependencies = [ + "thiserror-impl 2.0.11", ] [[package]] name = "thiserror-impl" -version = "1.0.64" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26afc1baea8a989337eeb52b6e72a039780ce45c3edfcc9c5b9d112feeb173c2" dependencies = [ "proc-macro2", "quote", @@ -3401,9 +3447,9 @@ dependencies = [ [[package]] name = "time" -version = "0.3.36" +version = "0.3.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" +checksum = "35e7868883861bd0e56d9ac6efcaaca0d6d5d82a2a7ec8209ff492c07cf37b21" dependencies = [ "deranged", "itoa", @@ -3422,34 +3468,29 @@ checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" [[package]] name = "time-macros" -version = "0.2.18" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" +checksum = "2834e6017e3e5e4b9834939793b282bc03b37a3336245fa820e35e233e2a85de" dependencies = [ "num-conv", "time-core", ] [[package]] -name = "tinyvec" -version = "1.8.0" +name = "tinystr" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" +checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" dependencies = [ - "tinyvec_macros", + "displaydoc", + "zerovec", ] -[[package]] -name = "tinyvec_macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" - [[package]] name = "tokio" -version = "1.40.0" +version = "1.43.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2b070231665d27ad9ec9b8df639893f46727666c6767db40317fbe920a5d998" +checksum = "3d61fa4ffa3de412bfea335c6ecff681de2b609ba3c77ef3e00e521813a9ed9e" dependencies = [ "backtrace", "bytes", @@ -3472,20 +3513,19 @@ dependencies = [ [[package]] name = "tokio-rustls" -version = "0.26.0" +version = "0.26.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4" +checksum = "5f6d0975eaace0cf0fcadee4e4aaa5da15b5c079146f2cffb67c113be122bf37" dependencies = [ "rustls", - "rustls-pki-types", "tokio", ] [[package]] name = "tokio-util" -version = "0.7.12" +version = "0.7.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61e7c3654c13bcd040d4a03abee2c75b1d14a37b423cf5a813ceae1cc903ec6a" +checksum = "d7fcaa8d55a2bdd6b83ace262b016eca0d79ee02818c5c1bcdf0305114081078" dependencies = [ "bytes", "futures-core", @@ -3528,6 +3568,27 @@ dependencies = [ "winnow", ] +[[package]] +name = "tower" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" +dependencies = [ + "futures-core", + "futures-util", + "pin-project-lite", + "sync_wrapper", + "tokio", + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-layer" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" + [[package]] name = "tower-service" version = "0.3.3" @@ -3536,9 +3597,9 @@ checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" [[package]] name = "tracing" -version = "0.1.40" +version = "0.1.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" dependencies = [ "pin-project-lite", "tracing-attributes", @@ -3547,9 +3608,9 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.27" +version = "0.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" dependencies = [ "proc-macro2", "quote", @@ -3558,9 +3619,9 @@ dependencies = [ [[package]] name = "tracing-core" -version = "0.1.32" +version = "0.1.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" dependencies = [ "once_cell", ] @@ -3608,26 +3669,11 @@ dependencies = [ "winapi", ] -[[package]] -name = "unicode-bidi" -version = "0.3.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" - [[package]] name = "unicode-ident" -version = "1.0.13" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" - -[[package]] -name = "unicode-normalization" -version = "0.1.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956" -dependencies = [ - "tinyvec", -] +checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" [[package]] name = "universal-hash" @@ -3647,12 +3693,12 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "url" -version = "2.5.2" +version = "2.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" +checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" dependencies = [ "form_urlencoded", - "idna 0.5.0", + "idna", "percent-encoding", ] @@ -3663,10 +3709,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" [[package]] -name = "uuid" -version = "1.10.0" +name = "utf16_iter" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81dfa00651efa65069b0b6b651f4aaa31ba9e3c3ce0137aaad053604ee7e0314" +checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + +[[package]] +name = "uuid" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3758f5e68192bb96cc8f9b7e2c2cfdabb435499a28499a42f8f984092adad4b" dependencies = [ "getrandom", "serde", @@ -3690,16 +3748,6 @@ version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" -[[package]] -name = "walkdir" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" -dependencies = [ - "same-file", - "winapi-util", -] - [[package]] name = "want" version = "0.3.1" @@ -3717,24 +3765,24 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.93" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a82edfc16a6c469f5f44dc7b571814045d60404b55a0ee849f9bcfa2e63dd9b5" +checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" dependencies = [ "cfg-if", "once_cell", + "rustversion", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.93" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9de396da306523044d3302746f1208fa71d7532227f15e347e2d93e4145dd77b" +checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" dependencies = [ "bumpalo", "log", - "once_cell", "proc-macro2", "quote", "syn", @@ -3743,21 +3791,22 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.43" +version = "0.4.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61e9300f63a621e96ed275155c108eb6f843b6a26d053f122ab69724559dc8ed" +checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" dependencies = [ "cfg-if", "js-sys", + "once_cell", "wasm-bindgen", "web-sys", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.93" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf" +checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -3765,9 +3814,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.93" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" +checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" dependencies = [ "proc-macro2", "quote", @@ -3778,15 +3827,18 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.93" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484" +checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +dependencies = [ + "unicode-ident", +] [[package]] name = "web-sys" -version = "0.3.70" +version = "0.3.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26fdeaafd9bd129f65e7c031593c24d62186301e0c72c8978fa1678be7d532c0" +checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" dependencies = [ "js-sys", "wasm-bindgen", @@ -3841,30 +3893,12 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" -[[package]] -name = "winapi-util" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" -dependencies = [ - "windows-sys 0.59.0", -] - [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" -[[package]] -name = "windows-core" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" -dependencies = [ - "windows-targets", -] - [[package]] name = "windows-registry" version = "0.2.0" @@ -3979,13 +4013,25 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" -version = "0.6.20" +version = "0.6.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b" +checksum = "c8d71a593cc5c42ad7876e2c1fda56f314f3754c084128833e64f1345ff8a03a" dependencies = [ "memchr", ] +[[package]] +name = "write16" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" + +[[package]] +name = "writeable" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" + [[package]] name = "xdg-home" version = "1.3.0" @@ -4007,6 +4053,30 @@ dependencies = [ "pkg-config", ] +[[package]] +name = "yoke" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" +dependencies = [ + "serde", + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + [[package]] name = "zbus" version = "4.4.0" @@ -4090,6 +4160,27 @@ dependencies = [ "syn", ] +[[package]] +name = "zerofrom" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cff3ee08c995dee1859d998dea82f7374f2826091dd9cd47def953cae446cd2e" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + [[package]] name = "zeroize" version = "1.8.1" @@ -4110,6 +4201,28 @@ dependencies = [ "syn", ] +[[package]] +name = "zerovec" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "zvariant" version = "4.2.0" diff --git a/Cargo.toml b/Cargo.toml index 9a61307..aaa6d8d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ resolver = "2" members = [ "libpaket", - "paket", + "paket-utils", ] [workspace.package] @@ -13,12 +13,64 @@ license = "AGPL-3.0-only" version = "0.1.0" [workspace.dependencies] -secrecy = { version = "0.10", features = ["serde"] } +adw = { package = "libadwaita", version = "0.7", features = ["v1_6"] } +aes-gcm = { version = "0.10.3"} +aperture = "0.9" +base64 = "0.22" +ed25519-dalek = { version = "2.1.0"} +futures = "0.3" +gettext = { package = "gettext-rs", version = "0.7", features = ["gettext-system"] } +glib = "0" +glycin = { version = "2.0", features = ["gdk4"] } +gtk = { package = "gtk4", version = "0.9", features = ["v4_16"] } +hmac = { version = "0.12.1"} +num_enum = { version = "0.7"} +oo7 = { version = "0.3" } +rand = "0.8" +random-string = "1.1.0" +regex = "1" +relm4 = { version = ">=0.9.1", features = [ "libadwaita", "macros", ] } reqwest = { version = "0.12", features = ["json", "cookies", "gzip", "http2"] } -serde = { version = "1.0.195", features = ["derive"] } -serde_json = "1.0.111" -uuid = { version = "1.7.0", features = ["v4"] } -relm4 = { git = "https://github.com/Relm4/Relm4.git", features = [ - "libadwaita", - "macros", -] } +secrecy = { version = "0.10", features = ["serde"] } +serde = { version = "1", features = ["derive"] } +serde_ignored = "0.1" +serde_json = "1" +serde_newtype = "0.1" +serde_repr = { version = "0.1.18" } +sha2 = "0.10.8" +thiserror = "1.0" +tracker = "0.2" +url = "2.5.0" +urlencoding = "2.1.3" +uuid = { version = "1.7", features = ["v4"] } +webkit = { package = "webkit6", version = "0.4" } + +[package] +name = "paket" +authors.workspace = true +edition.workspace = true +license.workspace = true +version.workspace = true +default-run = "paket" + +[dependencies] +adw = { workspace = true } +aperture = { workspace = true } +futures = { workspace = true } +gettext = { workspace = true } +glycin = { workspace = true } +gtk = { workspace = true } +libpaket = { path = "libpaket" } +oo7 = { workspace = true } +regex = { workspace = true } +relm4 = { workspace = true } +reqwest = { workspace = true } +secrecy = { workspace = true } +serde = { workspace = true } +serde_json = { workspace = true } +tracker = { workspace = true } +uuid = { workspace = true } +webkit = { workspace = true } + +[build-dependencies] +glib-build-tools = "0.20" diff --git a/README.md b/README.md new file mode 100644 index 0000000..69d1df9 --- /dev/null +++ b/README.md @@ -0,0 +1,29 @@ +# paket + +## Files and influences from other projects + +- `i18n.rs` and `build-aux/{checks,dist-vendor}.sh` are directly copied from amberol +- `src/lib.rs` has code from amberol for initalizing gettext +- `paket-utils/src/lib.rs` `system_get_accept_languages` is based on libsoup3 and GNOME Web +- the Relm4 book, libadwaita docs and so much more + +This project is standing upon of the shoulders of giants. I'm sorry if missed something here. I want to give proper attribution, please open an issue. Especially if I violated a copyright license by publishing it. + +## How to run + +### If you're a dev + +Use `cargo run`, meson devenv is not supported (yet, merge requests are welcome). + +### If you're a package maintainer + +I don't recommend publishing this software in conventional stable releases, I want people to use the latest version due to the volatile nature of a reverse engineering project. An exception is NixOS, even though the quality of many packages are lacking there, people can easily mixin `nixpkgs-unstable` to receive the latest version. + + +If you want to package it: + +- wait for a release +- make sure dependencies are met, `cargo auditable` and the ones in `./meson.build` +- do to usual meson dance (`setup`, `compile`, `install`). you're distro has probably an abstraction for meson packages, use that if possible. + - e.g. Alpine Linux has `abuild-meson` as an wrapper to `meson setup` to apply distro defaults for a release + - if you're distro doesn't have support for meson, then don't package this. please. i will write code that breaks running on you're system if you screw up packaging. [this comment reflects my opinion to 100%.](https://github.com/bottlesdevs/Bottles/issues/2345#issuecomment-1737334499) \ No newline at end of file diff --git a/assets/copy-symbolic.svg b/assets/copy-symbolic.svg new file mode 100644 index 0000000..7aad5a3 --- /dev/null +++ b/assets/copy-symbolic.svg @@ -0,0 +1,2 @@ + + diff --git a/assets/four-arrows-pointing-outward-symbolic.svg b/assets/four-arrows-pointing-outward-symbolic.svg new file mode 100644 index 0000000..1608c1b --- /dev/null +++ b/assets/four-arrows-pointing-outward-symbolic.svg @@ -0,0 +1,2 @@ + + diff --git a/assets/loupe-large-symbolic.svg b/assets/loupe-large-symbolic.svg new file mode 100644 index 0000000..da9a076 --- /dev/null +++ b/assets/loupe-large-symbolic.svg @@ -0,0 +1,4 @@ + + + + diff --git a/assets/mail-symbolic.svg b/assets/mail-symbolic.svg new file mode 100644 index 0000000..fd73d74 --- /dev/null +++ b/assets/mail-symbolic.svg @@ -0,0 +1,2 @@ + + diff --git a/assets/minus-symbolic.svg b/assets/minus-symbolic.svg new file mode 100644 index 0000000..1cf3321 --- /dev/null +++ b/assets/minus-symbolic.svg @@ -0,0 +1,2 @@ + + diff --git a/assets/package-x-generic-symbolic.svg b/assets/package-x-generic-symbolic.svg new file mode 100644 index 0000000..474ca69 --- /dev/null +++ b/assets/package-x-generic-symbolic.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/assets/parcel-locker-symbolic.svg b/assets/parcel-locker-symbolic.svg new file mode 100644 index 0000000..5612b33 --- /dev/null +++ b/assets/parcel-locker-symbolic.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/assets/person-symbolic.svg b/assets/person-symbolic.svg new file mode 100644 index 0000000..e100582 --- /dev/null +++ b/assets/person-symbolic.svg @@ -0,0 +1,39 @@ + + + + + + diff --git a/assets/plus-symbolic.svg b/assets/plus-symbolic.svg new file mode 100644 index 0000000..e87b95d --- /dev/null +++ b/assets/plus-symbolic.svg @@ -0,0 +1,2 @@ + + diff --git a/assets/qr-code-scanner-symbolic.svg b/assets/qr-code-scanner-symbolic.svg new file mode 100644 index 0000000..7bcdc55 --- /dev/null +++ b/assets/qr-code-scanner-symbolic.svg @@ -0,0 +1,2 @@ + + diff --git a/build-aux/checks.sh b/build-aux/checks.sh new file mode 100755 index 0000000..82a9599 --- /dev/null +++ b/build-aux/checks.sh @@ -0,0 +1,459 @@ +#!/bin/bash +# +# SPDX-FileCopyrightText: 2021 Alejandro Domínguez +# SPDX-FileCopyrightText: 2022 Kévin Commaile +# SPDX-License-Identifier: GPL-3.0-or-later + +export LC_ALL=C + +# Usage info +show_help() { +cat << EOF +Run conformity checks on the current Rust project. + +If a dependency is not found, helps the user to install it. + +USAGE: ${0##*/} [OPTIONS] + +OPTIONS: + -f, --force-install Install missing dependencies without asking + -v, --verbose Use verbose output + -h, --help Display this help and exit + +ERROR CODES: + 1 Check failed + 2 Missing dependency +EOF +} + +# Style helpers +act="\e[1;32m" +err="\e[1;31m" +pos="\e[32m" +neg="\e[31m" +res="\e[0m" + +# Common styled strings +Installing="${act}Installing${res}" +Checking=" ${act}Checking${res}" +Failed=" ${err}Failed${res}" +error="${err}error:${res}" +invalid="${neg}Invalid input${res}" +ok="${pos}ok${res}" +fail="${neg}fail${res}" + +# Initialize variables +force_install=0 +verbose=0 + +# Helper functions +# Sort to_sort in natural order. +sort() { + local size=${#to_sort[@]} + local swapped=0; + + for (( i = 0; i < $size-1; i++ )) + do + swapped=0 + for ((j = 0; j < $size-1-$i; j++ )) + do + if [[ "${to_sort[$j]}" > "${to_sort[$j+1]}" ]] + then + temp="${to_sort[$j]}"; + to_sort[$j]="${to_sort[$j+1]}"; + to_sort[$j+1]="$temp"; + swapped=1; + fi + done + + if [[ $swapped -eq 0 ]]; then + break; + fi + done +} + +# Remove common entries in to_diff1 and to_diff2. +diff() { + for i in ${!to_diff1[@]}; do + for j in ${!to_diff2[@]}; do + if [[ "${to_diff1[$i]}" == "${to_diff2[$j]}" ]]; then + unset to_diff1[$i] + unset to_diff2[$j] + break + fi + done + done +} + +# Check if rustup is available. +# Argument: +# '-i' to install if missing. +check_rustup() { + if ! which rustup &> /dev/null; then + if [[ "$1" == '-i' ]]; then + echo -e "$Installing rustup…" + curl https://sh.rustup.rs -sSf | sh -s -- -y --default-toolchain nightly + export PATH=$PATH:$HOME/.cargo/bin + if ! which rustup &> /dev/null; then + echo -e "$Failed to install rustup" + exit 2 + fi + else + exit 2 + fi + fi +} + +# Install cargo via rustup. +install_cargo() { + check_rustup -i + if ! which cargo >/dev/null 2>&1; then + echo -e "$Failed to install cargo" + exit 2 + fi +} + +# Check if cargo is available. If not, ask to install it. +check_cargo() { + if ! which cargo >/dev/null 2>&1; then + echo "Unable to find cargo" + + if [[ $force_install -eq 1 ]]; then + install_cargo + elif [ ! -t 1 ]; then + exit 2 + elif check_rustup; then + echo -e "$error rustup is installed but the cargo command isn't available" + exit 2 + else + echo "" + echo "y: Install cargo via rustup" + echo "N: Don't install cargo and abort checks" + echo "" + while true; do + echo -n "Install cargo? [y/N]: "; read yn < /dev/tty + case $yn in + [Yy]* ) + install_cargo + break + ;; + [Nn]* | "" ) + exit 2 + ;; + * ) + echo $invalid + ;; + esac + done + fi + fi + + if [[ $verbose -eq 1 ]]; then + echo "" + rustc -Vv && cargo +nightly -Vv + fi +} + +# Install rustfmt with rustup. +install_rustfmt() { + check_rustup -i + + echo -e "$Installing rustfmt…" + rustup component add --toolchain nightly rustfmt + if ! cargo +nightly fmt --version >/dev/null 2>&1; then + echo -e "$Failed to install rustfmt" + exit 2 + fi +} + +# Run rustfmt to enforce code style. +run_rustfmt() { + if ! cargo fmt --version >/dev/null 2>&1; then + if [[ $force_install -eq 1 ]]; then + install_rustfmt + elif [ ! -t 1 ]; then + echo "Unable to check the project's code style, because rustfmt could not be run" + exit 2 + else + echo "Rustfmt is needed to check the project's code style, but it isn’t available" + echo "" + echo "y: Install rustfmt via rustup" + echo "N: Don't install rustfmt and abort checks" + echo "" + while true; do + echo -n "Install rustfmt? [y/N]: "; read yn < /dev/tty + case $yn in + [Yy]* ) + install_rustfmt + break + ;; + [Nn]* | "" ) + exit 2 + ;; + * ) + echo $invalid + ;; + esac + done + fi + fi + + echo -e "$Checking code style…" + + if [[ $verbose -eq 1 ]]; then + echo "" + cargo fmt --version + echo "" + fi + + if ! cargo fmt --all -- --check; then + echo -e " Checking code style result: $fail" + echo "Please fix the above issues, either manually or by running: cargo fmt --all" + exit 1 + else + echo -e " Checking code style result: $ok" + fi +} + + +# Install typos with cargo. +install_typos() { + echo -e "$Installing typos…" + cargo install typos-cli + if ! typos --version >/dev/null 2>&1; then + echo -e "$Failed to install typos" + exit 2 + fi +} + +# Run typos to check for spelling mistakes. +run_typos() { + if ! typos --version >/dev/null 2>&1; then + if [[ $force_install -eq 1 ]]; then + install_typos + elif [ ! -t 1 ]; then + echo "Unable to check spelling mistakes, because typos could not be run" + exit 2 + else + echo "Typos is needed to check spelling mistakes, but it isn’t available" + echo "" + echo "y: Install typos via cargo" + echo "N: Don't install typos and abort checks" + echo "" + while true; do + echo -n "Install typos? [y/N]: "; read yn < /dev/tty + case $yn in + [Yy]* ) + install_typos + break + ;; + [Nn]* | "" ) + exit 2 + ;; + * ) + echo $invalid + ;; + esac + done + fi + fi + + echo -e "$Checking spelling mistakes…" + + if [[ $verbose -eq 1 ]]; then + echo "" + typos --version + echo "" + fi + + if ! typos --color always; then + echo -e " Checking spelling mistakes result: $fail" + echo "Please fix the above issues, either manually or by running: typos -w" + exit 1 + else + echo -e " Checking spelling mistakes result: $ok" + fi +} + +# Check if files in POTFILES are correct. +# +# This checks, in that order: +# - All files exist +# - All files with translatable strings are present and only those +# - Files are sorted alphabetically +# +# This assumes the following: +# - POTFILES.in is located at 'po/POTFILES.in' +# - UI (Glade) files are located in 'src/gtk/' and use 'translatable="yes"' +# - Rust files are located in 'src' and use 'i18n' methods or macros +check_potfiles() { + echo -e "$Checking po/POTFILES.in…" + + local ret=0 + + # Check that files in POTFILES exist. + while read -r line; do + if [[ -n $line && ${line::1} != '#' ]]; then + if [[ ! -f $line ]]; then + echo -e "$error File '$line' in POTFILES does not exist" + ret=1 + fi + if [[ ${line:(-3):3} == '.ui' ]]; then + ui_potfiles+=($line) + elif [[ ${line:(-3):3} == '.rs' ]]; then + rs_potfiles+=($line) + fi + fi + done < po/POTFILES.in + + if [[ ret -eq 1 ]]; then + echo -e " Checking po/POTFILES.in result: $fail" + echo "Please fix the above issues" + exit 1 + fi + + # Get UI files with 'translatable="yes"'. + #ui_files=(`grep -lIr 'translatable="yes"' src/gtk/*.ui`) + + # Get Rust files with regex 'i18n[!]?\('. + rs_files=(`grep -lIrE 'i18n[!]?\(' --exclude=src/i18n.rs src/*`) + + # Remove common files + #to_diff1=("${ui_potfiles[@]}") + #to_diff2=("${ui_files[@]}") + #diff + #ui_potfiles=("${to_diff1[@]}") + #ui_files=("${to_diff2[@]}") + + to_diff1=("${rs_potfiles[@]}") + to_diff2=("${rs_files[@]}") + diff + rs_potfiles=("${to_diff1[@]}") + rs_files=("${to_diff2[@]}") + + potfiles_count=$((${#ui_potfiles[@]} + ${#rs_potfiles[@]})) + if [[ $potfiles_count -eq 1 ]]; then + echo "" + echo -e "$error Found 1 file in POTFILES without translatable strings:" + ret=1 + elif [[ $potfiles_count -ne 0 ]]; then + echo "" + echo -e "$error Found $potfiles_count files in POTFILES without translatable strings:" + ret=1 + fi + for file in ${ui_potfiles[@]}; do + echo $file + done + for file in ${rs_potfiles[@]}; do + echo $file + done + + let files_count=$((${#ui_files[@]} + ${#rs_files[@]})) + if [[ $files_count -eq 1 ]]; then + echo "" + echo -e "$error Found 1 file with translatable strings not present in POTFILES:" + ret=1 + elif [[ $files_count -ne 0 ]]; then + echo "" + echo -e "$error Found $files_count with translatable strings not present in POTFILES:" + ret=1 + fi + for file in ${ui_files[@]}; do + echo $file + done + for file in ${rs_files[@]}; do + echo $file + done + + if [[ ret -eq 1 ]]; then + echo "" + echo -e " Checking po/POTFILES.in result: $fail" + echo "Please fix the above issues" + exit 1 + fi + + # Check sorted alphabetically + to_sort=("${potfiles[@]}") + sort + for i in ${!potfiles[@]}; do + if [[ "${potfiles[$i]}" != "${to_sort[$i]}" ]]; then + echo -e "$error Found file '${potfiles[$i]}' before '${to_sort[$i]}' in POTFILES" + ret=1 + break + fi + done + + if [[ ret -eq 1 ]]; then + echo "" + echo -e " Checking po/POTFILES.in result: $fail" + echo "Please fix the above issues" + exit 1 + else + echo -e " Checking po/POTFILES.in result: $ok" + fi +} + +# Check if files in src/paket.gresource.xml are sorted alphabetically. +check_resources() { + echo -e "$Checking paket.gresource.xml…" + + local ret=0 + + # Get files. + regex="(.*)" + while read -r line; do + if [[ $line =~ $regex ]]; then + files+=("${BASH_REMATCH[1]}") + fi + done < paket.gresource.xml + + # Check sorted alphabetically + to_sort=("${files[@]}") + sort + for i in ${!files[@]}; do + if [[ "${files[$i]}" != "${to_sort[$i]}" ]]; then + echo -e "$error Found file '${files[$i]#src/}' before '${to_sort[$i]#src/}' in paket.gresource.xml" + ret=1 + break + fi + done + + if [[ ret -eq 1 ]]; then + echo "" + echo -e " Checking paket.gresource.xml result: $fail" + echo "Please fix the above issues" + exit 1 + else + echo -e " Checking paket.gresource.xml result: $ok" + fi +} + +# Check arguments +while [[ "$1" ]]; do case $1 in + -f | --force-install ) + force_install=1 + ;; + -v | --verbose ) + verbose=1 + ;; + -h | --help ) + show_help + exit 0 + ;; + *) + show_help >&2 + exit 1 +esac; shift; done + +# Run +check_cargo +echo "" +run_rustfmt +echo "" +# TODO: typos +#run_typos +#echo "" +check_potfiles +echo "" +check_resources +echo "" diff --git a/build-aux/dist-vendor.sh b/build-aux/dist-vendor.sh new file mode 100755 index 0000000..8bce1e1 --- /dev/null +++ b/build-aux/dist-vendor.sh @@ -0,0 +1,13 @@ +#!/bin/sh + +# SPDX-FileCopyrightText: 2019 Christopher Davis +# SPDX-License-Identifier: GPL-3.0-or-later + +export SOURCE_ROOT="$1" +export DIST="$2" + +cd "$SOURCE_ROOT" +mkdir "$DIST"/.cargo +cargo vendor | sed 's/^directory = ".*"/directory = "vendor"/g' > $DIST/.cargo/config +# Move vendor into dist tarball directory +mv vendor "$DIST" diff --git a/build.rs b/build.rs new file mode 100644 index 0000000..058d883 --- /dev/null +++ b/build.rs @@ -0,0 +1,115 @@ +use std::{env::var, path::Path}; + +enum BuildEnv { + Meson, + Direct, +} +struct BuildContext { + profile: String, + application_id: String, + pkgdata_dir: String, + locale_dir: String, + gettext_package: String, + version: String, + config_init_code: String, +} + +fn env_changed_prefix(key: &str) { + println!("cargo::rerun-if-env-changed=PAKET_{}", key); +} + +fn env_changed(key: &str) { + println!("cargo::rerun-if-env-changed={}", key); +} + +fn env_get_prefix(key: &str) -> String { + env_get(format!("PAKET_{}", key).as_str()) +} + +fn env_get(key: &str) -> String { + var(key).expect(format!("{} not found, but required by PAKET_BUILD_BY", key).as_str()) +} + +fn main() { + let env = var("PAKET_BUILY_BY") + .map_or(Some(BuildEnv::Direct), |string| { + if string.eq_ignore_ascii_case("meson") { + Some(BuildEnv::Meson) + } else if string.eq_ignore_ascii_case("direct") || string.eq_ignore_ascii_case("cargo") + { + Some(BuildEnv::Direct) + } else { + None + } + }) + .expect("PAKET_BUILT_BY doesn't contain a valid build target"); + // shouldn't happen theortically + env_changed_prefix("BUILT_BY"); + + println!("cargo::rerun-if-changed=build.rs"); + + let out_dir = std::env::var("OUT_DIR").unwrap(); + let context = match env { + BuildEnv::Meson => { + env_changed("PROFILE"); + env_changed_prefix("APPLICATION_ID"); + env_changed_prefix("PKGDATADIR"); + env_changed_prefix("LOCALEDIR"); + env_changed_prefix("GETTEXT_PACKAGE"); + env_changed_prefix("VERSION"); + + BuildContext { + profile: env_get("PROFILE"), + application_id: env_get_prefix("APPLICATION_ID"), + pkgdata_dir: env_get_prefix("PKGDATADIR"), + locale_dir: env_get_prefix("LOCALEDIR"), + gettext_package: env_get_prefix("GETTEXT_PACKAGE"), + version: env_get_prefix("VERSION"), + config_init_code: "".to_string(), + } + } + BuildEnv::Direct => { + println!("cargo::rerun-if-changed=assets"); + + glib_build_tools::compile_resources(&["."], "paket.gresource.xml", "paket.gresource"); + + BuildContext { + profile: env_get("PROFILE"), + application_id: "de.j4ne.Paket".to_string(), + version: env_get("CARGO_PKG_VERSION"), + pkgdata_dir: format!("{}/pkgdata", env_get("OUT_DIR")), + locale_dir: format!("{}/locale", env_get("OUT_DIR")), + gettext_package: "paket".to_string(), + config_init_code: format!( + "gtk::gio::resources_register_include!(\"paket.gresource\").unwrap();" + ), + } + } + }; + + let dest_path = Path::new(&out_dir).join("config.rs"); + std::fs::write( + &dest_path, + format!( + " +pub static VERSION: &str = \"{version}\"; +pub static GETTEXT_PACKAGE: &str = \"{gettext_package}\"; +pub static LOCALEDIR: &str = \"{locale_dir}\"; +pub static PKGDATADIR: &str = \"{pkgdata_dir}\"; +pub static APPLICATION_ID: &str = \"{application_id}\"; +pub static PROFILE: &str = \"{profile}\"; + +pub fn init() {{ + {code_init} +}}", + version = context.version, + gettext_package = context.gettext_package, + locale_dir = context.locale_dir, + pkgdata_dir = context.pkgdata_dir, + application_id = context.application_id, + profile = context.profile, + code_init = context.config_init_code, + ), + ) + .unwrap(); +} diff --git a/default.nix b/default.nix index 062ae31..2094126 100644 --- a/default.nix +++ b/default.nix @@ -1,33 +1,32 @@ { pkgs ? import {} }: let package = { - rustPlatform, - nix-gitignore, - pkg-config, - openssl, - glib, - gdk-pixbuf, - graphene, cairo, - pango, - gtk4, - libsoup_3, - libadwaita, - webkitgtk_6_0, - libseccomp, - wrapGAppsHook4, + gdk-pixbuf, glib-networking, + glib, + graphene, + gst_all_1, + gtk4, + libadwaita, + libcamera, + libseccomp, + libsoup_3, + nix-gitignore, + openssl, + pango, + pipewire, + pkg-config, + rustPlatform, + webkitgtk_6_0, + wrapGAppsHook4, }: rustPlatform.buildRustPackage { pname = "paket"; - version = "unstable-2024-09-28"; + version = "unstable-2025-01-22"; src = nix-gitignore.gitignoreSource [] ./.; cargoLock = { lockFile = ./Cargo.lock; - outputHashes = { - "relm4-0.9.0" = "sha256-iFxi2ZWdzWtui85IOfMIfyuPDbQO69u5VLk0a9ebatM="; - "relm4-icons-0.9.0" = "sha256-UUo1wIvJL2MryUFICnmVq6LoPuNaZ9nKcNGCCF8cx+k="; - }; }; nativeBuildInputs = [ @@ -37,6 +36,13 @@ wrapGAppsHook4 ]; + preFixup = '' + gappsWrapperArgs+=( + # vp8enc preset + --prefix GST_PRESET_PATH : "${gst_all_1.gst-plugins-good}/share/gstreamer-1.0/presets" + ) + ''; + buildInputs = [ # Building openssl @@ -48,6 +54,15 @@ libadwaita webkitgtk_6_0 # for JSC + # scanner + libcamera # for the gstreamer plugin + pipewire # for device provider + gst_all_1.gst-plugins-bad + gst_all_1.gst-plugins-base + gst_all_1.gst-plugins-good + gst_all_1.gst-plugins-rs # for gtk4paintablesink + gst_all_1.gstreamer + # Linking libseccomp diff --git a/icons.toml b/icons.toml deleted file mode 100644 index 6a2ae04..0000000 --- a/icons.toml +++ /dev/null @@ -1,3 +0,0 @@ -app_id = "de.j4ne.Paket" - -icons = ["plus", "minus", "package-x-generic", "mail", "loupe-large", "person", "copy", "qr-code-scanner"] diff --git a/libpaket/Cargo.toml b/libpaket/Cargo.toml index 4d529aa..9b2f287 100644 --- a/libpaket/Cargo.toml +++ b/libpaket/Cargo.toml @@ -6,32 +6,32 @@ license.workspace = true version.workspace = true [dependencies] -aes-gcm = { version = "0.10.3", optional = true } -ed25519-dalek = { version = "2.1.0", optional = true } -hmac = { version = "0.12.1", optional = true } -num_enum = { version = "0.7", optional = true } +aes-gcm = { workspace = true, optional = true } +ed25519-dalek = { workspace = true, optional = true } +hmac = { workspace = true, optional = true } +num_enum = { workspace = true, optional = true } # TODO: Consolidate? -rand = "0.8.5" -random-string = "1.1.0" +rand = { workspace = true } +random-string = { workspace = true } reqwest = { workspace = true } -secrecy = { workspace = true} +secrecy = { workspace = true } serde = { workspace = true } serde_json = { workspace = true } -serde_repr = { version = "0.1.18", optional = true } -serde_ignored = "0.1" -url = "2.5.0" -base64 = "0.22" +serde_repr = { workspace = true, optional = true } +serde_ignored = { workspace = true } +url = { workspace = true } +base64 = { workspace = true } # TODO: consider splitting login.rs refresh_token and authorization_token # (sha2 and urlencoding only used with authorization_token) # sha2 also used in briefankuendigung and packstation_register_regtoken -sha2 = "0.10.8" -urlencoding = "2.1.3" +sha2 = { workspace = true } +urlencoding = { workspace = true } uuid = { workspace = true, features = ["serde"], optional = true } -serde_newtype = "0.1.1" -thiserror = "1.0.56" +serde_newtype = { workspace = true } +thiserror = { workspace = true } [features] default = [ @@ -42,6 +42,8 @@ default = [ unstable = [] +private_tests = [] + advices = [ #"dep:sha2", "dep:uuid", @@ -72,7 +74,6 @@ locker_register_base = [ "locker_base", "dep:hmac", #"dep:sha2", - ] locker_register_regtoken = [ diff --git a/libpaket/README.md b/libpaket/README.md index 4f0dbcf..b7f3ab4 100644 --- a/libpaket/README.md +++ b/libpaket/README.md @@ -22,6 +22,7 @@ It's recommended that consumers of this crate are always tracking the latest ver In the examples error-handling is ignored for simplicity. You don’t want to do that. ### Getting mail notifications (Briefankündigung) + ```rust // Requires a logged-in user. let token: libpaket::login::DHLIdToken; diff --git a/libpaket/src/advices/mod.rs b/libpaket/src/advices/mod.rs index f5c4494..03dfed4 100644 --- a/libpaket/src/advices/mod.rs +++ b/libpaket/src/advices/mod.rs @@ -1,5 +1,5 @@ -mod www; mod briefankuendigung; +mod www; +pub use briefankuendigung::*; pub use www::*; -pub use briefankuendigung::*; \ No newline at end of file diff --git a/libpaket/src/advices/www.rs b/libpaket/src/advices/www.rs index 7f774af..c231e4e 100644 --- a/libpaket/src/advices/www.rs +++ b/libpaket/src/advices/www.rs @@ -32,27 +32,28 @@ newtype! { #[serde(rename_all = "camelCase")] pub struct AdvicesResponse { // access_token_url, basic_auth, grant_token is null if no advices are available + pub(super) delay_text: Option, pub(super) access_token_url: Option, pub(super) basic_auth: Option, - current_advice: Option, + current_advice: Vec, pub(super) grant_token: Option, old_advices: Vec, } impl AdvicesResponse { pub fn has_any_advices(&self) -> bool { - self.has_current_advice() || self.has_old_advices() + self.has_current_advices() || self.has_old_advices() } - pub fn has_current_advice(&self) -> bool { - self.current_advice.is_some() + pub fn has_current_advices(&self) -> bool { + self.current_advice.len() > 0 } pub fn has_old_advices(&self) -> bool { self.old_advices.len() > 0 } - pub fn get_current_advice(&self) -> Option<&AdvicesList> { + pub fn get_current_advices(&self) -> &Vec { self.current_advice.as_ref() } @@ -66,21 +67,33 @@ fn endpoint_advices() -> url::Url { } impl crate::www::WebClient { - // FIXME: more error parsing - pub async fn advices(&self, dhli: &crate::login::DHLIdToken) -> crate::LibraryResult { - let res = request!(self.web_client, endpoint_advices, - header("Cookie", CookieHeaderValueBuilder::new().add_dhli(dhli).add_dhlcs(dhli).build_string()) + // FIXME: more error parsing + pub async fn advices( + &self, + dhli: &crate::login::DHLIdToken, + ) -> crate::LibraryResult { + let res = request!( + self.web_client, + endpoint_advices, + header( + "Cookie", + CookieHeaderValueBuilder::new() + .add_dhli(dhli) + .add_dhlcs(dhli) + .build_string() + ) ); - + let res = parse_json_response!(res, AdvicesResponse); - + if res.access_token_url.is_some() { - if res.access_token_url.as_ref().unwrap().as_str() != crate::advices::endpoint_access_tokens() { + if res.access_token_url.as_ref().unwrap().as_str() + != crate::advices::endpoint_access_tokens() + { return Err(crate::LibraryError::APIChange); } } Ok(res) } - } diff --git a/libpaket/src/constants.rs b/libpaket/src/constants.rs index d1db197..409b5e0 100644 --- a/libpaket/src/constants.rs +++ b/libpaket/src/constants.rs @@ -1,4 +1,3 @@ - pub fn app_version() -> &'static str { "9.9.1.95 (a72fec7be)" } @@ -8,7 +7,11 @@ pub fn linux_android_version() -> &'static str { } pub fn webview_user_agent() -> String { - format!("{} [DHL Paket Android App/{}]", web_user_agent(), app_version()) + format!( + "{} [DHL Paket Android App/{}]", + web_user_agent(), + app_version() + ) } pub fn device_string() -> &'static str { @@ -26,4 +29,4 @@ pub fn okhttp_user_agent() -> String { pub fn web_user_agent() -> String { format!("Mozilla/5.0 ({}; {}; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/117.0.0.0 Mobile Safari/537.36", linux_android_version(), device_string()) -} \ No newline at end of file +} diff --git a/libpaket/src/lib.rs b/libpaket/src/lib.rs index 9cc7b34..f5b6e6c 100644 --- a/libpaket/src/lib.rs +++ b/libpaket/src/lib.rs @@ -28,8 +28,9 @@ pub mod tracking; #[cfg(feature = "locker_ble")] pub use locker::LockerClient; -/*#[cfg(test)] -pub(crate) mod private;*/ +#[cfg(feature = "private_tests")] +#[cfg(test)] +pub(crate) mod private; use thiserror::Error; @@ -64,7 +65,7 @@ impl From for LibraryError { impl From for LibraryError { fn from(value: common::APIError) -> Self { match value.error { - common::APIErrorType::InvalidGrant => Self::Unauthorized + common::APIErrorType::InvalidGrant => Self::Unauthorized, } } } diff --git a/libpaket/src/locker/command.rs b/libpaket/src/locker/command.rs index b4d085e..7162a7b 100644 --- a/libpaket/src/locker/command.rs +++ b/libpaket/src/locker/command.rs @@ -1,6 +1,6 @@ +use super::utils::{PrimitiveBuilder, PrimitiveReader}; use num_enum::TryFromPrimitive; use uuid::Uuid; -use super::utils::{PrimitiveBuilder, PrimitiveReader}; use crate::{LibraryError, LibraryResult}; @@ -45,7 +45,7 @@ const REQUESTS: [CommandType; 10] = [ ]; struct ResponseInitSession { - raw_command: Command + raw_command: Command, } enum Response { @@ -57,7 +57,10 @@ impl TryFrom for Response { fn try_from(value: Command) -> Result { if REQUESTS.binary_search(&value.r#type).is_ok() { - return Err(LibraryError::InvalidArgument("TryFrom for Response: CommandType is a Request (expected Response)".to_string())) + return Err(LibraryError::InvalidArgument( + "TryFrom for Response: CommandType is a Request (expected Response)" + .to_string(), + )); } todo!() } @@ -74,12 +77,11 @@ impl TryFrom for Response { // Checksum function - pub struct Command { r#type: CommandType, payload: Vec, init_vector: Vec, - metadata: Vec + metadata: Vec, } impl Command { @@ -105,21 +107,26 @@ impl Command { pub fn parse(bin: Vec) -> LibraryResult { // command byte + message length + 3 empty message arguments (array with size 0) + 2 checksum bytes - let to_few_bytes = LibraryError::InvalidArgument("Command::parse: Invalid vec.len() (to few bytes)".to_string()); + let to_few_bytes = LibraryError::InvalidArgument( + "Command::parse: Invalid vec.len() (to few bytes)".to_string(), + ); if bin.len() < 1 + 4 + (4 * 3) + 2 { - return Err(to_few_bytes) + return Err(to_few_bytes); } - + { - let checksum = { + let checksum = { PrimitiveReader { offset: bin.len() - 2, vec: bin.as_slice(), - }.read_u16() + } + .read_u16() }; if checksum != Self::checksum(&bin[0..bin.len() - 2]) { - return Err(LibraryError::InvalidArgument("Command::parse: Invalid checksum".to_string())); + return Err(LibraryError::InvalidArgument( + "Command::parse: Invalid checksum".to_string(), + )); } } @@ -130,14 +137,16 @@ impl Command { let r#type = reader.read_u8(); let Ok(r#type) = CommandType::try_from_primitive(r#type) else { - return Err(LibraryError::DecodeError("unable determine CommandType".to_string())); + return Err(LibraryError::DecodeError( + "unable determine CommandType".to_string(), + )); }; let size_of_message = reader.read_u32() as usize; if reader.left_to_process() < size_of_message { return Err(to_few_bytes); } - + let payload: Vec = reader.read_arr_from_len(); let init_vector = reader.read_arr_from_len(); let metadata = reader.read_arr_from_len(); @@ -152,21 +161,21 @@ impl Command { pub fn finish(self) -> Vec { let vec1 = PrimitiveBuilder::new() - .write_array_with_len(&self.payload) - .write_array_with_len(&self.init_vector) - .write_array_with_len(&self.metadata) - .finish(); + .write_array_with_len(&self.payload) + .write_array_with_len(&self.init_vector) + .write_array_with_len(&self.metadata) + .finish(); let vec2 = PrimitiveBuilder::new() - .write_u8(self.r#type as u8) - .write_u32(vec1.len() as u32 + 2) - .write_array(&vec1) - .finish(); + .write_u8(self.r#type as u8) + .write_u32(vec1.len() as u32 + 2) + .write_array(&vec1) + .finish(); PrimitiveBuilder::new() - .write_array(&vec2) - .write_u16(Self::checksum(vec2.as_slice())) - .finish() + .write_array(&vec2) + .write_u16(Self::checksum(vec2.as_slice())) + .finish() } fn assert_only_decimal(str: &String) { @@ -183,15 +192,15 @@ impl Command { // de.dhl.paket does some kinky string conversion - let vec = Vec::::new().into_iter() - .chain(fields.0.to_be_bytes().into_iter()) - .chain(fields.1.to_be_bytes().into_iter()) - .chain(fields.2.to_be_bytes().into_iter()) - .chain(fields.3.to_vec().into_iter()) - .collect::>(); + let vec = Vec::::new() + .into_iter() + .chain(fields.0.to_be_bytes().into_iter()) + .chain(fields.1.to_be_bytes().into_iter()) + .chain(fields.2.to_be_bytes().into_iter()) + .chain(fields.3.to_vec().into_iter()) + .collect::>(); - let new_vec = PrimitiveBuilder::new() - .write_array_with_len(&vec).finish(); + let new_vec = PrimitiveBuilder::new().write_array_with_len(&vec).finish(); Ok(Command { r#type: CommandType::InitSessionRequest, @@ -202,9 +211,12 @@ impl Command { } } -/*#[cfg(test)] +#[cfg(feature = "private_tests")] +#[cfg(test)] mod test { - use crate::private::sample_packstation::{INIT_SESSION_REQUEST, INIT_SESSION_RESPONSE, INIT_SESSION_SERVICE_UUID}; + use crate::private::sample_packstation::{ + INIT_SESSION_REQUEST, INIT_SESSION_RESPONSE, INIT_SESSION_SERVICE_UUID, + }; use super::{Command, CommandType}; @@ -215,7 +227,10 @@ mod test { corrected.push(b as u8); } - let command = Command::init_session_request(uuid::uuid!(INIT_SESSION_SERVICE_UUID).try_into().unwrap()).unwrap(); + let command = Command::init_session_request( + uuid::uuid!(INIT_SESSION_SERVICE_UUID).try_into().unwrap(), + ) + .unwrap(); assert_eq!(command.finish().as_slice(), corrected.as_slice()) } @@ -229,5 +244,4 @@ mod test { let command = Command::parse(corrected); } - -}*/ \ No newline at end of file +} diff --git a/libpaket/src/locker/crypto.rs b/libpaket/src/locker/crypto.rs index 384491d..6b9a6c1 100644 --- a/libpaket/src/locker/crypto.rs +++ b/libpaket/src/locker/crypto.rs @@ -43,7 +43,7 @@ impl Seed { let mut rng = rand::thread_rng(); let mut bytes: Vec = vec![0; 32]; rng.fill_bytes(bytes.as_mut_slice()); - Seed (bytes) + Seed(bytes) } pub fn as_bytes(&self) -> &[u8; 32] { @@ -61,6 +61,15 @@ impl CustomerKeySeed { } } + pub(crate) fn from_partial(postnumber: &String, seed: Vec, uuid: &Uuid) -> Self { + CustomerKeySeed { + postnumber: postnumber.clone(), + seed: SecretBox::new(Box::new(Seed::from(seed))), + uuid: uuid.clone(), + device_id: None, + } + } + pub fn from(postnumber: &String, seed: Vec, uuid: &Uuid, device_id: String) -> Self { CustomerKeySeed { postnumber: postnumber.clone(), diff --git a/libpaket/src/locker/register_base.rs b/libpaket/src/locker/register_base.rs index fcf6146..ff2f738 100644 --- a/libpaket/src/locker/register_base.rs +++ b/libpaket/src/locker/register_base.rs @@ -68,7 +68,6 @@ pub enum APIRegisterError { UserCanNotYetRegister = 21, } - #[derive(Serialize, Debug)] #[serde(rename_all = "camelCase")] pub(super) struct RegistrationCommonDevice { diff --git a/libpaket/src/locker/register_regtoken.rs b/libpaket/src/locker/register_regtoken.rs index 28f6419..5878594 100644 --- a/libpaket/src/locker/register_regtoken.rs +++ b/libpaket/src/locker/register_regtoken.rs @@ -4,8 +4,11 @@ use hmac::{digest::CtOutput, Mac, SimpleHmac}; use sha2::Sha256; use crate::common::APIResult; -use crate::locker::register::{DeviceMetadata, DeviceRegistrationResponse, RegToken, RegistrationCommonDevice, RegistrationOption, RegistrationPayload}; use crate::locker::crypto::CustomerKeySeed; +use crate::locker::register::{ + DeviceMetadata, DeviceRegistrationResponse, RegToken, RegistrationCommonDevice, + RegistrationOption, RegistrationPayload, +}; use crate::login::DHLIdToken; use crate::LibraryResult; @@ -28,16 +31,18 @@ impl RegistrationRegToken { reg_token: &RegToken, device_metadata: DeviceMetadata, ) -> Self { - let challange_bin = general_purpose::STANDARD.decode(begin_registration.challenge.as_str()).unwrap(); + let challange_bin = general_purpose::STANDARD + .decode(begin_registration.challenge.as_str()) + .unwrap(); let mut mac = reg_token.hmac(); mac.update(challange_bin.as_slice()); - + let verifier_bin: CtOutput> = mac.finalize(); let verifier_bin = verifier_bin.into_bytes(); let verifier = general_purpose::STANDARD.encode(&verifier_bin); let verifier_signature = customer_key_seed.sign(&verifier_bin); - + Self { challenge: begin_registration.challenge, customer_password: reg_token.customer_password(), @@ -52,7 +57,6 @@ impl RegistrationRegToken { } impl crate::StammdatenClient { - pub async fn register_by_regtoken( &self, dhli: &DHLIdToken, @@ -61,7 +65,6 @@ impl crate::StammdatenClient { device_metadata: DeviceMetadata, reg_token: &crate::locker::register::RegToken, ) -> LibraryResult { - let mut valid = false; for option in registration_payload.registration_options.iter() { if *option == RegistrationOption::ByRegToken { @@ -74,7 +77,7 @@ impl crate::StammdatenClient { customer_key_seed, registration_payload, reg_token, - device_metadata + device_metadata, ); let body = serde_json::to_string(&body).unwrap(); @@ -103,16 +106,15 @@ impl crate::StammdatenClient { } } - fn endpoint_devices_register_by_regtoken() -> &'static str { "https://www.dhl.de/int-stammdaten/public/devices/registerByRegToken" } - -/*#[cfg(test)] +#[cfg(feature = "private_tests")] +#[cfg(test)] mod registration_api { - use std::str::FromStr as _; use base64::Engine as _; + use std::str::FromStr as _; use crate::private::init as private; @@ -120,33 +122,39 @@ mod registration_api { fn regtoken_customer_password() { let regtoken = super::RegToken::parse_from_qrcode_uri(&private::IN_REGTOKEN).unwrap(); assert!(regtoken.customer_password() == private::OUT_CUSTOMER_PASSWORD); - } #[test] - fn register_via_regtoken(){ + fn register_via_regtoken() { let regtoken = super::RegToken::parse_from_qrcode_uri(&private::IN_REGTOKEN).unwrap(); - let customer_key_seed = crate::locker::crypto::CustomerKeySeed::from(&private::IN_POSTNUMBER.to_string(), - base64::engine::general_purpose::STANDARD.decode(&private::IN_SEED_BASE64).unwrap(), - &uuid::Uuid::from_str(&private::IN_KEY_ID).unwrap()); + let customer_key_seed = crate::locker::crypto::CustomerKeySeed::from_partial( + &private::IN_POSTNUMBER.to_string(), + base64::engine::general_purpose::STANDARD + .decode(&private::IN_SEED_BASE64) + .unwrap(), + &uuid::Uuid::from_str(&private::IN_KEY_ID).unwrap(), + ); let registration_payload = super::RegistrationPayload { challenge: private::IN_CHALLENGE.to_string(), registration_options: Vec::new(), }; - + let reg_reg_token = super::RegistrationRegToken::new( &customer_key_seed, registration_payload, ®token, - super::DeviceMetadata { manufacturer_model: "".to_string(), manufacturer_name: "".to_string(), manufacturer_operating_system: "".to_string(), name: "".to_string() } + super::DeviceMetadata { + manufacturer_model: "".to_string(), + manufacturer_name: "".to_string(), + manufacturer_operating_system: "".to_string(), + name: "".to_string(), + }, ); assert!(reg_reg_token.verifier == private::OUT_VERIFIER); assert!(reg_reg_token.verifier_signature == private::OUT_VERIFIERSIGNATURE); assert!(reg_reg_token.device.public_key == private::OUT_DEVICE_PUBKEY); } - } -*/ \ No newline at end of file diff --git a/libpaket/src/locker/regtoken.rs b/libpaket/src/locker/regtoken.rs index dc5afe3..6ea5e56 100644 --- a/libpaket/src/locker/regtoken.rs +++ b/libpaket/src/locker/regtoken.rs @@ -2,7 +2,27 @@ use base64::{engine::general_purpose, Engine as _}; use hmac::{Mac, SimpleHmac}; use sha2::Sha256; -use crate::{LibraryResult, LibraryError}; +use thiserror::Error; + +use crate::LibraryError; + +#[derive(Error, Debug, PartialEq)] +pub enum RegTokenDecodeError { + #[error("RegTokenUri from QR-Code too short")] + LengthTooShort, + #[error("RegTokenUri from QR-Code has invalid magic")] + InvalidMagic, + #[error("RegTokenUri from QR-Code not decodeable (base64)")] + NotBase64, + #[error("RegToken longer than expected")] + LengthTooLong, +} + +impl From for LibraryError { + fn from(value: RegTokenDecodeError) -> Self { + Self::DecodeError(value.to_string()) + } +} pub struct RegToken(Vec); impl secrecy::zeroize::Zeroize for RegToken { @@ -12,23 +32,23 @@ impl secrecy::zeroize::Zeroize for RegToken { } impl RegToken { - pub fn parse_from_qrcode_uri(uri: &str) -> LibraryResult { + pub fn parse_from_qrcode_uri(uri: &str) -> Result { if uri.len() <= 23 { - return Err(LibraryError::DecodeError("RegTokenUri too short".to_string())); + return Err(RegTokenDecodeError::LengthTooShort); } if !uri.starts_with("urn:dhl.de:regtoken:v1:") { - return Err(LibraryError::DecodeError("RegTokenUri has invalid magic".to_string())); + return Err(RegTokenDecodeError::InvalidMagic); } let token = &uri[23..]; let token = general_purpose::STANDARD.decode(token); let Ok(mut token) = token else { - return Err(LibraryError::DecodeError("RegTokenUri not decodeable (base64)".to_string())); + return Err(RegTokenDecodeError::NotBase64); }; if token.len() > 64 { - return Err(LibraryError::DecodeError("RegToken longer than expected".to_string())); + return Err(RegTokenDecodeError::LengthTooLong); } if token.len() < 32 { diff --git a/libpaket/src/locker/types.rs b/libpaket/src/locker/types.rs index 615cdae..4fbf4ff 100644 --- a/libpaket/src/locker/types.rs +++ b/libpaket/src/locker/types.rs @@ -3,7 +3,13 @@ use crate::{LibraryError, LibraryResult}; // 601e7028-0565- pub static LOCKER_SERVICE_UUID_PREFIX: (u32, u16) = (0x601e7028, 0x0565); -pub static LOCKER_SERVICE_UUID: uuid::Uuid = uuid::Builder::from_fields(LOCKER_SERVICE_UUID_PREFIX.0, LOCKER_SERVICE_UUID_PREFIX.1, 0, &[0;8]).into_uuid(); +pub static LOCKER_SERVICE_UUID: uuid::Uuid = uuid::Builder::from_fields( + LOCKER_SERVICE_UUID_PREFIX.0, + LOCKER_SERVICE_UUID_PREFIX.1, + 0, + &[0; 8], +) +.into_uuid(); pub enum LockerVendor { Keba, @@ -38,10 +44,7 @@ pub struct LockerDevice { } impl LockerDevice { - pub fn new( - service_uuid: LockerServiceUUID, - service_data: &Vec, - ) -> LibraryResult { + pub fn new(service_uuid: LockerServiceUUID, service_data: &Vec) -> LibraryResult { mini_assert_inval!(service_data.len() == 14); let mut reader = PrimitiveReader { diff --git a/libpaket/src/login/constants.rs b/libpaket/src/login/constants.rs index af6bf31..5b29a92 100644 --- a/libpaket/src/login/constants.rs +++ b/libpaket/src/login/constants.rs @@ -40,9 +40,7 @@ pub mod token { ] } - pub fn revoke_form( - token: String - ) -> Vec<(String, String)> { + pub fn revoke_form(token: String) -> Vec<(String, String)> { vec![ ("token".into(), token), ("client_id".into(), client_id().into()), @@ -81,10 +79,13 @@ pub mod token { } pub mod logout { - use crate::{constants::web_user_agent, login::{ - constants::{client_id, redirect_uri_logout}, - DHLIdToken, - }}; + use crate::{ + constants::web_user_agent, + login::{ + constants::{client_id, redirect_uri_logout}, + DHLIdToken, + }, + }; pub fn form(id_token: &DHLIdToken) -> Vec<(String, String)> { vec![ diff --git a/libpaket/src/login/openid_token.rs b/libpaket/src/login/openid_token.rs index cca3aa3..6825a94 100644 --- a/libpaket/src/login/openid_token.rs +++ b/libpaket/src/login/openid_token.rs @@ -52,7 +52,8 @@ impl OpenIDToken { // requires valid system time pub fn is_expired(&self) -> bool { - let duration: Result = std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH); + let duration: Result = + std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH); let Ok(duration) = duration else { return true; @@ -104,14 +105,14 @@ where where E: serde::de::Error, { - let splits = s.split_terminator(".").collect::>(); let claims = serde_json::from_slice::>( general_purpose::URL_SAFE_NO_PAD .decode(splits[1]) .unwrap() .as_slice(), - ).unwrap(); + ) + .unwrap(); Ok(OpenIDToken { token: s.to_string(), diff --git a/libpaket/src/login/utils.rs b/libpaket/src/login/utils.rs index fdb83d7..f7a8416 100644 --- a/libpaket/src/login/utils.rs +++ b/libpaket/src/login/utils.rs @@ -38,4 +38,4 @@ impl CodeVerfier { pub fn code_verfier(&self) -> String { self.client_secret.clone() } -} \ No newline at end of file +} diff --git a/libpaket/src/stammdaten.rs b/libpaket/src/stammdaten.rs index 8bc408e..dbe4cc0 100644 --- a/libpaket/src/stammdaten.rs +++ b/libpaket/src/stammdaten.rs @@ -142,6 +142,5 @@ pub struct CustomerDataFull { pub requested_services: Option>, //pub customer_actions: Option, - pub address: CustomerAddress, } diff --git a/libpaket/src/tracking.rs b/libpaket/src/tracking.rs index 08fd91e..bd0d786 100644 --- a/libpaket/src/tracking.rs +++ b/libpaket/src/tracking.rs @@ -85,7 +85,7 @@ struct SendungEmpfaenger { #[derive(Deserialize)] #[serde(rename_all = "camelCase")] struct SendungsInfo { - gesuchte_sendungsnummer: String, + gesuchte_sendungsnummer: Option, sendungsrichtung: String, sendungsname: Option, sendungsliste: Option, @@ -169,16 +169,22 @@ struct Nachhaltigkeitsstatus { klimafreundlicher_empfang: bool, } +#[derive(Deserialize)] +#[serde(rename_all = "camelCase")] +struct Sendungsnummern { + sendungsnummer: String, +} + #[derive(Deserialize)] #[serde(rename_all = "camelCase")] struct SendungDetails { - quelle: SendungsQuelle, // PAKET + quelle: Option, // PAKET express_sendung: Option, is_shipper_plz: Option, ist_zugestellt: Option, retoure: Option, ruecksendung: Option, - sendungsverlauf: SendungsVerlauf, + sendungsverlauf: Option, show_quality_level_hint: Option, two_man_handling: Option, unplausibel: Option, @@ -192,7 +198,7 @@ struct SendungDetails { international: Option, pan_empfaenger: Option, produkt_name: Option, - //sendungsnummern: (), + sendungsnummern: Option, services: Option, show_digital_notification_cta_hint: Option, warenpost: Option, @@ -321,7 +327,7 @@ pub struct Shipment { pub needs_shipment_date: bool, pub needs_plz: bool, - pub quelle: SendungsQuelle, + pub quelle: Option, // probably not optional pub international: Option, @@ -330,7 +336,7 @@ pub struct Shipment { pub special: ShipmentSpecialDetails, - pub history: SendungsVerlauf, + pub history: Option, pub error: Option, } diff --git a/libpaket/src/utils.rs b/libpaket/src/utils.rs index 7f0d9b8..57f2a7d 100644 --- a/libpaket/src/utils.rs +++ b/libpaket/src/utils.rs @@ -27,8 +27,13 @@ impl CookieHeaderValueBuilder { } pub fn add_key_value(mut self, key: String, value: String) -> Self { - self.list - .push((key, value)); + self.list.push((key, value)); + + self + } + + pub fn add_key_value_str(mut self, key: &str, value: &str) -> Self { + self.list.push((key.to_string(), value.to_string())); self } @@ -188,6 +193,7 @@ macro_rules! parse_json_response { ($res: expr, $type: ty) => {{ let status = $res.status(); let res: String = $res.text().await.unwrap(); + println!("text: {}", res); // Catch HTML Response early if status == 200 { let res = res.clone(); diff --git a/meson.build b/meson.build new file mode 100644 index 0000000..216a3af --- /dev/null +++ b/meson.build @@ -0,0 +1,83 @@ +# SPDX-FileCopyrightText: 2022 Emmanuele Bassi +# SPDX-License-Identifier: GPL-3.0-or-later + +project('paket', 'rust', + version: '2024.10', + license: ['GPL-3.0'], + meson_version: '>= 0.59.0', + default_options: [ 'warning_level=2', ], +) + +dependency('gtk4', version: '>= 4.16') +dependency('libadwaita-1', version: '>= 1.6') +dependency('gstreamer-1.0', version: '>= 1.20') +dependency('gstreamer-video-1.0', version: '>= 1.20') +dependency('webkitgtk-6.0') +# Needed for camerabin +dependency('gstreamer-plugins-bad-1.0', version: '>= 1.20') + + +i18n = import('i18n') +gnome = import('gnome') +fs = import('fs') + +cargo = find_program('cargo', required: true) + +if get_option('profile') == 'development' + profile = '.Devel' + if fs.is_dir('.git') + vcs_tag = run_command('git', 'rev-parse', '--short', 'HEAD', check: true).stdout().strip() + if vcs_tag == '' + version_suffix = '-devel' + else + version_suffix = '-@0@'.format(vcs_tag) + endif + else + version_suffix = '-devel' + endif +else + profile = '' + version_suffix = '' +endif + +application_id = 'de.j4ne.Paket@0@'.format(profile) + + +pkgdatadir = get_option('prefix') / get_option('datadir') / meson.project_name() + +gnome.compile_resources('paket', + 'paket.gresource.xml', + gresource_bundle: true, + install: true, + install_dir: pkgdatadir, +) + +subdir('src') +subdir('po') + +meson.add_dist_script( + 'build-aux/dist-vendor.sh', + meson.project_source_root(), + meson.project_build_root() / 'meson-dist' / '@0@-@1@'.format(meson.project_name(), meson.project_version()), +) + +gnome.post_install( + glib_compile_schemas: true, + gtk_update_icon_cache: true, + update_desktop_database: true, +) + +summary({ + 'prefix': get_option('prefix'), + 'libdir': get_option('libdir'), + 'datadir': get_option('datadir'), + 'bindir': get_option('bindir'), + }, + section: 'Directories', +) + +summary({ + 'Profile': get_option('profile'), + }, + section: 'Build options', +) diff --git a/meson_options.txt b/meson_options.txt new file mode 100644 index 0000000..73c0e59 --- /dev/null +++ b/meson_options.txt @@ -0,0 +1,12 @@ +# SPDX-FileCopyrightText: 2022 Emmanuele Bassi +# SPDX-License-Identifier: GPL-3.0-or-later + +option ( + 'profile', + type: 'combo', + choices: [ + 'default', + 'development' + ], + value: 'default', +) diff --git a/paket-utils/Cargo.toml b/paket-utils/Cargo.toml new file mode 100644 index 0000000..b4f79c4 --- /dev/null +++ b/paket-utils/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "paket-utils" +authors.workspace = true +edition.workspace = true +license.workspace = true +version.workspace = true + +[dependencies] +glib = { workspace = true } diff --git a/paket-utils/src/lib.rs b/paket-utils/src/lib.rs new file mode 100644 index 0000000..f76d63e --- /dev/null +++ b/paket-utils/src/lib.rs @@ -0,0 +1,65 @@ +// Based on https://gitlab.gnome.org/GNOME/libsoup/-/blob/master/libsoup/soup-misc.c?ref_type=heads#L11 +// Which itself is based on epiphany-webkit (ephy_langs_append_languages()) + +use glib; + +fn map_posix_lang_to_rfc2616(posix_lang: T) -> Option { + posix_lang.run_with_gstr(|lang| { + if false + /* Don't include charset variants, etc */ + || lang.contains('.') + || lang.contains('@') + /* Ignore "C" locale, which g_get_language_names() always + * includes as a fallback. */ + || lang.starts_with('C') + { + None + } else { + Some(lang.as_str().to_ascii_lowercase().replace('_', "-")) + } + }) +} + +pub fn system_get_accept_languages() -> Vec { + let langs = glib::language_names(); + + let mut http_langs = langs + .iter() + .filter_map(map_posix_lang_to_rfc2616) + .collect::>(); + if http_langs.len() == 0 { + http_langs.push("de".to_owned()); + } + + if http_langs.len() == 1 { + http_langs + } else { + // Limit to 2 for privacy reasons for now + let len = std::cmp::min(langs.len(), 2); + let mut item = len; + + http_langs + .iter_mut() + .map_while(|string| { + while item <= 0 { + return None; + } + let item_cur = item; + item = item - 1; + + if item_cur != len { + let quality_number = len as f64 / item_cur as f64; + let quality = if quality_number % 0.1 == 0. { + format!(";q=0.{:.1}", quality_number) + } else { + format!(";q=0.{:.2}", quality_number) + }; + string.push_str(&quality); + } + + Some(string) + }) + .map(|item| item.to_owned()) + .collect::>() + } +} diff --git a/paket.gresource.xml b/paket.gresource.xml new file mode 100644 index 0000000..a65b676 --- /dev/null +++ b/paket.gresource.xml @@ -0,0 +1,17 @@ + + + + assets/copy-symbolic.svg + assets/four-arrows-pointing-outward-symbolic.svg + assets/loupe-large-symbolic.svg + assets/mail-symbolic.svg + assets/minus-symbolic.svg + assets/package-x-generic-symbolic.svg + assets/parcel-locker-symbolic.svg + assets/person-symbolic.svg + assets/plus-symbolic.svg + assets/qr-code-scanner-symbolic.svg + + + + \ No newline at end of file diff --git a/paket/Cargo.toml b/paket/Cargo.toml deleted file mode 100644 index 6fdc860..0000000 --- a/paket/Cargo.toml +++ /dev/null @@ -1,25 +0,0 @@ -[package] -name = "paket" -authors.workspace = true -edition.workspace = true -license.workspace = true -version.workspace = true - -[dependencies] -# using git version, for https://github.com/Relm4/Relm4/pull/677 -relm4 = { workspace = true } -relm4-icons = { version = "0.9" } -tracker = "0.2" -adw = { package = "libadwaita", version = "0.7", features = ["v1_6"] } -aperture = "0.7" -webkit = { package = "webkit6", version = "0.4" } -libpaket = { path = "../libpaket" } -glycin = { version = "2.0.0-beta", features = ["gdk4"] } -oo7 = { version = "0.3" } -futures = "0.3" -gtk = { package = "gtk4", version = "0.9", features = ["v4_16"] } -reqwest = { workspace = true } -secrecy = { workspace = true } -serde = { workspace = true } -serde_json = { workspace = true } -uuid = { workspace = true } diff --git a/paket/src/constants.rs b/paket/src/constants.rs deleted file mode 100644 index c5b8963..0000000 --- a/paket/src/constants.rs +++ /dev/null @@ -1 +0,0 @@ -pub const APP_ID: &str = "de.j4ne.Paket"; \ No newline at end of file diff --git a/paket/src/lib.rs b/paket/src/lib.rs deleted file mode 100644 index 7d607fc..0000000 --- a/paket/src/lib.rs +++ /dev/null @@ -1,17 +0,0 @@ -pub mod account; -pub mod advice; -pub mod advices; -pub mod constants; -pub mod keyring; -pub mod login; -pub mod packstation; -pub mod ready; -pub mod scanner; -pub mod tracking; - -pub use login::LoginSharedState; - -pub static LOGIN_BROKER: relm4::MessageBroker = relm4::MessageBroker::new(); -pub fn send_log_out() { - LOGIN_BROKER.send(login::LoginInput::LogOut); -} diff --git a/po/POTFILES.in b/po/POTFILES.in new file mode 100644 index 0000000..3acfe5d --- /dev/null +++ b/po/POTFILES.in @@ -0,0 +1,10 @@ +src/account.rs +src/advices.rs +src/app.rs +src/keyring.rs +src/login.rs +src/packstation.rs +src/ready.rs +src/scanner.rs +src/tracking.rs +src/utils.rs diff --git a/po/meson.build b/po/meson.build new file mode 100644 index 0000000..45b3108 --- /dev/null +++ b/po/meson.build @@ -0,0 +1,15 @@ +# SPDX-FileCopyrightText: 2022 Emmanuele Bassi +# SPDX-License-Identifier: GPL-3.0-or-later + +i18n.gettext( + 'paket', + args: [ + '--keyword=i18n', + '--keyword=i18n_f', + '--keyword=i18n_k', + '--keyword=ni18n:1,2', + '--keyword=ni18n_f:1,2', + '--keyword=ni18n_k:1,2', + ], + preset: 'glib', +) diff --git a/paket/src/account.rs b/src/account.rs similarity index 93% rename from paket/src/account.rs rename to src/account.rs index 7bd6a96..2dc06e8 100644 --- a/paket/src/account.rs +++ b/src/account.rs @@ -2,7 +2,7 @@ use adw::prelude::*; use libpaket::{stammdaten::CustomerDataFull, LibraryError, LibraryResult}; use relm4::{Component, ComponentParts}; -use crate::{send_log_out, LoginSharedState}; +use crate::{i18n::i18n, send_log_out, LoginSharedState}; #[tracker::track] pub struct AccountView { @@ -92,7 +92,7 @@ impl Component for AccountView { append = >k::Button { add_css_class: relm4::css::DESTRUCTIVE_ACTION, - set_label: "Log out", + set_label: &i18n("Log out"), connect_clicked => AccountInput::LogOut, }, @@ -101,7 +101,7 @@ impl Component for AccountView { // Postnumber add = &adw::ActionRow { add_css_class: relm4::css::NUMERIC, - set_subtitle: "Postnummer", + set_subtitle: &i18n("Postnummer"), #[track(model.changed_customer_data_full() && model.get_customer_data_full().is_some())] set_title?: get_str_from_customer_data!(model, post_number).as_ref(), @@ -112,8 +112,8 @@ impl Component for AccountView { #[wrap(Some)] set_child = &adw::ButtonContent { - set_icon_name: relm4_icons::icon_names::COPY, - set_label: "Copy", + set_icon_name: "copy-symbolic", + set_label: &i18n("Copy"), }, connect_clicked => AccountInput::Copy(CopyTargets::PostNumber), @@ -211,10 +211,11 @@ impl Component for AccountView { for service in services { match service { libpaket::stammdaten::CustomerDataService::Packstation => { - sender.output(AccountOutput::HaveService( - AccountServices::PackstationAvailable, - )) - .unwrap(); + sender + .output(AccountOutput::HaveService( + AccountServices::PackstationAvailable, + )) + .unwrap(); } _ => (), }; diff --git a/paket/src/advice.rs b/src/advice.rs similarity index 67% rename from paket/src/advice.rs rename to src/advice.rs index 161b20a..14482c8 100644 --- a/paket/src/advice.rs +++ b/src/advice.rs @@ -39,6 +39,15 @@ impl FactoryComponent for AdviceCard { type CommandOutput = AdviceCardCmds; type ParentWidget = gtk::FlowBox; + /* TODO: + * Action "View in Image viewer": + * Triggers: + * - One click on "enlarge" button + * - Double click on "texture" itself + * (Is there a better way than this?) + * Create temporary file, let application open this file... + */ + view! { #[root] gtk::FlowBoxChild { @@ -46,21 +55,36 @@ impl FactoryComponent for AdviceCard { set_valign: gtk::Align::Start, #[wrap(Some)] - set_child = >k::Overlay { - set_margin_all: 8, - - add_overlay = >k::Spinner { - start: (), - set_align: gtk::Align::Center, - - #[track(self.changed_texture())] - set_visible: self.texture.is_none(), - }, - + set_child = &adw::Clamp { #[wrap(Some)] - set_child = >k::Picture { - #[track(self.changed_texture())] - set_paintable: self.texture.as_ref() + set_child = >k::Overlay { + set_margin_all: 8, + + add_overlay = >k::Spinner { + start: (), + set_align: gtk::Align::Center, + + #[track(self.changed_texture())] + set_visible: self.texture.is_none(), + }, + + add_overlay = >k::Button { + set_valign: gtk::Align::End, + set_halign: gtk::Align::End, + + add_css_class: relm4::css::OSD, + add_css_class: relm4::css::CIRCULAR, + + set_icon_name: "four-arrows-pointing-outward-symbolic", + + set_margin_all: 8, + }, + + #[wrap(Some)] + set_child = >k::Picture { + #[track(self.changed_texture())] + set_paintable: self.texture.as_ref() + } } } } diff --git a/paket/src/advices.rs b/src/advices.rs similarity index 79% rename from paket/src/advices.rs rename to src/advices.rs index 23e1c0f..30ca743 100644 --- a/paket/src/advices.rs +++ b/src/advices.rs @@ -1,19 +1,16 @@ -use std::collections::HashMap; use std::sync::Arc; use futures::lock::Mutex; use libpaket::{AdviceClient as LibraryAdviceClient, LibraryResult}; use adw::prelude::*; -use gio::prelude::*; -use glib::prelude::*; -use gtk::{gio, glib}; -use relm4::prelude::*; use relm4::prelude::*; use relm4::factory::FactoryVecDeque; use crate::advice::{Advice, AdviceCard, AdviceProd}; +use crate::i18n::i18n; +use crate::utils; struct AdviceClientImpl { uat_token: libpaket::advices::UatToken, @@ -72,9 +69,7 @@ impl FactoryComponent for AdvicesDayView { set_label: self.date.as_str(), }, - self.factory.widget() -> >k::FlowBox { - - }, + self.factory.widget() -> >k::FlowBox {}, } } @@ -133,44 +128,36 @@ impl AsyncComponent for AdvicesView { type CommandOutput = AdvicesViewCommands; view! { - gtk::ScrolledWindow { - adw::Clamp { + adw::ViewStack { + #[name = "advices_page_loading"] + add = &utils::status_page(&i18n("Loading mail notifications..."), None, None) -> adw::StatusPage {}, + + #[name = "advices_page_no_available"] + add = &utils::status_page(&i18n("No mail notifications available."), None, None) -> adw::StatusPage {}, + + #[name = "advices_page_have_some"] + add = >k::ScrolledWindow { #[wrap(Some)] - set_child = &adw::ViewStack { - #[name = "advices_page_loading"] - add = &adw::StatusPage { - set_title: "Loading mail notifications...", - }, + set_child = >k::Viewport { + set_vscroll_policy: gtk::ScrollablePolicy::Natural, - #[name = "advices_page_no_available"] - add = &adw::StatusPage { - set_title: "No mail notifications available." - }, - - #[name = "advices_page_have_some"] - add = &adw::Clamp { - #[wrap(Some)] - set_child = >k::ScrolledWindow { - #[wrap(Some)] - set_child = model.factory.widget() -> >k::Box { - set_orientation: gtk::Orientation::Vertical, - - }, - }, - }, - - #[track(model.changed_state())] - set_visible_child: { - let page: >k::Widget = match model.state { - AdvicesViewState::Loading => advices_page_loading.upcast_ref::(), - AdvicesViewState::HaveNone => advices_page_no_available.upcast_ref::(), - AdvicesViewState::HaveSome => advices_page_have_some.upcast_ref::(), - }; - page + #[wrap(Some)] + set_child = model.factory.widget() -> >k::Box { + set_orientation: gtk::Orientation::Vertical, }, }, - } - } + }, + + #[track(model.changed_state())] + set_visible_child: { + let page: >k::Widget = match model.state { + AdvicesViewState::Loading => advices_page_loading.upcast_ref::(), + AdvicesViewState::HaveNone => advices_page_no_available.upcast_ref::(), + AdvicesViewState::HaveSome => advices_page_have_some.upcast_ref::(), + }; + page + }, + }, } async fn init( @@ -204,7 +191,7 @@ impl AsyncComponent for AdvicesView { AdvicesViewInput::Reset => { self.set_state(AdvicesViewState::Loading); self.factory.guard().clear(); - }, + } AdvicesViewInput::Fetch => { self.set_state(AdvicesViewState::Loading); @@ -232,9 +219,9 @@ impl AsyncComponent for AdvicesView { let client = AdviceClient::new(uat_token); - if let Some(current) = advices.get_current_advice() { + for new_n in advices.get_current_advices() { let mut advices_arr = Vec::new(); - for item in ¤t.list { + for item in &new_n.list { advices_arr.push(Advice::Prod(AdviceProd { client: client.clone(), model: item.clone(), @@ -242,7 +229,7 @@ impl AsyncComponent for AdvicesView { } arr.push(AdvicesForDay { - date: current.date.clone(), + date: new_n.date.clone(), advices: advices_arr, }); } diff --git a/paket/src/bin/paket.rs b/src/app.rs similarity index 85% rename from paket/src/bin/paket.rs rename to src/app.rs index 7c8992d..680e62f 100644 --- a/paket/src/bin/paket.rs +++ b/src/app.rs @@ -1,8 +1,8 @@ -use adw::{self, glib, prelude::*}; -use gtk; -use paket::login::{new_login_shared_state, Login, LoginOutput}; -use paket::ready::{Ready, ReadyOutput}; -use relm4::{main_adw_application, prelude::*, AsyncComponentSender, RELM_THREADS}; +use crate::i18n::i18n; +use crate::login::{new_login_shared_state, Login, LoginOutput}; +use crate::ready::{Ready, ReadyOutput}; +use adw::{self, prelude::*}; +use relm4::{main_adw_application, prelude::*, AsyncComponentSender}; #[derive(Debug, PartialEq)] enum AppState { @@ -13,13 +13,13 @@ enum AppState { } #[derive(Debug)] -struct AppError { +pub struct AppError { short: String, long: String, } #[derive(Debug)] -enum AppInput { +pub enum AppInput { AddBreakpoint(adw::Breakpoint), SwitchToLogin, SwitchToLoading, @@ -28,7 +28,7 @@ enum AppInput { } #[tracker::track] -struct App { +pub struct App { state: AppState, _network_fail: bool, @@ -38,7 +38,7 @@ struct App { ready: Controller, } -#[relm4::component(async)] +#[relm4::component(async, pub)] impl AsyncComponent for App { type Input = AppInput; type Output = (); @@ -111,7 +111,7 @@ impl AsyncComponent for App { .forward(sender.input_sender(), convert_ready_response); let login = Login::builder() - .launch_with_broker(login_shared_state.clone(), &paket::LOGIN_BROKER) + .launch_with_broker(login_shared_state.clone(), &crate::LOGIN_BROKER) .forward(sender.input_sender(), convert_login_response); let model = App { @@ -166,11 +166,11 @@ fn convert_login_response(response: LoginOutput) -> AppInput { LoginOutput::RequiresLogin => AppInput::SwitchToLogin, LoginOutput::RequiresLoading => AppInput::SwitchToLoading, LoginOutput::Error(library_error) => AppInput::FatalErr(AppError { - short: "Unhandled API error".to_string(), + short: i18n("Unhandled API error"), long: library_error.to_string(), }), LoginOutput::KeyringError(error) => AppInput::FatalErr(AppError { - short: "Keyring usage failed".to_string(), + short: i18n("Keyring usage failed"), long: error.to_string(), }), } @@ -182,17 +182,3 @@ fn convert_ready_response(response: ReadyOutput) -> AppInput { ReadyOutput::AddBreakpoint(breakpoint) => AppInput::AddBreakpoint(breakpoint), } } - -fn main() { - RELM_THREADS.set(4).unwrap(); - aperture::init(paket::constants::APP_ID); - let display = gtk::gdk::Display::default().unwrap(); - let theme = gtk::IconTheme::for_display(&display); - theme.add_resource_path("/de/j4ne/Paket/icons/"); - theme.add_resource_path("/de/j4ne/Paket/scalable/actions/"); - relm4_icons::initialize_icons(); - - let app = RelmApp::new(paket::constants::APP_ID); - - app.run_async::(()); -} diff --git a/src/bin/locker_scanner.rs b/src/bin/locker_scanner.rs new file mode 100644 index 0000000..d44c965 --- /dev/null +++ b/src/bin/locker_scanner.rs @@ -0,0 +1,81 @@ +/*use relm4::{gtk, prelude::*, RelmApp}; + +use gtk::prelude::*; +use libpaket::locker::LockerServiceUUID; +use paket::lockers::{Locker, LockerManager, LockerScannerInput}; + +struct App; + +#[derive(Debug)] +enum AppInput { + LockerFound(LockerServiceUUID), +} + +#[relm4::component(async)] +impl AsyncComponent for App { + type Init = (); + type Input = AppInput; + type Output = (); + type CommandOutput = (); + + view! { + #[root] + main_window = gtk::ApplicationWindow::new(&relm4::main_application()) { + #[wrap(Some)] + set_child = list_box = >k::ListBox { + + } + } + } + + async fn init( + init: Self::Init, + root: Self::Root, + sender: AsyncComponentSender, + ) -> AsyncComponentParts { + let model = App {}; + + let locker = LockerManager::builder() + .launch(()) + .forward(sender.input_sender(), transform_scanner_to_app); + + let widgets = view_output!(); + + AsyncComponentParts { model, widgets } + } + + async fn update_with_view( + &mut self, + widgets: &mut Self::Widgets, + message: Self::Input, + sender: AsyncComponentSender, + root: &Self::Root, + ) -> Self::Output { + match message { + AppInput::LockerFound(locker) => { + let label = gtk::Label::builder() + .label(Into::::into(locker).to_string()) + .build(); + widgets.list_box.append(&label); + } + } + + self.update_view(widgets, sender); + } +} + +fn transform_scanner_to_app(input: Locker) -> AppInput { + AppInput::LockerFound(input.get_service_uuid().unwrap()) +} + +fn main() { + paket::init(); + relm4::RELM_THREADS.set(10).unwrap(); + relm4::RELM_BLOCKING_THREADS.set(10).unwrap(); + + let app = RelmApp::new(format!("{}.TestBle", paket::config::APPLICATION_ID).as_str()); + app.run_async::(()); +} +*/ + +fn main() {} diff --git a/src/bin/paket.rs b/src/bin/paket.rs new file mode 100644 index 0000000..9335ecd --- /dev/null +++ b/src/bin/paket.rs @@ -0,0 +1,9 @@ +use relm4::RelmApp; + +fn main() { + paket::init(); + + let app = RelmApp::new(paket::config::APPLICATION_ID); + + app.run_async::(()); +} diff --git a/src/bin/qr_test.rs b/src/bin/qr_test.rs new file mode 100644 index 0000000..e1ee921 --- /dev/null +++ b/src/bin/qr_test.rs @@ -0,0 +1,83 @@ +use gtk; +use relm4::{RelmApp, RELM_THREADS}; + +use adw::{self, prelude::*}; +use relm4::{main_adw_application, prelude::*, AsyncComponentSender}; + +use paket::scanner::*; + +pub struct App { + scanner: AsyncController, +} + +#[relm4::component(async, pub)] +impl AsyncComponent for App { + type Input = (); + type Output = (); + type Init = (); + type CommandOutput = (); + + view! { + #[root] + main_window = adw::ApplicationWindow::new(&main_adw_application()) { + set_default_height: 600, + set_default_width: 800, + set_width_request: 300, + set_height_request: 300, + + #[wrap(Some)] + #[name = "navigation"] + set_content = &adw::NavigationView { + push = &adw::NavigationPage { + #[wrap(Some)] + set_child = &adw::ToolbarView { + add_top_bar = &adw::HeaderBar {}, + + #[wrap(Some)] + set_content = >k::Label { + set_label: "First page" + + } + }, + }, + push: model.scanner.widget(), + } + + }, + + } + + async fn init( + _: Self::Init, + root: Self::Root, + sender: AsyncComponentSender, + ) -> AsyncComponentParts { + let scanner = Scanner::builder().launch(()).detach(); + + let model = App { scanner }; + + let widgets = view_output!(); + + model.scanner.emit(ScannerInput::Activate); + + AsyncComponentParts { model, widgets } + } + + async fn update_with_view( + &mut self, + widgets: &mut Self::Widgets, + message: Self::Input, + sender: AsyncComponentSender, + root: &Self::Root, + ) -> Self::Output { + self.update_view(widgets, sender); + } +} + +fn main() { + paket::init(); + + let app = RelmApp::new(paket::config::APPLICATION_ID); + + app.run_async::(()); +} diff --git a/src/i18n.rs b/src/i18n.rs new file mode 100644 index 0000000..27eb535 --- /dev/null +++ b/src/i18n.rs @@ -0,0 +1,215 @@ +// i18n.rs +// +// Copyright 2020 Christopher Davis +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// SPDX-License-Identifier: GPL-3.0-or-later + +use gettext::{gettext, ngettext, npgettext, pgettext}; +use regex::{Captures, Regex}; + +#[allow(dead_code)] +fn freplace(input: String, args: &[&str]) -> String { + let mut parts = input.split("{}"); + let mut output = parts.next().unwrap_or_default().to_string(); + for (p, a) in parts.zip(args.iter()) { + output += &(a.to_string() + p); + } + output +} + +#[allow(dead_code)] +fn kreplace(input: String, kwargs: &[(&str, &str)]) -> String { + let mut s = input; + for (k, v) in kwargs { + if let Ok(re) = Regex::new(&format!("\\{{{}\\}}", k)) { + s = re + .replace_all(&s, |_: &Captures<'_>| v.to_string()) + .to_string(); + } + } + + s +} + +// Simple translations functions + +#[allow(dead_code)] +pub fn i18n(format: &str) -> String { + gettext(format) +} + +#[allow(dead_code)] +pub fn i18n_f(format: &str, args: &[&str]) -> String { + let s = gettext(format); + freplace(s, args) +} + +#[allow(dead_code)] +pub fn i18n_k(format: &str, kwargs: &[(&str, &str)]) -> String { + let s = gettext(format); + kreplace(s, kwargs) +} + +// Singular and plural translations functions + +#[allow(dead_code)] +pub fn ni18n(single: &str, multiple: &str, number: u32) -> String { + ngettext(single, multiple, number) +} + +#[allow(dead_code)] +pub fn ni18n_f(single: &str, multiple: &str, number: u32, args: &[&str]) -> String { + let s = ngettext(single, multiple, number); + freplace(s, args) +} + +#[allow(dead_code)] +pub fn ni18n_k(single: &str, multiple: &str, number: u32, kwargs: &[(&str, &str)]) -> String { + let s = ngettext(single, multiple, number); + kreplace(s, kwargs) +} + +// Translations with context functions + +#[allow(dead_code)] +pub fn pi18n(ctx: &str, format: &str) -> String { + pgettext(ctx, format) +} + +#[allow(dead_code)] +pub fn pi18n_f(ctx: &str, format: &str, args: &[&str]) -> String { + let s = pgettext(ctx, format); + freplace(s, args) +} + +#[allow(dead_code)] +pub fn pi18n_k(ctx: &str, format: &str, kwargs: &[(&str, &str)]) -> String { + let s = pgettext(ctx, format); + kreplace(s, kwargs) +} + +// Singular and plural with context + +#[allow(dead_code)] +pub fn pni18n(ctx: &str, single: &str, multiple: &str, number: u32) -> String { + npgettext(ctx, single, multiple, number) +} + +#[allow(dead_code)] +pub fn pni18n_f(ctx: &str, single: &str, multiple: &str, number: u32, args: &[&str]) -> String { + let s = npgettext(ctx, single, multiple, number); + freplace(s, args) +} + +#[allow(dead_code)] +pub fn pni18n_k( + ctx: &str, + single: &str, + multiple: &str, + number: u32, + kwargs: &[(&str, &str)], +) -> String { + let s = npgettext(ctx, single, multiple, number); + kreplace(s, kwargs) +} + +#[cfg(test)] +mod tests { + use super::*; + #[test] + fn test_i18n() { + let out = i18n("translate1"); + assert_eq!(out, "translate1"); + + let out = ni18n("translate1", "translate multi", 1); + assert_eq!(out, "translate1"); + let out = ni18n("translate1", "translate multi", 2); + assert_eq!(out, "translate multi"); + } + + #[test] + fn test_i18n_f() { + let out = i18n_f("{} param", &["one"]); + assert_eq!(out, "one param"); + + let out = i18n_f("middle {} param", &["one"]); + assert_eq!(out, "middle one param"); + + let out = i18n_f("end {}", &["one"]); + assert_eq!(out, "end one"); + + let out = i18n_f("multiple {} and {}", &["one", "two"]); + assert_eq!(out, "multiple one and two"); + + let out = ni18n_f("singular {} and {}", "plural {} and {}", 2, &["one", "two"]); + assert_eq!(out, "plural one and two"); + let out = ni18n_f("singular {} and {}", "plural {} and {}", 1, &["one", "two"]); + assert_eq!(out, "singular one and two"); + } + + #[test] + fn test_i18n_k() { + let out = i18n_k("{one} param", &[("one", "one")]); + assert_eq!(out, "one param"); + + let out = i18n_k("middle {one} param", &[("one", "one")]); + assert_eq!(out, "middle one param"); + + let out = i18n_k("end {one}", &[("one", "one")]); + assert_eq!(out, "end one"); + + let out = i18n_k("multiple {one} and {two}", &[("one", "1"), ("two", "two")]); + assert_eq!(out, "multiple 1 and two"); + + let out = i18n_k("multiple {two} and {one}", &[("one", "1"), ("two", "two")]); + assert_eq!(out, "multiple two and 1"); + + let out = i18n_k("multiple {one} and {one}", &[("one", "1"), ("two", "two")]); + assert_eq!(out, "multiple 1 and 1"); + + let out = ni18n_k( + "singular {one} and {two}", + "plural {one} and {two}", + 1, + &[("one", "1"), ("two", "two")], + ); + assert_eq!(out, "singular 1 and two"); + let out = ni18n_k( + "singular {one} and {two}", + "plural {one} and {two}", + 2, + &[("one", "1"), ("two", "two")], + ); + assert_eq!(out, "plural 1 and two"); + } + + #[test] + fn test_pi18n() { + let out = pi18n("This is the context", "translate1"); + assert_eq!(out, "translate1"); + + let out = pni18n("context", "translate1", "translate multi", 1); + assert_eq!(out, "translate1"); + let out = pni18n("The context string", "translate1", "translate multi", 2); + assert_eq!(out, "translate multi"); + + let out = pi18n_f("Context for translation", "{} param", &["one"]); + assert_eq!(out, "one param"); + + let out = pi18n_k("context", "{one} param", &[("one", "one")]); + assert_eq!(out, "one param"); + } +} diff --git a/paket/src/keyring.rs b/src/keyring.rs similarity index 96% rename from paket/src/keyring.rs rename to src/keyring.rs index 82747e9..8dd4e85 100644 --- a/paket/src/keyring.rs +++ b/src/keyring.rs @@ -4,10 +4,12 @@ use gtk::glib; use libpaket::{locker::crypto::CustomerKeySeed, login::RefreshToken, LibraryError}; use secrecy::{zeroize::Zeroize, ExposeSecret, SecretBox}; +use crate::i18n::i18n; + pub static KEYRING: OnceLock = OnceLock::new(); fn get_keyring_base_attribute() -> (&'static str, &'static str) { - ("app", crate::constants::APP_ID) + ("app", crate::config::APPLICATION_ID) } fn get_keyring_attributes_refresh_token() -> Vec<(&'static str, &'static str)> { @@ -60,7 +62,7 @@ pub async fn keyring_get_refresh_token() -> oo7::Result> { pub async fn keyring_set_refresh_token(value: String) -> oo7::Result<()> { get_keyring() .create_item( - "Paket: Login credentials", + &i18n("Paket: Login credentials"), &get_keyring_attributes_refresh_token(), value, true, @@ -168,7 +170,7 @@ pub async fn keyring_set_packstation(data: &CustomerKeySeed) -> KeyringResult<() Ok(get_keyring() .create_item( - "Paket: Device keys", + &i18n("Paket: Device keys for parcel locker service"), &get_keyring_attributes_packstation(), string.expose_secret(), true, diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..0ba1ae6 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,47 @@ +pub mod account; +pub mod advice; +pub mod advices; +pub mod app; +pub mod i18n; +pub mod keyring; +//pub mod lockers; +pub mod login; +pub mod packstation; +pub mod ready; +pub mod scanner; +pub mod tracking; +pub mod utils; + +pub mod config { + include!(concat!(env!("OUT_DIR"), "/config.rs")); +} + +pub fn init() { + config::init(); + + use config::{APPLICATION_ID, GETTEXT_PACKAGE, LOCALEDIR}; + use gettext::{bind_textdomain_codeset, bindtextdomain, setlocale, textdomain, LocaleCategory}; + + aperture::init(APPLICATION_ID); + + relm4::RELM_THREADS.set(4).unwrap(); + + //debug!("Setting up locale data"); + setlocale(LocaleCategory::LcAll, ""); + + bindtextdomain(GETTEXT_PACKAGE, LOCALEDIR).expect("Unable to bind the text domain"); + bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8") + .expect("Unable to set the text domain encoding"); + textdomain(GETTEXT_PACKAGE).expect("Unable to switch to the text domain"); + + let display = gtk::gdk::Display::default().unwrap(); + let theme: gtk::IconTheme = gtk::IconTheme::for_display(&display); + theme.add_resource_path("/de/j4ne/Paket/icons"); +} + +pub use login::LoginSharedState; + +pub static LOGIN_BROKER: relm4::MessageBroker = relm4::MessageBroker::new(); +pub fn send_log_out() { + LOGIN_BROKER.send(login::LoginInput::LogOut); +} diff --git a/src/lockers.rs b/src/lockers.rs new file mode 100644 index 0000000..b4072a2 --- /dev/null +++ b/src/lockers.rs @@ -0,0 +1,273 @@ +use std::borrow::Borrow; +use std::collections::HashMap; +use std::collections::HashSet; +use std::sync::Arc; +use std::sync::Mutex; +use std::sync::RwLock; + +use bluer::monitor::Monitor; +use bluer::DiscoveryFilter; +use channel::mpsc::UnboundedReceiver; +use channel::mpsc::UnboundedSender; +use futures::*; +use libpaket::locker::LockerDevice as LockerDeviceMetadata; +use libpaket::locker::LockerServiceUUID; +use relm4::prelude::*; +use relm4::{Component, ComponentSender}; + +pub enum LockerDeviceOutput { + Lost(LockerServiceUUID), +} + +pub enum LockerDeviceCommand {} + +#[derive(Debug)] +pub struct LockerDevice { + device: bluer::Device, + metadata: LockerDeviceMetadata, + rx: Arc>, + tx: Arc>>, +} + +#[derive(Debug, Clone)] +pub struct Locker { + arc: Arc>>, +} + +impl Locker { + fn new(device: LockerDevice) -> Self { + Self { + arc: Arc::new(Mutex::new(Some(device))), + } + } + + pub fn is_available(&self) -> bool { + let lock = self.arc.lock().unwrap(); + lock.is_some() + } + + pub fn get_device(&self) -> Option { + let lock = self.arc.lock().unwrap(); + let locker = lock.as_ref()?; + Some(locker.device.clone()) + } + + pub fn get_receiver(&self) -> Option>> { + let lock = self.arc.lock().unwrap(); + let locker = lock.as_ref()?; + Some(locker.rx.clone()) + } + + pub fn get_service_uuid(&self) -> Option { + let lock = self.arc.lock().unwrap(); + let locker = lock.as_ref()?; + Some(locker.metadata.service_uuid.clone()) + } + + async fn emit_lost(&self) { + let locker = self.arc.lock().unwrap().take().unwrap(); + + let tx = locker.tx.clone(); + let mut tx = tx.write().unwrap(); + tx.send(LockerDeviceOutput::Lost(locker.metadata.service_uuid)) + .await; + } +} + +// unsafe impl Send for Locker {} +// unsafe impl Sync for Locker {} + +impl LockerDevice { + fn new( + adapter: &bluer::Adapter, + address: &bluer::Address, + metadata: LockerDeviceMetadata, + ) -> Self { + let device = adapter.device(address.clone()).unwrap(); + + let (tx, rx) = futures::channel::mpsc::unbounded::(); + + LockerDevice { + device, + metadata, + rx: Arc::new(rx), + tx: Arc::new(RwLock::new(tx)), + } + } +} + +pub struct LockerManager { + session: bluer::Session, + + adapter: Option, + lockers: HashMap, +} + +#[derive(Debug)] +pub enum LockerScannerInput { + SetAdapter(bluer::Adapter), + Scan, +} + +#[derive(Debug)] +pub enum LockerManagerCommands { + LockerFound((bluer::Address, LockerDeviceMetadata)), + LockerLost(bluer::Address), +} + +impl AsyncComponent for LockerManager { + type Init = (); + type Input = LockerScannerInput; + type Output = Locker; + type Root = (); + type CommandOutput = LockerManagerCommands; + type Widgets = (); + + fn init_root() -> Self::Root { + () + } + + async fn init( + _: Self::Init, + _: Self::Root, + sender: AsyncComponentSender, + ) -> AsyncComponentParts { + let session = bluer::Session::new().await.unwrap(); + + let model = Self { + session, + adapter: None, + lockers: HashMap::with_capacity(1), + }; + + if let Ok(res) = model.session.default_adapter().await { + sender.input(LockerScannerInput::SetAdapter(res)); + } + + AsyncComponentParts { model, widgets: () } + } + + async fn update( + &mut self, + msg: Self::Input, + sender: AsyncComponentSender, + _: &Self::Root, + ) { + match msg { + LockerScannerInput::Scan => { + // TODO: move that into sender.command + // for some reason that didn't work + let adapter = self.adapter.as_ref().unwrap().clone(); + let uuids = HashSet::new(); + println!("A"); + if let Err(_) = adapter + .set_discovery_filter(DiscoveryFilter { + uuids, + rssi: None, + pathloss: None, + transport: bluer::DiscoveryTransport::Le, + duplicate_data: false, + discoverable: false, + pattern: None, + _non_exhaustive: (), + }) + .await + { + return; + } + println!("B"); + let Ok(mut discover) = adapter.discover_devices().await else { + return; + }; + + while let Some(evt) = discover.next().await { + println!("E: {:?}", evt); + match evt { + bluer::AdapterEvent::DeviceAdded(addr) => { + let device = adapter.device(addr).unwrap(); + println!("device: {:?}", device); + let Some(service_data) = device.service_data().await.unwrap() else { + continue; + }; + for (uuid, data) in service_data.iter() { + let Ok(locker_service_uuid) = + LockerServiceUUID::try_from(uuid.clone()) + else { + continue; + }; + let locker = + LockerDeviceMetadata::new(locker_service_uuid, data).unwrap(); + sender + .command_sender() + .emit(LockerManagerCommands::LockerFound((addr, locker))); + } + } + bluer::AdapterEvent::DeviceRemoved(_) => (), + _ => (), + } + } + } + LockerScannerInput::SetAdapter(adapter) => match adapter.is_powered().await { + Ok(powered) => { + if powered { + println!("Adapter: already turned on."); + self.adapter = Some(adapter); + sender.input(LockerScannerInput::Scan); + } else { + println!("Adapter: trying to turn on."); + match adapter.set_powered(true).await { + Ok(_) => { + println!("Adapter: turned on"); + self.adapter = Some(adapter); + sender.input(LockerScannerInput::Scan); + } + Err(err) => { + println!("Adapter: not turned on, error: {:?}", err); + } + }; + } + } + Err(err) => {} + }, + } + } + + async fn update_cmd( + &mut self, + message: Self::CommandOutput, + sender: AsyncComponentSender, + _: &Self::Root, + ) { + match message { + LockerManagerCommands::LockerFound((addr, metadata)) => { + for (iter_addr, _) in &self.lockers { + if *iter_addr == addr { + return; + } + } + let locker = LockerDevice::new(self.adapter.as_ref().unwrap(), &addr, metadata); + let locker = Locker::new(locker); + self.lockers.insert(addr.clone(), locker.clone()); + + let adapter = self.adapter.as_ref().unwrap().clone(); + + sender.command(move |sender, shutdown| { + shutdown + .register(async move { + let device = adapter.device(addr).unwrap(); + let mut events = device.events().await.unwrap(); + while let Some(_) = events.next().await {} + sender.emit(LockerManagerCommands::LockerLost(addr)); + }) + .drop_on_shutdown() + }); + + sender.output(locker).unwrap(); + } + LockerManagerCommands::LockerLost(addr) => { + let locker = self.lockers.remove(&addr).unwrap(); + locker.emit_lost().await; + } + } + } +} diff --git a/paket/src/login.rs b/src/login.rs similarity index 97% rename from paket/src/login.rs rename to src/login.rs index f514f27..fe62f83 100644 --- a/paket/src/login.rs +++ b/src/login.rs @@ -12,7 +12,11 @@ use relm4::{ }; use webkit::{prelude::WebViewExt, URIRequest, WebContext, WebView}; -use crate::keyring::{keyring_get_refresh_token, keyring_is_available, keyring_set_refresh_token}; +use crate::{ + i18n::i18n, + keyring::{keyring_get_refresh_token, keyring_is_available, keyring_set_refresh_token}, + utils::status_page, +}; #[derive(Debug)] pub enum LoginInput { @@ -109,7 +113,7 @@ impl AsyncComponent for Login { add = &adw::Bin { #[wrap(Some)] set_child = &adw::StatusPage { - set_title: "Welcome to Paket!", + set_title: &i18n("Welcome to Paket!"), #[wrap(Some)] set_child = &adw::Clamp { set_maximum_size: 260, @@ -119,7 +123,7 @@ impl AsyncComponent for Login { add_css_class: relm4::css::SUGGESTED_ACTION, add_css_class: relm4::css::PILL, - set_label: "Log In", + set_label: &i18n("Log In"), connect_clicked => LoginInput::ConsentToLogin, }, @@ -150,9 +154,7 @@ impl AsyncComponent for Login { }, #[name = "page_offline"] - add = &adw::Bin { - - }, + add = &status_page(&i18n("Internet unavailable"), None, None) -> adw::StatusPage {}, #[track(model.changed_state())] set_visible_child: { diff --git a/src/meson.build b/src/meson.build new file mode 100644 index 0000000..f35a1ef --- /dev/null +++ b/src/meson.build @@ -0,0 +1,49 @@ +# SPDX-FileCopyrightText: 2022 Emmanuele Bassi +# SPDX-License-Identifier: GPL-3.0-or-later + +pkgdatadir = get_option('prefix') / get_option('datadir') / meson.project_name() + +cargo_env = { + 'PAKET_VERSION': '@0@@1@'.format(meson.project_version(), version_suffix), + 'PAKET_GETTEXT_PACKAGE': 'paket', + 'PAKET_LOCALEDIR': get_option('prefix') / get_option('localedir'), + 'PAKET_PKGDATADIR': pkgdatadir, + 'PAKET_APPLICATION_ID': application_id, + 'PAKET_PROFILE': get_option('profile'), +} + +cargo_options = [ '--manifest-path', meson.project_source_root() / 'Cargo.toml' ] +cargo_options += [ '--target-dir', meson.project_build_root() / 'src' ] +cargo_options += [ '--bin', meson.project_name() ] + +cargo_cmd = [ cargo ] +if get_option('profile') == 'default' + cargo_options += [ '--release' ] + cargo_cmd += [ 'auditable' ] + rust_target = 'release' + message('Building in release mode') +else + rust_target = 'debug' + message('Building in debug mode') +endif + +cargo_env = { + 'CARGO_HOME': meson.project_build_root() / 'cargo-home', + 'PAKET_BUILT_BY': 'meson', +} + +cargo_release = custom_target( + 'cargo-build', + build_by_default: true, + output: meson.project_name(), + console: true, + install: true, + install_dir: get_option('bindir'), + env: cargo_env, + command: [ + cargo_cmd, 'build', '--locked', + cargo_options, + '&&', + 'cp', 'src' / rust_target / meson.project_name(), '@OUTPUT@', + ], +) diff --git a/paket/src/packstation.rs b/src/packstation.rs similarity index 87% rename from paket/src/packstation.rs rename to src/packstation.rs index 43bc136..acd00cb 100644 --- a/paket/src/packstation.rs +++ b/src/packstation.rs @@ -4,6 +4,7 @@ use relm4::prelude::*; use relm4::{Component, ComponentParts, WidgetRef}; use secrecy::{ExposeSecret, SecretBox}; +use crate::i18n::i18n; use crate::keyring::{keyring_get_packstation, keyring_set_packstation}; use crate::login::get_id_token; use crate::{ @@ -78,8 +79,8 @@ impl Component for PackstationView { add = page_registration = &adw::ViewStack { #[name = "registration_page_beginning"] add = &adw::StatusPage { - set_title: "Register your device", - set_description: Some("Registration is required to interact with parcel lockers\nYou'll need camera access and the letter with the registration qr-code you received. If you don't have one, request the Packstation service and come back later."), + set_title: &i18n("Register your device"), + set_description: Some(&i18n("Registration is required to interact with parcel lockers\nYou'll need camera access and the letter with the registration qr-code you received. If you don't have one, request the Packstation service and come back later.")), #[wrap(Some)] set_child = &adw::Clamp { @@ -92,8 +93,8 @@ impl Component for PackstationView { #[wrap(Some)] set_child = &adw::ButtonContent { - set_label: "Register", - set_icon_name: relm4_icons::icon_names::QR_CODE_SCANNER, + set_label: &i18n("Register"), + set_icon_name: "qr-code-scanner-symbolic", }, connect_clicked[sender = sender.clone()] => move |_| { @@ -108,7 +109,7 @@ impl Component for PackstationView { #[wrap(Some)] set_paintable = &adw::SpinnerPaintable::new(Some(registration_page_loading.widget_ref())) {}, - set_title: "Registering your device" + set_title: &i18n("Registering your device"), }, #[track(model.changed_state())] @@ -197,7 +198,7 @@ impl Component for PackstationView { Ok(value) => { keyring_set_packstation(&value).await.unwrap(); PackstationViewCommand::GotDeviceCredentials(value) - }, + } Err(err) => todo!(), }, Err(err) => todo!(), @@ -223,20 +224,18 @@ impl Component for PackstationView { } PackstationViewCommand::GotNothing => { self.set_state(State::RegisterWizard(RegisterState::Beginning)); - }, + } PackstationViewCommand::GotAPIRegisterError(error) => { todo!() - }, - PackstationViewCommand::GotLibraryError(error) => { - match error { - libpaket::LibraryError::Unauthorized => todo!(), - libpaket::LibraryError::DecodeError(_) => todo!(), - libpaket::LibraryError::APIChange => todo!(), - libpaket::LibraryError::Deprecated => todo!(), + } + PackstationViewCommand::GotLibraryError(error) => match error { + libpaket::LibraryError::Unauthorized => todo!(), + libpaket::LibraryError::DecodeError(_) => todo!(), + libpaket::LibraryError::APIChange => todo!(), + libpaket::LibraryError::Deprecated => todo!(), - libpaket::LibraryError::InvalidArgument(error) => panic!("{}", error), - libpaket::LibraryError::NetworkFetch => panic!(), - } + libpaket::LibraryError::InvalidArgument(error) => panic!("{}", error), + libpaket::LibraryError::NetworkFetch => panic!(), }, } } diff --git a/paket/src/ready.rs b/src/ready.rs similarity index 89% rename from paket/src/ready.rs rename to src/ready.rs index 1b07b78..ed05e6c 100644 --- a/paket/src/ready.rs +++ b/src/ready.rs @@ -1,8 +1,12 @@ +use crate::i18n::i18n; use adw::prelude::*; use relm4::prelude::*; use crate::{ - account::{AccountOutput, AccountServices, AccountView}, advices::{AdvicesView, AdvicesViewInput}, packstation::{PackstationView, PackstationViewInput, PackstationViewOutput}, tracking::{TrackingInput, TrackingOutput, TrackingView} + account::{AccountOutput, AccountServices, AccountView}, + advices::{AdvicesView, AdvicesViewInput}, + packstation::{PackstationView, PackstationViewInput, PackstationViewOutput}, + tracking::{TrackingInput, TrackingOutput, TrackingView}, }; #[tracker::track] @@ -73,14 +77,14 @@ impl Component for Ready { #[wrap(Some)] #[name = "ready_view_stack"] set_child = &adw::ViewStack { - add = &model.advices_component.widget().clone() -> gtk::ScrolledWindow { + add = &model.advices_component.widget().clone() -> adw::ViewStack { #[track(model.changed_have_service_advices())] set_visible: model.have_service_advices, } -> page_advices: adw::ViewStackPage { - set_title: Some("Mail notification"), - set_name: Some("page_advices"), - set_icon_name: Some(relm4_icons::icon_names::MAIL), + set_title: Some(&i18n("Mail")), + set_name: Some(&i18n("page_advices")), + set_icon_name: Some(&i18n("mail-symbolic")), #[track(model.changed_have_service_advices())] set_visible: model.have_service_advices, @@ -90,9 +94,9 @@ impl Component for Ready { #[track(model.changed_have_service_tracking())] set_visible: model.have_service_tracking, } -> page_tracking: adw::ViewStackPage { - set_title: Some("Shipment tracking"), + set_title: Some(&i18n("Tracking")), set_name: Some("page_tracking"), - set_icon_name: Some(relm4_icons::icon_names::PACKAGE_X_GENERIC), + set_icon_name: Some("package-x-generic-symbolic"), #[track(model.changed_have_service_tracking())] set_visible: model.have_service_tracking, @@ -102,8 +106,9 @@ impl Component for Ready { #[track(model.changed_have_service_packstation())] set_visible: model.have_service_packstation, } -> page_packstation: adw::ViewStackPage { - set_title: Some("Packstation"), + set_title: Some(&i18n("Locker")), set_name: Some("page_packstation"), + set_icon_name: Some("parcel-locker-symbolic"), #[track(model.changed_have_service_packstation())] set_visible: model.have_service_packstation, @@ -111,9 +116,9 @@ impl Component for Ready { add = &model.account_component.widget().clone() -> adw::Bin { } -> page_account: adw::ViewStackPage { - set_title: Some("Account"), + set_title: Some(&i18n("Account")), set_name: Some("page_account"), - set_icon_name: Some(relm4_icons::icon_names::PERSON) + set_icon_name: Some("person-symbolic") } }, }, @@ -205,7 +210,7 @@ impl Component for Ready { } AccountServices::SendungVerfolgung => { self.set_have_service_tracking(true); - self.tracking_component.emit(TrackingInput::Search(None)) + self.tracking_component.emit(TrackingInput::Init); } AccountServices::PackstationAvailable => { self.set_have_service_packstation(true); @@ -213,6 +218,7 @@ impl Component for Ready { } }, ReadyInput::ServiceBorked(service) => match service { + // TODO: localization AccountServices::Advices => { self.toast_overlay.add_toast( adw::Toast::builder() @@ -221,7 +227,7 @@ impl Component for Ready { .build(), ); self.set_have_service_advices(false); - }, + } AccountServices::SendungVerfolgung => { self.toast_overlay.add_toast( adw::Toast::builder() @@ -230,7 +236,7 @@ impl Component for Ready { .build(), ); self.set_have_service_tracking(false); - }, + } _ => (), }, ReadyInput::NavigationPageTemp(page) => { @@ -256,6 +262,7 @@ impl Component for Ready { fn convert_tracking_output(value: TrackingOutput) -> ReadyInput { match value { TrackingOutput::Borked => ReadyInput::ServiceBorked(AccountServices::SendungVerfolgung), + _ => todo!(), } } @@ -269,6 +276,8 @@ fn convert_account_output(value: AccountOutput) -> ReadyInput { fn convert_packstation_output(value: PackstationViewOutput) -> ReadyInput { match value { - PackstationViewOutput::NavigationPage(navigation_page) => ReadyInput::NavigationPageTemp(navigation_page), + PackstationViewOutput::NavigationPage(navigation_page) => { + ReadyInput::NavigationPageTemp(navigation_page) + } } } diff --git a/paket/src/scanner.rs b/src/scanner.rs similarity index 80% rename from paket/src/scanner.rs rename to src/scanner.rs index c2c8777..2cce798 100644 --- a/paket/src/scanner.rs +++ b/src/scanner.rs @@ -1,6 +1,8 @@ -pub use r#impl::{Scanner, ScannerOutput}; +pub use r#impl::{Scanner, ScannerInput, ScannerOutput}; mod r#impl { + use crate::i18n::{i18n, i18n_f}; + use crate::utils; use adw::prelude::*; use relm4::prelude::*; use relm4::WidgetRef; @@ -39,6 +41,8 @@ mod r#impl { pub struct Scanner { state: State, in_activation: bool, + #[no_eq] + error: Option, #[do_not_track] camera: Option, @@ -95,13 +99,8 @@ mod r#impl { add = &page_view_finder -> aperture::Viewfinder { set_detect_codes: true, - connect_code_detected[sender = sender.clone()] => move |_, code_type, value| { - match code_type { - aperture::CodeType::Qr => { - let _ = sender.output(ScannerOutput::CodeDetected(value.to_string())); - }, - _ => (), - } + connect_code_detected[sender = sender.clone()] => move |_, bytes| { + println!("Got connect_code_detected"); }, connect_state_notify[sender = sender.clone()] => move |view_finder|{ @@ -119,17 +118,19 @@ mod r#impl { #[name = "page_error"] add = &adw::Bin { #[wrap(Some)] - set_child = &adw::StatusPage { - set_title: "Error occured", + set_child = &utils::status_page_with_back_button("An error occured", None) -> adw::StatusPage { + #[track = "model.changed_error() && model.get_error().is_some()"] + set_title?: &model.error.as_ref().map(|error| error.title()), + #[track = "model.changed_error() && model.get_error().is_some()"] + set_description: model.error.as_ref().map(|error| error.description()).as_deref(), }, }, #[name = "page_no_cameras"] add = &adw::Bin { - #[wrap(Some)] - set_child = &adw::StatusPage { - set_title: "No cameras detected", - }, + set_child: Some( + &utils::status_page_with_back_button("No cameras found", None) + ), }, #[track(model.changed_state())] @@ -169,6 +170,7 @@ mod r#impl { let model = Scanner { state: State::Nothing, view_finder, + error: None, camera: None, in_activation: false, tracker: 0, @@ -301,4 +303,32 @@ mod r#impl { ErrorInternal::Provider(value) } } + + impl ErrorInternal { + fn title(&self) -> String { + match self { + ErrorInternal::Pipewire(error) => match error { + aperture::PipewireError::OldVersion => i18n("Pipewire needs upgrading"), + aperture::PipewireError::ProvidedStarted => i18n("Internal error"), + }, + ErrorInternal::Provider(error) => match error { + aperture::ProviderError::MissingPlugin(_) => i18n("A plugin is missing"), + aperture::ProviderError::BoolError(_) => i18n("External error"), + }, + } + } + + fn description(&self) -> String { + match self { + ErrorInternal::Pipewire(error) => match error { + aperture::PipewireError::OldVersion => i18n("The current Pipewire version is too old to use and must be upgraded"), + aperture::PipewireError::ProvidedStarted => i18n("The aperture::DeviceProvider was already started. This is a logic bug, please report this issue."), + }, + ErrorInternal::Provider(error) => match error { + aperture::ProviderError::MissingPlugin(plugin) => i18n_f("Please install `{}` to use the Scanner", &[&plugin]), + aperture::ProviderError::BoolError(bool_error) => i18n_f("Programming error: {}", &[&bool_error.message]), + }, + } + } + } } diff --git a/paket/src/tracking.rs b/src/tracking.rs similarity index 57% rename from paket/src/tracking.rs rename to src/tracking.rs index 979b5a6..32441e0 100644 --- a/paket/src/tracking.rs +++ b/src/tracking.rs @@ -4,16 +4,23 @@ use libpaket::{LibraryError, LibraryResult}; use relm4::factory::{FactoryComponent, FactoryHashMap}; use relm4::prelude::*; +use crate::i18n::{i18n, i18n_f}; use crate::login::get_id_token; use crate::LoginSharedState; +#[tracker::track] pub struct TrackingView { + #[do_not_track] factory: FactoryHashMap, + #[do_not_track] login: LoginSharedState, + + is_init: bool, } #[derive(Debug)] pub enum TrackingInput { + Init, Search(Option), Notification(String), Reset, @@ -27,6 +34,7 @@ pub enum TrackingCmds { #[derive(Debug)] pub enum TrackingOutput { Borked, + NavigationPageTemp(adw::NavigationPage), } #[relm4::component(pub)] @@ -40,27 +48,24 @@ impl Component for TrackingView { #[root] adw::ToastOverlay { #[wrap(Some)] - set_child = >k::ScrolledWindow { - set_propagate_natural_width: true, - #[wrap(Some)] - set_child = &adw::Clamp { - set_maximum_size: 1800, + set_child = >k::Box { + set_orientation: gtk::Orientation::Horizontal, + + gtk::ScrolledWindow { + set_propagate_natural_width: true, #[wrap(Some)] - set_child = >k::Box { - set_orientation: gtk::Orientation::Vertical, - set_margin_start: 6, - set_margin_end: 6, + set_child = &adw::Clamp { + #[wrap(Some)] + set_child = >k::Box { + set_orientation: gtk::Orientation::Vertical, + set_margin_start: 6, + set_margin_end: 6, - set_margin_bottom: 12, - - adw::Clamp { - #[wrap(Some)] - set_child = >k::Box { + gtk::Box { set_orientation: gtk::Orientation::Horizontal, - set_margin_all: 2, - - add_css_class: relm4::css::TOOLBAR, + add_css_class: relm4::css::LINKED, + set_margin_bottom: 12, #[name = "tracking_entry"] gtk::Entry { @@ -70,21 +75,22 @@ impl Component for TrackingView { #[name = "tracking_entry_button"] gtk::Button { - set_icon_name: relm4_icons::icon_names::LOUPE_LARGE + set_icon_name: "loupe-large-symbolic", + add_css_class: relm4::css::RAISED, } }, - }, - #[local_ref] - tracking_box -> gtk::Box { - set_spacing: 8, - set_orientation: gtk::Orientation::Vertical, - }, - } + #[local_ref] + tracking_box -> gtk::ListBox { + add_css_class: relm4::css::BOXED_LIST, + set_selection_mode: gtk::SelectionMode::None, + }, + } + }, }, - } + } } @@ -98,6 +104,8 @@ impl Component for TrackingView { let model = TrackingView { factory, login: init, + tracker: 0, + is_init: false, }; let tracking_box = model.factory.widget(); @@ -119,12 +127,20 @@ impl Component for TrackingView { }); } + sender.input(TrackingInput::Reset); + ComponentParts { model, widgets } } fn update(&mut self, message: Self::Input, sender: ComponentSender, root: &Self::Root) { + self.reset(); + match message { + TrackingInput::Init => { + self.set_is_init(true); + } TrackingInput::Reset => { + self.set_is_init(false); self.factory.clear(); } TrackingInput::Search(value) => { @@ -132,9 +148,9 @@ impl Component for TrackingView { if let Some(value) = &value { // https://www.pakete-verfolgen.de/dhl-sendungsnummer/ if value.len() < 8 { - sender.input(TrackingInput::Notification( - "The id is too short to be valid.".to_string(), - )); + sender.input(TrackingInput::Notification(i18n( + "The id is too short to be valid.", + ))); return; } } @@ -172,41 +188,46 @@ impl Component for TrackingView { sender: ComponentSender, root: &Self::Root, ) { + self.reset(); + match message { TrackingCmds::GotTracking(res) => match res { Ok(shipment_vec) => { + if self.is_init { + self.set_is_init(false); + } for item in shipment_vec { if let Some(err) = item.error { // TODO: gettext if err.id_invalid { - sender.input(TrackingInput::Notification(format!( + sender.input(TrackingInput::Notification(i18n_f( "The id is invalid ({})", - item.id + &[&item.id], ))); } else if err.letter_not_found { - sender.input(TrackingInput::Notification(format!( + sender.input(TrackingInput::Notification(i18n_f( "The letter wasn't found ({})", - item.id + &[&item.id], ))); } else if err.id_not_searchable { - sender.input(TrackingInput::Notification(format!( + sender.input(TrackingInput::Notification(i18n_f( "The id is not searchable ({})", - item.id + &[&item.id], ))); } else if err.data_to_old { - sender.input(TrackingInput::Notification(format!( - "No data available with id ({}) (data expired)", - item.id + sender.input(TrackingInput::Notification(i18n_f( + "Data was removed with the id ({})", + &[&item.id], ))); } else if err.not_from_dhl { - sender.input(TrackingInput::Notification(format!( + sender.input(TrackingInput::Notification(i18n_f( "The id is not from DHL ({})", - item.id + &[&item.id], ))); } else if err.no_data_available { - sender.input(TrackingInput::Notification(format!( + sender.input(TrackingInput::Notification(i18n_f( "No data available with id ({})", - item.id + &[&item.id], ))); } } else { @@ -216,9 +237,11 @@ impl Component for TrackingView { } Err(err) => { if err == LibraryError::APIChange { - println!("Upstream API for parcel tracking broke"); - sender.output(TrackingOutput::Borked).unwrap(); + println!( + "Upstream API for parcel tracking broke, assuming Captcha failure" + ); } else { + // TODO: Localize strings sender.input(TrackingInput::Notification(format!( "Unknown Error: {}", err.to_string() @@ -232,21 +255,10 @@ impl Component for TrackingView { struct ShipmentView { model: Shipment, - - // model abstraction - have_events: bool, - - // state - expanded: bool, - - // workarounds - list_box_history: gtk::Box, } #[derive(Debug)] -pub enum ViewInput { - ToggleExpand, -} +pub enum ViewInput {} #[relm4::factory] impl FactoryComponent for ShipmentView { @@ -254,19 +266,15 @@ impl FactoryComponent for ShipmentView { type Init = Shipment; type Output = (); type Input = ViewInput; - type ParentWidget = gtk::Box; + type ParentWidget = gtk::ListBox; type Index = String; view! { #[root] gtk::Box { - add_css_class: relm4::css::CARD, set_hexpand: true, - set_margin_all: 8, set_orientation: gtk::Orientation::Vertical, - inline_css: "border-radius: 12px 12px 0px 0px", - // title box gtk::Box { set_orientation: gtk::Orientation::Horizontal, set_hexpand: true, @@ -282,7 +290,8 @@ impl FactoryComponent for ShipmentView { add_css_class: relm4::css::CAPTION_HEADING, set_halign: gtk::Align::Start, - set_label: &self.model.id, + set_label?: self.subtitle(), + set_visible: self.has_subtitle(), }, gtk::Label { @@ -290,126 +299,53 @@ impl FactoryComponent for ShipmentView { set_halign: gtk::Align::Start, set_wrap: true, - set_label: { - // TODO: gettext - if let Some(shipment_name) = &self.model.special.shipment_name { - shipment_name.as_str() - } else if let Some(product_name) = &self.model.special.product_name { - product_name.as_str() - } else { - match self.model.quelle { - libpaket::tracking::SendungsQuelle::TTBRIEF => "Letter", - libpaket::tracking::SendungsQuelle::PAKET => "Parcel", - libpaket::tracking::SendungsQuelle::SVB => "quelle: SVB", - libpaket::tracking::SendungsQuelle::OPTIMA => "quelle: OPTIMA", - } - } - } + set_label: &self.maintitle(), } }, - - gtk::Button { - set_halign: gtk::Align::End, - add_css_class: relm4::css::FLAT, - - #[watch] - set_icon_name: { - if self.expanded { - relm4_icons::icon_names::MINUS - } else { - relm4_icons::icon_names::PLUS - } - }, - - connect_clicked => ViewInput::ToggleExpand, - } - }, // title box end - - gtk::ProgressBar { - add_css_class: relm4::css::OSD, - - set_fraction: f64::from(self.model.history.maximal_fortschritt) / f64::from(self.model.history.fortschritt), - }, - - gtk::Revealer { - #[watch] - set_reveal_child: self.expanded, - - #[wrap(Some)] - set_child = >k::Box { - set_margin_all: 8, - - // history viewstack - gtk::Label { - set_visible: !self.have_events, - - set_label: "No events", - }, - - append = &self.list_box_history.clone() { - set_visible: self.have_events, - }, - - } }, } } - fn init_model( - init: Self::Init, - index: &Self::Index, - sender: relm4::FactorySender, - ) -> Self { - let have_events = init.history.events.as_ref().is_some_and(|a| a.len() > 0); - - let list_box_history = gtk::Box::builder() - .orientation(gtk::Orientation::Vertical) - .spacing(8) - .build(); - - let _self = ShipmentView { - have_events, - model: init, - list_box_history, - expanded: false, - }; - - for elem in _self.model.history.events.as_ref().unwrap() { - let label_datum = gtk::Label::builder() - .css_classes([relm4::css::NUMERIC]) - .label(&elem.datum) - .halign(gtk::Align::Start) - .valign(gtk::Align::Start) - .build(); - - // TODO: is html, parse it - let label_status = gtk::Label::builder() - .wrap(true) - .halign(gtk::Align::Start) - .valign(gtk::Align::Start) - .build(); - label_status.set_markup(&elem.status); - - let boxie = gtk::Box::builder() - .orientation(gtk::Orientation::Horizontal) - .spacing(8) - .build(); - - boxie.append(&label_datum); - boxie.append(&label_status); - - _self.list_box_history.append(&boxie); - } + fn init_model(init: Self::Init, _: &Self::Index, _: relm4::FactorySender) -> Self { + let _self = ShipmentView { model: init }; _self } - fn update(&mut self, message: Self::Input, sender: FactorySender) { - match message { - ViewInput::ToggleExpand => { - self.expanded = !self.expanded; + fn update(&mut self, _: Self::Input, _: FactorySender) {} +} + +impl ShipmentView { + fn maintitle(&self) -> String { + // TODO: gettext + if let Some(shipment_name) = &self.model.special.shipment_name { + shipment_name.clone() + } else if let Some(product_name) = &self.model.special.product_name { + product_name.clone() + } else if let Some(quelle) = &self.model.quelle { + match quelle { + libpaket::tracking::SendungsQuelle::TTBRIEF => i18n("Letter"), + libpaket::tracking::SendungsQuelle::PAKET => i18n("Parcel"), + libpaket::tracking::SendungsQuelle::SVB => i18n("SVB"), + libpaket::tracking::SendungsQuelle::OPTIMA => i18n("OPTIMA"), } + } else { + self.model.id.clone() + } + } + + fn has_subtitle(&self) -> bool { + self.model.special.shipment_name.is_some() + || self.model.special.product_name.is_some() + || self.model.quelle.is_some() + } + + fn subtitle(&self) -> Option<&str> { + if self.has_subtitle() { + Some(self.model.id.as_str()) + } else { + None } } } diff --git a/src/utils.rs b/src/utils.rs new file mode 100644 index 0000000..954f90c --- /dev/null +++ b/src/utils.rs @@ -0,0 +1,46 @@ +use crate::i18n::i18n; +use relm4::WidgetRef; + +pub fn back_button() -> gtk::Button { + gtk::Button::builder() + .vexpand_set(false) + .css_classes([relm4::css::PILL]) + .action_name("navigation.pop") + .label(i18n("Return to previous page")) + .halign(gtk::Align::BaselineCenter) + .valign(gtk::Align::BaselineCenter) + .build() +} + +pub fn status_page_with_back_button(title: &str, description: Option<&str>) -> adw::StatusPage { + status_page(title, description, Some(back_button().widget_ref())) +} + +pub fn status_page( + title: &str, + description: Option<&str>, + child: Option<>k::Widget>, +) -> adw::StatusPage { + let mut builder = adw::StatusPage::builder().title(title); + + if let Some(description) = description { + builder = builder.description(description); + } + if let Some(child) = child { + builder = builder.child(child); + } + + builder.build() +} + +pub fn status_page_loading(title: &str, description: Option<&str>) -> adw::StatusPage { + let mut builder = adw::StatusPage::builder().title(title); + + if let Some(description) = description { + builder = builder.description(description); + } + + let status_page = builder.build(); + status_page.set_paintable(Some(adw::SpinnerPaintable::new(Some(&status_page))).as_ref()); + status_page +}