diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..24e166f --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,855 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "ahash" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", + "zerocopy", +] + +[[package]] +name = "autocfg" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" + +[[package]] +name = "bitflags" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" + +[[package]] +name = "cairo-rs" +version = "0.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8a0ea147c94108c9613235388f540e4d14c327f7081c9e471fc8ee8a2533e69" +dependencies = [ + "bitflags", + "cairo-sys-rs", + "glib", + "libc", +] + +[[package]] +name = "cairo-sys-rs" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "428290f914b9b86089f60f5d8a9f6e440508e1bcff23b25afd51502b0a2da88f" +dependencies = [ + "glib-sys", + "libc", + "system-deps", +] + +[[package]] +name = "cfg-expr" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0890061c4d3223e7267f3bad2ec40b997d64faac1c2815a4a9d95018e2b9e9c" +dependencies = [ + "smallvec", + "target-lexicon", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "fallible-iterator" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649" + +[[package]] +name = "fallible-streaming-iterator" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" + +[[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 = "futures-channel" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" +dependencies = [ + "futures-core", +] + +[[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-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-core", + "futures-macro", + "futures-task", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "gdk-pixbuf" +version = "0.20.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4c29071a9e92337d8270a85cb0510cda4ac478be26d09ad027cc1d081911b19" +dependencies = [ + "gdk-pixbuf-sys", + "gio", + "glib", + "libc", +] + +[[package]] +name = "gdk-pixbuf-sys" +version = "0.20.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "687343b059b91df5f3fbd87b4307038fa9e647fcc0461d0d3f93e94fee20bf3d" +dependencies = [ + "gio-sys", + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + +[[package]] +name = "gdk4" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c121aeeb0cf7545877ae615dac6bfd088b739d8abee4d55e7143b06927d16a31" +dependencies = [ + "cairo-rs", + "gdk-pixbuf", + "gdk4-sys", + "gio", + "glib", + "libc", + "pango", +] + +[[package]] +name = "gdk4-sys" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d3c03d1ea9d5199f14f060890fde68a3b5ec5699144773d1fa6abf337bfbc9c" +dependencies = [ + "cairo-sys-rs", + "gdk-pixbuf-sys", + "gio-sys", + "glib-sys", + "gobject-sys", + "libc", + "pango-sys", + "pkg-config", + "system-deps", +] + +[[package]] +name = "gio" +version = "0.20.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8d999e8fb09583e96080867e364bc1e701284ad206c76a5af480d63833ad43c" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-util", + "gio-sys", + "glib", + "libc", + "pin-project-lite", + "smallvec", +] + +[[package]] +name = "gio-sys" +version = "0.20.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f7efc368de04755344f0084104835b6bb71df2c1d41e37d863947392a894779" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps", + "windows-sys", +] + +[[package]] +name = "glib" +version = "0.20.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adcf1ec6d3650bf9fdbc6cee242d4fcebc6f6bfd9bea5b929b6a8b7344eb85ff" +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.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6bf88f70cd5720a6197639dcabcb378dd528d0cb68cb1f45e3b358bcb841cd7" +dependencies = [ + "heck", + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "glib-sys" +version = "0.20.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f9eca5d88cfa6a453b00d203287c34a2b7cac3a7831779aa2bb0b3c7233752b" +dependencies = [ + "libc", + "system-deps", +] + +[[package]] +name = "gobject-sys" +version = "0.20.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4c674d2ff8478cf0ec29d2be730ed779fef54415a2fb4b565c52def62696462" +dependencies = [ + "glib-sys", + "libc", + "system-deps", +] + +[[package]] +name = "graphene-rs" +version = "0.20.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f53144c7fe78292705ff23935f1477d511366fb2f73c43d63b37be89076d2fe" +dependencies = [ + "glib", + "graphene-sys", + "libc", +] + +[[package]] +name = "graphene-sys" +version = "0.20.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e741797dc5081e59877a4d72c442c72d61efdd99161a0b1c1b29b6b988934b99" +dependencies = [ + "glib-sys", + "libc", + "pkg-config", + "system-deps", +] + +[[package]] +name = "gsk4" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa21a2f7c51ee1c6cc1242c2faf3aae2b7566138f182696759987bde8219e922" +dependencies = [ + "cairo-rs", + "gdk4", + "glib", + "graphene-rs", + "gsk4-sys", + "libc", + "pango", +] + +[[package]] +name = "gsk4-sys" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f9fb607554f9f4e8829eb7ea301b0fde051e1dbfd5d16b143a8a9c2fac6c01b" +dependencies = [ + "cairo-sys-rs", + "gdk4-sys", + "glib-sys", + "gobject-sys", + "graphene-sys", + "libc", + "pango-sys", + "system-deps", +] + +[[package]] +name = "gtk4" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31e2d105ce672f5cdcb5af2602e91c2901e91c72da15ab76f613ad57ecf04c6d" +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.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9e7b362c8fccd2712297903717d65d30defdab2b509bc9d209cbe5ffb9fabaf" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "gtk4-sys" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbe4325908b1c1642dbb48e9f49c07a73185babf43e8b2065b0f881a589f55b8" +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 = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +dependencies = [ + "ahash", +] + +[[package]] +name = "hashbrown" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb" + +[[package]] +name = "hashlink" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ba4ff7128dee98c7dc9794b6a411377e1404dba1c97deb8d1a55297bd25d8af" +dependencies = [ + "hashbrown 0.14.5", +] + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "indexmap" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" +dependencies = [ + "equivalent", + "hashbrown 0.15.0", +] + +[[package]] +name = "libadwaita" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ff9c222b5c783729de45185f07b2fec2d43a7f9c63961e777d3667e20443878" +dependencies = [ + "gdk4", + "gio", + "glib", + "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", + "glib-sys", + "gobject-sys", + "gtk4-sys", + "libc", + "pango-sys", + "system-deps", +] + +[[package]] +name = "libc" +version = "0.2.159" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "561d97a539a36e26a9a5fad1ea11a3039a67714694aaa379433e580854bc3dc5" + +[[package]] +name = "libsqlite3-sys" +version = "0.30.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e99fb7a497b1e3339bc746195567ed8d3e24945ecd636e3619d20b9de9e9149" +dependencies = [ + "pkg-config", + "vcpkg", +] + +[[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 = "once_cell" +version = "1.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" + +[[package]] +name = "pango" +version = "0.20.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa26aa54b11094d72141a754901cd71d9356432bb8147f9cace8d9c7ba95f356" +dependencies = [ + "gio", + "glib", + "libc", + "pango-sys", +] + +[[package]] +name = "pango-sys" +version = "0.20.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84fd65917bf12f06544ae2bbc200abf9fc0a513a5a88a0fa81013893aef2b838" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + +[[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.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" + +[[package]] +name = "proc-macro-crate" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecf48c7ca261d60b74ab1a7b20da18bede46776b2e55535cb958eb595c5fa7b" +dependencies = [ + "toml_edit", +] + +[[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.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rusqlite" +version = "0.32.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7753b721174eb8ff87a9a0e799e2d7bc3749323e773db92e0984debb00019d6e" +dependencies = [ + "bitflags", + "fallible-iterator", + "fallible-streaming-iterator", + "hashlink", + "libsqlite3-sys", + "smallvec", +] + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + +[[package]] +name = "semver" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" + +[[package]] +name = "serde" +version = "1.0.210" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.210" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_spanned" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" +dependencies = [ + "serde", +] + +[[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 = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + +[[package]] +name = "syn" +version = "2.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[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 = "toml" +version = "0.8.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.22.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", +] + +[[package]] +name = "unicode-ident" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "version-compare" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "852e951cb7832cb45cb1169900d19760cfa39b82bc0ea9c0e5a14ae88411c98b" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[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.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[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.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[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.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[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.6.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b" +dependencies = [ + "memchr", +] + +[[package]] +name = "zerocopy" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zoodex" +version = "1.0.0" +dependencies = [ + "fallible-iterator", + "gtk4", + "libadwaita", + "rusqlite", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..514b137 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "zoodex" +version = "1.0.0" +authors = [ "Reinout Meliesie " ] +edition = "2021" +rust-version = "1.81.0" +license = "GPL-3.0-or-later" + +[dependencies] +fallible-iterator = "0.3.0" +gtk4 = { version = "0.9.2" , features = [ "v4_16" ] } +libadwaita = { version = "0.7.0", features = [ "v1_6" ] } +rusqlite = "0.32.1" diff --git a/src/collection.rs b/src/collection.rs new file mode 100644 index 0000000..7ac7ce9 --- /dev/null +++ b/src/collection.rs @@ -0,0 +1,39 @@ +use std :: path :: * ; + + + +pub struct Collection { + pub films : Vec , + pub series : Vec , +} + +pub struct Film { + pub uuid : String , + pub name : String , + pub original_name : Option , + pub release_date : String , // TODO: Switch to chrono types, I think rusqlite has crate option for it + pub runtime_minutes : u32 , + pub poster_file_path : Option , + pub video_file_path : Option , +} + +pub struct Series { + pub uuid : String , + pub name : String , + pub original_name : Option , + pub poster_file_path : Option , +} + +pub struct Season <'l> { + pub uuid : String , + pub name : Option , + pub series : & 'l Series , +} + +pub struct Episode <'l> { + pub uuid : String , + pub name : Option , + pub release_date : String , + pub season : & 'l Season <'l> , + pub video_file_path : Option , +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..7aa8678 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,36 @@ +mod collection ; +mod persistence ; +mod ui ; +mod utility ; + +use { + gtk4 :: { gio :: spawn_blocking , glib :: * } , + libadwaita :: { * , prelude :: * } , +} ; + +use crate :: { persistence :: * , ui :: * } ; + + + +fn main () -> ExitCode { + let application = Application :: builder () + . application_id ("com.kernelmaft.zoodex") + . build () ; + application . connect_activate (on_activate) ; + application . run () +} + +fn on_activate ( app : & Application ) { + let mut ui = UI :: new (app) ; + + let collection_handle = spawn_blocking ( || + read_collection_file () . unwrap () + ) ; + + ui . show_window () ; + + spawn_future_local ( async move { + let collection = collection_handle . await . unwrap () ; + ui . render_collection (collection) ; + } ) ; +} diff --git a/src/persistence.rs b/src/persistence.rs new file mode 100644 index 0000000..4a989b3 --- /dev/null +++ b/src/persistence.rs @@ -0,0 +1,51 @@ +use { fallible_iterator :: * , rusqlite :: * } ; + +use crate :: collection :: * ; + + + +pub fn read_collection_file () -> Result { + let sqlite_connection = Connection :: open ("zoodex.sqlite") ? ; + + let mut films_query = sqlite_connection + . prepare ("select * from films order by release_date desc") ? ; + let films = films_query . query (()) ? . map ( |row| { + let uuid = row . get (0) ? ; + let name = row . get (1) ? ; + let original_name = row . get (2) ? ; + let release_date = row . get (3) ? ; + let runtime_minutes = row . get (4) ? ; + let poster_file_path : Option = row . get (5) ? ; + let video_file_path : Option = row . get (6) ? ; + + Ok ( Film { + uuid , + name , + original_name , + release_date , + runtime_minutes , + poster_file_path : poster_file_path . map ( String :: into ) , + video_file_path : video_file_path . map ( String :: into ) , + } ) + } ) ; + + let mut series_query = sqlite_connection . prepare ("select * from series") ? ; + let series = series_query . query (()) ? . map ( |row| { + let uuid = row . get (0) ? ; + let name = row . get (1) ? ; + let original_name = row . get (2) ? ; + let poster_file_path : Option = row . get (3) ? ; + + Ok ( Series { + uuid , + name , + original_name , + poster_file_path : poster_file_path . map ( String :: into ) , + } ) + } ) ; + + Ok ( Collection { + films : films . collect () ? , + series : series . collect () ? , + } ) +} diff --git a/src/ui/collection_menu.rs b/src/ui/collection_menu.rs new file mode 100644 index 0000000..38675e4 --- /dev/null +++ b/src/ui/collection_menu.rs @@ -0,0 +1,115 @@ +use { + gtk4 :: { + Image , Label , ListBox , ListBoxRow , Popover , + Align :: * , + Orientation :: * , + prelude :: * , + } , + libadwaita :: * , +} ; + +use crate :: ui :: { internal :: * , utility :: * } ; + + + +pub fn create_film_collection_menu ( on_sort : F ) -> gtk4 :: Box +where F : Fn (FilmsSortedBy) + 'static { + let container = gtk4 :: Box :: builder () + . orientation (Horizontal) + . halign (Center) + . spacing (20) + . css_classes ( ["toolbar"] ) + . build () ; + + container . append ( & create_sort_button ( & create_films_sort_menu (on_sort) ) ) ; + container . append ( & SplitButton :: builder () . label ("Filter") . build () ) ; + + container +} + +pub fn create_series_collection_menu ( on_sort : fn (SeriesSortedBy) ) -> gtk4 :: Box { + let container = gtk4 :: Box :: builder () + . orientation (Horizontal) + . halign (Center) + . spacing (20) + . css_classes ( ["toolbar"] ) + . build () ; + + container . append ( & create_sort_button ( & create_series_sort_menu (on_sort) ) ) ; + container . append ( & SplitButton :: builder () . label ("Filter") . build () ) ; + + container +} + +fn create_sort_button ( sort_menu : & Popover ) -> SplitButton { + SplitButton :: builder () + . child ( & create_label ("Sort") ) + . popover (sort_menu) + . build () +} + +fn create_films_sort_menu ( on_sort : F ) -> Popover +where F : Fn (FilmsSortedBy) + 'static { + let container = ListBox :: new () ; + + container . append ( & create_sort_menu_entry ( "Name" , false , true ) ) ; + container . append ( & create_sort_menu_entry ( "Release date" , false , false ) ) ; + container . append ( & create_sort_menu_entry ( "Runtime" , false , false ) ) ; + + container . select_row ( container . row_at_index (0) . as_ref () ) ; + + container . connect_row_activated ( move | _ , row | on_sort ( match row . index () { + 0 => FilmsSortedBy :: Name , + 1 => FilmsSortedBy :: ReleaseDate , + 2 => FilmsSortedBy :: Runtime , + _ => panic ! () , + } ) ) ; + + Popover :: builder () + . child ( & container ) + . css_classes ( ["menu"] ) + . build () +} + +fn create_series_sort_menu ( on_sort : fn (SeriesSortedBy) ) -> Popover { + let container = ListBox :: new () ; + + container . append ( & create_sort_menu_entry ( "Name" , false , true ) ) ; + container . append ( & create_sort_menu_entry ( "First release date" , false , false ) ) ; + + container . select_row ( container . row_at_index (0) . as_ref () ) ; + + container . connect_row_activated ( move | _ , row | on_sort ( match row . index () { + 0 => SeriesSortedBy :: Name , + 1 => SeriesSortedBy :: FirstReleaseDate , + _ => panic ! () , + } ) ) ; + + Popover :: builder () + . child ( & container ) + . css_classes ( ["menu"] ) + . build () +} + +fn create_sort_menu_entry ( label : & str , reverse : bool , selected : bool ) -> ListBoxRow { + let icon = match reverse { + false => Image :: from_icon_name ("view-sort-ascending-symbolic") , + true => Image :: from_icon_name ("view-sort-descending-symbolic") , + } ; + if ! selected { icon . set_opacity (0.0) } + + let container = create_horizontal_box ! ( + & Label :: builder () + . label (label) + . hexpand (true) + . halign (Start) + . build () , + & icon , + ) ; + container . set_spacing (20) ; + + // TODO : Highlight `:selected` row using CSS, parent with `.menu` prevents default behaviour + + ListBoxRow :: builder () . child ( & container ) . build () +} + diff --git a/src/ui/internal.rs b/src/ui/internal.rs new file mode 100644 index 0000000..63b9d3c --- /dev/null +++ b/src/ui/internal.rs @@ -0,0 +1,269 @@ +use { + gtk4 :: { + FlowBox, + HeaderBar, + Image, + Justification, + Label, + ScrolledWindow, + SelectionMode, + Widget, + Align :: * , + Orientation :: * , + gdk :: Texture , + prelude :: * , + } , + libadwaita :: { * , ViewSwitcherPolicy :: * } , + std :: { cell :: * , path :: * } , +} ; + +use crate :: { + collection :: * , + ui :: { collection_menu :: * , utility :: * } , + utility :: * , +} ; + + + +pub fn create_window ( + application : & Application , + header_bar : & HeaderBar , + collection_view_stack : & ViewStack , +) -> ApplicationWindow { + let content = gtk4 :: Box :: builder () . orientation (Vertical) . build () ; + content . append (header_bar) ; + content . append (collection_view_stack) ; + + ApplicationWindow :: builder () + . application (application) + . content ( & content ) + . title ("Zoƶdex") + . build () +} + +pub fn create_header_bar ( collection_view_stack : & ViewStack ) -> HeaderBar { + HeaderBar :: builder () + . title_widget ( + & create_collection_view_switcher ( collection_view_stack ) , + ) + . build () +} + +pub fn create_collection_view_switcher ( stack : & ViewStack ) -> ViewSwitcher { + ViewSwitcher :: builder () + . policy (Wide) + . stack (stack) + . build () +} + +pub struct FilmsFlowBox { widget : FlowBox } +pub struct SeriesFlowBox { widget : FlowBox } + +impl FilmsFlowBox { + pub fn new ( films : & [Film] ) -> Self { + let widget = create_collection_flow_box () ; + + for film in films { + widget . append ( & create_film_item (film) ) ; + } + + Self { widget } + } + + pub fn set_films ( & self , films : & [Film] ) { + self . widget . remove_all () ; + for film in films { + self . widget . append ( & create_film_item (film) ) ; + } + } + + pub fn get_widget ( & self ) -> & FlowBox { & self . widget } +} +impl SeriesFlowBox { + pub fn new ( series : & [Series] ) -> Self { + let widget = create_collection_flow_box () ; + + for series in series { + widget . append ( & create_series_item (series) ) ; + } + + Self { widget } + } + + pub fn set_series ( & self , series : & [Series] ) { + self . widget . remove_all () ; + for series in series { + self . widget . append ( & create_series_item (series) ) ; + } + } + + pub fn get_widget ( & self ) -> & FlowBox { & self . widget } +} + +fn create_collection_flow_box () -> FlowBox { + FlowBox :: builder () + . orientation (Horizontal) + . homogeneous (true) + . selection_mode ( SelectionMode :: None ) + . build () +} + +pub enum FilmsSortedBy { Name , ReleaseDate , Runtime } +pub enum SeriesSortedBy { Name , FirstReleaseDate } + +pub struct FilmsContainer { + films : Vec , + flow_box : FilmsFlowBox , + widget : gtk4 :: Box , + sorted_by : & 'static Cell , +} +pub struct SeriesContainer { + series : Vec , + flow_box : SeriesFlowBox , + widget : gtk4 :: Box , +} + +impl FilmsContainer { + pub fn new ( films : Vec ) -> Self { + let flow_box = FilmsFlowBox :: new ( films . as_slice () ) ; + let sorted_by = leak ( Cell :: new ( FilmsSortedBy :: Name ) ) ; + let widget = create_vertical_box ! ( + & create_film_collection_menu ( |new_sorted_by| { + sorted_by . replace (new_sorted_by) ; + } ) , + & create_collection_scrolled_window ( flow_box . get_widget () ) , + ) ; + + Self { films , flow_box , widget , sorted_by } + } + + pub fn set_films ( & mut self , films : Vec ) { + self . films = films ; + self . flow_box . set_films ( self . films . as_slice () ) ; + } + + pub fn get_widget ( & self ) -> & gtk4 :: Box { & self . widget } +} +impl SeriesContainer { + pub fn new ( series : Vec ) -> Self { + let flow_box = SeriesFlowBox :: new ( series . as_slice () ) ; + let widget = create_vertical_box ! ( + & create_series_collection_menu ( |sorted_by| { + // TODO + } ) , + & create_collection_scrolled_window ( flow_box . get_widget () ) , + ) ; + + Self { series, flow_box , widget } + } + + pub fn set_series ( & mut self , series : Vec ) { + self . series = series ; + self . flow_box . set_series ( self . series . as_slice () ) ; + } + + pub fn get_widget ( & self ) -> & gtk4 :: Box { & self . widget } +} + +fn create_collection_scrolled_window ( child : & impl IsA ) -> ScrolledWindow { + ScrolledWindow :: builder () + . child ( & create_vertical_filler_container (child) ) + . propagate_natural_height (true) + . build () +} + +pub fn create_film_item ( film : & Film ) -> gtk4 :: Box { + create_collection_item ( + film . name . as_str () , + film . original_name . as_deref () , + film . poster_file_path . as_deref () , + & create_film_details (film) , + ) +} +pub fn create_series_item ( series : & Series ) -> gtk4 :: Box { + create_collection_item ( + series . name . as_str () , + series . original_name . as_deref () , + series . poster_file_path . as_deref () , + & create_series_details (series) , + ) +} + +fn create_film_details ( film : & Film ) -> gtk4 :: Box { + let container = gtk4 :: Box :: builder () + . orientation (Horizontal) + . halign (Center) + . spacing (20) + . build () ; + + container . append ( + & Label :: builder () . label ( & film . release_date ) . build () + ) ; + container . append ( + & Label :: builder () . label ( format ! ( "{}m" , film . runtime_minutes ) ) . build () + ) ; + + container +} +fn create_series_details ( series : & Series ) -> gtk4 :: Box { + let container = gtk4 :: Box :: builder () + . orientation (Horizontal) + . halign (Center) + . spacing (20) + . build () ; + + // TODO + + container +} + +fn create_collection_item ( + name : & str , + original_name : Option < & str > , + poster_file_path : Option < & Path > , + details_widget : & gtk4 :: Box , +) -> gtk4 :: Box { + let container = gtk4 :: Box :: builder () + . orientation (Vertical) + . margin_top (20) + . margin_bottom (20) + . build () ; + + if let Some (poster_file_path) = poster_file_path { + if let Ok (poster_texture) = Texture :: from_filename (poster_file_path) { + container . append ( + & Image :: builder () + . paintable ( & poster_texture ) + . width_request (300) + . height_request (300) + . margin_bottom (10) + . build () + ) ; + } + } + + container . append ( + & Label :: builder () + . label ( format ! ( "{}" , name ) ) + . use_markup (true) + . justify ( Justification :: Center ) + . wrap (true) + . max_width_chars (1) // Not the actual limit, used instead to wrap more aggressively + . build () + ) ; + + if let Some (original_name) = original_name { + container . append ( + & Label :: builder () + . label (original_name) + . justify ( Justification :: Center ) + . wrap (true) + . max_width_chars (1) // Not the actual limit, used instead to wrap more aggressively + . build () + ) ; + } + + container . append (details_widget) ; + + container +} diff --git a/src/ui/mod.rs b/src/ui/mod.rs new file mode 100644 index 0000000..4190518 --- /dev/null +++ b/src/ui/mod.rs @@ -0,0 +1,37 @@ +mod collection_menu ; +mod internal ; +mod utility ; + +use { gtk4 :: prelude :: * , libadwaita :: * } ; + +use crate :: { collection :: * , ui :: { internal :: * , utility :: * } } ; + + + +pub struct UI { + window : ApplicationWindow , + films_container : FilmsContainer , + series_container : SeriesContainer , +} + +impl UI { + pub fn new ( application : & Application ) -> UI { + let films_container = FilmsContainer :: new ( vec ! () ) ; + let series_container = SeriesContainer :: new ( vec ! () ) ; + let collection_view_stack = create_view_stack ! ( + "Films" , "camera-video-symbolic" , films_container . get_widget () , + "Series" , "video-display-symbolic" , series_container . get_widget () , + ) ; + let header_bar = create_header_bar ( & collection_view_stack ) ; + let window = create_window ( application , & header_bar , & collection_view_stack ) ; + + UI { window , films_container , series_container } + } + + pub fn show_window ( & self ) { self . window . set_visible (true) } + + pub fn render_collection ( & mut self , collection : Collection ) { + self . films_container . set_films ( collection . films ) ; + self . series_container . set_series ( collection . series ) ; + } +} diff --git a/src/ui/utility.rs b/src/ui/utility.rs new file mode 100644 index 0000000..0ce11a5 --- /dev/null +++ b/src/ui/utility.rs @@ -0,0 +1,45 @@ +use { gtk4 :: { Label , Widget , prelude :: * } , libadwaita :: * } ; + + + +pub fn create_label ( label : & str ) -> Label { + Label :: builder () . label (label) . build () +} + +macro_rules ! create_horizontal_box { ( $ ( $ child : expr , ) * ) => { { + let container = gtk4 :: Box :: builder () + . orientation ( gtk4 :: Orientation :: Horizontal ) + . build () ; + $ ( container . append ( $ child ) ; ) * + container +} } } + +macro_rules ! create_vertical_box { ( $ ( $ child : expr , ) * ) => { { + let container = gtk4 :: Box :: builder () + . orientation ( gtk4 :: Orientation :: Vertical) + . build () ; + $ ( container . append ( $ child ) ; ) * + container +} } } + +macro_rules ! create_view_stack { ( + $ ( $ title : expr , $ icon : expr , $ widget : expr , ) * +) => { { + let container = ViewStack :: new () ; + $ ( container . add_titled_with_icon ( $ widget , None , $ title , $ icon ) ; ) * + container +} } } + +pub fn create_vertical_filler_container ( child : & impl IsA ) -> gtk4 :: Box { + create_vertical_box ! ( + child , + & Bin :: builder () + . css_name ("filler") + . vexpand (true) + . build () , + ) +} + + + +pub (crate) use { create_horizontal_box , create_vertical_box , create_view_stack } ; diff --git a/src/utility.rs b/src/utility.rs new file mode 100644 index 0000000..8084cb1 --- /dev/null +++ b/src/utility.rs @@ -0,0 +1,3 @@ +pub fn leak < 'l , T > ( inner : T ) -> & 'l mut T { + Box :: leak ( Box :: new (inner) ) +}