diff --git a/Cargo.lock b/Cargo.lock index 43689ac..b23e806 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -78,14 +78,47 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" [[package]] -name = "cc" -version = "1.2.21" +name = "cairo-rs" +version = "0.20.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8691782945451c1c383942c4874dbe63814f61cb57ef773cda2972682b7bb3c0" +checksum = "ae50b5510d86cf96ac2370e66d8dc960882f3df179d6a5a1e52bd94a1416c0f7" +dependencies = [ + "bitflags", + "cairo-sys-rs", + "glib", + "libc", +] + +[[package]] +name = "cairo-sys-rs" +version = "0.20.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f18b6bb8e43c7eb0f2aac7976afe0c61b6f5fc2ab7bc4c139537ea56c92290df" +dependencies = [ + "glib-sys", + "libc", + "system-deps", +] + +[[package]] +name = "cc" +version = "1.2.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32db95edf998450acc7881c932f94cd9b05c87b4b2599e8bab064753da4acfd1" dependencies = [ "shlex", ] +[[package]] +name = "cfg-expr" +version = "0.17.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d4ba6e40bd1184518716a6e1a781bf9160e286d219ccdb8ab2612e74cfe4789" +dependencies = [ + "smallvec", + "target-lexicon", +] + [[package]] name = "cfg-if" version = "1.0.0" @@ -182,6 +215,28 @@ dependencies = [ "once_cell", ] +[[package]] +name = "field-offset" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38e2275cc4e4fc009b0669731a1e5ab7ebf11f469eaede2bab9309a5b4d6057f" +dependencies = [ + "memoffset", + "rustc_version", +] + +[[package]] +name = "flume" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da0e4dd2a88388a1f4ccc7c9ce104604dab68d9f408dc34cd45823d5a9069095" +dependencies = [ + "futures-core", + "futures-sink", + "nanorand", + "spin", +] + [[package]] name = "fnv" version = "1.0.7" @@ -197,6 +252,27 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "fragile" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28dd6caf6059519a65843af8fe2a3ae298b14b80179855aeb4adc2c1934ee619" + +[[package]] +name = "futures" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + [[package]] name = "futures-channel" version = "0.3.31" @@ -213,12 +289,34 @@ version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" +[[package]] +name = "futures-executor" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + [[package]] name = "futures-io" version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" +[[package]] +name = "futures-macro" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "futures-sink" version = "0.3.31" @@ -237,8 +335,10 @@ version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" dependencies = [ + "futures-channel", "futures-core", "futures-io", + "futures-macro", "futures-sink", "futures-task", "memchr", @@ -247,6 +347,63 @@ dependencies = [ "slab", ] +[[package]] +name = "gdk-pixbuf" +version = "0.20.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7563afd6ff0a221edfbb70a78add5075b8d9cb48e637a40a24c3ece3fea414d0" +dependencies = [ + "gdk-pixbuf-sys", + "gio", + "glib", + "libc", +] + +[[package]] +name = "gdk-pixbuf-sys" +version = "0.20.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67f2587c9202bf997476bbba6aaed4f78a11538a2567df002a5f57f5331d0b5c" +dependencies = [ + "gio-sys", + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + +[[package]] +name = "gdk4" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4850c9d9c1aecd1a3eb14fadc1cdb0ac0a2298037e116264c7473e1740a32d60" +dependencies = [ + "cairo-rs", + "gdk-pixbuf", + "gdk4-sys", + "gio", + "glib", + "libc", + "pango", +] + +[[package]] +name = "gdk4-sys" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f6eb95798e2b46f279cf59005daf297d5b69555428f185650d71974a910473a" +dependencies = [ + "cairo-sys-rs", + "gdk-pixbuf-sys", + "gio-sys", + "glib-sys", + "gobject-sys", + "libc", + "pango-sys", + "pkg-config", + "system-deps", +] + [[package]] name = "getrandom" version = "0.2.16" @@ -262,9 +419,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73fea8450eea4bac3940448fb7ae50d91f034f941199fcd9d909a5a07aa455f0" +checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" dependencies = [ "cfg-if", "js-sys", @@ -280,6 +437,197 @@ version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" +[[package]] +name = "gio" +version = "0.20.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4f00c70f8029d84ea7572dd0e1aaa79e5329667b4c17f329d79ffb1e6277487" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-util", + "gio-sys", + "glib", + "libc", + "pin-project-lite", + "smallvec", +] + +[[package]] +name = "gio-sys" +version = "0.20.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "160eb5250a26998c3e1b54e6a3d4ea15c6c7762a6062a19a7b63eff6e2b33f9e" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps", + "windows-sys 0.59.0", +] + +[[package]] +name = "glib" +version = "0.20.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "707b819af8059ee5395a2de9f2317d87a53dbad8846a2f089f0bb44703f37686" +dependencies = [ + "bitflags", + "futures-channel", + "futures-core", + "futures-executor", + "futures-task", + "futures-util", + "gio-sys", + "glib-macros", + "glib-sys", + "gobject-sys", + "libc", + "memchr", + "smallvec", +] + +[[package]] +name = "glib-macros" +version = "0.20.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "715601f8f02e71baef9c1f94a657a9a77c192aea6097cf9ae7e5e177cd8cde68" +dependencies = [ + "heck", + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "glib-sys" +version = "0.20.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8928869a44cfdd1fccb17d6746e4ff82c8f82e41ce705aa026a52ca8dc3aefb" +dependencies = [ + "libc", + "system-deps", +] + +[[package]] +name = "gobject-sys" +version = "0.20.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c773a3cb38a419ad9c26c81d177d96b4b08980e8bdbbf32dace883e96e96e7e3" +dependencies = [ + "glib-sys", + "libc", + "system-deps", +] + +[[package]] +name = "graphene-rs" +version = "0.20.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3cbc5911bfb32d68dcfa92c9510c462696c2f715548fcd7f3f1be424c739de19" +dependencies = [ + "glib", + "graphene-sys", + "libc", +] + +[[package]] +name = "graphene-sys" +version = "0.20.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11a68d39515bf340e879b72cecd4a25c1332557757ada6e8aba8654b4b81d23a" +dependencies = [ + "glib-sys", + "libc", + "pkg-config", + "system-deps", +] + +[[package]] +name = "gsk4" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61f5e72f931c8c9f65fbfc89fe0ddc7746f147f822f127a53a9854666ac1f855" +dependencies = [ + "cairo-rs", + "gdk4", + "glib", + "graphene-rs", + "gsk4-sys", + "libc", + "pango", +] + +[[package]] +name = "gsk4-sys" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "755059de55fa6f85a46bde8caf03e2184c96bfda1f6206163c72fb0ea12436dc" +dependencies = [ + "cairo-sys-rs", + "gdk4-sys", + "glib-sys", + "gobject-sys", + "graphene-sys", + "libc", + "pango-sys", + "system-deps", +] + +[[package]] +name = "gtk4" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af1c491051f030994fd0cde6f3c44f3f5640210308cff1298c7673c47408091d" +dependencies = [ + "cairo-rs", + "field-offset", + "futures-channel", + "gdk-pixbuf", + "gdk4", + "gio", + "glib", + "graphene-rs", + "gsk4", + "gtk4-macros", + "gtk4-sys", + "libc", + "pango", +] + +[[package]] +name = "gtk4-macros" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ed1786c4703dd196baf7e103525ce0cf579b3a63a0570fe653b7ee6bac33999" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "gtk4-sys" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41e03b01e54d77c310e1d98647d73f996d04b2f29b9121fe493ea525a7ec03d6" +dependencies = [ + "cairo-sys-rs", + "gdk-pixbuf-sys", + "gdk4-sys", + "gio-sys", + "glib-sys", + "gobject-sys", + "graphene-sys", + "gsk4-sys", + "libc", + "pango-sys", + "system-deps", +] + [[package]] name = "h2" version = "0.4.10" @@ -305,6 +653,12 @@ version = "0.15.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "84b26c544d002229e640969970a2e74021aadf6e2f96372b9c58eff97de08eb3" +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + [[package]] name = "http" version = "1.3.1" @@ -554,6 +908,37 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" +[[package]] +name = "libadwaita" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "500135d29c16aabf67baafd3e7741d48e8b8978ca98bac39e589165c8dc78191" +dependencies = [ + "gdk4", + "gio", + "glib", + "gtk4", + "libadwaita-sys", + "libc", + "pango", +] + +[[package]] +name = "libadwaita-sys" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6680988058c2558baf3f548a370e4e78da3bf7f08469daa822ac414842c912db" +dependencies = [ + "gdk4-sys", + "gio-sys", + "glib-sys", + "gobject-sys", + "gtk4-sys", + "libc", + "pango-sys", + "system-deps", +] + [[package]] name = "libc" version = "0.2.172" @@ -576,12 +961,28 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" +[[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.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" +[[package]] +name = "lru-slab" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" + [[package]] name = "matchers" version = "0.1.0" @@ -597,6 +998,15 @@ 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 = "mime" version = "0.3.17" @@ -633,6 +1043,15 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "nanorand" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a51313c5820b0b02bd422f4b44776fbf47961755c74ce64afc73bfad10226c3" +dependencies = [ + "getrandom 0.2.16", +] + [[package]] name = "nu-ansi-term" version = "0.46.0" @@ -676,6 +1095,30 @@ version = "4.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1036865bb9422d3300cf723f657c2851d0e9ab12567854b1f4eba3d77decf564" +[[package]] +name = "pango" +version = "0.20.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b1f5dc1b8cf9bc08bfc0843a04ee0fa2e78f1e1fa4b126844a383af4f25f0ec" +dependencies = [ + "gio", + "glib", + "libc", + "pango-sys", +] + +[[package]] +name = "pango-sys" +version = "0.20.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dbb9b751673bd8fe49eb78620547973a1e719ed431372122b20abd12445bab5" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + [[package]] name = "percent-encoding" version = "2.3.1" @@ -694,6 +1137,12 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "pkg-config" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" + [[package]] name = "potential_utf" version = "0.1.2" @@ -712,6 +1161,15 @@ dependencies = [ "zerocopy", ] +[[package]] +name = "proc-macro-crate" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edce586971a4dfaa28950c6f18ed55e0406c1ab88bbce2c6f6293a7aaba73d35" +dependencies = [ + "toml_edit", +] + [[package]] name = "proc-macro2" version = "1.0.95" @@ -723,9 +1181,9 @@ dependencies = [ [[package]] name = "quinn" -version = "0.11.7" +version = "0.11.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3bd15a6f2967aef83887dcb9fec0014580467e33720d073560cf015a5683012" +checksum = "626214629cda6781b6dc1d316ba307189c85ba657213ce642d9c77670f8202c8" dependencies = [ "bytes", "cfg_aliases", @@ -743,12 +1201,13 @@ dependencies = [ [[package]] name = "quinn-proto" -version = "0.11.11" +version = "0.11.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcbafbbdbb0f638fe3f35f3c56739f77a8a1d070cb25603226c83339b391472b" +checksum = "49df843a9161c85bb8aae55f101bc0bac8bcafd637a620d9122fd7e0b2f7422e" dependencies = [ "bytes", - "getrandom 0.3.2", + "getrandom 0.3.3", + "lru-slab", "rand", "ring", "rustc-hash", @@ -816,7 +1275,7 @@ version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" dependencies = [ - "getrandom 0.3.2", + "getrandom 0.3.3", ] [[package]] @@ -874,6 +1333,41 @@ version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" +[[package]] +name = "relm4" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30837553c1a8cfea1a404c83ec387c5c8ff9358e1060b057c274c5daa5035ad1" +dependencies = [ + "flume", + "fragile", + "futures", + "gtk4", + "libadwaita", + "once_cell", + "relm4-css", + "relm4-macros", + "tokio", + "tracing", +] + +[[package]] +name = "relm4-css" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d3b924557df1cddc687b60b313c4b76620fdbf0e463afa4b29f67193ccf37f9" + +[[package]] +name = "relm4-macros" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a895a7455441a857d100ca679bd24a92f91d28b5e3df63296792ac1af2eddde" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "reqwest" version = "0.12.15" @@ -947,6 +1441,15 @@ version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + [[package]] name = "rustls" version = "0.23.27" @@ -1003,6 +1506,18 @@ version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "semver" +version = "1.0.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0" + [[package]] name = "serde" version = "1.0.219" @@ -1096,6 +1611,15 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +dependencies = [ + "lock_api", +] + [[package]] name = "stable_deref_trait" version = "1.2.0" @@ -1139,6 +1663,25 @@ dependencies = [ "syn", ] +[[package]] +name = "system-deps" +version = "7.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66d23aaf9f331227789a99e8de4c91bf46703add012bdfd45fdecdfb2975a005" +dependencies = [ + "cfg-expr", + "heck", + "pkg-config", + "toml", + "version-compare", +] + +[[package]] +name = "target-lexicon" +version = "0.12.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" + [[package]] name = "thiserror" version = "2.0.12" @@ -1383,6 +1926,7 @@ version = "0.1.0" dependencies = [ "color-eyre", "dirs", + "relm4", "reqwest", "serde", "toml", @@ -1438,6 +1982,12 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" +[[package]] +name = "version-compare" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "852e951cb7832cb45cb1169900d19760cfa39b82bc0ea9c0e5a14ae88411c98b" + [[package]] name = "want" version = "0.3.1" diff --git a/Cargo.toml b/Cargo.toml index ebbb063..c425477 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,6 +6,7 @@ edition = "2024" [dependencies] color-eyre = "0.6.4" dirs = "6.0.0" +relm4 = { version = "0.9.1", features = ["gnome_47", "libadwaita"] } reqwest = { version = "0.12.15", default-features = false, features = ["http2", "charset", "rustls-tls", "blocking", "json", "multipart"] } serde = { version = "1.0.219", features = ["derive"] } toml = "0.8.22" diff --git a/package.nix b/package.nix index 84cb79a..831109d 100644 --- a/package.nix +++ b/package.nix @@ -3,6 +3,9 @@ lib, craneLib, mold-wrapped, + libadwaita, + pkg-config, + wrapGAppsHook4, }: let cargoToml = lib.importTOML ./Cargo.toml; @@ -14,7 +17,13 @@ craneLib.buildPackage { src = craneLib.cleanCargoSource ./.; - nativeBuildInputs = [ mold-wrapped ]; + nativeBuildInputs = [ + mold-wrapped + pkg-config + wrapGAppsHook4 + ]; + + buildInputs = [ libadwaita ]; RUSTFLAGS = "-C link-arg=-fuse-ld=mold"; diff --git a/src/gobject.rs b/src/gobject.rs new file mode 100644 index 0000000..458e595 --- /dev/null +++ b/src/gobject.rs @@ -0,0 +1,80 @@ +use std::{cell::Cell, sync::LazyLock}; + +use relm4::{ + adw::subclass::prelude::ObjectSubclassType, + gtk::{ + glib::{self, ParamSpecString, Type, object::ObjectExt, value::ToValue}, + subclass::prelude::{ObjectImpl, ObjectSubclass}, + }, +}; + +use crate::zipline::ZiplineFolder; + +glib::wrapper! { + pub struct GtkZiplineFolder(ObjectSubclass); +} + +impl GtkZiplineFolder { + pub fn r#type() -> Type { + GtkZiplineFolderImpl::type_() + } + + pub fn from_folder(folder: &ZiplineFolder) -> Self { + glib::Object::builder() + .property("id", folder.id.clone()) + .property("name", folder.name.clone()) + .build() + } + + pub fn as_folder(&self) -> ZiplineFolder { + ZiplineFolder { + id: self.property("id"), + name: self.property("name"), + } + } +} + +#[derive(Default)] +pub struct GtkZiplineFolderImpl { + id: Cell, + name: Cell, +} + +#[glib::object_subclass] +impl ObjectSubclass for GtkZiplineFolderImpl { + const NAME: &str = "TyrolienneZiplineFolder"; + type Type = GtkZiplineFolder; +} + +impl ObjectImpl for GtkZiplineFolderImpl { + fn properties() -> &'static [glib::ParamSpec] { + static PARAMS: LazyLock<[glib::ParamSpec; 2]> = LazyLock::new(|| { + [ + ParamSpecString::builder("id").build(), + ParamSpecString::builder("name").build(), + ] + }); + + PARAMS.as_ref() + } + + fn set_property(&self, _id: usize, value: &glib::Value, pspec: &glib::ParamSpec) { + let value = value.get().unwrap(); + + match pspec.name() { + "id" => self.id.replace(value), + "name" => self.name.replace(value), + _ => unreachable!(), + }; + } + + fn property(&self, _id: usize, pspec: &glib::ParamSpec) -> glib::Value { + unsafe { + match pspec.name() { + "id" => (*self.id.as_ptr()).to_value(), + "name" => (*self.name.as_ptr()).to_value(), + _ => unreachable!(), + } + } + } +} diff --git a/src/main.rs b/src/main.rs index 484ecc4..7bfeabe 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,11 +1,19 @@ +mod gobject; mod zipline; -use std::{path::Path, time::Duration}; +use std::{borrow::Cow, path::PathBuf, time::Duration}; use color_eyre::eyre::{OptionExt, Result, bail}; +use gobject::GtkZiplineFolder; +use relm4::{ + ComponentParts, RelmApp, SimpleComponent, + adw::{self, prelude::*}, + gtk::{self, gio, glib::clone}, +}; use tracing::Level; use tracing_subscriber::EnvFilter; -use urlencoding::encode; +use urlencoding::Encoded; +use zipline::ZiplineFolder; #[derive(Debug, Default, serde::Deserialize, serde::Serialize)] struct Config { @@ -27,6 +35,172 @@ impl Config { } } +#[derive(Debug)] +enum Message { + SetPath(PathBuf), + SetFolder(ZiplineFolder), + StartTheProcess, +} + +struct Widgets { + file_picker_row: adw::ActionRow, + send_button: gtk::Button, +} + +struct Tyrolienne { + config: Config, + video_path: Option, + folder: ZiplineFolder, +} + +impl Tyrolienne { + fn display_video_path(&self) -> Cow<'_, str> { + self.video_path + .as_ref() + .map(|p| p.to_string_lossy()) + .unwrap_or(Cow::Borrowed("None")) + } +} + +impl SimpleComponent for Tyrolienne { + type Input = Message; + type Output = (); + type Init = (Config, Vec); + type Root = adw::ApplicationWindow; + type Widgets = Widgets; + + fn init_root() -> Self::Root { + adw::ApplicationWindow::builder() + .title("Tyrolienne") + .default_width(500) + .build() + } + + fn init( + (config, folders): Self::Init, + root: Self::Root, + sender: relm4::ComponentSender, + ) -> relm4::ComponentParts { + let model = Tyrolienne { + config, + video_path: None, + folder: folders[0].clone(), + }; + + let file_picker_row = adw::ActionRow::builder() + .activatable(true) + .title("Video file") + .subtitle(model.display_video_path()) + .css_classes(["property"]) + .build(); + file_picker_row.connect_activated(clone!( + #[strong] + sender, + #[strong] + root, + move |_| { + let file_dialog = gtk::FileDialog::new(); + file_dialog.open( + Some(&root), + gio::Cancellable::NONE, + clone!( + #[strong] + sender, + move |file| { + if let Some(path) = file.ok().and_then(|f| f.path()) { + sender.input(Message::SetPath(path)); + } + } + ), + ); + } + )); + + let gtk_folders = folders + .iter() + .map(GtkZiplineFolder::from_folder) + .collect::>(); + let store = gio::ListStore::new::(); + store.extend_from_slice(>k_folders); + + let folder_row = adw::ComboRow::builder() + .title("Folder") + .model(&store) + .expression(gtk::PropertyExpression::new( + GtkZiplineFolder::r#type(), + None::<>k::Expression>, + "name", + )) + .build(); + folder_row.connect_activated(clone!( + #[strong] + sender, + move |r| { + if let Some(item) = r + .selected_item() + .and_then(|i| i.downcast::().ok()) + { + sender.input(Message::SetFolder(item.as_folder())); + } + } + )); + + let send_button = gtk::Button::builder() + .label("Send") + .sensitive(false) + .margin_start(32) + .margin_end(32) + .margin_bottom(32) + .build(); + send_button.connect_clicked(clone!( + #[strong] + sender, + move |_| sender.input(Message::StartTheProcess) + )); + + let list = gtk::ListBox::builder() + .margin_top(32) + .margin_end(32) + .margin_bottom(32) + .margin_start(32) + .selection_mode(gtk::SelectionMode::None) + .css_classes(["boxed-list"]) + .build(); + list.append(&file_picker_row); + list.append(&folder_row); + + let root_box = gtk::Box::new(gtk::Orientation::Vertical, 0); + root_box.append(&adw::HeaderBar::new()); + root_box.append(&list); + root_box.append(&send_button); + + root.set_content(Some(&root_box)); + + let widgets = Widgets { + file_picker_row, + send_button, + }; + + ComponentParts { model, widgets } + } + + fn update(&mut self, message: Self::Input, _sender: relm4::ComponentSender) { + match message { + Message::SetPath(path) => self.video_path = Some(path), + Message::SetFolder(folder) => self.folder = folder, + Message::StartTheProcess => the_process(self).unwrap(), + } + } + + fn update_view(&self, widgets: &mut Self::Widgets, _sender: relm4::ComponentSender) { + widgets + .file_picker_row + .set_subtitle(&self.display_video_path()); + + widgets.send_button.set_sensitive(self.video_path.is_some()); + } +} + fn main() -> Result<()> { tracing_subscriber::fmt() .with_env_filter(EnvFilter::from_default_env().add_directive(Level::INFO.into())) @@ -34,6 +208,17 @@ fn main() -> Result<()> { color_eyre::install()?; + // TODO: show dialog in case these error + let config = get_config()?; + let folders = zipline::get_folders(&config)?; + + let app = RelmApp::new("net.uku3lig.Tyrolienne"); + app.run::((config, folders)); + + Ok(()) +} + +fn get_config() -> Result { let Some(config_dir) = dirs::config_dir() else { bail!("could not get config dir!"); }; @@ -59,37 +244,33 @@ fn main() -> Result<()> { bail!("zipline url is empty! please set it at {config_file_path:?}"); } - let args: Vec = std::env::args().collect(); - let file = args.get(1).ok_or_eyre( - "please specify the path to the file you want to upload as the first command line argument", - )?; + Ok(config) +} - let folders = zipline::get_folders(&config)?; - let folder = &folders[0]; +fn the_process(app: &Tyrolienne) -> Result<()> { + tracing::info!("uploading to folder '{}'...", app.folder.name); - tracing::info!("uploading to folder '{}'...", folder.name); - - let res = zipline::upload_file(&config, folder, Path::new(file))?; + let res = zipline::upload_file(&app.config, &app.folder, app.video_path.as_ref().unwrap())?; let zp_file = &res.files[0]; tracing::info!("recalculating thumbnails..."); - zipline::recalc_thumbnails(&config)?; + zipline::recalc_thumbnails(&app.config)?; std::thread::sleep(Duration::from_secs(5)); tracing::info!("fetching thumbnail url..."); - let res = zipline::get_file_details(&config, &zp_file.id)?; + let res = zipline::get_file_details(&app.config, &zp_file.id)?; let thumbnail_url = res - .thumbnail_url(&config) + .thumbnail_url(&app.config) .ok_or_eyre("could not get thumbnail url")?; // TODO get w&h from video let autocomp_url = format!( "https://autocompressor.net/av1?v={}&i={}&w=1920&h=1080", - encode(&zp_file.url), - encode(&thumbnail_url) + Encoded(&zp_file.url), + Encoded(&thumbnail_url) ); tracing::info!("url: {autocomp_url}"); diff --git a/src/zipline.rs b/src/zipline.rs index 13a849c..1db512e 100644 --- a/src/zipline.rs +++ b/src/zipline.rs @@ -9,7 +9,7 @@ use crate::Config; static CLIENT: LazyLock = LazyLock::new(Client::new); -#[derive(Debug, serde::Deserialize)] +#[derive(Debug, Clone, serde::Deserialize)] pub struct ZiplineFolder { pub id: String, pub name: String,