From 64a1dfec2031e133646f10be4765029ae7373a87 Mon Sep 17 00:00:00 2001 From: Ward from fusion-voyager-3 Date: Sun, 25 Aug 2024 06:12:19 +0300 Subject: [PATCH] fix git cache --- .gitignore | 2 + Cargo.lock | 2299 +++++++++++++++++ Cargo.toml | 40 + ...kaos-linux.pikmanupdatemanager.gschema.xml | 24 + just_in_case/__main__.py | 91 + locales/en_US.json | 84 + src/bin/apt/apt_full_upgrade/main.rs | 89 + src/bin/apt/apt_update/main.rs | 26 + src/bin/gui/apt_package_row/imp.rs | 597 +++++ src/bin/gui/apt_package_row/mod.rs | 45 + src/bin/gui/apt_update_page/mod.rs | 492 ++++ src/bin/gui/apt_update_page/process.rs | 622 +++++ src/bin/gui/build_ui/mod.rs | 295 +++ src/bin/gui/config.rs | 4 + src/bin/gui/flatpak_ref_row/imp.rs | 486 ++++ src/bin/gui/flatpak_ref_row/mod.rs | 54 + src/bin/gui/flatpak_update_page/mod.rs | 784 ++++++ src/bin/gui/flatpak_update_page/process.rs | 541 ++++ src/bin/gui/main.rs | 56 + src/bin/gui/style.css | 67 + src/lib/apt_install_progress_socket/mod.rs | 83 + src/lib/apt_update_progress_socket/mod.rs | 152 ++ src/lib/lib.rs | 3 + src/lib/pika_unixsocket_tools/mod.rs | 177 ++ 24 files changed, 7113 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 data/com.github.pikaos-linux.pikmanupdatemanager.gschema.xml create mode 100755 just_in_case/__main__.py create mode 100644 locales/en_US.json create mode 100644 src/bin/apt/apt_full_upgrade/main.rs create mode 100644 src/bin/apt/apt_update/main.rs create mode 100644 src/bin/gui/apt_package_row/imp.rs create mode 100644 src/bin/gui/apt_package_row/mod.rs create mode 100644 src/bin/gui/apt_update_page/mod.rs create mode 100644 src/bin/gui/apt_update_page/process.rs create mode 100644 src/bin/gui/build_ui/mod.rs create mode 100644 src/bin/gui/config.rs create mode 100644 src/bin/gui/flatpak_ref_row/imp.rs create mode 100644 src/bin/gui/flatpak_ref_row/mod.rs create mode 100644 src/bin/gui/flatpak_update_page/mod.rs create mode 100644 src/bin/gui/flatpak_update_page/process.rs create mode 100644 src/bin/gui/main.rs create mode 100644 src/bin/gui/style.css create mode 100644 src/lib/apt_install_progress_socket/mod.rs create mode 100644 src/lib/apt_update_progress_socket/mod.rs create mode 100644 src/lib/lib.rs create mode 100644 src/lib/pika_unixsocket_tools/mod.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..40d9aca --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/target +/.idea \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..304188d --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,2299 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "addr2line" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "android-tzdata" +version = "0.1.1" +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" +dependencies = [ + "libc", +] + +[[package]] +name = "arc-swap" +version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457" + +[[package]] +name = "async-channel" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89b47800b0be77592da0afd425cc03468052844aff33b84e33cc696f64e77b6a" +dependencies = [ + "concurrent-queue", + "event-listener-strategy", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-trait" +version = "0.1.81" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e0c28dcc82d7c8ead5cb13beb15405b57b8546e93215673ff8ca0349a028107" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi 0.1.19", + "libc", + "winapi 0.3.9", +] + +[[package]] +name = "autocfg" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" + +[[package]] +name = "backtrace" +version = "0.3.73" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cc23269a4f8976d0a4d2e7109211a419fe30e8d88d677cd60b6bc79c5732e0a" +dependencies = [ + "addr2line", + "cc", + "cfg-if 1.0.0", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" + +[[package]] +name = "bstr" +version = "1.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05efc5cfd9110c8416e471df0e96702d58690178e206e61b7173706673c93706" +dependencies = [ + "memchr", + "serde", +] + +[[package]] +name = "bumpalo" +version = "3.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "206fdffcfa2df7cbe15601ef46c813fce0965eb3286db6b56c583b814b51c81c" +dependencies = [ + "byteorder", + "iovec", +] + +[[package]] +name = "bytes" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" + +[[package]] +name = "cairo-rs" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "797fd5a634dcb0ad0d7d583df794deb0a236d88e759cd34b7da20198c6c9d145" +dependencies = [ + "bitflags 2.6.0", + "cairo-sys-rs", + "glib 0.20.0", + "libc", + "thiserror", +] + +[[package]] +name = "cairo-sys-rs" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "428290f914b9b86089f60f5d8a9f6e440508e1bcff23b25afd51502b0a2da88f" +dependencies = [ + "glib-sys 0.20.0", + "libc", + "system-deps 7.0.1", +] + +[[package]] +name = "cc" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eaff6f8ce506b9773fa786672d63fc7a191ffea1be33f72bbd4aeacefca9ffc8" + +[[package]] +name = "cfg-expr" +version = "0.15.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d067ad48b8650848b989a59a86c6c36a995d02d2bf778d45c3c5d57bc2718f02" +dependencies = [ + "smallvec 1.13.2", + "target-lexicon", +] + +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[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", + "js-sys", + "num-traits", + "wasm-bindgen", + "windows-targets 0.52.6", +] + +[[package]] +name = "cloudabi" +version = "0.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" +dependencies = [ + "bitflags 1.3.2", +] + +[[package]] +name = "codespan-reporting" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" +dependencies = [ + "termcolor", + "unicode-width", +] + +[[package]] +name = "concurrent-queue" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" +dependencies = [ + "crossbeam-utils 0.8.20", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" + +[[package]] +name = "crossbeam-deque" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils 0.8.20", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils 0.8.20", +] + +[[package]] +name = "crossbeam-utils" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8" +dependencies = [ + "autocfg", + "cfg-if 0.1.10", + "lazy_static", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" + +[[package]] +name = "cxx" +version = "1.0.124" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "273dcfd3acd4e1e276af13ed2a43eea7001318823e7a726a6b3ed39b4acc0b82" +dependencies = [ + "cc", + "cxxbridge-flags", + "cxxbridge-macro", + "link-cplusplus", +] + +[[package]] +name = "cxx-build" +version = "1.0.124" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8b2766fbd92be34e9ed143898fce6c572dc009de39506ed6903e5a05b68914e" +dependencies = [ + "cc", + "codespan-reporting", + "once_cell", + "proc-macro2", + "quote", + "scratch", + "syn", +] + +[[package]] +name = "cxxbridge-flags" +version = "1.0.124" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "839fcd5e43464614ffaa989eaf1c139ef1f0c51672a1ed08023307fa1b909ccd" + +[[package]] +name = "cxxbridge-macro" +version = "1.0.124" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b2c1c1776b986979be68bb2285da855f8d8a35851a769fca8740df7c3d07877" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "errno" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "event-listener" +version = "5.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6032be9bd27023a771701cc49f9f053c751055f71efb2e0ae5c15809093675ba" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", +] + +[[package]] +name = "event-listener-strategy" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f214dc438f977e6d4e3500aaa277f5ad94ca83fbbd9b1a15713ce2344ccc5a1" +dependencies = [ + "event-listener", + "pin-project-lite", +] + +[[package]] +name = "field-offset" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38e2275cc4e4fc009b0669731a1e5ab7ebf11f469eaede2bab9309a5b4d6057f" +dependencies = [ + "memoffset", + "rustc_version 0.4.0", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "fuchsia-zircon" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" +dependencies = [ + "bitflags 1.3.2", + "fuchsia-zircon-sys", +] + +[[package]] +name = "fuchsia-zircon-sys" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" + +[[package]] +name = "futures" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a471a38ef8ed83cd6e40aa59c1ffe17db6855c18e3604d9c4ed8c08ebc28678" + +[[package]] +name = "futures" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" + +[[package]] +name = "futures-executor" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" + +[[package]] +name = "futures-macro" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" + +[[package]] +name = "futures-task" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" + +[[package]] +name = "futures-util" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "gdk-pixbuf" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28bb53ecb56857c683c9ec859908e076dd3969c7d67598bd8b1ce095d211304a" +dependencies = [ + "gdk-pixbuf-sys", + "gio 0.20.0", + "glib 0.20.0", + "libc", +] + +[[package]] +name = "gdk-pixbuf-sys" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f6681a0c1330d1d3968bec1529f7172d62819ef0bdbb0d18022320654158b03" +dependencies = [ + "gio-sys 0.20.0", + "glib-sys 0.20.0", + "gobject-sys 0.20.0", + "libc", + "system-deps 7.0.1", +] + +[[package]] +name = "gdk4" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b7d7237c1487ed4b300aac7744efcbf1319e12d60d7afcd6f505414bd5b5dea" +dependencies = [ + "cairo-rs", + "gdk-pixbuf", + "gdk4-sys", + "gio 0.20.0", + "glib 0.20.0", + "libc", + "pango", +] + +[[package]] +name = "gdk4-sys" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a67576c8ec012156d7f680e201a807b4432a77babb3157e0555e990ab6bcd878" +dependencies = [ + "cairo-sys-rs", + "gdk-pixbuf-sys", + "gio-sys 0.20.0", + "glib-sys 0.20.0", + "gobject-sys 0.20.0", + "libc", + "pango-sys", + "pkg-config", + "system-deps 7.0.1", +] + +[[package]] +name = "getopts" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5" +dependencies = [ + "unicode-width", +] + +[[package]] +name = "gimli" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" + +[[package]] +name = "gio" +version = "0.19.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c49f117d373ffcc98a35d114db5478bc223341cff53e39a5d6feced9e2ddffe" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-util", + "gio-sys 0.19.8", + "glib 0.19.9", + "libc", + "pin-project-lite", + "smallvec 1.13.2", + "thiserror", +] + +[[package]] +name = "gio" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "398e3da68749fdc32783cbf7521ec3f65c9cf946db8c7774f8460af49e52c6e2" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-util", + "gio-sys 0.20.0", + "glib 0.20.0", + "libc", + "pin-project-lite", + "smallvec 1.13.2", + "thiserror", +] + +[[package]] +name = "gio-sys" +version = "0.19.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2cd743ba4714d671ad6b6234e8ab2a13b42304d0e13ab7eba1dcdd78a7d6d4ef" +dependencies = [ + "glib-sys 0.19.8", + "gobject-sys 0.19.8", + "libc", + "system-deps 6.2.2", + "windows-sys 0.52.0", +] + +[[package]] +name = "gio-sys" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4feb96b31c32730ea3e1e89aecd2e4e37ecb1c473ad8f685e3430a159419f63" +dependencies = [ + "glib-sys 0.20.0", + "gobject-sys 0.20.0", + "libc", + "system-deps 7.0.1", + "windows-sys 0.52.0", +] + +[[package]] +name = "glib" +version = "0.19.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39650279f135469465018daae0ba53357942a5212137515777d5fdca74984a44" +dependencies = [ + "bitflags 2.6.0", + "futures-channel", + "futures-core", + "futures-executor", + "futures-task", + "futures-util", + "gio-sys 0.19.8", + "glib-macros 0.19.9", + "glib-sys 0.19.8", + "gobject-sys 0.19.8", + "libc", + "memchr", + "smallvec 1.13.2", + "thiserror", +] + +[[package]] +name = "glib" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fee90a615ce05be7a32932cfb8adf2c4bbb4700e80d37713c981fb24c0c56238" +dependencies = [ + "bitflags 2.6.0", + "futures-channel", + "futures-core", + "futures-executor", + "futures-task", + "futures-util", + "gio-sys 0.20.0", + "glib-macros 0.20.0", + "glib-sys 0.20.0", + "gobject-sys 0.20.0", + "libc", + "memchr", + "smallvec 1.13.2", + "thiserror", +] + +[[package]] +name = "glib-macros" +version = "0.19.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4429b0277a14ae9751350ad9b658b1be0abb5b54faa5bcdf6e74a3372582fad7" +dependencies = [ + "heck", + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "glib-macros" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4da558d8177c0c8c54368818b508a4244e1286fce2858cef4e547023f0cfa5ef" +dependencies = [ + "heck", + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "glib-sys" +version = "0.19.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c2dc18d3a82b0006d470b13304fbbb3e0a9bd4884cf985a60a7ed733ac2c4a5" +dependencies = [ + "libc", + "system-deps 6.2.2", +] + +[[package]] +name = "glib-sys" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4958c26e5a01c9af00dea669a97369eccbec29a8e6d125c24ea2d85ee7467b60" +dependencies = [ + "libc", + "system-deps 7.0.1", +] + +[[package]] +name = "glob" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" + +[[package]] +name = "globset" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57da3b9b5b85bd66f31093f8c408b90a74431672542466497dcbdfdc02034be1" +dependencies = [ + "aho-corasick", + "bstr", + "log", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "globwalk" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93e3af942408868f6934a7b85134a3230832b9977cf66125df2f9edcfce4ddcc" +dependencies = [ + "bitflags 1.3.2", + "ignore", + "walkdir", +] + +[[package]] +name = "gobject-sys" +version = "0.19.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e697e252d6e0416fd1d9e169bda51c0f1c926026c39ca21fbe8b1bb5c3b8b9e" +dependencies = [ + "glib-sys 0.19.8", + "libc", + "system-deps 6.2.2", +] + +[[package]] +name = "gobject-sys" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6908864f5ffff15b56df7e90346863904f49b949337ed0456b9287af61903b8" +dependencies = [ + "glib-sys 0.20.0", + "libc", + "system-deps 7.0.1", +] + +[[package]] +name = "graphene-rs" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "630e940ad5824f90221d6579043a9cd1f8bec86b4a17faaf7827d58eb16e8c1f" +dependencies = [ + "glib 0.20.0", + "graphene-sys", + "libc", +] + +[[package]] +name = "graphene-sys" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fb8fade7b754982f47ebbed241fd2680816fdd4598321784da10b9e1168836a" +dependencies = [ + "glib-sys 0.20.0", + "libc", + "pkg-config", + "system-deps 7.0.1", +] + +[[package]] +name = "gsk4" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3cf2091e1af185b347b3450817d93dea6fe435df7abd4c2cd7fb5bcb4cfda8" +dependencies = [ + "cairo-rs", + "gdk4", + "glib 0.20.0", + "graphene-rs", + "gsk4-sys", + "libc", + "pango", +] + +[[package]] +name = "gsk4-sys" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6aa69614a26d8760c186c3690f1b0fbb917572ca23ef83137445770ceddf8cde" +dependencies = [ + "cairo-sys-rs", + "gdk4-sys", + "glib-sys 0.20.0", + "gobject-sys 0.20.0", + "graphene-sys", + "libc", + "pango-sys", + "system-deps 7.0.1", +] + +[[package]] +name = "gtk4" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eaffc6c743c9160514cc9b67eace364e5dc5798369fa809cdb04e035c21c5c5d" +dependencies = [ + "cairo-rs", + "field-offset", + "futures-channel", + "gdk-pixbuf", + "gdk4", + "gio 0.20.0", + "glib 0.20.0", + "graphene-rs", + "gsk4", + "gtk4-macros", + "gtk4-sys", + "libc", + "pango", +] + +[[package]] +name = "gtk4-macros" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "188211f546ce5801f6d0245c37b6249143a2cb4fa040e54829ca1e76796e9f09" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "gtk4-sys" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1114a207af8ada02cf4658a76692f4190f06f093380d5be07e3ca8b43aa7c666" +dependencies = [ + "cairo-sys-rs", + "gdk-pixbuf-sys", + "gdk4-sys", + "gio-sys 0.20.0", + "glib-sys 0.20.0", + "gobject-sys 0.20.0", + "graphene-sys", + "gsk4-sys", + "libc", + "pango-sys", + "system-deps 7.0.1", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "hermit-abi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" + +[[package]] +name = "iana-time-zone" +version = "0.1.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "ignore" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b46810df39e66e925525d6e38ce1e7f6e1d208f72dc39757880fcb66e2c58af1" +dependencies = [ + "crossbeam-deque", + "globset", + "log", + "memchr", + "regex-automata", + "same-file", + "walkdir", + "winapi-util", +] + +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", +] + +[[package]] +name = "indexmap" +version = "2.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" +dependencies = [ + "equivalent", + "hashbrown 0.14.5", +] + +[[package]] +name = "iovec" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e" +dependencies = [ + "libc", +] + +[[package]] +name = "itoa" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" + +[[package]] +name = "js-sys" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "kernel32-sys" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" +dependencies = [ + "winapi 0.2.8", + "winapi-build", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "libadwaita" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ff9c222b5c783729de45185f07b2fec2d43a7f9c63961e777d3667e20443878" +dependencies = [ + "gdk4", + "gio 0.20.0", + "glib 0.20.0", + "gtk4", + "libadwaita-sys", + "libc", + "pango", +] + +[[package]] +name = "libadwaita-sys" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c44d8bdbad31d6639e1f20cc9c1424f1a8e02d751fc28d44659bf743fb9eca6" +dependencies = [ + "gdk4-sys", + "gio-sys 0.20.0", + "glib-sys 0.20.0", + "gobject-sys 0.20.0", + "gtk4-sys", + "libc", + "pango-sys", + "system-deps 7.0.1", +] + +[[package]] +name = "libc" +version = "0.2.155" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" + +[[package]] +name = "libflatpak" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a0466e5bc49c8a9d03c1daf924833474640bf3b334b01efde4234e77efe9780" +dependencies = [ + "gio 0.19.8", + "glib 0.19.9", + "libc", + "libflatpak-sys", +] + +[[package]] +name = "libflatpak-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0022c1b20fa3959a52299cd531c6afc9433182f612b1210f18776a7857a58f2b" +dependencies = [ + "gio-sys 0.19.8", + "glib-sys 0.19.8", + "gobject-sys 0.19.8", + "libc", + "pkg-config", + "system-deps 6.2.2", +] + +[[package]] +name = "link-cplusplus" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d240c6f7e1ba3a28b0249f774e6a9dd0175054b52dfbb61b16eb8505c3785c9" +dependencies = [ + "cc", +] + +[[package]] +name = "linked-hash-map" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" + +[[package]] +name = "linux-raw-sys" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" + +[[package]] +name = "lock_api" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4da24a77a3d8a6d4862d95f72e6fdb9c09a643ecdb402d754004a557f2bec75" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "lock_api" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" + +[[package]] +name = "maybe-uninit" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00" + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "memoffset" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" +dependencies = [ + "autocfg", +] + +[[package]] +name = "miniz_oxide" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" +dependencies = [ + "adler", +] + +[[package]] +name = "mio" +version = "0.6.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4afd66f5b91bf2a3bc13fad0e21caedac168ca4c707504e75585648ae80e4cc4" +dependencies = [ + "cfg-if 0.1.10", + "fuchsia-zircon", + "fuchsia-zircon-sys", + "iovec", + "kernel32-sys", + "libc", + "log", + "miow", + "net2", + "slab", + "winapi 0.2.8", +] + +[[package]] +name = "mio" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" +dependencies = [ + "libc", + "wasi", + "windows-sys 0.48.0", +] + +[[package]] +name = "mio-uds" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afcb699eb26d4332647cc848492bbc15eafb26f08d0304550d5aa1f612e066f0" +dependencies = [ + "iovec", + "libc", + "mio 0.6.23", +] + +[[package]] +name = "miow" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebd808424166322d4a38da87083bfddd3ac4c131334ed55856112eb06d46944d" +dependencies = [ + "kernel32-sys", + "net2", + "winapi 0.2.8", + "ws2_32-sys", +] + +[[package]] +name = "net2" +version = "0.2.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b13b648036a2339d06de780866fbdfda0dde886de7b3af2ddeba8b14f4ee34ac" +dependencies = [ + "cfg-if 0.1.10", + "libc", + "winapi 0.3.9", +] + +[[package]] +name = "normpath" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5831952a9476f2fed74b77d74182fa5ddc4d21c72ec45a333b250e3ed0272804" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +dependencies = [ + "hermit-abi 0.3.9", + "libc", +] + +[[package]] +name = "object" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "081b846d1d56ddfc18fdf1a922e4f6e07a11768ea1b92dec44e42b72712ccfce" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "pango" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54768854025df6903061d0084fd9702a253ddfd60db7d9b751d43b76689a7f0a" +dependencies = [ + "gio 0.20.0", + "glib 0.20.0", + "libc", + "pango-sys", +] + +[[package]] +name = "pango-sys" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b07cc57d10cee4ec661f718a6902cee18c2f4cfae08e87e5a390525946913390" +dependencies = [ + "glib-sys 0.20.0", + "gobject-sys 0.20.0", + "libc", + "system-deps 7.0.1", +] + +[[package]] +name = "parking" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb813b8af86854136c6922af0598d719255ecb2179515e6e7730d468f05c9cae" + +[[package]] +name = "parking_lot" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f842b1982eb6c2fe34036a4fbfb06dd185a3f5c8edfaacdf7d1ea10b07de6252" +dependencies = [ + "lock_api 0.3.4", + "parking_lot_core 0.6.3", + "rustc_version 0.2.3", +] + +[[package]] +name = "parking_lot" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" +dependencies = [ + "lock_api 0.4.12", + "parking_lot_core 0.9.10", +] + +[[package]] +name = "parking_lot_core" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bda66b810a62be75176a80873726630147a5ca780cd33921e0b5709033e66b0a" +dependencies = [ + "cfg-if 0.1.10", + "cloudabi", + "libc", + "redox_syscall 0.1.57", + "rustc_version 0.2.3", + "smallvec 0.6.14", + "winapi 0.3.9", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "redox_syscall 0.5.2", + "smallvec 1.13.2", + "windows-targets 0.52.6", +] + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "pikman-update-manager" +version = "0.1.0" +dependencies = [ + "async-channel", + "async-trait", + "chrono", + "crossbeam-utils 0.8.20", + "futures 0.3.30", + "gtk4", + "libadwaita", + "libflatpak", + "lock_api 0.4.12", + "pretty-bytes", + "rust-apt", + "rust-i18n", + "serde", + "serde_json", + "tokio", + "tokio-uds", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkg-config" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" + +[[package]] +name = "pretty-bytes" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "009d6edd2c1dbf2e1c0cd48a2f7766e03498d49ada7109a01c6911815c685316" +dependencies = [ + "atty", + "getopts", +] + +[[package]] +name = "proc-macro-crate" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d37c51ca738a55da99dc0c4a34860fd675453b8b36209178c2249bb13651284" +dependencies = [ + "toml_edit 0.21.1", +] + +[[package]] +name = "proc-macro2" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "redox_syscall" +version = "0.1.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" + +[[package]] +name = "redox_syscall" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c82cf8cff14456045f55ec4241383baeff27af886adb72ffb2162f99911de0fd" +dependencies = [ + "bitflags 2.6.0", +] + +[[package]] +name = "regex" +version = "1.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" + +[[package]] +name = "rust-apt" +version = "0.7.0" +source = "git+https://gitlab.com/volian/rust-apt#89f7f127b3af5595bec61ab520a813d8bf9fc502" +dependencies = [ + "cxx", + "cxx-build", + "paste", + "terminal_size", +] + +[[package]] +name = "rust-i18n" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dcd94370631e5658a0a23635f7f47e43d06a00ad948e0bb5de79b00d85b880c" +dependencies = [ + "globwalk", + "once_cell", + "regex", + "rust-i18n-macro", + "rust-i18n-support", + "smallvec 1.13.2", +] + +[[package]] +name = "rust-i18n-macro" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "355763801dcf287e777e42def7c578410783477b804b1107852119e0b2518396" +dependencies = [ + "glob", + "once_cell", + "proc-macro2", + "quote", + "rust-i18n-support", + "serde", + "serde_json", + "serde_yaml", + "syn", +] + +[[package]] +name = "rust-i18n-support" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "399801f4d955abf1c3ce3ce2215dc76bd40beb4ae39e3a84936b21a79ce2caa5" +dependencies = [ + "arc-swap", + "globwalk", + "lazy_static", + "normpath", + "once_cell", + "proc-macro2", + "regex", + "serde", + "serde_json", + "serde_yaml", + "toml 0.7.8", + "triomphe", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" + +[[package]] +name = "rustc_version" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" +dependencies = [ + "semver 0.9.0", +] + +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver 1.0.23", +] + +[[package]] +name = "rustix" +version = "0.38.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" +dependencies = [ + "bitflags 2.6.0", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.52.0", +] + +[[package]] +name = "ryu" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" + +[[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 = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "scratch" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3cf7c11c38cb994f3d40e8a8cde3bbd1f72a435e4c49e85d6553d8312306152" + +[[package]] +name = "semver" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" +dependencies = [ + "semver-parser", +] + +[[package]] +name = "semver" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" + +[[package]] +name = "semver-parser" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" + +[[package]] +name = "serde" +version = "1.0.204" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc76f558e0cbb2a839d37354c575f1dc3fdc6546b5be373ba43d95f231bf7c12" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.204" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.120" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e0d21c9a8cae1235ad58a00c11cb40d4b1e5c784f1ef2c537876ed6ffd8b7c5" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_spanned" +version = "0.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79e674e01f999af37c49f70a6ede167a8a60b2503e56c5599532a65baa5969a0" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_yaml" +version = "0.8.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "578a7433b776b56a35785ed5ce9a7e777ac0598aac5a6dd1b4b18a307c7fc71b" +dependencies = [ + "indexmap 1.9.3", + "ryu", + "serde", + "yaml-rust", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" +dependencies = [ + "libc", +] + +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + +[[package]] +name = "smallvec" +version = "0.6.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b97fcaeba89edba30f044a10c6a3cc39df9c3f17d7cd829dd1446cab35f890e0" +dependencies = [ + "maybe-uninit", +] + +[[package]] +name = "smallvec" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + +[[package]] +name = "socket2" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "syn" +version = "2.0.70" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f0209b68b3613b093e0ec905354eccaedcfe83b8cb37cbdeae64026c3064c16" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "system-deps" +version = "6.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e535eb8dded36d55ec13eddacd30dec501792ff23a0b1682c38601b8cf2349" +dependencies = [ + "cfg-expr", + "heck", + "pkg-config", + "toml 0.8.14", + "version-compare", +] + +[[package]] +name = "system-deps" +version = "7.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c81f13d9a334a6c242465140bd262fae382b752ff2011c4f7419919a9c97922" +dependencies = [ + "cfg-expr", + "heck", + "pkg-config", + "toml 0.8.14", + "version-compare", +] + +[[package]] +name = "target-lexicon" +version = "0.12.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4873307b7c257eddcb50c9bedf158eb669578359fb28428bef438fec8e6ba7c2" + +[[package]] +name = "termcolor" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "terminal_size" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21bebf2b7c9e0a515f6e0f8c51dc0f8e4696391e6f1ff30379559f8365fb0df7" +dependencies = [ + "rustix", + "windows-sys 0.48.0", +] + +[[package]] +name = "thiserror" +version = "1.0.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio" +version = "1.38.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba4f4a02a7a80d6f274636f0aa95c7e383b912d41fe721a31f29e29698585a4a" +dependencies = [ + "backtrace", + "bytes 1.6.0", + "libc", + "mio 0.8.11", + "num_cpus", + "parking_lot 0.12.3", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys 0.48.0", +] + +[[package]] +name = "tokio-codec" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25b2998660ba0e70d18684de5d06b70b70a3a747469af9dea7618cc59e75976b" +dependencies = [ + "bytes 0.4.12", + "futures 0.1.31", + "tokio-io", +] + +[[package]] +name = "tokio-executor" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb2d1b8f4548dbf5e1f7818512e9c406860678f29c300cdf0ebac72d1a3a1671" +dependencies = [ + "crossbeam-utils 0.7.2", + "futures 0.1.31", +] + +[[package]] +name = "tokio-io" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57fc868aae093479e3131e3d165c93b1c7474109d13c90ec0dda2a1bbfff0674" +dependencies = [ + "bytes 0.4.12", + "futures 0.1.31", + "log", +] + +[[package]] +name = "tokio-macros" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f5ae998a069d4b5aba8ee9dad856af7d520c3699e6159b185c2acd48155d39a" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio-reactor" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09bc590ec4ba8ba87652da2068d150dcada2cfa2e07faae270a5e0409aa51351" +dependencies = [ + "crossbeam-utils 0.7.2", + "futures 0.1.31", + "lazy_static", + "log", + "mio 0.6.23", + "num_cpus", + "parking_lot 0.9.0", + "slab", + "tokio-executor", + "tokio-io", + "tokio-sync", +] + +[[package]] +name = "tokio-sync" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edfe50152bc8164fcc456dab7891fa9bf8beaf01c5ee7e1dd43a397c3cf87dee" +dependencies = [ + "fnv", + "futures 0.1.31", +] + +[[package]] +name = "tokio-uds" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab57a4ac4111c8c9dbcf70779f6fc8bc35ae4b2454809febac840ad19bd7e4e0" +dependencies = [ + "bytes 0.4.12", + "futures 0.1.31", + "iovec", + "libc", + "log", + "mio 0.6.23", + "mio-uds", + "tokio-codec", + "tokio-io", + "tokio-reactor", +] + +[[package]] +name = "toml" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd79e69d3b627db300ff956027cc6c3798cef26d22526befdfcd12feeb6d2257" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit 0.19.15", +] + +[[package]] +name = "toml" +version = "0.8.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f49eb2ab21d2f26bd6db7bf383edc527a7ebaee412d17af4d40fdccd442f335" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit 0.22.15", +] + +[[package]] +name = "toml_datetime" +version = "0.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4badfd56924ae69bcc9039335b2e017639ce3f9b001c393c1b2d1ef846ce2cbf" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.19.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" +dependencies = [ + "indexmap 2.2.6", + "serde", + "serde_spanned", + "toml_datetime", + "winnow 0.5.40", +] + +[[package]] +name = "toml_edit" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1" +dependencies = [ + "indexmap 2.2.6", + "toml_datetime", + "winnow 0.5.40", +] + +[[package]] +name = "toml_edit" +version = "0.22.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d59a3a72298453f564e2b111fa896f8d07fabb36f51f06d7e875fc5e0b5a3ef1" +dependencies = [ + "indexmap 2.2.6", + "serde", + "serde_spanned", + "toml_datetime", + "winnow 0.6.13", +] + +[[package]] +name = "triomphe" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6631e42e10b40c0690bf92f404ebcfe6e1fdb480391d15f17cc8e96eeed5369" +dependencies = [ + "arc-swap", + "serde", + "stable_deref_trait", +] + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "unicode-width" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d" + +[[package]] +name = "version-compare" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "852e951cb7832cb45cb1169900d19760cfa39b82bc0ea9c0e5a14ae88411c98b" + +[[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 = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" +dependencies = [ + "cfg-if 1.0.0", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" + +[[package]] +name = "winapi" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-build" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b" +dependencies = [ + "windows-sys 0.52.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 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "winnow" +version = "0.5.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" +dependencies = [ + "memchr", +] + +[[package]] +name = "winnow" +version = "0.6.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59b5e5f6c299a3c7890b876a2a587f3115162487e704907d9b6cd29473052ba1" +dependencies = [ + "memchr", +] + +[[package]] +name = "ws2_32-sys" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" +dependencies = [ + "winapi 0.2.8", + "winapi-build", +] + +[[package]] +name = "yaml-rust" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" +dependencies = [ + "linked-hash-map", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..8ef248e --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,40 @@ +[package] +name = "pikman-update-manager" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[lib] +name = "pika_unixsocket_tools" +path = "src/lib/lib.rs" + +[[bin]] +name = "pikman-update-manager" +path = "src/bin/gui/main.rs" + +[[bin]] +name = "apt_update" +path = "src/bin/apt/apt_update/main.rs" + +[[bin]] +name = "apt_full_upgrade" +path = "src/bin/apt/apt_full_upgrade/main.rs" + +[dependencies] +adw = { version = "0.7.0", package = "libadwaita", features = ["v1_4"] } +gtk = { version = "0.9.0", package = "gtk4", features = ["v4_12"] } +async-channel = "2.1.1" +rust-i18n = "3.0.1" +rust-apt = { git = "https://gitlab.com/volian/rust-apt" } +tokio = { version = "1", features = ["full"] } +tokio-uds = "0.2" +serde = { version = "1.0.203", features = ["derive"] } +serde_json = "1.0.118" +async-trait = "0.1.80" +futures = "0.3.30" +pretty-bytes = "0.2.2" +crossbeam-utils = "0.8.20" +chrono = "0.4.38" +lock_api = "0.4.2" +libflatpak = { version = "0.5.0", package = "libflatpak", features = ["v1_11_1"] } + diff --git a/data/com.github.pikaos-linux.pikmanupdatemanager.gschema.xml b/data/com.github.pikaos-linux.pikmanupdatemanager.gschema.xml new file mode 100644 index 0000000..033a9b9 --- /dev/null +++ b/data/com.github.pikaos-linux.pikmanupdatemanager.gschema.xml @@ -0,0 +1,24 @@ + + + + + 1400 + Default window width + + + 700 + Default window height + + + false + Default window maximized behaviour + + + true + Show PikaOS Welcome on startup. + + Show PikaOS Welcome on startup. + + + + \ No newline at end of file diff --git a/just_in_case/__main__.py b/just_in_case/__main__.py new file mode 100755 index 0000000..5d6f26f --- /dev/null +++ b/just_in_case/__main__.py @@ -0,0 +1,91 @@ +#! /bin/python3 + +import socket +import os + +import sys +import time + +import apt_pkg + +import apt +import apt.progress.base + +def get_change(current, total): + if current == total: + return 100.0 + try: + return float("{:.1f}".format(((current * 100) / total))) + except ZeroDivisionError: + return 0.0 + +class UpdateProgressSocket(apt.progress.base.AcquireProgress): + # Init + def __init__(self): + pass + + # Start + def start(self): + self.current_bytes = 0 + self.total_bytes = 0 + print("Starting APT Cache Update.") + return super().start() + + # Stop + def stop(self): + print("\nAPT Cache Update Complete!") + return super().stop() + + # Progrss pulse + def pulse(self, owner): + # Calculate current progress percentage + progress_percent = get_change(self.current_bytes, self.total_bytes) + + # apt_update_progress ipc sock + socket_path = "/tmp/pika_apt_update.sock" + + # Create a Unix domain socket + with socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) as client: + client.connect(socket_path) + # Send percentage to socket as UTF-8 + client.sendall(str(progress_percent).encode('utf-8')) + + #response = client.recv(1024) + #print(f"Received: {response.decode('utf-8')}") + + return True + + + def fail(self, item): + print("Failure at: %s %s" % (item.uri, item.shortdesc)) + + def fetch(self, item): + print("Fetch: %s %s" % (item.uri, item.shortdesc)) + + def ims_hit(self, item): + print("Download: %s %s" % (item.uri, item.shortdesc)) + + def media_change(self, medium, drive): + print(f"Please insert medium {medium} in drive {drive}") + sys.stdin.readline() + # return False + +def update_cache(): + # First of all, open the cache + cache = apt.Cache() + # Now, lets update the package list + cache.update(UpdateProgressSocket()) + # We need to re-open the cache because it needs to read the package list + cache.open(None) + # We need to re-open the cache because it needs to read the package list + for pkg in cache: + if pkg.is_upgradable: + print(f"{pkg.name} ({pkg.installed.version} -> {pkg.candidate.version})") + +def process(data): + # Echo the input data + return data + +if __name__ == "__main__": + + update_cache() diff --git a/locales/en_US.json b/locales/en_US.json new file mode 100644 index 0000000..8eda58c --- /dev/null +++ b/locales/en_US.json @@ -0,0 +1,84 @@ +{ + "application_name": "Pikman Update Manager", + "developer_name": "Cosmo", + "installed_version_badge_text": "Installed", + "candidate_version_badge_text": "Upgradable", + "arch_label_label": "Arch", + "mark_for_update": "Mark This Package for Upgrade", + "description_button_label": "Description", + "extra_info_page_button_label": "Additional Info", + "uris_page_button_label": "Download URIs", + "changelog_page_button_label": "Changelog (todo)", + "extra_info_maintainer": "Maintainer", + "extra_info_download_size": "Download Size", + "extra_info_installed_size": "Size on Disk", + "update_status_error_perms": "Unknown Error! (Likely Permission Denied)", + "apt_update_dialog_heading": "APT Cache Update", + "apt_update_dialog_retry_label": "Retry", + "select_button_deselect_all": "De-Select All", + "select_button_select_all": "Select All", + "update_button_label": "Commit Upgrade", + "installed_version_to_be_installed": "Not Installed Yet", + "apt_pkg_property_unknown": "Unknown", + "apt_update_dialog_status_failed": "APT Cache Update: Failed!", + "excluded_updates_alert_dialog_heading": "Alert: Partial Upgrade", + "excluded_updates_alert_dialog_body": "It seems you have deselected some upgrades.\nThis is generally not recommended, and APT partial update security systems might not respect your input.", + "excluded_updates_alert_dialog_cancel_label": "Cancel", + "excluded_updates_alert_continue_label": "Continue", + "gui_changes_emu_msg_0": "The following opreations will be sent to the root process:", + "gui_changes_emu_msg_upgrading": "Upgrading", + "gui_changes_emu_msg_installing": "Installing", + "gui_changes_emu_msg_downgrading": "Downgrading", + "gui_changes_emu_msg_removing": "Uninstalling", + "package_count_upgrade_badge_label": "Packages to Upgrade", + "package_count_install_badge_label": "New Packages to Install", + "package_count_downgrade_badge_label": "Packages to Downgrade", + "package_count_remove_badge_label": "Packages to Uninstall", + "total_download_size_badge_label": "Total Download Size", + "total_installed_size_badge_label": "Total Size on Disk", + "apt_confirm_dialog_heading": "APT Upgrade Transaction: Please Review", + "apt_confirm_dialog_body": "The following changes are going to be made:", + "apt_confirm_dialog_cancel_label": "Decline", + "apt_confirm_dialog_confirm_label": "Confirm & Accept", + "apt_remove_confirm_dialog_heading": "APT Removal Confirmation: Please Review", + "apt_remove_confirm_dialog_body": "The Upgrade wants to remove the following packages: (This Might be extremely Dangerous!)", + "apt_remove_confirm_dialog_cancel_label": "Cancel", + "apt_remove_confirm_dialog_confirm_label": "Allow & Confirm", + "upgrade_status_error_perms": "Unknown Error! (Likely Permission Denied)", + "apt_upgrade_dialog_heading": "APT Upgrade Transaction: Hang Tight!", + "apt_upgrade_dialog_ok_label": "OK", + "apt_upgrade_dialog_open_log_file_label": "Open Apt Log File", + "apt_upgrade_dialog_status_successful": "APT Upgrade Transaction Successful!", + "apt_upgrade_dialog_status_failed": "APT Upgrade Transaction Failed!", + "banner_text_no_internet": "Warning: No Internet Connection!", + "refresh_button_tooltip_text": "Refresh Opened Page", + "apt_update_page_title": "Native Updates (APT)", + "flatpak_update_page_title" : "Flatpak Updates", + "apt_packages_no_viewport_page_title": "All Native APT Packages are Up to date!", + "summary_button_label": "Summary", + "flatpak_extra_info_ref_name": "Ref Codename", + "flatpak_extra_info_download_size": "Download Size", + "flatpak_extra_info_installed_size": "Size on Disk", + "remote_label_label": "Remote", + "flatpak_update_dialog_heading": "Flatpak Appstream Sync", + "flatpak_update_dialog_retry_label": "Retry", + "flatpak_update_dialog_status_failed": "Flatpak Appstream Sync: Failed", + "flatpak_packages_no_viewport_page_title": "All Flatpak Packages are Up to date!", + "flatpak_type_user": "User", + "flatpak_type_system": "System", + "flatpak_ref": "Flatpak Ref", + "flatpak_status": "Status", + "flatpak_transaction_bytes_transferred": "Downloaded", + "flatpak_transaction_installed_size": "Saved to Disk", + "flatpak_confirm_dialog_body": "The following changes are going to be made:", + "system_flatref_count_badge_label": "System Flatpaks to Update", + "user_flatref_count_badge_label": "User Flatpaks to Update", + "flatpak_confirm_dialog_heading": "Flatpak Update Transaction: Please Review", + "flatpak_confirm_dialog_cancel_label": "Decline", + "flatpak_confirm_dialog_confirm_label": "Confirm & Accept", + "flatpak_transaction_dialog_heading": "Flatpak Update Transaction: Hang Tight!", + "flatpak_transaction_dialog_ok_label": "OK", + "flatpak_transaction_dialog_open_log_file_label": "Open Flatpak Log File", + "flatpak_transaction_dialog_status_successful": "Flatpak Update Transaction: Successful!", + "flatpak_transaction_dialog_status_failed": "Flatpak Update Transaction: Failed!" +} \ No newline at end of file diff --git a/src/bin/apt/apt_full_upgrade/main.rs b/src/bin/apt/apt_full_upgrade/main.rs new file mode 100644 index 0000000..49d0654 --- /dev/null +++ b/src/bin/apt/apt_full_upgrade/main.rs @@ -0,0 +1,89 @@ +use pika_unixsocket_tools::apt_install_progress_socket::AptInstallProgressSocket; +use pika_unixsocket_tools::apt_update_progress_socket::AptUpdateProgressSocket; +use pika_unixsocket_tools::pika_unixsocket_tools::*; +use rust_apt::cache::Upgrade; +use rust_apt::new_cache; +use rust_apt::progress::{AcquireProgress, InstallProgress}; +use tokio::runtime::Runtime; + +fn main() { + let percent_socket_path = "/tmp/pika_apt_upgrade_percent.sock"; + let status_socket_path = "/tmp/pika_apt_upgrade_status.sock"; + let json_file_path = "/tmp/pika-apt-exclusions.json"; + let mut excluded_updates_vec: Vec = Vec::new(); + + if std::path::Path::new(json_file_path).exists() { + let data = std::fs::read_to_string(json_file_path).expect("Unable to read file"); + let json: serde_json::Value = + serde_json::from_str(&data).expect("JSON was not well-formatted"); + + if let serde_json::Value::Array(exclusions) = &json["exclusions"] { + for exclusion in exclusions { + match exclusion["package"].as_str() { + Some(v) => { + excluded_updates_vec.push(v.to_owned()); + } + None => {} + } + } + } + } + + let apt_cache = new_cache!().unwrap(); + + apt_cache.upgrade(Upgrade::FullUpgrade).unwrap(); + + let apt_upgrade_cache = if excluded_updates_vec.is_empty() { + apt_cache + } else { + let apt_upgrade_cache = new_cache!().unwrap(); + for change in apt_cache.get_changes(false) { + if !excluded_updates_vec + .iter() + .any(|e| change.name().contains(e)) + { + let pkg = apt_upgrade_cache.get(change.name()).unwrap(); + if change.marked_upgrade() || change.marked_install() || change.marked_downgrade() { + pkg.mark_install(true, false); + } else if change.marked_delete() { + pkg.mark_delete(false); + } + pkg.protect(); + } + } + apt_upgrade_cache + }; + + apt_upgrade_cache.resolve(true).unwrap(); + + let mut acquire_progress = AcquireProgress::new(AptUpdateProgressSocket::new( + percent_socket_path, + status_socket_path, + )); + let mut install_progress = InstallProgress::new(AptInstallProgressSocket::new( + percent_socket_path, + status_socket_path, + )); + + apt_upgrade_cache.resolve(true).unwrap(); + + match apt_upgrade_cache.get_archives(&mut acquire_progress) { + Ok(_) => {} + Err(e) => { + panic!("{}", e.to_string()) + } + }; + + match apt_upgrade_cache.do_install(&mut install_progress) { + Ok(_) => {} + Err(e) => { + Runtime::new() + .unwrap() + .block_on(send_failed_to_socket(percent_socket_path)); + Runtime::new() + .unwrap() + .block_on(send_failed_to_socket(status_socket_path)); + panic!("{}", e.to_string()) + } + }; +} diff --git a/src/bin/apt/apt_update/main.rs b/src/bin/apt/apt_update/main.rs new file mode 100644 index 0000000..ff6d760 --- /dev/null +++ b/src/bin/apt/apt_update/main.rs @@ -0,0 +1,26 @@ +use pika_unixsocket_tools::apt_update_progress_socket::AptUpdateProgressSocket; +use pika_unixsocket_tools::pika_unixsocket_tools::*; +use rust_apt::new_cache; +use rust_apt::progress::AcquireProgress; +use tokio::runtime::Runtime; + +fn main() { + let update_cache = new_cache!().unwrap(); + let percent_socket_path = "/tmp/pika_apt_update_percent.sock"; + let status_socket_path = "/tmp/pika_apt_update_status.sock"; + match update_cache.update(&mut AcquireProgress::new(AptUpdateProgressSocket::new( + percent_socket_path, + status_socket_path, + ))) { + Ok(_) => {} + Err(e) => { + Runtime::new() + .unwrap() + .block_on(send_failed_to_socket(percent_socket_path)); + Runtime::new() + .unwrap() + .block_on(send_failed_to_socket(status_socket_path)); + panic!("{}", e.to_string()) + } + }; +} diff --git a/src/bin/gui/apt_package_row/imp.rs b/src/bin/gui/apt_package_row/imp.rs new file mode 100644 index 0000000..330341d --- /dev/null +++ b/src/bin/gui/apt_package_row/imp.rs @@ -0,0 +1,597 @@ +use std::{cell::RefCell, sync::OnceLock}; + +use adw::*; +use adw::{prelude::*, subclass::prelude::*}; +use glib::{clone, subclass::Signal, Properties}; +use gtk::*; +use pretty_bytes::converter::convert; +use std::env; + +// ANCHOR: custom_button +// Object holding the state +#[derive(Properties, Default)] +#[properties(wrapper_type = super::AptPackageRow)] +pub struct AptPackageRow { + #[property(get, set)] + package_name: RefCell, + #[property(get, set)] + package_arch: RefCell, + #[property(get, set)] + package_installed_version: RefCell, + #[property(get, set)] + package_candidate_version: RefCell, + #[property(get, set)] + package_description: RefCell, + #[property(get, set)] + package_source_uri: RefCell, + #[property(get, set)] + package_maintainer: RefCell, + #[property(get, set)] + package_size: RefCell, + #[property(get, set)] + package_installed_size: RefCell, + #[property(get, set)] + package_marked: RefCell, +} +// ANCHOR_END: custom_button + +// The central trait for subclassing a GObject +#[glib::object_subclass] +impl ObjectSubclass for AptPackageRow { + const NAME: &'static str = "AptPackageRow"; + type Type = super::AptPackageRow; + type ParentType = ExpanderRow; +} + +// ANCHOR: object_impl +// Trait shared by all GObjects +#[glib::derived_properties] +impl ObjectImpl for AptPackageRow { + fn signals() -> &'static [Signal] { + static SIGNALS: OnceLock> = OnceLock::new(); + SIGNALS.get_or_init(|| { + vec![ + Signal::builder("checkbutton-toggled").build(), + Signal::builder("checkbutton-untoggled").build(), + ] + }) + } + fn constructed(&self) { + let current_locale = match env::var_os("LANG") { + Some(v) => v + .into_string() + .unwrap() + .chars() + .take_while(|&ch| ch != '.') + .collect::(), + None => panic!("$LANG is not set"), + }; + rust_i18n::set_locale(¤t_locale); + + self.parent_constructed(); + + // Bind label to number + // `SYNC_CREATE` ensures that the label will be immediately set + let obj = self.obj(); + + let prefix_box = Box::new(Orientation::Vertical, 0); + + let expandable_box = Box::new(Orientation::Vertical, 0); + + obj.connect_package_name_notify(clone!( + #[weak] + prefix_box, + #[weak] + expandable_box, + #[strong] + obj, + move |_| { + remove_all_children_from_box(&prefix_box); + remove_all_children_from_box(&expandable_box); + // + let package_name = obj.package_name(); + let package_arch = obj.package_arch(); + let package_installed_version = obj.package_installed_version(); + let package_candidate_version = obj.package_candidate_version(); + let package_description = obj.package_description(); + let package_source_uri = obj.package_source_uri(); + let package_maintainer = obj.package_maintainer(); + let package_size = obj.package_size(); + let package_installed_size = obj.package_installed_size(); + // + create_prefix_content( + &prefix_box, + &package_name, + &package_arch, + &package_installed_version, + &package_candidate_version, + ); + // + create_expandable_content( + &obj, + &expandable_box, + package_description, + package_source_uri, + package_maintainer, + package_size, + package_installed_size, + ); + } + )); + + obj.add_prefix(&prefix_box); + obj.add_row(&expandable_box); + + let suffix_toggle = CheckButton::builder() + .tooltip_text(t!("mark_for_update")) + .halign(Align::Center) + .valign(Align::Center) + .hexpand(false) + .vexpand(false) + .build(); + + suffix_toggle.connect_toggled(clone!( + #[weak] + obj, + #[weak] + suffix_toggle, + move |_| { + if suffix_toggle.is_active() { + obj.emit_by_name::<()>("checkbutton-toggled", &[]); + } else { + obj.emit_by_name::<()>("checkbutton-untoggled", &[]); + } + } + )); + + obj.add_suffix(&suffix_toggle); + + let obj = self.obj(); + obj.bind_property("package-marked", &suffix_toggle, "active") + .sync_create() + .bidirectional() + .build(); + + // turn on by default + obj.set_property("package-marked", true) + } +} +// Trait shared by all widgets +impl WidgetImpl for AptPackageRow {} + +// Trait shared by all buttons +// Trait shared by all buttons + +impl ListBoxRowImpl for AptPackageRow {} +impl PreferencesRowImpl for AptPackageRow {} +impl ExpanderRowImpl for AptPackageRow {} + +fn create_version_badge(installed_version: &str, candidate_version: &str) -> ListBox { + let (base_version, installed_diff, candidate_diff) = + get_diff_by_prefix(installed_version, candidate_version); + + let badge_box = Box::builder() + .halign(Align::Start) + .hexpand(false) + .orientation(Orientation::Horizontal) + .margin_start(5) + .margin_end(5) + .margin_bottom(5) + .margin_top(5) + .build(); + + let installed_version_box = Box::builder() + .halign(Align::Start) + .hexpand(false) + .orientation(Orientation::Horizontal) + .tooltip_text(t!("installed_version_badge_text")) + .build(); + + let installed_version_base_version_label = Label::builder() + .label(format!( + "{}: {}", + t!("installed_version_badge_text"), + &base_version + )) + .valign(Align::Center) + .halign(Align::Start) + .hexpand(false) + .vexpand(true) + .build(); + + let installed_diff_label = Label::builder() + .label(installed_diff) + .valign(Align::Center) + .halign(Align::Start) + .hexpand(false) + .vexpand(true) + .build(); + installed_diff_label.add_css_class("destructive-color-text"); + + installed_version_box.append(&installed_version_base_version_label.clone()); + installed_version_box.append(&installed_diff_label); + + let label_separator = Separator::builder().margin_start(5).margin_end(5).build(); + + let candidate_version_box = Box::builder() + .halign(Align::Start) + .hexpand(false) + .orientation(Orientation::Horizontal) + .tooltip_text(t!("candidate_version_badge_text")) + .build(); + + let candidate_version_base_version_label = Label::builder() + .label(format!( + "{}: {}", + t!("candidate_version_badge_text"), + &base_version + )) + .valign(Align::Center) + .halign(Align::Start) + .hexpand(false) + .vexpand(true) + .build(); + + let candidate_diff_label = Label::builder() + .label(candidate_diff) + .valign(Align::Center) + .halign(Align::Start) + .hexpand(false) + .vexpand(true) + .build(); + candidate_diff_label.add_css_class("success-color-text"); + + candidate_version_box.append(&candidate_version_base_version_label); + candidate_version_box.append(&candidate_diff_label); + + badge_box.append(&installed_version_box); + badge_box.append(&label_separator); + badge_box.append(&candidate_version_box); + + let boxedlist = ListBox::builder() + .selection_mode(SelectionMode::None) + .halign(Align::Start) + .valign(Align::End) + .margin_start(5) + .margin_end(5) + .margin_bottom(10) + .build(); + + boxedlist.add_css_class("boxed-list"); + boxedlist.append(&badge_box); + boxedlist +} + +fn create_arch_badge(arch: &str) -> ListBox { + let arch_label = Label::builder() + .halign(Align::Start) + .hexpand(false) + .label(format!("{}: {}", t!("arch_label_label"), arch)) + .margin_start(5) + .margin_end(5) + .margin_bottom(5) + .margin_top(5) + .build(); + + let boxedlist = ListBox::builder() + .selection_mode(SelectionMode::None) + .halign(Align::Start) + .valign(Align::End) + .margin_start(5) + .margin_end(5) + .margin_bottom(10) + .build(); + + boxedlist.add_css_class("boxed-list"); + boxedlist.append(&arch_label); + boxedlist +} + +fn remove_all_children_from_box(parent: >k::Box) { + while let Some(child) = parent.last_child() { + parent.remove(&child); + } +} + +fn create_prefix_content( + prefix_box: >k::Box, + package_name: &str, + package_arch: &str, + package_installed_version: &str, + package_candidate_version: &str, +) { + let package_label = Label::builder() + .halign(Align::Start) + .margin_start(5) + .margin_end(5) + .margin_bottom(5) + .margin_top(5) + .label(package_name) + .build(); + package_label.add_css_class("size-20-bold-text"); + let version_box = Box::new(Orientation::Horizontal, 0); + version_box.append(&create_version_badge( + package_installed_version, + package_candidate_version, + )); + version_box.append(&create_arch_badge(package_arch)); + prefix_box.append(&package_label); + prefix_box.append(&version_box); +} + +fn create_expandable_content( + apt_package_row: &impl IsA, + expandable_box: >k::Box, + package_description: String, + package_source_uri: String, + package_maintainer: String, + package_size: u64, + package_installed_size: u64, +) { + let expandable_page_selection_box = Box::builder() + .orientation(Orientation::Horizontal) + .hexpand(false) + .vexpand(false) + .halign(Align::Start) + .valign(Align::Start) + .margin_bottom(10) + .margin_top(10) + .margin_start(10) + .margin_end(10) + .build(); + expandable_page_selection_box.add_css_class("linked"); + // + let description_page_button = ToggleButton::builder() + .label(t!("description_button_label")) + .active(true) + .build(); + let extra_info_page_button = ToggleButton::builder() + .label(t!("extra_info_page_button_label")) + .group(&description_page_button) + .build(); + let uris_page_button = ToggleButton::builder() + .label(t!("uris_page_button_label")) + .group(&description_page_button) + .build(); + let changelog_page_button = ToggleButton::builder() + .label(t!("changelog_page_button_label")) + // till we find a way to implement + .sensitive(false) + .group(&description_page_button) + .build(); + expandable_page_selection_box.append(&description_page_button); + expandable_page_selection_box.append(&extra_info_page_button); + expandable_page_selection_box.append(&uris_page_button); + expandable_page_selection_box.append(&changelog_page_button); + // + expandable_box.append(&expandable_page_selection_box); + // + let expandable_bin = Bin::builder().hexpand(true).vexpand(true).build(); + // + description_page_button.connect_clicked(clone!( + #[strong] + expandable_bin, + #[strong] + description_page_button, + move |_| { + if description_page_button.is_active() { + expandable_bin.set_child(Some(&description_stack_page(&package_description))); + } + } + )); + + extra_info_page_button.connect_clicked(clone!( + #[strong] + expandable_bin, + #[strong] + extra_info_page_button, + move |_| { + if extra_info_page_button.is_active() { + expandable_bin.set_child(Some(&extra_info_stack_page( + &package_maintainer, + package_size, + package_installed_size, + ))); + } + } + )); + + uris_page_button.connect_clicked(clone!( + #[strong] + expandable_bin, + #[strong] + uris_page_button, + move |_| { + if uris_page_button.is_active() { + expandable_bin.set_child(Some(&uris_stack_page(&package_source_uri))); + } + } + )); + + apt_package_row.connect_expanded_notify(clone!( + #[strong] + expandable_bin, + #[strong] + expandable_box, + #[strong] + apt_package_row, + #[strong] + description_page_button, + move |_| { + if apt_package_row.property("expanded") { + description_page_button.set_active(true); + description_page_button.emit_by_name::<()>("clicked", &[]); + expandable_box.append(&expandable_bin) + } else { + expandable_box.remove(&expandable_bin) + } + } + )); + //expandable_bin.add_named(&extra_info_stack_page(package_maintainer, package_size, package_installed_size), Some("extra_info_page")); + // +} + +fn uris_stack_page(package_source_uri: &str) -> gtk::Box { + let uris_content_box = Box::builder() + .hexpand(true) + .vexpand(true) + .orientation(Orientation::Vertical) + .build(); + let uris_text_buffer = TextBuffer::builder() + .text(package_source_uri.to_owned() + "\n") + .build(); + let uris_text_view = TextView::builder() + .buffer(&uris_text_buffer) + .hexpand(true) + .vexpand(true) + .margin_top(15) + .margin_bottom(15) + .margin_start(15) + .margin_end(15) + .editable(false) + .buffer(&uris_text_buffer) + .build(); + uris_content_box.append(&uris_text_view); + uris_content_box +} + +fn description_stack_page(package_description: &str) -> gtk::Box { + let description_content_box = Box::builder() + .hexpand(true) + .vexpand(true) + .orientation(Orientation::Vertical) + .build(); + let description_text_buffer = TextBuffer::builder() + .text(package_description.to_owned() + "\n") + .build(); + let description_text_view = TextView::builder() + .buffer(&description_text_buffer) + .hexpand(true) + .vexpand(true) + .margin_top(0) + .margin_bottom(10) + .margin_start(15) + .margin_end(15) + .editable(false) + .build(); + description_content_box.append(&description_text_view); + description_content_box +} + +fn extra_info_stack_page( + package_maintainer: &str, + package_size: u64, + package_installed_size: u64, +) -> gtk::Box { + let extra_info_badges_content_box = Box::builder() + .hexpand(true) + .vexpand(true) + .orientation(Orientation::Vertical) + .build(); + let extra_info_badges_size_group = SizeGroup::new(SizeGroupMode::Both); + let extra_info_badges_size_group0 = SizeGroup::new(SizeGroupMode::Both); + let extra_info_badges_size_group1 = SizeGroup::new(SizeGroupMode::Both); + let package_size = package_size as f64; + let package_installed_size = package_installed_size as f64; + extra_info_badges_content_box.append(&create_color_badge( + &t!("extra_info_maintainer").to_string(), + package_maintainer, + "background-accent-bg", + &extra_info_badges_size_group, + &extra_info_badges_size_group0, + &extra_info_badges_size_group1, + )); + extra_info_badges_content_box.append(&create_color_badge( + &t!("extra_info_download_size").to_string(), + &convert(package_size), + "background-accent-bg", + &extra_info_badges_size_group, + &extra_info_badges_size_group0, + &extra_info_badges_size_group1, + )); + extra_info_badges_content_box.append(&create_color_badge( + &t!("extra_info_installed_size").to_string(), + &convert(package_installed_size), + "background-accent-bg", + &extra_info_badges_size_group, + &extra_info_badges_size_group0, + &extra_info_badges_size_group1, + )); + extra_info_badges_content_box +} +fn create_color_badge( + label0_text: &str, + label1_text: &str, + css_style: &str, + group_size: &SizeGroup, + group_size0: &SizeGroup, + group_size1: &SizeGroup, +) -> ListBox { + let badge_box = Box::builder().build(); + + let label0 = Label::builder() + .label(label0_text) + .margin_start(5) + .margin_end(5) + .margin_bottom(1) + .margin_top(1) + .valign(Align::Center) + .halign(Align::Center) + .hexpand(true) + .vexpand(true) + .build(); + group_size0.add_widget(&label0); + + let label_separator = Separator::builder().build(); + + let label1 = Label::builder() + .label(label1_text) + .margin_start(3) + .margin_end(0) + .margin_bottom(1) + .margin_top(1) + .valign(Align::Center) + .halign(Align::Center) + .hexpand(true) + .vexpand(true) + .build(); + group_size1.add_widget(&label1); + + label1.add_css_class(css_style); + + badge_box.append(&label0); + badge_box.append(&label_separator); + badge_box.append(&label1); + + let boxedlist = ListBox::builder() + .selection_mode(SelectionMode::None) + .halign(Align::Start) + .valign(Align::Start) + .margin_start(10) + .margin_end(10) + .margin_bottom(10) + .margin_top(10) + .build(); + + boxedlist.add_css_class("boxed-list"); + boxedlist.append(&badge_box); + group_size.add_widget(&boxedlist); + boxedlist +} + +pub fn get_diff_by_prefix(xs: &str, ys: &str) -> (String, String, String) { + let mut count = String::new(); + for (x, y) in xs.chars().zip(ys.chars()) { + if x == y { + count.push(x) + } else { + break; + } + } + let count_clone0 = count.clone(); + return ( + count_clone0, + xs.trim_start_matches(&count.as_str()).to_string(), + ys.trim_start_matches(&count.as_str()).to_string(), + ); +} diff --git a/src/bin/gui/apt_package_row/mod.rs b/src/bin/gui/apt_package_row/mod.rs new file mode 100644 index 0000000..e04e3f0 --- /dev/null +++ b/src/bin/gui/apt_package_row/mod.rs @@ -0,0 +1,45 @@ +mod imp; + +use crate::apt_update_page::AptPackageSocket; +use glib::Object; +use gtk::glib; + +glib::wrapper! { + pub struct AptPackageRow(ObjectSubclass) + @extends adw::ExpanderRow, gtk::Widget, gtk::ListBoxRow, adw::PreferencesRow, + @implements gtk::Accessible, gtk::Actionable, gtk::Buildable, gtk::ConstraintTarget; +} + +impl AptPackageRow { + pub fn new(package: AptPackageSocket) -> Self { + Object::builder() + .property("package-name", package.name) + .property("package-arch", package.arch) + .property("package-installed-version", package.installed_version) + .property("package-candidate-version", package.candidate_version) + .property("package-description", package.description) + .property("package-source-uri", package.source_uri) + .property("package-maintainer", package.maintainer) + .property("package-size", package.size) + .property("package-installed-size", package.installed_size) + .build() + } +} +// ANCHOR_END: mod + +impl Default for AptPackageRow { + fn default() -> Self { + Self::new(AptPackageSocket { + name: "name".to_string(), + arch: "arch".to_string(), + installed_version: "0.0".to_string(), + candidate_version: "0.0".to_string(), + description: "??".to_string(), + source_uri: "??".to_string(), + maintainer: "??".to_string(), + size: 0, + installed_size: 0, + is_last: false, + }) + } +} diff --git a/src/bin/gui/apt_update_page/mod.rs b/src/bin/gui/apt_update_page/mod.rs new file mode 100644 index 0000000..0429666 --- /dev/null +++ b/src/bin/gui/apt_update_page/mod.rs @@ -0,0 +1,492 @@ +mod process; + +use crate::apt_package_row::AptPackageRow; +use adw::gio::SimpleAction; +use adw::prelude::*; +use gtk::glib::*; +use gtk::*; +use pika_unixsocket_tools::pika_unixsocket_tools::*; +use rust_apt::cache::*; +use rust_apt::new_cache; +use rust_apt::records::RecordField; +use std::cell::RefCell; +use std::process::Command; +use std::rc::Rc; +use std::thread; +use tokio::runtime::Runtime; + +#[derive(Clone)] +pub struct AptPackageSocket { + pub name: String, + pub arch: String, + pub installed_version: String, + pub candidate_version: String, + pub description: String, + pub source_uri: String, + pub maintainer: String, + pub size: u64, + pub installed_size: u64, + pub is_last: bool, +} +pub fn apt_update_page( + window: adw::ApplicationWindow, + retry_signal_action: &SimpleAction, + flatpak_retry_signal_action: &SimpleAction, + flatpak_ran_once: Rc> +) -> gtk::Box { + let (update_percent_sender, update_percent_receiver) = async_channel::unbounded::(); + let update_percent_sender = update_percent_sender.clone(); + let (update_status_sender, update_status_receiver) = async_channel::unbounded::(); + let update_status_sender = update_status_sender.clone(); + let update_status_sender_clone0 = update_status_sender.clone(); + let (get_upgradable_sender, get_upgradable_receiver) = async_channel::unbounded(); + let get_upgradable_sender = get_upgradable_sender.clone(); + + let excluded_updates_vec: Rc>> = Rc::new(RefCell::new(Vec::new())); + + thread::spawn(move || { + Runtime::new().unwrap().block_on(start_socket_server_no_log( + update_percent_sender, + "/tmp/pika_apt_update_percent.sock", + )); + }); + + thread::spawn(move || { + Runtime::new().unwrap().block_on(start_socket_server( + update_status_sender, + "/tmp/pika_apt_update_status.sock", + "/tmp/pika-apt-update.log", + )); + }); + + thread::spawn(move || { + let apt_update_command = Command::new("pkexec") + .args([ + "/home/ward/RustroverProjects/pkg-pikman-update-manager/target/debug/apt_update", + ]) + .status() + .unwrap(); + match apt_update_command.code().unwrap() { + 0 => update_status_sender_clone0 + .send_blocking("FN_OVERRIDE_SUCCESSFUL".to_owned()) + .unwrap(), + 53 => {} + _ => { + update_status_sender_clone0 + .send_blocking(t!("update_status_error_perms").to_string()) + .unwrap(); + update_status_sender_clone0 + .send_blocking("FN_OVERRIDE_FAILED".to_owned()) + .unwrap() + } + } + }); + + let main_box = Box::builder() + .hexpand(true) + .vexpand(true) + .orientation(Orientation::Vertical) + .build(); + + let searchbar = SearchEntry::builder() + .search_delay(500) + .margin_top(15) + .margin_bottom(15) + .margin_end(30) + .margin_start(30) + .build(); + searchbar.add_css_class("rounded-all-25"); + + let packages_boxedlist = ListBox::builder() + .selection_mode(SelectionMode::None) + .sensitive(false) + .build(); + packages_boxedlist.add_css_class("boxed-list"); + packages_boxedlist.add_css_class("round-all-scroll"); + + let packages_viewport = ScrolledWindow::builder() + .vexpand(true) + .hexpand(true) + .has_frame(true) + .margin_bottom(15) + .margin_top(15) + .margin_end(15) + .margin_start(15) + .height_request(390) + .child(&packages_boxedlist) + .build(); + packages_viewport.add_css_class("round-all-scroll"); + + let packages_no_viewport_page = adw::StatusPage::builder() + .icon_name("emblem-default-symbolic") + .title(t!("apt_packages_no_viewport_page_title")) + .hexpand(true) + .vexpand(true) + .build(); + + let viewport_bin = adw::Bin::builder() + .child(&packages_no_viewport_page) + .build(); + + let apt_update_dialog_child_box = Box::builder().orientation(Orientation::Vertical).build(); + + let apt_update_dialog_progress_bar = + ProgressBar::builder().show_text(true).hexpand(true).build(); + + let apt_update_dialog_spinner = Spinner::builder() + .hexpand(true) + .valign(Align::Start) + .halign(Align::Center) + .spinning(true) + .height_request(128) + .width_request(128) + .build(); + + apt_update_dialog_child_box.append(&apt_update_dialog_spinner); + apt_update_dialog_child_box.append(&apt_update_dialog_progress_bar); + + let apt_update_dialog = adw::MessageDialog::builder() + .transient_for(&window) + .extra_child(&apt_update_dialog_child_box) + .heading(t!("apt_update_dialog_heading")) + .width_request(500) + .build(); + + apt_update_dialog.add_response( + "apt_update_dialog_retry", + &t!("apt_update_dialog_retry_label").to_string(), + ); + + apt_update_dialog.set_response_appearance( + "apt_update_dialog_retry", + adw::ResponseAppearance::Suggested, + ); + + apt_update_dialog.set_response_enabled("apt_update_dialog_retry", false); + + let retry_signal_action0 = retry_signal_action.clone(); + + apt_update_dialog + .clone() + .choose(None::<&gio::Cancellable>, move |choice| { + if choice == "apt_update_dialog_retry" { + retry_signal_action0.activate(None); + } + }); + + let bottom_bar = Box::builder().valign(Align::End).build(); + + let select_button = Button::builder() + .halign(Align::End) + .valign(Align::Center) + .hexpand(true) + .margin_start(10) + .margin_end(10) + .margin_bottom(15) + .label(t!("select_button_deselect_all")) + .build(); + + select_button.connect_clicked(clone!( + #[weak] + select_button, + #[weak] + packages_boxedlist, + move |_| { + let select_button_label = select_button.label().unwrap(); + let value_to_mark = if select_button_label == t!("select_button_select_all").to_string() + { + true + } else if select_button_label == t!("select_button_deselect_all").to_string() { + false + } else { + panic!("Unexpected label on selection button") + }; + set_all_apt_row_marks_to(&packages_boxedlist, value_to_mark) + } + )); + + let update_button = Button::builder() + .halign(Align::End) + .valign(Align::Center) + .hexpand(false) + .margin_start(10) + .margin_end(30) + .margin_bottom(15) + .label(t!("update_button_label")) + .build(); + update_button.add_css_class("destructive-action"); + + update_button.connect_clicked(clone!( + #[weak] + window, + #[weak] + retry_signal_action, + #[strong] + excluded_updates_vec, + move |_| { + process::apt_process_update( + &excluded_updates_vec.borrow(), + window, + &retry_signal_action, + ); + } + )); + + bottom_bar.append(&select_button); + bottom_bar.append(&update_button); + + let update_percent_server_context = MainContext::default(); + // The main loop executes the asynchronous block + update_percent_server_context.spawn_local(clone!( + #[weak] + apt_update_dialog_progress_bar, + async move { + while let Ok(state) = update_percent_receiver.recv().await { + match state.parse::() { + Ok(p) => apt_update_dialog_progress_bar.set_fraction(p / 100.0), + Err(_) => {} + } + } + } + )); + + let update_status_server_context = MainContext::default(); + // The main loop executes the asynchronous block + update_status_server_context.spawn_local(clone!( + #[weak] + apt_update_dialog, + #[weak] + apt_update_dialog_child_box, + #[weak] + flatpak_retry_signal_action, + async move { + while let Ok(state) = update_status_receiver.recv().await { + match state.as_ref() { + "FN_OVERRIDE_SUCCESSFUL" => { + let get_upgradable_sender = get_upgradable_sender.clone(); + thread::spawn(move || { + // Create upgradable list cache + let upgradable_cache = new_cache!().unwrap(); + // + upgradable_cache.upgrade(Upgrade::FullUpgrade).unwrap(); + + upgradable_cache.resolve(true).unwrap(); + + let mut upgradeable_iter = + upgradable_cache.get_changes(false).peekable(); + while let Some(pkg) = upgradeable_iter.next() { + if !pkg.marked_delete() { + let candidate_version_pkg = pkg.candidate().unwrap(); + let package_struct = AptPackageSocket { + name: pkg.name().to_string(), + arch: pkg.arch().to_string(), + installed_version: match pkg.installed() { + Some(t) => t.version().to_string(), + _ => { + t!("installed_version_to_be_installed").to_string() + } + }, + candidate_version: candidate_version_pkg + .version() + .to_string(), + description: match candidate_version_pkg.description() { + Some(s) => s, + _ => t!("apt_pkg_property_unknown").to_string(), + }, + source_uri: candidate_version_pkg + .uris() + .collect::>() + .join("\n"), + maintainer: match candidate_version_pkg + .get_record(RecordField::Maintainer) + { + Some(s) => s, + _ => t!("apt_pkg_property_unknown").to_string(), + }, + size: candidate_version_pkg.size(), + installed_size: candidate_version_pkg.installed_size(), + is_last: upgradeable_iter.peek().is_none(), + }; + get_upgradable_sender.send_blocking(package_struct).unwrap() + } + } + }); + apt_update_dialog.close(); + let mut flatpak_ran_once_borrow = flatpak_ran_once.borrow_mut(); + if *flatpak_ran_once_borrow != true { + flatpak_retry_signal_action.activate(None); + *flatpak_ran_once_borrow = true; + } + } + "FN_OVERRIDE_FAILED" => { + apt_update_dialog_child_box.set_visible(false); + apt_update_dialog.set_extra_child(Some( + &Image::builder() + .pixel_size(128) + .icon_name("dialog-error-symbolic") + .halign(Align::Center) + .build(), + )); + apt_update_dialog + .set_title(Some(&t!("apt_update_dialog_status_failed").to_string())); + apt_update_dialog.set_response_enabled("apt_update_dialog_retry", true); + } + _ => apt_update_dialog.set_body(&state), + } + } + } + )); + + let get_upgradable_server_context = MainContext::default(); + // The main loop executes the asynchronous block + get_upgradable_server_context.spawn_local(clone!( + #[strong] + select_button, + #[strong] + update_button, + #[strong] + packages_boxedlist, + #[strong] + packages_viewport, + #[strong] + viewport_bin, + #[strong] + excluded_updates_vec, + async move { + while let Ok(state) = get_upgradable_receiver.recv().await { + viewport_bin.set_child(Some(&packages_viewport)); + let apt_row = AptPackageRow::new(state.clone()); + apt_row.connect_closure( + "checkbutton-toggled", + false, + closure_local!( + #[strong] + select_button, + #[strong] + update_button, + #[strong] + packages_boxedlist, + #[strong] + excluded_updates_vec, + move |apt_row: AptPackageRow| { + if is_widget_select_all_ready(&packages_boxedlist) { + select_button + .set_label(&t!("select_button_select_all").to_string()); + } else { + select_button + .set_label(&t!("select_button_deselect_all").to_string()); + } + update_button + .set_sensitive(!is_all_children_unmarked(&packages_boxedlist)); + excluded_updates_vec + .borrow_mut() + .retain(|x| x != &apt_row.package_name()); + } + ), + ); + apt_row.connect_closure( + "checkbutton-untoggled", + false, + closure_local!( + #[strong] + select_button, + #[strong] + update_button, + #[strong] + packages_boxedlist, + #[strong] + excluded_updates_vec, + move |apt_row: AptPackageRow| { + select_button.set_label(&t!("select_button_select_all").to_string()); + update_button + .set_sensitive(!is_all_children_unmarked(&packages_boxedlist)); + excluded_updates_vec + .borrow_mut() + .push(apt_row.package_name()) + } + ), + ); + packages_boxedlist.append(&apt_row); + if state.is_last { + packages_boxedlist.set_sensitive(true); + } + } + } + )); + + searchbar.connect_search_changed(clone!( + #[weak] + searchbar, + #[weak] + packages_boxedlist, + move |_| { + let mut counter = packages_boxedlist.first_child(); + while let Some(row) = counter { + if row.widget_name() == "AptPackageRow" { + if !searchbar.text().is_empty() { + if row + .property::("package-name") + .to_lowercase() + .contains(&searchbar.text().to_string().to_lowercase()) + { + row.set_property("visible", true); + searchbar.grab_focus(); + } else { + row.set_property("visible", false); + } + } else { + row.set_property("visible", true); + } + } + counter = row.next_sibling(); + } + } + )); + + main_box.append(&searchbar); + main_box.append(&viewport_bin); + main_box.append(&bottom_bar); + + apt_update_dialog.present(); + main_box +} + +fn is_widget_select_all_ready(parent_listbox: &impl IsA) -> bool { + let mut is_ready = false; + let mut child_counter = parent_listbox.borrow().first_child(); + while let Some(child) = child_counter { + let next_child = child.next_sibling(); + let downcast = child.downcast::().unwrap(); + if !downcast.package_marked() { + is_ready = true; + break; + } + child_counter = next_child + } + is_ready +} + +fn is_all_children_unmarked(parent_listbox: &impl IsA) -> bool { + let mut is_all_unmarked = true; + let mut child_counter = parent_listbox.borrow().first_child(); + while let Some(child) = child_counter { + let next_child = child.next_sibling(); + let downcast = child.downcast::().unwrap(); + if downcast.package_marked() { + is_all_unmarked = false; + break; + } + child_counter = next_child + } + is_all_unmarked +} + +fn set_all_apt_row_marks_to(parent_listbox: &impl IsA, value: bool) { + let mut child_counter = parent_listbox.borrow().first_child(); + while let Some(child) = child_counter { + let next_child = child.next_sibling(); + let downcast = child.downcast::().unwrap(); + downcast.set_package_marked(value); + child_counter = next_child + } +} diff --git a/src/bin/gui/apt_update_page/process.rs b/src/bin/gui/apt_update_page/process.rs new file mode 100644 index 0000000..def792a --- /dev/null +++ b/src/bin/gui/apt_update_page/process.rs @@ -0,0 +1,622 @@ +use adw::gio::SimpleAction; +use adw::prelude::*; +use gtk::glib::*; +use gtk::*; +use pika_unixsocket_tools::pika_unixsocket_tools::{ + start_socket_server, start_socket_server_no_log, +}; +use pretty_bytes::converter::convert; +use rust_apt::cache::Upgrade; +use rust_apt::new_cache; +use serde::Serialize; +use serde_json::Value; +use std::cell::RefCell; +use std::path::Path; +use std::process::Command; +use std::rc::Rc; +use std::thread; +use tokio::runtime::Runtime; + +struct AptChangesInfo { + package_count_upgrade: u64, + package_count_install: u64, + package_count_downgrade: u64, + package_count_remove: u64, + total_download_size: u64, + total_installed_size: i64, +} +#[derive(Serialize)] +struct Exclusions { + exclusions: Vec, +} + +impl AptChangesInfo { + fn add_upgrade(&mut self) { + self.package_count_upgrade += 1; + } + fn add_install(&mut self) { + self.package_count_install += 1; + } + fn add_downgrade(&mut self) { + self.package_count_downgrade += 1; + } + fn add_remove(&mut self) { + self.package_count_remove += 1; + } + + fn increase_total_download_size_by(&mut self, value: u64) { + self.total_download_size += value; + } + + fn increase_total_installed_size_by(&mut self, value: u64) { + self.total_installed_size += value as i64; + } + + fn decrease_total_installed_size_by(&mut self, value: u64) { + self.total_installed_size -= value as i64; + } +} + +pub fn apt_process_update( + excluded_updates_vec: &Vec, + window: adw::ApplicationWindow, + retry_signal_action: &SimpleAction, +) { + let excluded_updates_alert_dialog = adw::MessageDialog::builder() + .transient_for(&window) + .heading(t!("excluded_updates_alert_dialog_heading")) + .body(t!("excluded_updates_alert_dialog_body")) + .build(); + + excluded_updates_alert_dialog.add_response( + "excluded_updates_alert_dialog_cancel", + &t!("excluded_updates_alert_dialog_cancel_label").to_string(), + ); + + excluded_updates_alert_dialog.add_response( + "excluded_updates_alert_continue", + &t!("excluded_updates_alert_continue_label").to_string(), + ); + + excluded_updates_alert_dialog.set_response_appearance( + "excluded_updates_alert_continue", + adw::ResponseAppearance::Destructive, + ); + + excluded_updates_alert_dialog.set_default_response(Some("excluded_updates_alert_continue")); + + let excluded_updates_alert_dialog_action = + SimpleAction::new("excluded_updates_alert_dialog_action", None); + + excluded_updates_alert_dialog_action.connect_activate(clone!( + #[weak] + window, + #[weak] + retry_signal_action, + #[strong] + excluded_updates_vec, + move |_, _| apt_confirm_window(&excluded_updates_vec, window, &retry_signal_action) + )); + + if excluded_updates_vec.is_empty() { + excluded_updates_alert_dialog_action.activate(None); + } else { + excluded_updates_alert_dialog.choose(None::<&gio::Cancellable>, move |choice| { + if choice == "excluded_updates_alert_continue" { + excluded_updates_alert_dialog_action.activate(None); + } + }); + } +} + +fn apt_confirm_window( + excluded_updates_vec: &Vec, + window: adw::ApplicationWindow, + retry_signal_action: &SimpleAction, +) { + let to_be_removed_packages_vec: Rc>> = Rc::new(RefCell::new(Vec::new())); + // Emulate Apt Full Upgrade to get transaction info + let mut apt_changes_struct = AptChangesInfo { + package_count_upgrade: 0, + package_count_install: 0, + package_count_downgrade: 0, + package_count_remove: 0, + total_download_size: 0, + total_installed_size: 0, + }; + + let apt_cache = new_cache!().unwrap(); + let apt_upgrade_cache = new_cache!().unwrap(); + + apt_cache.upgrade(Upgrade::FullUpgrade).unwrap(); + + for change in apt_cache.get_changes(false) { + if !excluded_updates_vec + .iter() + .any(|e| change.name().contains(e)) + { + let pkg = apt_upgrade_cache.get(change.name()).unwrap(); + if change.marked_upgrade() || change.marked_install() || change.marked_downgrade() { + pkg.mark_install(true, false); + } else if change.marked_delete() { + pkg.mark_delete(false); + to_be_removed_packages_vec + .borrow_mut() + .push(pkg.name().to_owned()); + } + pkg.protect(); + } + } + + apt_upgrade_cache.resolve(true).unwrap(); + + println!("{}", t!("gui_changes_emu_msg_0")); + for change in apt_upgrade_cache.get_changes(false) { + if change.is_installed() { + apt_changes_struct + .decrease_total_installed_size_by(change.installed().unwrap().installed_size()); + } + if change.marked_upgrade() && change.is_installed() { + println!("{}: {}", t!("gui_changes_emu_msg_upgrading"), change.name()); + apt_changes_struct.add_upgrade(); + apt_changes_struct.increase_total_download_size_by(change.candidate().unwrap().size()); + apt_changes_struct + .increase_total_installed_size_by(change.candidate().unwrap().installed_size()); + } else if change.marked_install() || change.marked_upgrade() && !change.is_installed() { + println!( + "{}: {}", + t!("gui_changes_emu_msg_installing"), + change.name() + ); + apt_changes_struct.add_install(); + apt_changes_struct.increase_total_download_size_by(change.candidate().unwrap().size()); + apt_changes_struct + .increase_total_installed_size_by(change.candidate().unwrap().installed_size()); + } else if change.marked_downgrade() { + println!( + "{}: {}", + t!("gui_changes_emu_msg_downgrading"), + change.name() + ); + apt_changes_struct.add_downgrade(); + apt_changes_struct.increase_total_download_size_by(change.candidate().unwrap().size()); + apt_changes_struct + .increase_total_installed_size_by(change.candidate().unwrap().installed_size()); + } else if change.marked_delete() { + println!("{}: {}", t!("gui_changes_emu_msg_removing"), change.name()); + apt_changes_struct.add_remove(); + } + } + + let apt_confirm_dialog_child_box = Box::builder().orientation(Orientation::Vertical).build(); + + let apt_update_dialog_badges_size_group = SizeGroup::new(SizeGroupMode::Both); + let apt_update_dialog_badges_size_group0 = SizeGroup::new(SizeGroupMode::Both); + let apt_update_dialog_badges_size_group1 = SizeGroup::new(SizeGroupMode::Both); + + apt_confirm_dialog_child_box.append(&create_color_badge( + &t!("package_count_upgrade_badge_label"), + &apt_changes_struct.package_count_upgrade.to_string(), + "background-accent-bg", + &apt_update_dialog_badges_size_group, + &apt_update_dialog_badges_size_group0, + &apt_update_dialog_badges_size_group1, + )); + + apt_confirm_dialog_child_box.append(&create_color_badge( + &t!("package_count_install_badge_label"), + &apt_changes_struct.package_count_install.to_string(), + "background-accent-bg", + &apt_update_dialog_badges_size_group, + &apt_update_dialog_badges_size_group0, + &apt_update_dialog_badges_size_group1, + )); + + apt_confirm_dialog_child_box.append(&create_color_badge( + &t!("package_count_downgrade_badge_label"), + &apt_changes_struct.package_count_downgrade.to_string(), + "background-accent-bg", + &apt_update_dialog_badges_size_group, + &apt_update_dialog_badges_size_group0, + &apt_update_dialog_badges_size_group1, + )); + + apt_confirm_dialog_child_box.append(&create_color_badge( + &t!("package_count_remove_badge_label"), + &apt_changes_struct.package_count_remove.to_string(), + "background-accent-bg", + &apt_update_dialog_badges_size_group, + &apt_update_dialog_badges_size_group0, + &apt_update_dialog_badges_size_group1, + )); + + apt_confirm_dialog_child_box.append(&create_color_badge( + &t!("total_download_size_badge_label"), + &convert(apt_changes_struct.total_download_size as f64), + "background-accent-bg", + &apt_update_dialog_badges_size_group, + &apt_update_dialog_badges_size_group0, + &apt_update_dialog_badges_size_group1, + )); + + apt_confirm_dialog_child_box.append(&create_color_badge( + &t!("total_installed_size_badge_label"), + &convert(apt_changes_struct.total_installed_size as f64), + "background-accent-bg", + &apt_update_dialog_badges_size_group, + &apt_update_dialog_badges_size_group0, + &apt_update_dialog_badges_size_group1, + )); + + let apt_confirm_dialog = adw::MessageDialog::builder() + .transient_for(&window) + .heading(t!("apt_confirm_dialog_heading")) + .body(t!("apt_confirm_dialog_body")) + .extra_child(&apt_confirm_dialog_child_box) + .build(); + + apt_confirm_dialog.add_response( + "apt_confirm_dialog_cancel", + &t!("apt_confirm_dialog_cancel_label").to_string(), + ); + + apt_confirm_dialog.add_response( + "apt_confirm_dialog_confirm", + &t!("apt_confirm_dialog_confirm_label").to_string(), + ); + + apt_confirm_dialog.set_response_appearance( + "apt_confirm_dialog_confirm", + adw::ResponseAppearance::Destructive, + ); + + apt_confirm_dialog.set_default_response(Some("apt_confirm_dialog_confirm")); + apt_confirm_dialog.set_close_response("apt_confirm_dialog_cancel"); + + let json_file_path = "/tmp/pika-apt-exclusions.json"; + + if Path::new(json_file_path).exists() { + std::fs::remove_file(json_file_path).expect("Failed to remove old json file"); + } + + if !excluded_updates_vec.is_empty() { + let exclusions_array = Exclusions { + exclusions: excluded_updates_vec + .into_iter() + .map(|i| serde_json::from_str(format!("{{\"package\":\"{}\"}}", i).as_str())) + .collect::, _>>() + .unwrap(), + }; + + std::fs::write( + json_file_path, + serde_json::to_string_pretty(&exclusions_array).unwrap(), + ) + .expect("Failed to write to json file"); + } + + let apt_confirm_start_signal_action = SimpleAction::new("apt_confirm_start", None); + + apt_confirm_start_signal_action.connect_activate(clone!( + #[weak] + window, + #[strong] + retry_signal_action, + #[strong] + apt_confirm_dialog, + move |_, _| { + let retry_signal_action0 = retry_signal_action.clone(); + apt_confirm_dialog + .clone() + .choose(None::<&gio::Cancellable>, move |choice| { + if choice == "apt_confirm_dialog_confirm" { + apt_full_upgrade_from_socket(window, &retry_signal_action0); + } + }); + } + )); + + let to_be_removed_packages_borrow = to_be_removed_packages_vec.borrow(); + if to_be_removed_packages_borrow.is_empty() { + apt_confirm_start_signal_action.activate(None); + } else { + let apt_remove_confirm_text_buffer = TextBuffer::builder() + .text( + to_be_removed_packages_borrow + .iter() + .map(|x| x.to_string() + "\n") + .collect::() + + "\n", + ) + .build(); + + let apt_remove_confirm_text_view = TextView::builder() + .buffer(&apt_remove_confirm_text_buffer) + .hexpand(true) + .vexpand(true) + .editable(false) + .build(); + + let apt_remove_confirm_text_viewport = gtk::ScrolledWindow::builder() + .vexpand(true) + .hexpand(true) + .has_frame(true) + .hscrollbar_policy(PolicyType::Never) + .child(&apt_remove_confirm_text_view) + .build(); + apt_remove_confirm_text_viewport.add_css_class("round-all-scroll"); + + let apt_remove_confirm_dialog = adw::MessageDialog::builder() + .transient_for(&window) + .heading(t!("apt_remove_confirm_dialog_heading")) + .body(t!("apt_remove_confirm_dialog_body")) + .extra_child(&apt_remove_confirm_text_viewport) + .build(); + + apt_remove_confirm_dialog.add_response( + "apt_remove_confirm_dialog_cancel", + &t!("apt_remove_confirm_dialog_cancel_label").to_string(), + ); + + apt_remove_confirm_dialog.add_response( + "apt_remove_confirm_dialog_confirm", + &t!("apt_remove_confirm_dialog_confirm_label").to_string(), + ); + + apt_remove_confirm_dialog.set_response_appearance( + "apt_remove_confirm_dialog_confirm", + adw::ResponseAppearance::Destructive, + ); + + apt_remove_confirm_dialog.set_default_response(Some("apt_remove_confirm_dialog_confirm")); + apt_remove_confirm_dialog.set_close_response("apt_remove_confirm_dialog_cancel"); + + apt_remove_confirm_dialog.choose(None::<&gio::Cancellable>, move |choice| { + if choice == "apt_remove_confirm_dialog_confirm" { + apt_confirm_start_signal_action.activate(None); + } + }); + } +} + +fn apt_full_upgrade_from_socket( + window: adw::ApplicationWindow, + retry_signal_action: &SimpleAction, +) { + let (upgrade_percent_sender, upgrade_percent_receiver) = async_channel::unbounded::(); + let upgrade_percent_sender = upgrade_percent_sender.clone(); + let (upgrade_status_sender, upgrade_status_receiver) = async_channel::unbounded::(); + let upgrade_status_sender = upgrade_status_sender.clone(); + let upgrade_status_sender_clone0 = upgrade_status_sender.clone(); + + let log_file_path = format!( + "/tmp/pika-apt-upgrade_{}.log", + chrono::offset::Local::now().format("%Y-%m-%d_%H:%M") + ); + let log_file_path_clone0 = log_file_path.clone(); + + thread::spawn(move || { + Runtime::new().unwrap().block_on(start_socket_server_no_log( + upgrade_percent_sender, + "/tmp/pika_apt_upgrade_percent.sock", + )); + }); + + thread::spawn(move || { + Runtime::new().unwrap().block_on(start_socket_server( + upgrade_status_sender, + "/tmp/pika_apt_upgrade_status.sock", + &log_file_path, + )); + }); + + thread::spawn(move || { + let apt_upgrade_command = Command::new("pkexec") + .args([ + "/home/ward/RustroverProjects/pkg-pikman-update-manager/target/debug/apt_full_upgrade", + ]) + .status() + .unwrap(); + match apt_upgrade_command.code().unwrap() { + 0 => upgrade_status_sender_clone0 + .send_blocking("FN_OVERRIDE_SUCCESSFUL".to_owned()) + .unwrap(), + 53 => {} + _ => { + upgrade_status_sender_clone0 + .send_blocking(t!("upgrade_status_error_perms").to_string()) + .unwrap(); + upgrade_status_sender_clone0 + .send_blocking("FN_OVERRIDE_FAILED".to_owned()) + .unwrap() + } + } + }); + + let apt_upgrade_dialog_child_box = Box::builder().orientation(Orientation::Vertical).build(); + + let apt_upgrade_dialog_progress_bar = + ProgressBar::builder().show_text(true).hexpand(true).build(); + + let apt_upgrade_dialog_spinner = Spinner::builder() + .hexpand(true) + .valign(Align::Start) + .halign(Align::Center) + .spinning(true) + .height_request(128) + .width_request(128) + .build(); + + apt_upgrade_dialog_child_box.append(&apt_upgrade_dialog_spinner); + apt_upgrade_dialog_child_box.append(&apt_upgrade_dialog_progress_bar); + + let apt_upgrade_dialog = adw::MessageDialog::builder() + .transient_for(&window) + .extra_child(&apt_upgrade_dialog_child_box) + .heading(t!("apt_upgrade_dialog_heading")) + .width_request(500) + .build(); + + apt_upgrade_dialog.add_response( + "apt_upgrade_dialog_ok", + &t!("apt_upgrade_dialog_ok_label").to_string(), + ); + + let apt_upgrade_dialog_child_box_done = + Box::builder().orientation(Orientation::Vertical).build(); + + let apt_upgrade_log_image = Image::builder() + .pixel_size(128) + .halign(Align::Center) + .build(); + + let apt_upgrade_log_button = Button::builder() + .label(t!("apt_upgrade_dialog_open_log_file_label")) + .halign(Align::Center) + .margin_start(15) + .margin_end(15) + .margin_top(15) + .margin_bottom(15) + .build(); + + apt_upgrade_dialog_child_box_done.append(&apt_upgrade_log_image); + apt_upgrade_dialog_child_box_done.append(&apt_upgrade_log_button); + + apt_upgrade_dialog.set_response_enabled("apt_upgrade_dialog_ok", false); + apt_upgrade_dialog.set_close_response("apt_upgrade_dialog_ok"); + + let upgrade_percent_server_context = MainContext::default(); + // The main loop executes the asynchronous block + upgrade_percent_server_context.spawn_local(clone!( + #[weak] + apt_upgrade_dialog_progress_bar, + async move { + while let Ok(state) = upgrade_percent_receiver.recv().await { + match state.as_ref() { + "FN_OVERRIDE_SUCCESSFUL" => {} + _ => match state.parse::() { + Ok(p) => apt_upgrade_dialog_progress_bar.set_fraction(p / 100.0), + Err(_) => {} + }, + } + } + } + )); + + let upgrade_status_server_context = MainContext::default(); + // The main loop executes the asynchronous block + upgrade_status_server_context.spawn_local(clone!( + #[weak] + apt_upgrade_dialog, + #[weak] + apt_upgrade_dialog_child_box, + #[strong] + apt_upgrade_dialog_child_box_done, + #[strong] + apt_upgrade_log_image, + async move { + while let Ok(state) = upgrade_status_receiver.recv().await { + match state.as_ref() { + "FN_OVERRIDE_SUCCESSFUL" => { + apt_upgrade_dialog_child_box.set_visible(false); + apt_upgrade_log_image.set_icon_name(Some("face-cool-symbolic")); + apt_upgrade_dialog + .set_extra_child(Some(&apt_upgrade_dialog_child_box_done)); + apt_upgrade_dialog.set_title(Some( + &t!("apt_upgrade_dialog_status_successful").to_string(), + )); + apt_upgrade_dialog.set_response_enabled("apt_upgrade_dialog_ok", true); + } + "FN_OVERRIDE_FAILED" => { + apt_upgrade_dialog_child_box.set_visible(false); + apt_upgrade_log_image.set_icon_name(Some("dialog-error-symbolic")); + apt_upgrade_dialog + .set_extra_child(Some(&apt_upgrade_dialog_child_box_done)); + apt_upgrade_dialog + .set_title(Some(&t!("apt_upgrade_dialog_status_failed").to_string())); + apt_upgrade_dialog.set_response_enabled("apt_upgrade_dialog_ok", true); + apt_upgrade_dialog + .set_response_enabled("apt_upgrade_dialog_open_log_file", true); + } + _ => apt_upgrade_dialog.set_body(&state), + } + } + } + )); + + let retry_signal_action0 = retry_signal_action.clone(); + + apt_upgrade_log_button.connect_clicked(move |_| { + let _ = Command::new("xdg-open") + .arg(log_file_path_clone0.to_owned()) + .spawn(); + }); + + apt_upgrade_dialog.choose(None::<&gio::Cancellable>, move |choice| { + match choice.as_str() { + "apt_upgrade_dialog_ok" => { + retry_signal_action0.activate(None); + } + _ => {} + } + }); +} + +fn create_color_badge( + label0_text: &str, + label1_text: &str, + css_style: &str, + group_size: &SizeGroup, + group_size0: &SizeGroup, + group_size1: &SizeGroup, +) -> ListBox { + let badge_box = Box::builder().build(); + + let label0 = Label::builder() + .label(label0_text) + .margin_start(5) + .margin_end(5) + .margin_bottom(1) + .margin_top(1) + .valign(Align::Center) + .halign(Align::Center) + .hexpand(true) + .vexpand(true) + .build(); + group_size0.add_widget(&label0); + + let label_separator = Separator::builder().build(); + + let label1 = Label::builder() + .label(label1_text) + .margin_start(3) + .margin_end(0) + .margin_bottom(1) + .margin_top(1) + .valign(Align::Center) + .halign(Align::Center) + .hexpand(true) + .vexpand(true) + .build(); + group_size1.add_widget(&label1); + + label1.add_css_class(css_style); + + badge_box.append(&label0); + badge_box.append(&label_separator); + badge_box.append(&label1); + + let boxedlist = ListBox::builder() + .selection_mode(SelectionMode::None) + .halign(Align::Center) + .margin_start(10) + .margin_end(10) + .margin_bottom(10) + .margin_top(10) + .build(); + + boxedlist.add_css_class("boxed-list"); + boxedlist.append(&badge_box); + group_size.add_widget(&boxedlist); + boxedlist +} diff --git a/src/bin/gui/build_ui/mod.rs b/src/bin/gui/build_ui/mod.rs new file mode 100644 index 0000000..777d8f0 --- /dev/null +++ b/src/bin/gui/build_ui/mod.rs @@ -0,0 +1,295 @@ +use crate::apt_update_page; +use crate::config::{APP_GITHUB, APP_ICON, APP_ID, VERSION}; +use crate::flatpak_update_page; +use adw::prelude::*; +use adw::*; +use gtk::glib::{clone, MainContext}; +use gtk::License; +use std::cell::RefCell; +use std::process::Command; +use std::rc::Rc; +use std::thread; + +pub fn build_ui(app: &Application) { + // setup glib + glib::set_prgname(Some(t!("application_name").to_string())); + glib::set_application_name(&t!("application_name").to_string()); + let glib_settings = gio::Settings::new(APP_ID); + + let internet_connected = Rc::new(RefCell::new(false)); + let (internet_loop_sender, internet_loop_receiver) = async_channel::unbounded(); + let internet_loop_sender = internet_loop_sender.clone(); + + thread::spawn(move || loop { + match Command::new("ping").arg("google.com").arg("-c 1").output() { + Ok(t) if t.status.success() => internet_loop_sender + .send_blocking(true) + .expect("The channel needs to be open"), + _ => internet_loop_sender + .send_blocking(false) + .expect("The channel needs to be open"), + }; + thread::sleep(std::time::Duration::from_secs(5)); + }); + + let window_banner = Banner::builder().revealed(false).build(); + + let internet_connected_status = internet_connected.clone(); + + let internet_loop_context = MainContext::default(); + // The main loop executes the asynchronous block + internet_loop_context.spawn_local(clone!( + #[weak] + window_banner, + async move { + while let Ok(state) = internet_loop_receiver.recv().await { + let banner_text = t!("banner_text_no_internet").to_string(); + if state == true { + *internet_connected_status.borrow_mut() = true; + if window_banner.title() == banner_text { + window_banner.set_revealed(false) + } + } else { + *internet_connected_status.borrow_mut() = false; + window_banner.set_title(&banner_text); + window_banner.set_revealed(true) + } + } + } + )); + + let window_headerbar = HeaderBar::builder() + .title_widget(&WindowTitle::builder().title(t!("application_name")).build()) + .show_title(false) + .build(); + + let window_breakpoint = adw::Breakpoint::new(BreakpointCondition::new_length( + BreakpointConditionLengthType::MaxWidth, + 800.0, + LengthUnit::Px, + )); + + let window_adw_stack = gtk::Stack::builder() + .hhomogeneous(true) + .vhomogeneous(true) + .transition_type(gtk::StackTransitionType::SlideUpDown) + .build(); + + let window_toolbar = ToolbarView::builder() + .content(&window_adw_stack) + .top_bar_style(ToolbarStyle::Flat) + .bottom_bar_style(ToolbarStyle::Flat) + .build(); + + let window_adw_view_switcher_sidebar = gtk::StackSidebar::builder() + .vexpand(true) + .hexpand(true) + .margin_start(5) + .margin_end(5) + .stack(&window_adw_stack) + .build(); + + let window_adw_view_switcher_sidebar_box = gtk::Box::new(gtk::Orientation::Vertical, 0); + window_adw_view_switcher_sidebar_box.append(&WindowTitle::builder().title(t!("application_name")).margin_top(20).margin_bottom(20).margin_start(5).margin_end(5).build()); + window_adw_view_switcher_sidebar_box.append(&window_adw_view_switcher_sidebar); + + let window_adw_view_sidebar_navigation_page = adw::NavigationPage::new(&window_adw_view_switcher_sidebar_box, "sidebar_view"); + + let sidebar_toggle_button = gtk::ToggleButton::builder() + .icon_name("view-right-pane-symbolic") + .visible(false) + .build(); + + let window_content_page_split_view = adw::OverlaySplitView::builder() + .vexpand(true) + .hexpand(true) + .content(&window_toolbar) + .sidebar(&window_adw_view_sidebar_navigation_page) + .max_sidebar_width(300.0) + .min_sidebar_width(300.0) + .enable_hide_gesture(true) + .enable_show_gesture(true) + .build(); + + let _sidebar_toggle_button_binding = window_content_page_split_view + .bind_property("show_sidebar", &sidebar_toggle_button, "active") + .sync_create() + .bidirectional() + .build(); + + window_breakpoint.add_setter( + &window_content_page_split_view, + "collapsed", + Some(&true.to_value()), + ); + window_breakpoint.add_setter( + &sidebar_toggle_button, + "visible", + Some(&true.to_value()), + ); + window_breakpoint.add_setter( + &window_headerbar, + "show_title", + Some(&true.to_value()), + ); + + window_headerbar.pack_end(&sidebar_toggle_button); + + window_toolbar.add_top_bar(&window_headerbar); + window_toolbar.add_top_bar(&window_banner); + + // create the main Application window + let window = ApplicationWindow::builder() + // The text on the titlebar + .title(t!("application_name")) + // link it to the application "app" + .application(app) + // Add the box called "window_box" to it + // Application icon + .icon_name(APP_ICON) + // Minimum Size/Default + .default_width(glib_settings.int("window-width")) + .default_height(glib_settings.int("window-height")) + // + .width_request(700) + .height_request(500) + .content(&window_content_page_split_view) + // Startup + .startup_id(APP_ID) + // build the window + .build(); + + window.add_breakpoint(window_breakpoint); + + if glib_settings.boolean("is-maximized") == true { + window.maximize() + } + + window.connect_close_request(move |window| { + if let Some(application) = window.application() { + let size = window.default_size(); + let _ = glib_settings.set_int("window-width", size.0); + let _ = glib_settings.set_int("window-height", size.1); + let _ = glib_settings.set_boolean("is-maximized", window.is_maximized()); + application.remove_window(window); + } + glib::Propagation::Proceed + }); + + let credits_button = gtk::Button::builder() + .icon_name("dialog-information-symbolic") + .build(); + + let refresh_button = gtk::Button::builder() + .icon_name("view-refresh-symbolic") + .tooltip_text(t!("refresh_button_tooltip_text")) + .build(); + + let credits_window = AboutWindow::builder() + .application_icon(APP_ICON) + .application_name(t!("application_name")) + .transient_for(&window) + .version(VERSION) + .hide_on_close(true) + .developer_name(t!("developer_name")) + .license_type(License::Mpl20) + .issue_url(APP_GITHUB.to_owned() + "/issues") + .build(); + + window_headerbar.pack_end(&refresh_button); + window_headerbar.pack_end(&credits_button); + credits_button.connect_clicked(move |_| credits_window.present()); + + // show the window + + window.present(); + + // Flatpak Update Page + + let flatpak_retry_signal_action = gio::SimpleAction::new("retry", None); + + let flatpak_update_view_stack_bin = Bin::builder() + .build(); + + flatpak_retry_signal_action.connect_activate(clone!( + #[weak] + window, + #[strong] + flatpak_retry_signal_action, + #[strong] + flatpak_update_view_stack_bin, + move |_, _| { + flatpak_update_view_stack_bin.set_child(Some(&flatpak_update_page::flatpak_update_page( + window, + &flatpak_retry_signal_action, + ))); + } + )); + + // Apt Update Page + let apt_retry_signal_action = gio::SimpleAction::new("retry", None); + + let flatpak_ran_once = Rc::new(RefCell::new(false)); + + let apt_update_view_stack_bin = Bin::builder().build(); + + apt_retry_signal_action.connect_activate(clone!( + #[weak] + window, + #[strong] + apt_retry_signal_action, + #[strong] + flatpak_retry_signal_action, + #[strong] + apt_update_view_stack_bin, + #[weak] + flatpak_ran_once, + move |_, _| { + apt_update_view_stack_bin.set_child(Some(&apt_update_page::apt_update_page( + window, + &apt_retry_signal_action, + &flatpak_retry_signal_action, + flatpak_ran_once, + ))); + } + )); + + apt_update_view_stack_bin.set_child(Some(&apt_update_page::apt_update_page( + window.clone(), + &apt_retry_signal_action, + &flatpak_retry_signal_action, + flatpak_ran_once, + ))); + + // Add to stack switcher + + window_adw_stack.add_titled( + &apt_update_view_stack_bin, + Some("apt_update_page"), + &t!("apt_update_page_title"), + ); + + window_adw_stack.add_titled( + &flatpak_update_view_stack_bin, + Some("flatpak_update_page"), + &t!("flatpak_update_page_title"), + ); + + // Refresh button + + refresh_button.connect_clicked(clone!( + #[weak] + apt_retry_signal_action, + #[weak] + flatpak_retry_signal_action, + #[weak] + window_adw_stack, + move |_| { + match window_adw_stack.visible_child_name().unwrap().as_str() { + "apt_update_page" => apt_retry_signal_action.activate(None), + "flatpak_update_page" => flatpak_retry_signal_action.activate(None), + _ => {} + } + } + )); +} diff --git a/src/bin/gui/config.rs b/src/bin/gui/config.rs new file mode 100644 index 0000000..63ce5f3 --- /dev/null +++ b/src/bin/gui/config.rs @@ -0,0 +1,4 @@ +pub const APP_ID: &str = "com.github.pikaos-linux.pikmanupdatemanager"; +pub const VERSION: &str = env!("CARGO_PKG_VERSION"); +pub const APP_ICON: &str = "com.github.pikaos-linux.pikmanupdatemanager"; +pub const APP_GITHUB: &str = "https://github.com/PikaOS-Linux/pkg-pikman-update-manager"; diff --git a/src/bin/gui/flatpak_ref_row/imp.rs b/src/bin/gui/flatpak_ref_row/imp.rs new file mode 100644 index 0000000..3f11b0d --- /dev/null +++ b/src/bin/gui/flatpak_ref_row/imp.rs @@ -0,0 +1,486 @@ +use std::{cell::RefCell, sync::OnceLock}; + +use adw::*; +use adw::{prelude::*, subclass::prelude::*}; +use glib::{clone, subclass::Signal, Properties}; +use gtk::*; +use pretty_bytes::converter::convert; +use std::env; + +// ANCHOR: custom_button +// Object holding the state +#[derive(Properties, Default)] +#[properties(wrapper_type = super::FlatpakRefRow)] +pub struct FlatpakRefRow { + #[property(get, set)] + flatref_name: RefCell, + #[property(get, set)] + flatref_arch: RefCell, + #[property(get, set)] + flatref_ref_name: RefCell, + #[property(get, set)] + flatref_summary: RefCell, + #[property(get, set)] + flatref_remote_name: RefCell, + #[property(get, set)] + flatref_installed_size_installed: RefCell, + #[property(get, set)] + flatref_installed_size_remote: RefCell, + #[property(get, set)] + flatref_download_size: RefCell, + #[property(get, set)] + flatref_ref_format: RefCell, + #[property(get, set)] + flatref_is_system: RefCell, + #[property(get, set)] + flatref_marked: RefCell, +} +// ANCHOR_END: custom_button + +// The central trait for subclassing a GObject +#[glib::object_subclass] +impl ObjectSubclass for FlatpakRefRow { + const NAME: &'static str = "FlatpakRefRow"; + type Type = super::FlatpakRefRow; + type ParentType = ExpanderRow; +} + +// ANCHOR: object_impl +// Trait shared by all GObjects +#[glib::derived_properties] +impl ObjectImpl for FlatpakRefRow { + fn signals() -> &'static [Signal] { + static SIGNALS: OnceLock> = OnceLock::new(); + SIGNALS.get_or_init(|| { + vec![ + Signal::builder("checkbutton-toggled").build(), + Signal::builder("checkbutton-untoggled").build(), + ] + }) + } + fn constructed(&self) { + let current_locale = match env::var_os("LANG") { + Some(v) => v + .into_string() + .unwrap() + .chars() + .take_while(|&ch| ch != '.') + .collect::(), + None => panic!("$LANG is not set"), + }; + rust_i18n::set_locale(¤t_locale); + + self.parent_constructed(); + + // Bind label to number + // `SYNC_CREATE` ensures that the label will be immediately set + let obj = self.obj(); + + let prefix_box = Box::new(Orientation::Vertical, 0); + + let expandable_box = Box::new(Orientation::Vertical, 0); + + obj.connect_flatref_name_notify(clone!( + #[weak] + prefix_box, + #[weak] + expandable_box, + #[strong] + obj, + move |_| { + remove_all_children_from_box(&prefix_box); + remove_all_children_from_box(&expandable_box); + // + let flatref_name = obj.flatref_name(); + let flatref_arch = obj.flatref_arch(); + let flatref_ref_name = obj.flatref_ref_name(); + let flatref_summary = obj.flatref_summary(); + let flatref_remote_name = obj.flatref_remote_name(); + let flatref_installed_size_installed = obj.flatref_installed_size_installed(); + let flatref_installed_size_remote = obj.flatref_installed_size_remote(); + let flatref_download_size = obj.flatref_download_size(); + let flatref_ref_format = obj.flatref_download_size(); + let flatref_is_system = obj.flatref_is_system(); + let flatref_marked = obj.flatref_marked(); + // + create_prefix_content( + &prefix_box, + &flatref_name, + &flatref_arch, + flatref_is_system, + &flatref_remote_name, + ); + // + create_expandable_content( + &obj, + &expandable_box, + flatref_ref_name, + flatref_summary, + flatref_download_size, + flatref_installed_size_remote, + ); + } + )); + + obj.add_prefix(&prefix_box); + obj.add_row(&expandable_box); + + let suffix_toggle = CheckButton::builder() + .tooltip_text(t!("mark_for_update")) + .halign(Align::Center) + .valign(Align::Center) + .hexpand(false) + .vexpand(false) + .build(); + + suffix_toggle.connect_toggled(clone!( + #[weak] + obj, + #[weak] + suffix_toggle, + move |_| { + if suffix_toggle.is_active() { + obj.emit_by_name::<()>("checkbutton-toggled", &[]); + } else { + obj.emit_by_name::<()>("checkbutton-untoggled", &[]); + } + } + )); + + obj.add_suffix(&suffix_toggle); + + let obj = self.obj(); + obj.bind_property("flatref_marked", &suffix_toggle, "active") + .sync_create() + .bidirectional() + .build(); + + // turn on by default + obj.set_property("flatref_marked", true) + } +} +// Trait shared by all widgets +impl WidgetImpl for FlatpakRefRow {} + +// Trait shared by all buttons +// Trait shared by all buttons + +impl ListBoxRowImpl for FlatpakRefRow {} +impl PreferencesRowImpl for FlatpakRefRow {} +impl ExpanderRowImpl for FlatpakRefRow {} + +fn create_remote_badge(remote_name: &str) -> ListBox { + let remote_label = Label::builder() + .halign(Align::Start) + .hexpand(false) + .label(format!("{}: {}", t!("remote_label_label"), remote_name)) + .margin_start(5) + .margin_end(5) + .margin_bottom(5) + .margin_top(5) + .build(); + + let boxedlist = ListBox::builder() + .selection_mode(SelectionMode::None) + .halign(Align::Start) + .valign(Align::End) + .margin_start(5) + .margin_end(5) + .margin_bottom(10) + .build(); + + boxedlist.add_css_class("boxed-list"); + boxedlist.append(&remote_label); + boxedlist +} +fn create_arch_badge(arch: &str) -> ListBox { + let arch_label = Label::builder() + .halign(Align::Start) + .hexpand(false) + .label(format!("{}: {}", t!("arch_label_label"), arch)) + .margin_start(5) + .margin_end(5) + .margin_bottom(5) + .margin_top(5) + .build(); + + let boxedlist = ListBox::builder() + .selection_mode(SelectionMode::None) + .halign(Align::Start) + .valign(Align::End) + .margin_start(5) + .margin_end(5) + .margin_bottom(10) + .build(); + + boxedlist.add_css_class("boxed-list"); + boxedlist.append(&arch_label); + boxedlist +} + +fn create_system_badge(is_system: bool) -> ListBox { + let system_label = Label::builder() + .halign(Align::Start) + .hexpand(false) + .label(match is_system { + true => "System", + false => "User", + }) + .margin_start(5) + .margin_end(5) + .margin_bottom(5) + .margin_top(5) + .build(); + + let boxedlist = ListBox::builder() + .selection_mode(SelectionMode::None) + .halign(Align::Start) + .valign(Align::End) + .margin_start(5) + .margin_end(5) + .margin_bottom(10) + .build(); + + boxedlist.add_css_class("boxed-list"); + boxedlist.append(&system_label); + boxedlist +} + +fn remove_all_children_from_box(parent: >k::Box) { + while let Some(child) = parent.last_child() { + parent.remove(&child); + } +} + +fn create_prefix_content( + prefix_box: >k::Box, + flatref_name: &str, + flatref_arch: &str, + flatref_is_system: bool, + flatref_remote_name: &str, +) { + let package_label = Label::builder() + .halign(Align::Start) + .margin_start(5) + .margin_end(5) + .margin_bottom(5) + .margin_top(5) + .label(flatref_name) + .build(); + package_label.add_css_class("size-20-bold-text"); + let prefix_badge_box = Box::new(Orientation::Horizontal, 0); + prefix_badge_box.append(&create_remote_badge(flatref_remote_name)); + prefix_badge_box.append(&create_arch_badge(flatref_arch)); + prefix_badge_box.append(&create_system_badge(flatref_is_system)); + prefix_box.append(&package_label); + prefix_box.append(&prefix_badge_box); +} + +fn create_expandable_content( + flatpak_package_row: &impl IsA, + expandable_box: >k::Box, + flatref_ref_name: String, + flatref_summary: String, + flatref_download_size: u64, + flatref_installed_size_remote: u64, +) { + let expandable_page_selection_box = Box::builder() + .orientation(Orientation::Horizontal) + .hexpand(false) + .vexpand(false) + .halign(Align::Start) + .valign(Align::Start) + .margin_bottom(10) + .margin_top(10) + .margin_start(10) + .margin_end(10) + .build(); + expandable_page_selection_box.add_css_class("linked"); + // + let summary_page_button = ToggleButton::builder() + .label(t!("summary_button_label")) + .active(true) + .build(); + let extra_info_page_button = ToggleButton::builder() + .label(t!("extra_info_page_button_label")) + .group(&summary_page_button) + .build(); + expandable_page_selection_box.append(&summary_page_button); + expandable_page_selection_box.append(&extra_info_page_button); + // + expandable_box.append(&expandable_page_selection_box); + // + let expandable_bin = Bin::builder().hexpand(true).vexpand(true).build(); + // + summary_page_button.connect_clicked(clone!( + #[strong] + expandable_bin, + #[strong] + summary_page_button, + move |_| { + if summary_page_button.is_active() { + expandable_bin.set_child(Some(&summary_stack_page(&flatref_summary))); + } + } + )); + + extra_info_page_button.connect_clicked(clone!( + #[strong] + expandable_bin, + #[strong] + extra_info_page_button, + move |_| { + if extra_info_page_button.is_active() { + expandable_bin.set_child(Some(&extra_info_stack_page( + &flatref_ref_name, + flatref_download_size, + flatref_installed_size_remote, + ))); + } + } + )); + + flatpak_package_row.connect_expanded_notify(clone!( + #[strong] + expandable_bin, + #[strong] + expandable_box, + #[strong] + flatpak_package_row, + #[strong] + summary_page_button, + move |_| { + if flatpak_package_row.property("expanded") { + summary_page_button.set_active(true); + summary_page_button.emit_by_name::<()>("clicked", &[]); + expandable_box.append(&expandable_bin) + } else { + expandable_box.remove(&expandable_bin) + } + } + )); +} + +fn summary_stack_page(flatref_summary: &str) -> gtk::Box { + let summary_content_box = Box::builder() + .hexpand(true) + .vexpand(true) + .orientation(Orientation::Vertical) + .build(); + let summary_text_buffer = TextBuffer::builder() + .text(flatref_summary.to_owned() + "\n") + .build(); + let summary_text_view = TextView::builder() + .buffer(&summary_text_buffer) + .hexpand(true) + .vexpand(true) + .margin_top(0) + .margin_bottom(10) + .margin_start(15) + .margin_end(15) + .editable(false) + .build(); + summary_content_box.append(&summary_text_view); + summary_content_box +} + +fn extra_info_stack_page( + flatref_ref_name: &str, + flatref_download_size: u64, + flatref_installed_size_remote: u64, +) -> gtk::Box { + let extra_info_badges_content_box = Box::builder() + .hexpand(true) + .vexpand(true) + .orientation(Orientation::Vertical) + .build(); + let extra_info_badges_size_group = SizeGroup::new(SizeGroupMode::Both); + let extra_info_badges_size_group0 = SizeGroup::new(SizeGroupMode::Both); + let extra_info_badges_size_group1 = SizeGroup::new(SizeGroupMode::Both); + let package_size = flatref_download_size as f64; + let package_installed_size = flatref_installed_size_remote as f64; + extra_info_badges_content_box.append(&create_color_badge( + &t!("flatpak_extra_info_ref_name").to_string(), + flatref_ref_name, + "background-accent-bg", + &extra_info_badges_size_group, + &extra_info_badges_size_group0, + &extra_info_badges_size_group1, + )); + extra_info_badges_content_box.append(&create_color_badge( + &t!("flatpak_extra_info_download_size").to_string(), + &convert(package_size), + "background-accent-bg", + &extra_info_badges_size_group, + &extra_info_badges_size_group0, + &extra_info_badges_size_group1, + )); + extra_info_badges_content_box.append(&create_color_badge( + &t!("flatpak_extra_info_installed_size").to_string(), + &convert(package_installed_size), + "background-accent-bg", + &extra_info_badges_size_group, + &extra_info_badges_size_group0, + &extra_info_badges_size_group1, + )); + extra_info_badges_content_box +} +fn create_color_badge( + label0_text: &str, + label1_text: &str, + css_style: &str, + group_size: &SizeGroup, + group_size0: &SizeGroup, + group_size1: &SizeGroup, +) -> ListBox { + let badge_box = Box::builder().build(); + + let label0 = Label::builder() + .label(label0_text) + .margin_start(5) + .margin_end(5) + .margin_bottom(1) + .margin_top(1) + .valign(Align::Center) + .halign(Align::Center) + .hexpand(true) + .vexpand(true) + .build(); + group_size0.add_widget(&label0); + + let label_separator = Separator::builder().build(); + + let label1 = Label::builder() + .label(label1_text) + .margin_start(3) + .margin_end(0) + .margin_bottom(1) + .margin_top(1) + .valign(Align::Center) + .halign(Align::Center) + .hexpand(true) + .vexpand(true) + .build(); + group_size1.add_widget(&label1); + + label1.add_css_class(css_style); + + badge_box.append(&label0); + badge_box.append(&label_separator); + badge_box.append(&label1); + + let boxedlist = ListBox::builder() + .selection_mode(SelectionMode::None) + .halign(Align::Start) + .valign(Align::Start) + .margin_start(10) + .margin_end(10) + .margin_bottom(10) + .margin_top(10) + .build(); + + boxedlist.add_css_class("boxed-list"); + boxedlist.append(&badge_box); + group_size.add_widget(&boxedlist); + boxedlist +} diff --git a/src/bin/gui/flatpak_ref_row/mod.rs b/src/bin/gui/flatpak_ref_row/mod.rs new file mode 100644 index 0000000..7f03258 --- /dev/null +++ b/src/bin/gui/flatpak_ref_row/mod.rs @@ -0,0 +1,54 @@ +mod imp; + +use crate::flatpak_update_page::FlatpakRefStruct; +use glib::Object; +use gtk::glib; + +glib::wrapper! { + pub struct FlatpakRefRow(ObjectSubclass) + @extends adw::ExpanderRow, gtk::Widget, gtk::ListBoxRow, adw::PreferencesRow, + @implements gtk::Accessible, gtk::Actionable, gtk::Buildable, gtk::ConstraintTarget; +} + +impl FlatpakRefRow { + pub fn new(flatref: &FlatpakRefStruct) -> Self { + let flatref = flatref.clone(); + Object::builder() + .property("flatref-name", flatref.name) + .property("flatref-arch", flatref.arch) + .property("flatref-ref-name", flatref.ref_name) + .property("flatref-summary", flatref.summary) + .property("flatref-remote-name", flatref.remote_name) + .property( + "flatref-installed-size-installed", + flatref.installed_size_installed, + ) + .property( + "flatref-installed-size-remote", + flatref.installed_size_remote, + ) + .property("flatref-download-size", flatref.download_size) + .property("flatref-ref-format", flatref.ref_format) + .property("flatref-is-system", flatref.is_system) + .build() + } +} +// ANCHOR_END: mod + +impl Default for FlatpakRefRow { + fn default() -> Self { + Self::new(&FlatpakRefStruct { + ref_name: "??".to_owned(), + name: "??".to_owned(), + arch: "??".to_owned(), + summary: "??".to_owned(), + remote_name: "??".to_owned(), + installed_size_installed: 0, + installed_size_remote: 0, + download_size: 0, + ref_format: "??".to_owned(), + is_system: false, + is_last: false, + }) + } +} diff --git a/src/bin/gui/flatpak_update_page/mod.rs b/src/bin/gui/flatpak_update_page/mod.rs new file mode 100644 index 0000000..38d5535 --- /dev/null +++ b/src/bin/gui/flatpak_update_page/mod.rs @@ -0,0 +1,784 @@ +mod process; + +use crate::apt_package_row::AptPackageRow; +use crate::flatpak_ref_row::FlatpakRefRow; +use adw::gio::SimpleAction; +use adw::prelude::*; +use gtk::glib::*; +use gtk::*; +use libflatpak::prelude::*; +use libflatpak::InstalledRef; +use std::cell::RefCell; +use std::process::Command; +use std::rc::Rc; +use std::thread; + +#[derive(Clone)] +pub struct FlatpakRefStruct { + pub ref_name: String, + pub name: String, + pub arch: String, + pub summary: String, + pub remote_name: String, + pub installed_size_installed: u64, + pub installed_size_remote: u64, + pub download_size: u64, + pub ref_format: String, + pub is_system: bool, + pub is_last: bool, +} +pub fn flatpak_update_page( + window: adw::ApplicationWindow, + retry_signal_action: &SimpleAction, +) -> gtk::Box { + let (appstream_sync_percent_sender, appstream_sync_percent_receiver) = + async_channel::unbounded::(); + let appstream_sync_percent_sender = appstream_sync_percent_sender.clone(); + let (appstream_sync_status_sender, appstream_sync_status_receiver) = + async_channel::unbounded::(); + let appstream_sync_status_sender = appstream_sync_status_sender.clone(); + let appstream_sync_status_sender_clone0 = appstream_sync_status_sender.clone(); + + let system_refs_for_upgrade_vec: Rc>> = + Rc::new(RefCell::new(Vec::new())); + + let user_refs_for_upgrade_vec: Rc>> = + Rc::new(RefCell::new(Vec::new())); + + let system_refs_for_upgrade_vec_all: Rc>> = + Rc::new(RefCell::new(Vec::new())); + + let user_refs_for_upgrade_vec_all: Rc>> = + Rc::new(RefCell::new(Vec::new())); + + let cancellable_no = libflatpak::gio::Cancellable::NONE; + + thread::spawn(move || { + let cancellable_no = libflatpak::gio::Cancellable::NONE; + let flatpak_system_installation = + libflatpak::Installation::new_user(cancellable_no).unwrap(); + if let Ok(remotes) = + libflatpak::Installation::list_remotes(&flatpak_system_installation, cancellable_no) + { + for remote in remotes { + if remote.is_disabled() { + continue; + }; + let mut remote_clousre = |status: &str, progress: u32, _: bool| { + appstream_sync_percent_sender + .send_blocking(progress) + .expect("appstream_sync_percent_receiver closed"); + appstream_sync_status_sender + .send_blocking(format!( + "{} - {}: {}", + t!("flatpak_type_system"), + remote.name().unwrap_or("Unknown remote".into()), + status + )) + .expect("appstream_sync_status_receiver closed"); + }; + match libflatpak::Installation::update_appstream_full_sync( + &flatpak_system_installation, + &remote.name().unwrap(), + None, + Some(&mut remote_clousre), + cancellable_no, + ) { + Ok(_) => {} + Err(e) => { + appstream_sync_status_sender + .send_blocking(e.to_string()) + .expect("appstream_sync_status_receiver closed"); + appstream_sync_status_sender + .send_blocking("FN_OVERRIDE_FAILED".to_owned()) + .expect("appstream_sync_status_receiver closed"); + break; + } + } + } + } + let flatpak_user_installation = libflatpak::Installation::new_user(cancellable_no).unwrap(); + if let Ok(remotes) = + libflatpak::Installation::list_remotes(&flatpak_user_installation, cancellable_no) + { + for remote in remotes { + if remote.is_disabled() { + continue; + }; + let mut remote_clousre = |status: &str, progress: u32, _: bool| { + appstream_sync_percent_sender + .send_blocking(progress) + .expect("appstream_sync_percent_receiver closed"); + appstream_sync_status_sender + .send_blocking(format!( + "{} - {}: {}", + t!("flatpak_type_user"), + remote.name().unwrap_or("Unknown remote".into()), + status + )) + .expect("appstream_sync_status_receiver closed"); + }; + match libflatpak::Installation::update_appstream_full_sync( + &flatpak_user_installation, + &remote.name().unwrap(), + None, + Some(&mut remote_clousre), + cancellable_no, + ) { + Ok(_) => { + appstream_sync_status_sender + .send_blocking("FN_OVERRIDE_SUCCESSFUL".to_owned()) + .expect("appstream_sync_status_receiver closed"); + } + Err(e) => { + appstream_sync_status_sender + .send_blocking(e.to_string()) + .expect("appstream_sync_status_receiver closed"); + appstream_sync_status_sender + .send_blocking("FN_OVERRIDE_FAILED".to_owned()) + .expect("appstream_sync_status_receiver closed"); + break; + } + } + } + } + }); + + let main_box = Box::builder() + .hexpand(true) + .vexpand(true) + .orientation(Orientation::Vertical) + .build(); + + let searchbar = SearchEntry::builder() + .search_delay(500) + .margin_top(15) + .margin_bottom(15) + .margin_end(30) + .margin_start(30) + .build(); + searchbar.add_css_class("rounded-all-25"); + + let packages_boxedlist = ListBox::builder() + .selection_mode(SelectionMode::None) + .sensitive(false) + .build(); + packages_boxedlist.add_css_class("boxed-list"); + packages_boxedlist.add_css_class("round-all-scroll"); + + let packages_viewport = ScrolledWindow::builder() + .vexpand(true) + .hexpand(true) + .has_frame(true) + .margin_bottom(15) + .margin_top(15) + .margin_end(15) + .margin_start(15) + .height_request(390) + .child(&packages_boxedlist) + .build(); + packages_viewport.add_css_class("round-all-scroll"); + + let packages_no_viewport_page = adw::StatusPage::builder() + .icon_name("emblem-default-symbolic") + .title(t!("flatpak_packages_no_viewport_page_title")) + .hexpand(true) + .vexpand(true) + .build(); + + let viewport_bin = adw::Bin::builder() + .child(&packages_no_viewport_page) + .build(); + + let flatpak_update_dialog_child_box = Box::builder().orientation(Orientation::Vertical).build(); + + let flatpak_update_dialog_progress_bar = + ProgressBar::builder().show_text(true).hexpand(true).build(); + + let flatpak_update_dialog_spinner = Spinner::builder() + .hexpand(true) + .valign(Align::Start) + .halign(Align::Center) + .spinning(true) + .height_request(128) + .width_request(128) + .build(); + + flatpak_update_dialog_child_box.append(&flatpak_update_dialog_spinner); + flatpak_update_dialog_child_box.append(&flatpak_update_dialog_progress_bar); + + let flatpak_update_dialog = adw::MessageDialog::builder() + .transient_for(&window) + .extra_child(&flatpak_update_dialog_child_box) + .heading(t!("flatpak_update_dialog_heading")) + .width_request(500) + .build(); + + flatpak_update_dialog.add_response( + "flatpak_update_dialog_retry", + &t!("flatpak_update_dialog_retry_label").to_string(), + ); + + flatpak_update_dialog.set_response_appearance( + "flatpak_update_dialog_retry", + adw::ResponseAppearance::Suggested, + ); + + flatpak_update_dialog.set_response_enabled("flatpak_update_dialog_retry", false); + + let retry_signal_action0 = retry_signal_action.clone(); + + flatpak_update_dialog + .clone() + .choose(None::<&gio::Cancellable>, move |choice| { + if choice == "flatpak_update_dialog_retry" { + retry_signal_action0.activate(None); + } + }); + + let bottom_bar = Box::builder().valign(Align::End).build(); + + let select_button = Button::builder() + .halign(Align::End) + .valign(Align::Center) + .hexpand(true) + .margin_start(10) + .margin_end(10) + .margin_bottom(15) + .label(t!("select_button_deselect_all")) + .build(); + + select_button.connect_clicked(clone!( + #[weak] + select_button, + #[weak] + packages_boxedlist, + move |_| { + let select_button_label = select_button.label().unwrap(); + let value_to_mark = if select_button_label == t!("select_button_select_all").to_string() + { + true + } else if select_button_label == t!("select_button_deselect_all").to_string() { + false + } else { + panic!("Unexpected label on selection button") + }; + set_all_flatpak_row_marks_to(&packages_boxedlist, value_to_mark) + } + )); + + let update_button = Button::builder() + .halign(Align::End) + .valign(Align::Center) + .hexpand(false) + .margin_start(10) + .margin_end(30) + .margin_bottom(15) + .label(t!("update_button_label")) + .build(); + update_button.add_css_class("destructive-action"); + + let system_refs_for_upgrade_vec_all_clone0 = &system_refs_for_upgrade_vec_all.clone(); + let user_refs_for_upgrade_vec_all_clone0 = user_refs_for_upgrade_vec_all.clone(); + + let system_refs_for_upgrade_vec_clone0 = system_refs_for_upgrade_vec.clone(); + let user_refs_for_upgrade_vec_clone0 = user_refs_for_upgrade_vec.clone(); + + update_button.connect_clicked(clone!( + #[weak] + window, + #[weak] + retry_signal_action, + #[strong] + system_refs_for_upgrade_vec_all_clone0, + #[strong] + user_refs_for_upgrade_vec_all_clone0, + move |_| { + process::flatpak_process_update( + Some(&system_refs_for_upgrade_vec_clone0.borrow()), + Some(&user_refs_for_upgrade_vec_clone0.borrow()), + &system_refs_for_upgrade_vec_all_clone0.borrow(), + &user_refs_for_upgrade_vec_all_clone0.borrow(), + window, + &retry_signal_action, + ) + } + )); + + bottom_bar.append(&select_button); + bottom_bar.append(&update_button); + + let appstream_sync_percent_server_context = MainContext::default(); + // The main loop executes the asynchronous block + appstream_sync_percent_server_context.spawn_local(clone!( + #[weak] + flatpak_update_dialog_progress_bar, + async move { + while let Ok(state) = appstream_sync_percent_receiver.recv().await { + flatpak_update_dialog_progress_bar.set_fraction(state as f64 / 100.0) + } + } + )); + + let appstream_sync_status_server_context = MainContext::default(); + // The main loop executes the asynchronous block + appstream_sync_status_server_context.spawn_local(clone!( + #[weak] + flatpak_update_dialog, + #[weak] + flatpak_update_dialog_child_box, + #[strong] + packages_boxedlist, + #[strong] + system_refs_for_upgrade_vec_all, + #[strong] + user_refs_for_upgrade_vec_all, + #[strong] + system_refs_for_upgrade_vec, + #[strong] + user_refs_for_upgrade_vec, + #[strong] + viewport_bin, + #[strong] + packages_viewport, + async move { + while let Ok(state) = appstream_sync_status_receiver.recv().await { + match state.as_ref() { + "FN_OVERRIDE_SUCCESSFUL" => { + let flatpak_system_installation = + libflatpak::Installation::new_system(cancellable_no).unwrap(); + let flatpak_system_updates = flatpak_system_installation + .list_installed_refs_for_update(cancellable_no) + .unwrap(); + let flatpak_system_transaction = libflatpak::Transaction::for_installation( + &flatpak_system_installation, + cancellable_no, + ) + .unwrap(); + // + let flatpak_user_installation = + libflatpak::Installation::new_user(cancellable_no).unwrap(); + let flatpak_user_updates = flatpak_user_installation + .list_installed_refs_for_update(cancellable_no) + .unwrap(); + let flatpak_user_transaction = libflatpak::Transaction::for_installation( + &flatpak_user_installation, + cancellable_no, + ) + .unwrap(); + // + let mut system_last_triggered = false; + let mut user_last_triggered = false; + // + if !flatpak_system_updates.is_empty() || !flatpak_user_updates.is_empty() { + viewport_bin.set_child(Some(&packages_viewport)); + // + let mut flatpak_system_updates_iter = + &mut flatpak_system_updates.iter().peekable(); + // + while let Some(flatpak_ref) = flatpak_system_updates_iter.next() { + let mut remote_flatpak_ref: Option = None; + while let Ok(remotes) = libflatpak::Installation::list_remotes( + &flatpak_system_installation, + cancellable_no, + ) { + for remote in remotes { + if remote.is_disabled() { + continue; + }; + match libflatpak::Installation::fetch_remote_ref_sync( + &flatpak_system_installation, + &match remote.name() { + Some(t) => t, + None => continue, + }, + flatpak_ref.kind(), + &match flatpak_ref.name() { + Some(t) => t, + None => continue, + }, + flatpak_ref.arch().as_deref(), + flatpak_ref.branch().as_deref(), + cancellable_no, + ) { + Ok(t) => { + remote_flatpak_ref = Some(t); + break; + } + Err(_) => continue, + } + } + if remote_flatpak_ref.is_some() { + break; + } + } + let flatref_struct = FlatpakRefStruct { + ref_name: flatpak_ref + .name() + .unwrap_or("Unknown".into()) + .to_string(), + name: flatpak_ref + .appdata_name() + .unwrap_or(flatpak_ref.name().unwrap_or("Unknown".into())) + .to_string(), + arch: flatpak_ref + .arch() + .unwrap_or("Unknown Arch".into()) + .to_string(), + summary: flatpak_ref + .appdata_summary() + .unwrap_or("No Summary".into()) + .to_string(), + remote_name: match remote_flatpak_ref { + Some(ref t) => { + t.remote_name().unwrap_or("Unknown".into()).to_string() + } + None => "Unknown".into(), + }, + installed_size_installed: flatpak_ref.installed_size(), + installed_size_remote: match remote_flatpak_ref { + Some(ref t) => t.installed_size(), + None => 0, + }, + download_size: match remote_flatpak_ref { + Some(t) => t.download_size(), + None => 0, + }, + ref_format: flatpak_ref.format_ref().unwrap().into(), + is_system: true, + is_last: flatpak_system_updates_iter.peek().is_none(), + }; + + let flatpak_row = FlatpakRefRow::new(&flatref_struct); + + system_refs_for_upgrade_vec + .borrow_mut() + .push(flatpak_row.clone()); + + system_refs_for_upgrade_vec_all + .borrow_mut() + .push(flatpak_row.clone()); + + flatpak_row.connect_closure( + "checkbutton-toggled", + false, + closure_local!( + #[strong] + select_button, + #[strong] + update_button, + #[strong] + packages_boxedlist, + #[strong] + system_refs_for_upgrade_vec, + move |flatpak_row: FlatpakRefRow| { + if is_widget_select_all_ready(&packages_boxedlist) { + select_button.set_label( + &t!("select_button_select_all").to_string(), + ); + } else { + select_button.set_label( + &t!("select_button_deselect_all").to_string(), + ); + } + update_button.set_sensitive(!is_all_children_unmarked( + &packages_boxedlist, + )); + system_refs_for_upgrade_vec + .borrow_mut() + .push(flatpak_row); + } + ), + ); + flatpak_row.connect_closure( + "checkbutton-untoggled", + false, + closure_local!( + #[strong] + select_button, + #[strong] + update_button, + #[strong] + packages_boxedlist, + #[strong] + system_refs_for_upgrade_vec, + move |flatpak_row: FlatpakRefRow| { + select_button.set_label( + &t!("select_button_select_all").to_string(), + ); + update_button.set_sensitive(!is_all_children_unmarked( + &packages_boxedlist, + )); + system_refs_for_upgrade_vec.borrow_mut().retain(|x| { + x.flatref_ref_format() + != flatpak_row.flatref_ref_format() + }); + } + ), + ); + + packages_boxedlist.append(&flatpak_row); + if flatpak_system_updates.is_empty() + || flatref_struct.is_system && flatref_struct.is_last + { + system_last_triggered = true + } + } + // + let mut flatpak_user_updates_iter = + &mut flatpak_user_updates.iter().peekable(); + // + while let Some(flatpak_ref) = flatpak_user_updates_iter.next() { + let mut remote_flatpak_ref: Option = None; + while let Ok(remotes) = libflatpak::Installation::list_remotes( + &flatpak_user_installation, + cancellable_no, + ) { + for remote in remotes { + if remote.is_disabled() { + continue; + }; + match libflatpak::Installation::fetch_remote_ref_sync( + &flatpak_user_installation, + &match remote.name() { + Some(t) => t, + None => continue, + }, + flatpak_ref.kind(), + &match flatpak_ref.name() { + Some(t) => t, + None => continue, + }, + flatpak_ref.arch().as_deref(), + flatpak_ref.branch().as_deref(), + cancellable_no, + ) { + Ok(t) => { + remote_flatpak_ref = Some(t); + break; + } + Err(_) => continue, + } + } + if remote_flatpak_ref.is_some() { + break; + } + } + let flatref_struct = FlatpakRefStruct { + ref_name: flatpak_ref + .name() + .unwrap_or("Unknown".into()) + .to_string(), + name: flatpak_ref + .appdata_name() + .unwrap_or(flatpak_ref.name().unwrap_or("Unknown".into())) + .to_string(), + arch: flatpak_ref + .arch() + .unwrap_or("Unknown Arch".into()) + .to_string(), + summary: flatpak_ref + .appdata_summary() + .unwrap_or("No Summary".into()) + .to_string(), + remote_name: match remote_flatpak_ref { + Some(ref t) => { + t.remote_name().unwrap_or("Unknown".into()).to_string() + } + None => "Unknown".into(), + }, + installed_size_installed: flatpak_ref.installed_size(), + installed_size_remote: match remote_flatpak_ref { + Some(ref t) => t.installed_size(), + None => 0, + }, + download_size: match remote_flatpak_ref { + Some(t) => t.download_size(), + None => 0, + }, + ref_format: flatpak_ref.format_ref().unwrap().into(), + is_system: false, + is_last: flatpak_user_updates_iter.peek().is_none(), + }; + + let flatpak_row = FlatpakRefRow::new(&flatref_struct); + + user_refs_for_upgrade_vec + .borrow_mut() + .push(flatpak_row.clone()); + + user_refs_for_upgrade_vec_all + .borrow_mut() + .push(flatpak_row.clone()); + + flatpak_row.connect_closure( + "checkbutton-toggled", + false, + closure_local!( + #[strong] + select_button, + #[strong] + update_button, + #[strong] + packages_boxedlist, + #[strong] + user_refs_for_upgrade_vec, + move |flatpak_row: FlatpakRefRow| { + if is_widget_select_all_ready(&packages_boxedlist) { + select_button.set_label( + &t!("select_button_select_all").to_string(), + ); + } else { + select_button.set_label( + &t!("select_button_deselect_all").to_string(), + ); + } + update_button.set_sensitive(!is_all_children_unmarked( + &packages_boxedlist, + )); + user_refs_for_upgrade_vec + .borrow_mut() + .push(flatpak_row); + } + ), + ); + flatpak_row.connect_closure( + "checkbutton-untoggled", + false, + closure_local!( + #[strong] + select_button, + #[strong] + update_button, + #[strong] + packages_boxedlist, + #[strong] + user_refs_for_upgrade_vec, + move |flatpak_row: FlatpakRefRow| { + select_button.set_label( + &t!("select_button_select_all").to_string(), + ); + update_button.set_sensitive(!is_all_children_unmarked( + &packages_boxedlist, + )); + user_refs_for_upgrade_vec.borrow_mut().retain(|x| { + x.flatref_ref_format() + != flatpak_row.flatref_ref_format() + }); + } + ), + ); + packages_boxedlist.append(&flatpak_row); + if flatpak_user_updates.is_empty() + || !flatref_struct.is_system && flatref_struct.is_last + { + user_last_triggered = true + } + } + if user_last_triggered && system_last_triggered { + packages_boxedlist.set_sensitive(true); + } + } + flatpak_update_dialog.close(); + } + "FN_OVERRIDE_FAILED" => { + flatpak_update_dialog_child_box.set_visible(false); + flatpak_update_dialog.set_extra_child(Some( + &Image::builder() + .pixel_size(128) + .icon_name("dialog-error-symbolic") + .halign(Align::Center) + .build(), + )); + flatpak_update_dialog.set_title(Some( + &t!("flatpak_update_dialog_status_failed").to_string(), + )); + flatpak_update_dialog + .set_response_enabled("flatpak_update_dialog_retry", true); + } + _ => flatpak_update_dialog.set_body(&state), + } + } + } + )); + + searchbar.connect_search_changed(clone!( + #[weak] + searchbar, + #[weak] + packages_boxedlist, + move |_| { + let mut counter = packages_boxedlist.first_child(); + while let Some(row) = counter { + if row.widget_name() == "FlatpakRefRow" { + if !searchbar.text().is_empty() { + if row + .property::("flatref-name") + .to_lowercase() + .contains(&searchbar.text().to_string().to_lowercase()) + || row + .property::("flatref-ref-name") + .to_lowercase() + .contains(&searchbar.text().to_string().to_lowercase()) + { + row.set_property("visible", true); + searchbar.grab_focus(); + } else { + row.set_property("visible", false); + } + } else { + row.set_property("visible", true); + } + } + counter = row.next_sibling(); + } + } + )); + + main_box.append(&searchbar); + main_box.append(&viewport_bin); + main_box.append(&bottom_bar); + + flatpak_update_dialog.present(); + main_box +} + +fn is_widget_select_all_ready(parent_listbox: &impl adw::prelude::IsA) -> bool { + let mut is_ready = false; + let mut child_counter = parent_listbox.borrow().first_child(); + while let Some(child) = child_counter { + let next_child = child.next_sibling(); + let downcast = child.downcast::().unwrap(); + if !downcast.flatref_marked() { + is_ready = true; + break; + } + child_counter = next_child + } + is_ready +} + +fn is_all_children_unmarked(parent_listbox: &impl adw::prelude::IsA) -> bool { + let mut is_all_unmarked = true; + let mut child_counter = parent_listbox.borrow().first_child(); + while let Some(child) = child_counter { + let next_child = child.next_sibling(); + let downcast = child.downcast::().unwrap(); + if downcast.flatref_marked() { + is_all_unmarked = false; + break; + } + child_counter = next_child + } + is_all_unmarked +} + +fn set_all_flatpak_row_marks_to(parent_listbox: &impl adw::prelude::IsA, value: bool) { + let mut child_counter = parent_listbox.borrow().first_child(); + while let Some(child) = child_counter { + let next_child = child.next_sibling(); + let downcast = child.downcast::().unwrap(); + downcast.set_flatref_marked(value); + child_counter = next_child + } +} diff --git a/src/bin/gui/flatpak_update_page/process.rs b/src/bin/gui/flatpak_update_page/process.rs new file mode 100644 index 0000000..202ed5a --- /dev/null +++ b/src/bin/gui/flatpak_update_page/process.rs @@ -0,0 +1,541 @@ +use crate::flatpak_ref_row::FlatpakRefRow; +use adw::gio::SimpleAction; +use adw::prelude::*; +use gtk::glib::*; +use gtk::*; +use libflatpak::prelude::*; +use libflatpak::Transaction; +use pretty_bytes::converter::convert; +use serde::Serialize; +use serde_json::Value; +use std::cell::RefCell; +use std::fs::OpenOptions; +use std::io::Write; +use std::path::Path; +use std::process::Command; +use std::rc::Rc; +use std::sync::{Arc, Mutex}; +use std::{fs, thread}; +use tokio::runtime::Runtime; + +struct FlatpakChangesInfo { + system_flatref_count: u64, + user_flatref_count: u64, + total_download_size: u64, + total_installed_size: i64, +} +#[derive(Serialize)] +struct Exclusions { + exclusions: Vec, +} + +impl FlatpakChangesInfo { + fn add_system(&mut self) { + self.system_flatref_count += 1; + } + fn add_user(&mut self) { + self.user_flatref_count += 1; + } + + fn increase_total_download_size_by(&mut self, value: u64) { + self.total_download_size += value; + } + + fn increase_total_installed_size_by(&mut self, value: u64) { + self.total_installed_size += value as i64; + } + + fn decrease_total_installed_size_by(&mut self, value: u64) { + self.total_installed_size -= value as i64; + } +} + +pub fn flatpak_process_update( + system_refs_for_upgrade_vec_opt: Option<&Vec>, + user_refs_for_upgrade_vec_opt: Option<&Vec>, + system_refs_for_upgrade_vec_all: &Vec, + user_refs_for_upgrade_vec_all: &Vec, + window: adw::ApplicationWindow, + retry_signal_action: &SimpleAction, +) { + let cancellable = libflatpak::gio::Cancellable::NONE; + // Emulate Flatpak Full Upgrade to get transaction info + let mut flatpak_changes_struct = FlatpakChangesInfo { + system_flatref_count: 0, + user_flatref_count: 0, + total_download_size: 0, + total_installed_size: 0, + }; + + let mut system_refs_for_upgrade_vec = Vec::new(); + + match system_refs_for_upgrade_vec_opt { + Some(t) => { + for flatpak_row in t { + flatpak_changes_struct.add_system(); + // + let installed_size_installed = flatpak_row.flatref_installed_size_installed(); + let installed_size_remote = flatpak_row.flatref_installed_size_installed(); + let installed_download_size = flatpak_row.flatref_download_size(); + let ref_format = flatpak_row.flatref_ref_format(); + // + flatpak_changes_struct.decrease_total_installed_size_by(installed_size_installed); + flatpak_changes_struct.increase_total_installed_size_by(installed_size_remote); + // + flatpak_changes_struct.increase_total_download_size_by(installed_download_size); + // + system_refs_for_upgrade_vec.push(ref_format); + } + } + None => { + for flatpak_row in system_refs_for_upgrade_vec_all { + flatpak_changes_struct.add_system(); + // + let installed_size_installed = flatpak_row.flatref_installed_size_installed(); + let installed_size_remote = flatpak_row.flatref_installed_size_installed(); + let installed_download_size = flatpak_row.flatref_download_size(); + let ref_format = flatpak_row.flatref_ref_format(); + // + flatpak_changes_struct.decrease_total_installed_size_by(installed_size_installed); + flatpak_changes_struct.increase_total_installed_size_by(installed_size_remote); + // + flatpak_changes_struct.increase_total_download_size_by(installed_download_size); + // + system_refs_for_upgrade_vec.push(ref_format); + } + } + }; + + let mut user_refs_for_upgrade_vec = Vec::new(); + + match user_refs_for_upgrade_vec_opt { + Some(t) => { + for flatpak_row in t { + flatpak_changes_struct.add_user(); + // + let installed_size_installed = flatpak_row.flatref_installed_size_installed(); + let installed_size_remote = flatpak_row.flatref_installed_size_installed(); + let installed_download_size = flatpak_row.flatref_download_size(); + let ref_format = flatpak_row.flatref_ref_format(); + // + flatpak_changes_struct.decrease_total_installed_size_by(installed_size_installed); + flatpak_changes_struct.increase_total_installed_size_by(installed_size_remote); + // + flatpak_changes_struct.increase_total_download_size_by(installed_download_size); + // + user_refs_for_upgrade_vec.push(ref_format); + } + } + None => { + for flatpak_row in user_refs_for_upgrade_vec_all { + flatpak_changes_struct.add_user(); + // + let installed_size_installed = flatpak_row.flatref_installed_size_installed(); + let installed_size_remote = flatpak_row.flatref_installed_size_installed(); + let installed_download_size = flatpak_row.flatref_download_size(); + let ref_format = flatpak_row.flatref_ref_format(); + // + flatpak_changes_struct.decrease_total_installed_size_by(installed_size_installed); + flatpak_changes_struct.increase_total_installed_size_by(installed_size_remote); + // + flatpak_changes_struct.increase_total_download_size_by(installed_download_size); + // + user_refs_for_upgrade_vec.push(ref_format); + } + } + }; + + let flatpak_confirm_dialog_child_box = + Box::builder().orientation(Orientation::Vertical).build(); + + let flatpak_update_dialog_badges_size_group = SizeGroup::new(SizeGroupMode::Both); + let flatpak_update_dialog_badges_size_group0 = SizeGroup::new(SizeGroupMode::Both); + let flatpak_update_dialog_badges_size_group1 = SizeGroup::new(SizeGroupMode::Both); + + flatpak_confirm_dialog_child_box.append(&create_color_badge( + &t!("system_flatref_count_badge_label"), + &flatpak_changes_struct.system_flatref_count.to_string(), + "background-accent-bg", + &flatpak_update_dialog_badges_size_group, + &flatpak_update_dialog_badges_size_group0, + &flatpak_update_dialog_badges_size_group1, + )); + + flatpak_confirm_dialog_child_box.append(&create_color_badge( + &t!("user_flatref_count_badge_label"), + &flatpak_changes_struct.user_flatref_count.to_string(), + "background-accent-bg", + &flatpak_update_dialog_badges_size_group, + &flatpak_update_dialog_badges_size_group0, + &flatpak_update_dialog_badges_size_group1, + )); + + flatpak_confirm_dialog_child_box.append(&create_color_badge( + &t!("total_download_size_badge_label"), + &convert(flatpak_changes_struct.total_download_size as f64), + "background-accent-bg", + &flatpak_update_dialog_badges_size_group, + &flatpak_update_dialog_badges_size_group0, + &flatpak_update_dialog_badges_size_group1, + )); + + flatpak_confirm_dialog_child_box.append(&create_color_badge( + &t!("total_installed_size_badge_label"), + &convert(flatpak_changes_struct.total_installed_size as f64), + "background-accent-bg", + &flatpak_update_dialog_badges_size_group, + &flatpak_update_dialog_badges_size_group0, + &flatpak_update_dialog_badges_size_group1, + )); + + let flatpak_confirm_dialog = adw::MessageDialog::builder() + .transient_for(&window) + .heading(t!("flatpak_confirm_dialog_heading")) + .body(t!("flatpak_confirm_dialog_body")) + .extra_child(&flatpak_confirm_dialog_child_box) + .build(); + + flatpak_confirm_dialog.add_response( + "flatpak_confirm_dialog_cancel", + &t!("flatpak_confirm_dialog_cancel_label").to_string(), + ); + + flatpak_confirm_dialog.add_response( + "flatpak_confirm_dialog_confirm", + &t!("flatpak_confirm_dialog_confirm_label").to_string(), + ); + + flatpak_confirm_dialog.set_response_appearance( + "flatpak_confirm_dialog_confirm", + adw::ResponseAppearance::Destructive, + ); + + flatpak_confirm_dialog.set_default_response(Some("flatpak_confirm_dialog_confirm")); + flatpak_confirm_dialog.set_close_response("flatpak_confirm_dialog_cancel"); + + let retry_signal_action0 = retry_signal_action.clone(); + flatpak_confirm_dialog + .clone() + .choose(None::<&gio::Cancellable>, move |choice| { + if choice == "flatpak_confirm_dialog_confirm" { + flatpak_run_transactions( + system_refs_for_upgrade_vec, + user_refs_for_upgrade_vec, + window, + &retry_signal_action0, + ); + } + }); +} + +fn flatpak_run_transactions( + system_refs_for_upgrade_vec: Vec, + user_refs_for_upgrade_vec: Vec, + window: adw::ApplicationWindow, + retry_signal_action: &SimpleAction, +) { + let (transaction_percent_sender, transaction_percent_receiver) = + async_channel::unbounded::(); + let transaction_percent_sender = transaction_percent_sender.clone(); + let (transaction_status_sender, transaction_status_receiver) = + async_channel::unbounded::(); + let transaction_status_sender = transaction_status_sender.clone(); + + thread::spawn(move || { + let cancellable_no = libflatpak::gio::Cancellable::NONE; + + let transaction_status_sender0 = transaction_status_sender.clone(); + let transaction_percent_sender0 = transaction_percent_sender.clone(); + + let transaction_run_closure = + move |transaction: &libflatpak::Transaction, + transaction_operation: &libflatpak::TransactionOperation, + transaction_progress: &libflatpak::TransactionProgress| { + let transaction_status_sender = transaction_status_sender0.clone(); + let transaction_percent_sender = transaction_percent_sender0.clone(); + transaction_progress.connect_changed(clone!(@strong transaction_progress, @strong transaction_operation => move |_| { + let status_message = format!("{}: {}\n{}: {}\n{}: {}/{}\n{}: {}", t!("flatpak_ref"), transaction_operation.get_ref().unwrap_or(libflatpak::glib::GString::from_string_unchecked("Unknown".to_owned())), t!("flatpak_status") ,transaction_progress.status().unwrap_or(libflatpak::glib::GString::from_string_unchecked("Unknown".to_owned())), t!("flatpak_transaction_bytes_transferred"), convert(transaction_progress.bytes_transferred() as f64), convert(transaction_operation.download_size() as f64), t!("flatpak_transaction_installed_size"), convert(transaction_operation.installed_size() as f64)); + transaction_status_sender.send_blocking(status_message).expect("transaction_status_receiver closed!"); + transaction_percent_sender.send_blocking(transaction_progress.progress().try_into().unwrap_or(0)).expect("transaction_percent_receiver closed!"); + })); + }; + + // + + let flatpak_system_installation = + libflatpak::Installation::new_system(cancellable_no).unwrap(); + let flatpak_system_transaction = + libflatpak::Transaction::for_installation(&flatpak_system_installation, cancellable_no) + .unwrap(); + + for ref_format in system_refs_for_upgrade_vec { + flatpak_system_transaction + .add_update(&ref_format, &[], None) + .unwrap(); + } + + flatpak_system_transaction.connect_new_operation(transaction_run_closure.clone()); + + match flatpak_system_transaction.run(cancellable_no) { + Ok(_) => {} + Err(e) => { + transaction_status_sender + .send_blocking(e.to_string()) + .expect("transaction_sync_status_receiver closed"); + transaction_status_sender + .send_blocking("FN_OVERRIDE_FAILED".to_owned()) + .expect("transaction_sync_status_receiver closed"); + panic!("{}", e); + } + } + + // + + let flatpak_user_installation = libflatpak::Installation::new_user(cancellable_no).unwrap(); + let flatpak_user_transaction = + libflatpak::Transaction::for_installation(&flatpak_user_installation, cancellable_no) + .unwrap(); + + flatpak_user_transaction.connect_new_operation(transaction_run_closure); + + for ref_format in user_refs_for_upgrade_vec { + flatpak_user_transaction + .add_update(&ref_format, &[], None) + .unwrap(); + } + + match flatpak_user_transaction.run(cancellable_no) { + Ok(_) => { + transaction_status_sender + .send_blocking("FN_OVERRIDE_SUCCESSFUL".to_owned()) + .expect("transaction_sync_status_receiver closed"); + } + Err(e) => { + transaction_status_sender + .send_blocking(e.to_string()) + .expect("transaction_sync_status_receiver closed"); + transaction_status_sender + .send_blocking("FN_OVERRIDE_FAILED".to_owned()) + .expect("transaction_sync_status_receiver closed"); + panic!("{}", e); + } + } + }); + + let log_file_path = format!( + "/tmp/pika-flatpak-transaction_{}.log", + chrono::offset::Local::now().format("%Y-%m-%d_%H:%M") + ); + + let log_file_path_clone0 = log_file_path.clone(); + + if !Path::new(&log_file_path).exists() { + match fs::File::create(&log_file_path) { + Ok(_) => {} + Err(_) => { + eprintln!("Warning: {} file couldn't be created", log_file_path); + } + }; + } + + let flatpak_transaction_dialog_child_box = + Box::builder().orientation(Orientation::Vertical).build(); + + let flatpak_transaction_dialog_progress_bar = + ProgressBar::builder().show_text(true).hexpand(true).build(); + + let flatpak_transaction_dialog_spinner = Spinner::builder() + .hexpand(true) + .valign(Align::Start) + .halign(Align::Center) + .spinning(true) + .height_request(128) + .width_request(128) + .build(); + + flatpak_transaction_dialog_child_box.append(&flatpak_transaction_dialog_spinner); + flatpak_transaction_dialog_child_box.append(&flatpak_transaction_dialog_progress_bar); + + let flatpak_transaction_dialog = adw::MessageDialog::builder() + .transient_for(&window) + .extra_child(&flatpak_transaction_dialog_child_box) + .heading(t!("flatpak_transaction_dialog_heading")) + .width_request(500) + .build(); + + flatpak_transaction_dialog.add_response( + "flatpak_transaction_dialog_ok", + &t!("flatpak_transaction_dialog_ok_label").to_string(), + ); + + let flatpak_transaction_dialog_child_box_done = + Box::builder().orientation(Orientation::Vertical).build(); + + let flatpak_transaction_log_image = Image::builder() + .pixel_size(128) + .halign(Align::Center) + .build(); + + let flatpak_transaction_log_button = Button::builder() + .label(t!("flatpak_transaction_dialog_open_log_file_label")) + .halign(Align::Center) + .margin_start(15) + .margin_end(15) + .margin_top(15) + .margin_bottom(15) + .build(); + + flatpak_transaction_dialog_child_box_done.append(&flatpak_transaction_log_image); + flatpak_transaction_dialog_child_box_done.append(&flatpak_transaction_log_button); + + flatpak_transaction_dialog.set_response_enabled("flatpak_transaction_dialog_ok", false); + flatpak_transaction_dialog.set_close_response("flatpak_transaction_dialog_ok"); + + let transaction_percent_server_context = MainContext::default(); + // The main loop executes the asynchronous block + transaction_percent_server_context.spawn_local(clone!( + #[weak] + flatpak_transaction_dialog_progress_bar, + async move { + while let Ok(state) = transaction_percent_receiver.recv().await { + flatpak_transaction_dialog_progress_bar.set_fraction((state as f32 / 100.0).into()); + } + } + )); + + let transaction_status_server_context = MainContext::default(); + // The main loop executes the asynchronous block + transaction_status_server_context.spawn_local(clone!( + #[weak] + flatpak_transaction_dialog, + #[weak] + flatpak_transaction_dialog_child_box, + #[strong] + flatpak_transaction_dialog_child_box_done, + #[strong] + flatpak_transaction_log_image, + async move { + while let Ok(state) = transaction_status_receiver.recv().await { + match state.as_ref() { + "FN_OVERRIDE_SUCCESSFUL" => { + flatpak_transaction_dialog_child_box.set_visible(false); + flatpak_transaction_log_image.set_icon_name(Some("face-cool-symbolic")); + flatpak_transaction_dialog + .set_extra_child(Some(&flatpak_transaction_dialog_child_box_done)); + flatpak_transaction_dialog.set_title(Some( + &t!("flatpak_transaction_dialog_status_successful").to_string(), + )); + flatpak_transaction_dialog + .set_response_enabled("flatpak_transaction_dialog_ok", true); + } + "FN_OVERRIDE_FAILED" => { + flatpak_transaction_dialog_child_box.set_visible(false); + flatpak_transaction_log_image.set_icon_name(Some("dialog-error-symbolic")); + flatpak_transaction_dialog + .set_extra_child(Some(&flatpak_transaction_dialog_child_box_done)); + flatpak_transaction_dialog.set_title(Some( + &t!("flatpak_transaction_dialog_status_failed").to_string(), + )); + flatpak_transaction_dialog + .set_response_enabled("flatpak_transaction_dialog_ok", true); + flatpak_transaction_dialog + .set_response_enabled("flatpak_transaction_dialog_open_log_file", true); + } + _ => { + flatpak_transaction_dialog.set_body(&state); + let mut log_file = OpenOptions::new() + .write(true) + .append(true) + .open(&log_file_path) + .unwrap(); + + if let Err(e) = writeln!( + log_file, + "[{}] {}", + chrono::offset::Local::now().format("%Y/%m/%d_%H:%M"), + state + ) { + eprintln!("Couldn't write to file: {}", e); + } + } + } + } + } + )); + + let retry_signal_action0 = retry_signal_action.clone(); + + flatpak_transaction_log_button.connect_clicked(move |_| { + let _ = Command::new("xdg-open") + .arg(log_file_path_clone0.to_owned()) + .spawn(); + }); + + flatpak_transaction_dialog.choose(None::<&gio::Cancellable>, move |choice| { + match choice.as_str() { + "flatpak_transaction_dialog_ok" => { + retry_signal_action0.activate(None); + } + _ => {} + } + }); +} + +fn create_color_badge( + label0_text: &str, + label1_text: &str, + css_style: &str, + group_size: &SizeGroup, + group_size0: &SizeGroup, + group_size1: &SizeGroup, +) -> ListBox { + let badge_box = Box::builder().build(); + + let label0 = Label::builder() + .label(label0_text) + .margin_start(5) + .margin_end(5) + .margin_bottom(1) + .margin_top(1) + .valign(Align::Center) + .halign(Align::Center) + .hexpand(true) + .vexpand(true) + .build(); + group_size0.add_widget(&label0); + + let label_separator = Separator::builder().build(); + + let label1 = Label::builder() + .label(label1_text) + .margin_start(3) + .margin_end(0) + .margin_bottom(1) + .margin_top(1) + .valign(Align::Center) + .halign(Align::Center) + .hexpand(true) + .vexpand(true) + .build(); + group_size1.add_widget(&label1); + + label1.add_css_class(css_style); + + badge_box.append(&label0); + badge_box.append(&label_separator); + badge_box.append(&label1); + + let boxedlist = ListBox::builder() + .selection_mode(SelectionMode::None) + .halign(Align::Center) + .margin_start(10) + .margin_end(10) + .margin_bottom(10) + .margin_top(10) + .build(); + + boxedlist.add_css_class("boxed-list"); + boxedlist.append(&badge_box); + group_size.add_widget(&boxedlist); + boxedlist +} diff --git a/src/bin/gui/main.rs b/src/bin/gui/main.rs new file mode 100644 index 0000000..bbbc648 --- /dev/null +++ b/src/bin/gui/main.rs @@ -0,0 +1,56 @@ +mod apt_package_row; +mod apt_update_page; +mod build_ui; +mod config; +mod flatpak_ref_row; +mod flatpak_update_page; + +use crate::config::APP_ID; +use adw::prelude::*; +use adw::*; +use build_ui::build_ui; +use gdk::Display; +use gtk::*; +use std::boxed::Box; +use std::env; + +// Init translations for current crate. +#[macro_use] +extern crate rust_i18n; +i18n!("locales", fallback = "en_US"); + +/// main function +fn main() { + let current_locale = match env::var_os("LANG") { + Some(v) => v + .into_string() + .unwrap() + .chars() + .take_while(|&ch| ch != '.') + .collect::(), + None => panic!("$LANG is not set"), + }; + rust_i18n::set_locale(¤t_locale); + let application = adw::Application::new(Some(APP_ID), Default::default()); + application.connect_startup(|app| { + // The CSS "magic" happens here. + let provider = CssProvider::new(); + provider.load_from_string(include_str!("style.css")); + // We give the CssProvided to the default screen so the CSS rules we added + // can be applied to our window. + gtk::style_context_add_provider_for_display( + &Display::default().expect("Could not connect to a display."), + &provider, + STYLE_PROVIDER_PRIORITY_APPLICATION, + ); + app.connect_activate(build_ui); + }); + + //if get_current_username().unwrap() == "pikaos" { + // application.run(); + //} else { + // println!("Error: This program can only be run via pikaos user"); + // std::process::exit(1) + //} + application.run(); +} diff --git a/src/bin/gui/style.css b/src/bin/gui/style.css new file mode 100644 index 0000000..f1fbea2 --- /dev/null +++ b/src/bin/gui/style.css @@ -0,0 +1,67 @@ +.symbolic-accent-bg { + color: @accent_bg_color; +} + +.size-20-font { + font-size: 20px; +} + +.rounded-all-25 { + border-radius: 25px; +} + +.round-all-scroll { + border-top-right-radius: 15px; + border-top-left-radius: 15px; + border-bottom-right-radius: 15px; + border-bottom-left-radius: 15px; + padding-top: 3px; + padding-right: 3px; + padding-left: 3px; + padding-bottom: 3px; +} + +.background-accent-bg { + background: @accent_bg_color; + border-radius: 10px; + padding: 5px; +} + +.background-green-bg { + background: green; + border-radius: 10px; + padding: 5px; +} + +.background-red-bg { + background: #ff2a03; + border-radius: 10px; + padding: 5px; +} + +.round-border-only-top { + border-top-right-radius: 15px; + border-top-left-radius: 15px; + border-bottom-right-radius: 0px; + border-bottom-left-radius: 0px; +} + +.round-border-only-bottom { + border-top-right-radius: 0px; + border-top-left-radius: 0px; + border-bottom-right-radius: 15px; + border-bottom-left-radius: 15px; +} + +.destructive-color-text { + color: @destructive_bg_color; +} + +.success-color-text { + color: @success_bg_color; +} + +.size-20-bold-text { + font-weight: 800; + font-size: 20px; +} \ No newline at end of file diff --git a/src/lib/apt_install_progress_socket/mod.rs b/src/lib/apt_install_progress_socket/mod.rs new file mode 100644 index 0000000..9a740af --- /dev/null +++ b/src/lib/apt_install_progress_socket/mod.rs @@ -0,0 +1,83 @@ +use crate::pika_unixsocket_tools::*; +use rust_apt::progress::DynInstallProgress; +use std::process::exit; +use tokio::io::AsyncWriteExt; +use tokio::net::UnixStream; +use tokio::runtime::Runtime; + +pub struct AptInstallProgressSocket<'a> { + percent_socket_path: &'a str, + status_socket_path: &'a str, +} + +impl<'a> AptInstallProgressSocket<'a> { + /// Returns a new default progress instance. + pub fn new(percent_socket_path: &'a str, status_socket_path: &'a str) -> Self { + let progress = Self { + percent_socket_path: percent_socket_path, + status_socket_path: status_socket_path, + }; + progress + } +} + +impl<'a> DynInstallProgress for AptInstallProgressSocket<'a> { + fn status_changed( + &mut self, + _pkgname: String, + steps_done: u64, + total_steps: u64, + action: String, + ) { + let progress_percent: f32 = (steps_done as f32 * 100.0) / total_steps as f32; + Runtime::new().unwrap().block_on(send_progress_percent( + progress_percent, + self.percent_socket_path, + )); + Runtime::new() + .unwrap() + .block_on(send_progress_status(&action, self.status_socket_path)); + } + + fn error(&mut self, pkgname: String, _steps_done: u64, _total_steps: u64, error: String) { + let message = format!("dpkg failure on {}: {}", pkgname, error); + eprintln!("{}", &message); + Runtime::new() + .unwrap() + .block_on(send_progress_status(&message, self.status_socket_path)); + Runtime::new() + .unwrap() + .block_on(send_failed_to_socket(self.percent_socket_path)); + Runtime::new() + .unwrap() + .block_on(send_failed_to_socket(self.status_socket_path)); + exit(53) + } +} + +async fn send_progress_percent(progress_f32: f32, socket_path: &str) { + // Connect to the Unix socket + let mut stream = UnixStream::connect(socket_path) + .await + .expect("Could not connect to server"); + + let message = progress_f32.to_string(); + // Send the message to the server + stream + .write_all(message.as_bytes()) + .await + .expect("Failed to write to stream"); +} + +async fn send_progress_status(message: &str, socket_path: &str) { + // Connect to the Unix socket + let mut stream = UnixStream::connect(socket_path) + .await + .expect("Could not connect to server"); + + // Send the message to the server + stream + .write_all(message.as_bytes()) + .await + .expect("Failed to write to stream"); +} diff --git a/src/lib/apt_update_progress_socket/mod.rs b/src/lib/apt_update_progress_socket/mod.rs new file mode 100644 index 0000000..f156350 --- /dev/null +++ b/src/lib/apt_update_progress_socket/mod.rs @@ -0,0 +1,152 @@ +use crate::pika_unixsocket_tools::*; +use rust_apt::progress::DynAcquireProgress; +use rust_apt::raw::{AcqTextStatus, ItemDesc, PkgAcquire}; +use std::process::exit; +use tokio::io::AsyncWriteExt; +use tokio::net::UnixStream; +use tokio::runtime::Runtime; + +pub struct AptUpdateProgressSocket<'a> { + pulse_interval: usize, + percent_socket_path: &'a str, + status_socket_path: &'a str, +} + +impl<'a> AptUpdateProgressSocket<'a> { + /// Returns a new default progress instance. + pub fn new(percent_socket_path: &'a str, status_socket_path: &'a str) -> Self { + let progress = Self { + pulse_interval: 0, + percent_socket_path: percent_socket_path, + status_socket_path: status_socket_path, + }; + progress + } +} + +impl<'a> DynAcquireProgress for AptUpdateProgressSocket<'a> { + /// Used to send the pulse interval to the apt progress class. + /// + /// Pulse Interval is in microseconds. + /// + /// Example: 1 second = 1000000 microseconds. + /// + /// Apt default is 500000 microseconds or 0.5 seconds. + /// + /// The higher the number, the less frequent pulse updates will be. + /// + /// Pulse Interval set to 0 assumes the apt defaults. + fn pulse_interval(&self) -> usize { + self.pulse_interval + } + + /// Called when an item is confirmed to be up-to-date. + /// + /// Prints out the short description and the expected size. + fn hit(&mut self, item: &ItemDesc) { + let message = format!("Up-to-date: {} {}", item.description(), item.short_desc()); + println!("{}", message); + Runtime::new() + .unwrap() + .block_on(send_progress_status(&message, self.status_socket_path)); + } + + /// Called when an Item has started to download + /// + /// Prints out the short description and the expected size. + fn fetch(&mut self, item: &ItemDesc) { + let message = format!("Fetching: {} {}", item.description(), item.short_desc()); + println!("{}", message); + Runtime::new() + .unwrap() + .block_on(send_progress_status(&message, self.status_socket_path)); + } + + /// Called when an item is successfully and completely fetched. + /// + /// We don't print anything here to remain consistent with apt. + fn done(&mut self, item: &ItemDesc) { + let message = format!("Downloading: {} {}", item.description(), item.short_desc()); + println!("{}", message); + Runtime::new() + .unwrap() + .block_on(send_progress_status(&message, self.status_socket_path)); + } + + /// Called when progress has started. + /// + /// Start does not pass information into the method. + /// + /// We do not print anything here to remain consistent with apt. + fn start(&mut self) {} + + /// Called when progress has finished. + /// + /// Stop does not pass information into the method. + /// + /// prints out the bytes downloaded and the overall average line speed. + fn stop(&mut self, _status: &AcqTextStatus) {} + + /// Called when an Item fails to download. + /// + /// Print out the ErrorText for the Item. + fn fail(&mut self, item: &ItemDesc) { + let message = format!( + "Download Failed: {} {}", + item.description(), + item.short_desc() + ); + eprintln!("{}", &message); + Runtime::new() + .unwrap() + .block_on(send_progress_status(&message, self.status_socket_path)); + Runtime::new() + .unwrap() + .block_on(send_failed_to_socket(self.percent_socket_path)); + Runtime::new() + .unwrap() + .block_on(send_failed_to_socket(self.status_socket_path)); + exit(53) + } + + /// Called periodically to provide the overall progress information + /// + /// Draws the current progress. + /// Each line has an overall percent meter and a per active item status + /// meter along with an overall bandwidth and ETA indicator. + fn pulse(&mut self, status: &AcqTextStatus, _owner: &PkgAcquire) { + let progress_percent: f32 = + (status.current_bytes() as f32 * 100.0) / status.total_bytes() as f32; + Runtime::new().unwrap().block_on(send_progress_percent( + progress_percent, + self.percent_socket_path, + )); + } +} + +async fn send_progress_percent(progress_f32: f32, socket_path: &str) { + // Connect to the Unix socket + let mut stream = UnixStream::connect(socket_path) + .await + .expect("Could not connect to server"); + + let message = progress_f32.to_string(); + // Send the message to the server + stream + .write_all(message.as_bytes()) + .await + .expect("Failed to write to stream"); +} + +async fn send_progress_status(message: &str, socket_path: &str) { + // Connect to the Unix socket + let mut stream = UnixStream::connect(socket_path) + .await + .expect("Could not connect to server"); + + // Send the message to the server + stream + .write_all(message.as_bytes()) + .await + .expect("Failed to write to stream"); +} diff --git a/src/lib/lib.rs b/src/lib/lib.rs new file mode 100644 index 0000000..aac95e7 --- /dev/null +++ b/src/lib/lib.rs @@ -0,0 +1,3 @@ +pub mod apt_install_progress_socket; +pub mod apt_update_progress_socket; +pub mod pika_unixsocket_tools; diff --git a/src/lib/pika_unixsocket_tools/mod.rs b/src/lib/pika_unixsocket_tools/mod.rs new file mode 100644 index 0000000..a63b772 --- /dev/null +++ b/src/lib/pika_unixsocket_tools/mod.rs @@ -0,0 +1,177 @@ +use chrono; +use std::fs; +use std::fs::OpenOptions; +use std::io::Write; +use std::path::Path; +use tokio::io::{AsyncReadExt, AsyncWriteExt}; +use tokio::net::{UnixListener, UnixStream}; +use tokio::task; + +pub async fn send_successful_to_socket(socket_path: &str) { + // Connect to the Unix socket + let mut stream = UnixStream::connect(socket_path) + .await + .expect("Could not connect to server"); + + let message = "FN_OVERRIDE_SUCCESSFUL"; + + // Send the message to the server + stream + .write_all(message.as_bytes()) + .await + .expect("Failed to write to stream"); +} + +pub async fn send_failed_to_socket(socket_path: &str) { + // Connect to the Unix socket + let mut stream = UnixStream::connect(socket_path) + .await + .expect("Could not connect to server"); + + let message = "FN_OVERRIDE_FAILED"; + + // Send the message to the server + stream + .write_all(message.as_bytes()) + .await + .expect("Failed to write to stream"); +} + +// Function to handle a single client connection +pub async fn handle_client( + mut stream: UnixStream, + buffer_sender: async_channel::Sender, + log_file_path: String, +) { + // Buffer to store incoming data + let mut buffer = [0; 1024]; + + if !Path::new(&log_file_path).exists() { + match fs::File::create(&log_file_path) { + Ok(_) => {} + Err(_) => { + eprintln!("Warning: {} file couldn't be created", log_file_path); + } + }; + } + + // Read data from the stream + match stream.read(&mut buffer).await { + Ok(size) => { + let message = String::from_utf8_lossy(&buffer[..size]).to_string(); + // Send to async buffer sender + buffer_sender + .send_blocking(message.clone()) + .expect("Buffer channel closed"); + // Write to log file + let mut log_file = OpenOptions::new() + .write(true) + .append(true) + .open(&log_file_path) + .unwrap(); + + if let Err(e) = writeln!( + log_file, + "[{}] {}", + chrono::offset::Local::now().format("%Y/%m/%d_%H:%M"), + message + ) { + eprintln!("Couldn't write to file: {}", e); + } + } + Err(e) => { + // Print error message if reading fails + eprintln!("Failed to read from stream: {}", e); + } + } +} + +pub async fn start_socket_server( + buffer_sender: async_channel::Sender, + socket_path: &str, + log_file_path: &str, +) { + // Remove the socket file if it already exists + if Path::new(socket_path).exists() { + fs::remove_file(socket_path).expect("Could not remove existing socket file"); + } + + // Bind the Unix listener to the socket path + let listener = UnixListener::bind(socket_path).expect("Could not bind"); + + println!("Server listening on {}", socket_path); + + // Loop to accept incoming connections + loop { + // Accept an incoming connection + match listener.accept().await { + Ok((stream, _)) => { + // Handle the connection in a separate task + task::spawn(handle_client( + stream, + buffer_sender.clone(), + log_file_path.to_owned(), + )); + } + Err(e) => { + // Print error message if a connection fails + eprintln!("Connection failed: {}", e); + } + } + } +} + +pub async fn handle_client_no_log( + mut stream: UnixStream, + buffer_sender: async_channel::Sender, +) { + // Buffer to store incoming data + let mut buffer = [0; 1024]; + + // Read data from the stream + match stream.read(&mut buffer).await { + Ok(size) => { + let message = String::from_utf8_lossy(&buffer[..size]).to_string(); + // Write to log file + + // Send to async buffer sender + buffer_sender + .send_blocking(message) + .expect("Buffer channel closed") + } + Err(e) => { + // Print error message if reading fails + eprintln!("Failed to read from stream: {}", e); + } + } +} + +pub async fn start_socket_server_no_log( + buffer_sender: async_channel::Sender, + socket_path: &str, +) { + // Remove the socket file if it already exists + if Path::new(socket_path).exists() { + fs::remove_file(socket_path).expect("Could not remove existing socket file"); + } + + // Bind the Unix listener to the socket path + let listener = UnixListener::bind(socket_path).expect("Could not bind"); + + println!("Server listening on {}", socket_path); + + // Loop to accept incoming connections + loop { + // Accept an incoming connection + match listener.accept().await { + Ok((stream, _)) => { + // Handle the connection in a separate task + task::spawn(handle_client_no_log(stream, buffer_sender.clone())); + } + Err(e) => { + // Print error message if a connection fails + eprintln!("Connection failed: {}", e); + } + } + } +}