diff --git a/Cargo.lock b/Cargo.lock index d672e35..d0ad784 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,4718 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "ab_glyph" +version = "0.2.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01c0457472c38ea5bd1c3b5ada5e368271cb550be7a4ca4a0b4634e9913f6cc2" +dependencies = [ + "ab_glyph_rasterizer", + "owned_ttf_parser", +] + +[[package]] +name = "ab_glyph_rasterizer" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "366ffbaa4442f4684d91e2cd7c5ea7c4ed8add41959a31447066e279e432b618" + +[[package]] +name = "accesskit" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74a4b14f3d99c1255dcba8f45621ab1a2e7540a0009652d33989005a4d0bfc6b" + +[[package]] +name = "accesskit_consumer" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c17cca53c09fbd7288667b22a201274b9becaa27f0b91bf52a526db95de45e6" +dependencies = [ + "accesskit", +] + +[[package]] +name = "accesskit_macos" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd3b6ae1eabbfbced10e840fd3fce8a93ae84f174b3e4ba892ab7bcb42e477a7" +dependencies = [ + "accesskit", + "accesskit_consumer", + "objc2 0.3.0-beta.3.patch-leaks.3", + "once_cell", +] + +[[package]] +name = "accesskit_unix" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09f46c18d99ba61ad7123dd13eeb0c104436ab6af1df6a1cd8c11054ed394a08" +dependencies = [ + "accesskit", + "accesskit_consumer", + "async-channel", + "async-once-cell", + "atspi", + "futures-lite 1.13.0", + "once_cell", + "serde", + "zbus 3.15.2", +] + +[[package]] +name = "accesskit_windows" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afcae27ec0974fc7c3b0b318783be89fd1b2e66dd702179fe600166a38ff4a0b" +dependencies = [ + "accesskit", + "accesskit_consumer", + "once_cell", + "paste", + "static_assertions", + "windows 0.48.0", +] + +[[package]] +name = "accesskit_winit" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5284218aca17d9e150164428a0ebc7b955f70e3a9a78b4c20894513aabf98a67" +dependencies = [ + "accesskit", + "accesskit_macos", + "accesskit_unix", + "accesskit_windows", + "winit", +] + +[[package]] +name = "adler2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" + +[[package]] +name = "ahash" +version = "0.8.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" +dependencies = [ + "cfg-if", + "getrandom 0.3.4", + "once_cell", + "version_check", + "zerocopy", +] + +[[package]] +name = "aho-corasick" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +dependencies = [ + "memchr", +] + +[[package]] +name = "allocator-api2" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" + +[[package]] +name = "android-activity" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee91c0c2905bae44f84bfa4e044536541df26b7703fd0888deeb9060fcc44289" +dependencies = [ + "android-properties", + "bitflags 2.11.0", + "cc", + "cesu8", + "jni", + "jni-sys", + "libc", + "log", + "ndk", + "ndk-context", + "ndk-sys", + "num_enum", + "thiserror 1.0.69", +] + +[[package]] +name = "android-properties" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc7eb209b1518d6bb87b283c20095f5228ecda460da70b44f0802523dea6da04" + +[[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 = "anyhow" +version = "1.0.102" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" + +[[package]] +name = "arboard" +version = "3.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0348a1c054491f4bfe6ab86a7b6ab1e44e45d899005de92f58b3df180b36ddaf" +dependencies = [ + "clipboard-win", + "log", + "objc2 0.6.4", + "objc2-app-kit", + "objc2-foundation", + "parking_lot", + "percent-encoding", + "windows-sys 0.60.2", + "x11rb", +] + +[[package]] +name = "arrayref" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" + +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" + +[[package]] +name = "as-raw-xcb-connection" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "175571dd1d178ced59193a6fc02dde1b972eb0bc56c892cde9beeceac5bf0f6b" + +[[package]] +name = "ash" +version = "0.37.3+1.3.251" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39e9c3835d686b0a6084ab4234fcd1b07dbf6e4767dce60874b12356a25ecd4a" +dependencies = [ + "libloading 0.7.4", +] + +[[package]] +name = "ashpd" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd884d7c72877a94102c3715f3b1cd09ff4fac28221add3e57cfbe25c236d093" +dependencies = [ + "async-fs 2.2.0", + "async-net", + "enumflags2", + "futures-channel", + "futures-util", + "rand", + "serde", + "serde_repr", + "url", + "zbus 4.4.0", +] + +[[package]] +name = "async-broadcast" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c48ccdbf6ca6b121e0f586cbc0e73ae440e56c67c30fa0873b4e110d9c26d2b" +dependencies = [ + "event-listener 2.5.3", + "futures-core", +] + +[[package]] +name = "async-broadcast" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "435a87a52755b8f27fcf321ac4f04b2802e337c8c4872923137471ec39c37532" +dependencies = [ + "event-listener 5.4.1", + "event-listener-strategy", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-channel" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "924ed96dd52d1b75e9c1a3e6275715fd320f5f9439fb5a4a11fa51f4221158d2" +dependencies = [ + "concurrent-queue", + "event-listener-strategy", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-executor" +version = "1.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c96bf972d85afc50bf5ab8fe2d54d1586b4e0b46c97c50a0c9e71e2f7bcd812a" +dependencies = [ + "async-task", + "concurrent-queue", + "fastrand 2.3.0", + "futures-lite 2.6.1", + "pin-project-lite", + "slab", +] + +[[package]] +name = "async-fs" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "279cf904654eeebfa37ac9bb1598880884924aab82e290aa65c9e77a0e142e06" +dependencies = [ + "async-lock 2.8.0", + "autocfg", + "blocking", + "futures-lite 1.13.0", +] + +[[package]] +name = "async-fs" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8034a681df4aed8b8edbd7fbe472401ecf009251c8b40556b304567052e294c5" +dependencies = [ + "async-lock 3.4.2", + "blocking", + "futures-lite 2.6.1", +] + +[[package]] +name = "async-io" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fc5b45d93ef0529756f812ca52e44c221b35341892d3dcc34132ac02f3dd2af" +dependencies = [ + "async-lock 2.8.0", + "autocfg", + "cfg-if", + "concurrent-queue", + "futures-lite 1.13.0", + "log", + "parking", + "polling 2.8.0", + "rustix 0.37.28", + "slab", + "socket2", + "waker-fn", +] + +[[package]] +name = "async-io" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "456b8a8feb6f42d237746d4b3e9a178494627745c3c56c6ea55d92ba50d026fc" +dependencies = [ + "autocfg", + "cfg-if", + "concurrent-queue", + "futures-io", + "futures-lite 2.6.1", + "parking", + "polling 3.11.0", + "rustix 1.1.4", + "slab", + "windows-sys 0.61.2", +] + +[[package]] +name = "async-lock" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "287272293e9d8c41773cec55e365490fe034813a2f172f502d6ddcf75b2f582b" +dependencies = [ + "event-listener 2.5.3", +] + +[[package]] +name = "async-lock" +version = "3.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290f7f2596bd5b78a9fec8088ccd89180d7f9f55b94b0576823bbbdc72ee8311" +dependencies = [ + "event-listener 5.4.1", + "event-listener-strategy", + "pin-project-lite", +] + +[[package]] +name = "async-net" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b948000fad4873c1c9339d60f2623323a0cfd3816e5181033c6a5cb68b2accf7" +dependencies = [ + "async-io 2.6.0", + "blocking", + "futures-lite 2.6.1", +] + +[[package]] +name = "async-once-cell" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4288f83726785267c6f2ef073a3d83dc3f9b81464e9f99898240cced85fce35a" + +[[package]] +name = "async-process" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea6438ba0a08d81529c69b36700fa2f95837bfe3e776ab39cde9c14d9149da88" +dependencies = [ + "async-io 1.13.0", + "async-lock 2.8.0", + "async-signal", + "blocking", + "cfg-if", + "event-listener 3.1.0", + "futures-lite 1.13.0", + "rustix 0.38.44", + "windows-sys 0.48.0", +] + +[[package]] +name = "async-process" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc50921ec0055cdd8a16de48773bfeec5c972598674347252c0399676be7da75" +dependencies = [ + "async-channel", + "async-io 2.6.0", + "async-lock 3.4.2", + "async-signal", + "async-task", + "blocking", + "cfg-if", + "event-listener 5.4.1", + "futures-lite 2.6.1", + "rustix 1.1.4", +] + +[[package]] +name = "async-recursion" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "async-signal" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43c070bbf59cd3570b6b2dd54cd772527c7c3620fce8be898406dd3ed6adc64c" +dependencies = [ + "async-io 2.6.0", + "async-lock 3.4.2", + "atomic-waker", + "cfg-if", + "futures-core", + "futures-io", + "rustix 1.1.4", + "signal-hook-registry", + "slab", + "windows-sys 0.61.2", +] + +[[package]] +name = "async-task" +version = "4.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" + +[[package]] +name = "async-trait" +version = "0.1.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + +[[package]] +name = "atspi" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6059f350ab6f593ea00727b334265c4dfc7fd442ee32d264794bd9bdc68e87ca" +dependencies = [ + "atspi-common", + "atspi-connection", + "atspi-proxies", +] + +[[package]] +name = "atspi-common" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92af95f966d2431f962bc632c2e68eda7777330158bf640c4af4249349b2cdf5" +dependencies = [ + "enumflags2", + "serde", + "static_assertions", + "zbus 3.15.2", + "zbus_names 2.6.1", + "zvariant 3.15.2", +] + +[[package]] +name = "atspi-connection" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0c65e7d70f86d4c0e3b2d585d9bf3f979f0b19d635a336725a88d279f76b939" +dependencies = [ + "atspi-common", + "atspi-proxies", + "futures-lite 1.13.0", + "zbus 3.15.2", +] + +[[package]] +name = "atspi-proxies" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6495661273703e7a229356dcbe8c8f38223d697aacfaf0e13590a9ac9977bb52" +dependencies = [ + "atspi-common", + "serde", + "zbus 3.15.2", +] + +[[package]] +name = "autocfg" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" + +[[package]] +name = "bit-set" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" +dependencies = [ + "bit-vec", +] + +[[package]] +name = "bit-vec" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" + +[[package]] +name = "block" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "block-sys" +version = "0.1.0-beta.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fa55741ee90902547802152aaf3f8e5248aab7e21468089560d4c8840561146" +dependencies = [ + "objc-sys 0.2.0-beta.2", +] + +[[package]] +name = "block-sys" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae85a0696e7ea3b835a453750bf002770776609115e6d25c6d2ff28a8200f7e7" +dependencies = [ + "objc-sys 0.3.5", +] + +[[package]] +name = "block2" +version = "0.2.0-alpha.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8dd9e63c1744f755c2f60332b88de39d341e5e86239014ad839bd71c106dec42" +dependencies = [ + "block-sys 0.1.0-beta.1", + "objc2-encode 2.0.0-pre.2", +] + +[[package]] +name = "block2" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15b55663a85f33501257357e6421bb33e769d5c9ffb5ba0921c975a123e35e68" +dependencies = [ + "block-sys 0.2.1", + "objc2 0.4.1", +] + +[[package]] +name = "blocking" +version = "1.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e83f8d02be6967315521be875afa792a316e28d57b5a2d401897e2a7921b7f21" +dependencies = [ + "async-channel", + "async-task", + "futures-io", + "futures-lite 2.6.1", + "piper", +] + +[[package]] +name = "bumpalo" +version = "3.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" + +[[package]] +name = "bytemuck" +version = "1.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8efb64bd706a16a1bdde310ae86b351e4d21550d98d056f22f8a7f7a2183fec" +dependencies = [ + "bytemuck_derive", +] + +[[package]] +name = "bytemuck_derive" +version = "1.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9abbd1bc6865053c427f7198e6af43bfdedc55ab791faed4fbd361d789575ff" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" + +[[package]] +name = "calloop" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fba7adb4dd5aa98e5553510223000e7148f621165ec5f9acd7113f6ca4995298" +dependencies = [ + "bitflags 2.11.0", + "log", + "polling 3.11.0", + "rustix 0.38.44", + "slab", + "thiserror 1.0.69", +] + +[[package]] +name = "calloop" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4dbf9978365bac10f54d1d4b04f7ce4427e51f71d61f2fe15e3fed5166474df7" +dependencies = [ + "bitflags 2.11.0", + "polling 3.11.0", + "rustix 1.1.4", + "slab", + "tracing", +] + +[[package]] +name = "calloop-wayland-source" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f0ea9b9476c7fad82841a8dbb380e2eae480c21910feba80725b46931ed8f02" +dependencies = [ + "calloop 0.12.4", + "rustix 0.38.44", + "wayland-backend", + "wayland-client", +] + +[[package]] +name = "calloop-wayland-source" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "138efcf0940a02ebf0cc8d1eff41a1682a46b431630f4c52450d6265876021fa" +dependencies = [ + "calloop 0.14.4", + "rustix 1.1.4", + "wayland-backend", + "wayland-client", +] + +[[package]] +name = "cc" +version = "1.2.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aebf35691d1bfb0ac386a69bac2fde4dd276fb618cf8bf4f5318fe285e821bb2" +dependencies = [ + "find-msvc-tools", + "jobserver", + "libc", + "shlex", +] + +[[package]] +name = "cesu8" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "cfg_aliases" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" + +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + +[[package]] +name = "cgl" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ced0551234e87afee12411d535648dd89d2e7f34c78b753395567aff3d447ff" +dependencies = [ + "libc", +] + +[[package]] +name = "clipboard-win" +version = "5.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bde03770d3df201d4fb868f2c9c59e66a3e4e2bd06692a0fe701e7103c7e84d4" +dependencies = [ + "error-code", +] + +[[package]] +name = "cocoa" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6140449f97a6e97f9511815c5632d84c8aacf8ac271ad77c559218161a1373c" +dependencies = [ + "bitflags 1.3.2", + "block", + "cocoa-foundation", + "core-foundation", + "core-graphics", + "foreign-types", + "libc", + "objc", +] + +[[package]] +name = "cocoa-foundation" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c6234cbb2e4c785b456c0644748b1ac416dd045799740356f8363dfe00c93f7" +dependencies = [ + "bitflags 1.3.2", + "block", + "core-foundation", + "core-graphics-types", + "libc", + "objc", +] + +[[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 = "color_quant" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" + +[[package]] +name = "com" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e17887fd17353b65b1b2ef1c526c83e26cd72e74f598a8dc1bee13a48f3d9f6" +dependencies = [ + "com_macros", +] + +[[package]] +name = "com_macros" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d375883580a668c7481ea6631fc1a8863e33cc335bf56bfad8d7e6d4b04b13a5" +dependencies = [ + "com_macros_support", + "proc-macro2", + "syn 1.0.109", +] + +[[package]] +name = "com_macros_support" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad899a1087a9296d5644792d7cb72b8e34c1bec8e7d4fbc002230169a6e8710c" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "combine" +version = "4.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" +dependencies = [ + "bytes", + "memchr", +] + +[[package]] +name = "concurrent-queue" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "core-graphics" +version = "0.23.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c07782be35f9e1140080c6b96f0d44b739e2278479f64e02fdab4e32dfd8b081" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "core-graphics-types", + "foreign-types", + "libc", +] + +[[package]] +name = "core-graphics-types" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45390e6114f68f718cc7a830514a96f903cccd70d02a8f6d9f643ac4ba45afaf" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "libc", +] + +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[package]] +name = "crc32fast" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + +[[package]] +name = "crypto-common" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "cursor-icon" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f27ae1dd37df86211c42e150270f82743308803d90a6f6e6651cd730d5e1732f" + +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + +[[package]] +name = "dispatch" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b" + +[[package]] +name = "dispatch2" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e0e367e4e7da84520dedcac1901e4da967309406d1e51017ae1abfb97adbd38" +dependencies = [ + "bitflags 2.11.0", + "objc2 0.6.4", +] + +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "dlib" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab8ecd87370524b461f8557c119c405552c396ed91fc0a8eec68679eab26f94a" +dependencies = [ + "libloading 0.8.9", +] + +[[package]] +name = "document-features" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4b8a88685455ed29a21542a33abd9cb6510b6b129abadabdcef0f4c55bc8f61" +dependencies = [ + "litrs", +] + +[[package]] +name = "downcast-rs" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" + +[[package]] +name = "ecolor" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20930a432bbd57a6d55e07976089708d4893f3d556cf42a0d79e9e321fa73b10" +dependencies = [ + "bytemuck", +] + +[[package]] +name = "eframe" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "020e2ccef6bbcec71dbc542f7eed64a5846fc3076727f5746da8fd307c91bab2" +dependencies = [ + "bytemuck", + "cocoa", + "document-features", + "egui", + "egui-wgpu", + "egui-winit", + "egui_glow", + "glow", + "glutin", + "glutin-winit", + "image", + "js-sys", + "log", + "objc", + "parking_lot", + "percent-encoding", + "raw-window-handle 0.5.2", + "raw-window-handle 0.6.2", + "static_assertions", + "thiserror 1.0.69", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "web-time", + "winapi", + "winit", +] + +[[package]] +name = "egui" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "584c5d1bf9a67b25778a3323af222dbe1a1feb532190e103901187f92c7fe29a" +dependencies = [ + "accesskit", + "ahash", + "epaint", + "log", + "nohash-hasher", +] + +[[package]] +name = "egui-wgpu" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "469ff65843f88a702b731a1532b7d03b0e8e96d283e70f3a22b0e06c46cb9b37" +dependencies = [ + "bytemuck", + "document-features", + "egui", + "epaint", + "log", + "thiserror 1.0.69", + "type-map", + "web-time", + "wgpu", + "winit", +] + +[[package]] +name = "egui-winit" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e3da0cbe020f341450c599b35b92de4af7b00abde85624fd16f09c885573609" +dependencies = [ + "accesskit_winit", + "arboard", + "egui", + "log", + "raw-window-handle 0.6.2", + "smithay-clipboard", + "web-time", + "webbrowser", + "winit", +] + +[[package]] +name = "egui_glow" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0e5d975f3c86edc3d35b1db88bb27c15dde7c55d3b5af164968ab5ede3f44ca" +dependencies = [ + "bytemuck", + "egui", + "glow", + "log", + "memoffset 0.9.1", + "wasm-bindgen", + "web-sys", + "winit", +] + +[[package]] +name = "emath" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4c3a552cfca14630702449d35f41c84a0d15963273771c6059175a803620f3f" +dependencies = [ + "bytemuck", +] + +[[package]] +name = "endi" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66b7e2430c6dff6a955451e2cfc438f09cea1965a9d6f87f7e3b90decc014099" + +[[package]] +name = "enumflags2" +version = "0.7.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1027f7680c853e056ebcec683615fb6fbbc07dbaa13b4d5d9442b146ded4ecef" +dependencies = [ + "enumflags2_derive", + "serde", +] + +[[package]] +name = "enumflags2_derive" +version = "0.7.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67c78a4d8fdf9953a5c9d458f9efe940fd97a0cab0941c075a813ac594733827" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "epaint" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b381f8b149657a4acf837095351839f32cd5c4aec1817fc4df84e18d76334176" +dependencies = [ + "ab_glyph", + "ahash", + "bytemuck", + "ecolor", + "emath", + "log", + "nohash-hasher", + "parking_lot", +] + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "errno" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + +[[package]] +name = "error-code" +version = "3.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dea2df4cf52843e0452895c455a1a2cfbb842a1e7329671acf418fdc53ed4c59" + +[[package]] +name = "event-listener" +version = "2.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" + +[[package]] +name = "event-listener" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d93877bcde0eb80ca09131a08d23f0a5c18a620b01db137dba666d18cd9b30c2" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", +] + +[[package]] +name = "event-listener" +version = "5.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13b66accf52311f30a0db42147dadea9850cb48cd070028831ae5f5d4b856ab" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", +] + +[[package]] +name = "event-listener-strategy" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93" +dependencies = [ + "event-listener 5.4.1", + "pin-project-lite", +] + +[[package]] +name = "fastrand" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" +dependencies = [ + "instant", +] + +[[package]] +name = "fastrand" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" + +[[package]] +name = "fdeflate" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e6853b52649d4ac5c0bd02320cddc5ba956bdb407c4b75a2c6b75bf51500f8c" +dependencies = [ + "simd-adler32", +] + +[[package]] +name = "find-msvc-tools" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" + +[[package]] +name = "flate2" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + +[[package]] +name = "foreign-types" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965" +dependencies = [ + "foreign-types-macros", + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-macros" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "foreign-types-shared" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" + +[[package]] +name = "form_urlencoded" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "futures-channel" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" +dependencies = [ + "futures-core", +] + +[[package]] +name = "futures-core" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" + +[[package]] +name = "futures-io" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" + +[[package]] +name = "futures-lite" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49a9d51ce47660b1e808d3c990b4709f2f415d928835a17dfd16991515c46bce" +dependencies = [ + "fastrand 1.9.0", + "futures-core", + "futures-io", + "memchr", + "parking", + "pin-project-lite", + "waker-fn", +] + +[[package]] +name = "futures-lite" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f78e10609fe0e0b3f4157ffab1876319b5b0db102a2c60dc4626306dc46b44ad" +dependencies = [ + "fastrand 2.3.0", + "futures-core", + "futures-io", + "parking", + "pin-project-lite", +] + +[[package]] +name = "futures-macro" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "futures-sink" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" + +[[package]] +name = "futures-task" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" + +[[package]] +name = "futures-util" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +dependencies = [ + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "gethostname" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bd49230192a3797a9a4d6abe9b3eed6f7fa4c8a8a4947977c6f80025f92cbd8" +dependencies = [ + "rustix 1.1.4", + "windows-link", +] + +[[package]] +name = "getrandom" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "getrandom" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasip2", +] + +[[package]] +name = "getrandom" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "139ef39800118c7683f2fd3c98c1b23c09ae076556b435f8e9064ae108aaeeec" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasip2", + "wasip3", +] + +[[package]] +name = "gl_generator" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a95dfc23a2b4a9a2f5ab41d194f8bfda3cabec42af4e39f08c339eb2a0c124d" +dependencies = [ + "khronos_api", + "log", + "xml-rs", +] + +[[package]] +name = "glow" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd348e04c43b32574f2de31c8bb397d96c9fcfa1371bd4ca6d8bdc464ab121b1" +dependencies = [ + "js-sys", + "slotmap", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "glutin" +version = "0.31.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18fcd4ae4e86d991ad1300b8f57166e5be0c95ef1f63f3f5b827f8a164548746" +dependencies = [ + "bitflags 2.11.0", + "cfg_aliases 0.1.1", + "cgl", + "core-foundation", + "dispatch", + "glutin_egl_sys", + "glutin_glx_sys", + "glutin_wgl_sys", + "icrate", + "libloading 0.8.9", + "objc2 0.4.1", + "once_cell", + "raw-window-handle 0.5.2", + "wayland-sys", + "windows-sys 0.48.0", + "x11-dl", +] + +[[package]] +name = "glutin-winit" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ebcdfba24f73b8412c5181e56f092b5eff16671c514ce896b258a0a64bd7735" +dependencies = [ + "cfg_aliases 0.1.1", + "glutin", + "raw-window-handle 0.5.2", + "winit", +] + +[[package]] +name = "glutin_egl_sys" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77cc5623f5309ef433c3dd4ca1223195347fe62c413da8e2fdd0eb76db2d9bcd" +dependencies = [ + "gl_generator", + "windows-sys 0.48.0", +] + +[[package]] +name = "glutin_glx_sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a165fd686c10dcc2d45380b35796e577eacfd43d4660ee741ec8ebe2201b3b4f" +dependencies = [ + "gl_generator", + "x11-dl", +] + +[[package]] +name = "glutin_wgl_sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c8098adac955faa2d31079b65dc48841251f69efd3ac25477903fc424362ead" +dependencies = [ + "gl_generator", +] + +[[package]] +name = "gpu-alloc" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbcd2dba93594b227a1f57ee09b8b9da8892c34d55aa332e034a228d0fe6a171" +dependencies = [ + "bitflags 2.11.0", + "gpu-alloc-types", +] + +[[package]] +name = "gpu-alloc-types" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98ff03b468aa837d70984d55f5d3f846f6ec31fe34bbb97c4f85219caeee1ca4" +dependencies = [ + "bitflags 2.11.0", +] + +[[package]] +name = "gpu-allocator" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f56f6318968d03c18e1bcf4857ff88c61157e9da8e47c5f29055d60e1228884" +dependencies = [ + "log", + "presser", + "thiserror 1.0.69", + "winapi", + "windows 0.52.0", +] + +[[package]] +name = "gpu-descriptor" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc11df1ace8e7e564511f53af41f3e42ddc95b56fd07b3f4445d2a6048bc682c" +dependencies = [ + "bitflags 2.11.0", + "gpu-descriptor-types", + "hashbrown 0.14.5", +] + +[[package]] +name = "gpu-descriptor-types" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bf0b36e6f090b7e1d8a4b49c0cb81c1f8376f72198c65dd3ad9ff3556b8b78c" +dependencies = [ + "bitflags 2.11.0", +] + +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +dependencies = [ + "ahash", + "allocator-api2", +] + +[[package]] +name = "hashbrown" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +dependencies = [ + "foldhash", +] + +[[package]] +name = "hashbrown" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" + +[[package]] +name = "hassle-rs" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af2a7e73e1f34c48da31fb668a907f250794837e08faa144fd24f0b8b741e890" +dependencies = [ + "bitflags 2.11.0", + "com", + "libc", + "libloading 0.8.9", + "thiserror 1.0.69", + "widestring", + "winapi", +] + +[[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.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" + +[[package]] +name = "hermit-abi" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hexf-parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df" + +[[package]] +name = "home" +version = "0.5.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc627f471c528ff0c4a49e1d5e60450c8f6461dd6d10ba9dcd3a61d3dff7728d" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "icrate" +version = "0.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99d3aaff8a54577104bafdf686ff18565c3b6903ca5782a2026ef06e2c7aa319" +dependencies = [ + "block2 0.3.0", + "dispatch", + "objc2 0.4.1", +] + +[[package]] +name = "icu_collections" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c6b649701667bbe825c3b7e6388cb521c23d88644678e83c0c4d0a621a34b43" +dependencies = [ + "displaydoc", + "potential_utf", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locale_core" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edba7861004dd3714265b4db54a3c390e880ab658fec5f7db895fae2046b5bb6" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_normalizer" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f6c8828b67bf8908d82127b2054ea1b4427ff0230ee9141c54251934ab1b599" +dependencies = [ + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a" + +[[package]] +name = "icu_properties" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "020bfc02fe870ec3a66d93e677ccca0562506e5872c650f893269e08615d74ec" +dependencies = [ + "icu_collections", + "icu_locale_core", + "icu_properties_data", + "icu_provider", + "zerotrie", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "616c294cf8d725c6afcd8f55abc17c56464ef6211f9ed59cccffe534129c77af" + +[[package]] +name = "icu_provider" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85962cf0ce02e1e0a629cc34e7ca3e373ce20dda4c4d7294bbd0bf1fdb59e614" +dependencies = [ + "displaydoc", + "icu_locale_core", + "writeable", + "yoke", + "zerofrom", + "zerotrie", + "zerovec", +] + +[[package]] +name = "id-arena" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" + +[[package]] +name = "idna" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + +[[package]] +name = "image" +version = "0.24.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5690139d2f55868e080017335e4b94cb7414274c74f1669c84fb5feba2c9f69d" +dependencies = [ + "bytemuck", + "byteorder", + "color_quant", + "num-traits", + "png", +] + +[[package]] +name = "indexmap" +version = "2.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" +dependencies = [ + "equivalent", + "hashbrown 0.16.1", + "serde", + "serde_core", +] + +[[package]] +name = "instant" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "io-lifetimes" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" +dependencies = [ + "hermit-abi 0.3.9", + "libc", + "windows-sys 0.48.0", +] + +[[package]] +name = "itoa" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" + +[[package]] +name = "jni" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" +dependencies = [ + "cesu8", + "cfg-if", + "combine", + "jni-sys", + "log", + "thiserror 1.0.69", + "walkdir", + "windows-sys 0.45.0", +] + +[[package]] +name = "jni-sys" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" + +[[package]] +name = "jobserver" +version = "0.1.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" +dependencies = [ + "getrandom 0.3.4", + "libc", +] + +[[package]] +name = "js-sys" +version = "0.3.91" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b49715b7073f385ba4bc528e5747d02e66cb39c6146efb66b781f131f0fb399c" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "khronos-egl" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6aae1df220ece3c0ada96b8153459b67eebe9ae9212258bb0134ae60416fdf76" +dependencies = [ + "libc", + "libloading 0.8.9", + "pkg-config", +] + +[[package]] +name = "khronos_api" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc" + +[[package]] +name = "leb128fmt" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" + +[[package]] +name = "libc" +version = "0.2.182" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6800badb6cb2082ffd7b6a67e6125bb39f18782f793520caee8cb8846be06112" + +[[package]] +name = "libloading" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" +dependencies = [ + "cfg-if", + "winapi", +] + +[[package]] +name = "libloading" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55" +dependencies = [ + "cfg-if", + "windows-link", +] + +[[package]] +name = "libredox" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1744e39d1d6a9948f4f388969627434e31128196de472883b39f148769bfe30a" +dependencies = [ + "bitflags 2.11.0", + "libc", + "plain", + "redox_syscall 0.7.3", +] + +[[package]] +name = "linux-raw-sys" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" + +[[package]] +name = "linux-raw-sys" +version = "0.4.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" + +[[package]] +name = "linux-raw-sys" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" + +[[package]] +name = "litemap" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77" + +[[package]] +name = "litrs" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11d3d7f243d5c5a8b9bb5d6dd2b1602c0cb0b9db1621bafc7ed66e35ff9fe092" + +[[package]] +name = "lock_api" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" + +[[package]] +name = "malloc_buf" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" +dependencies = [ + "libc", +] + +[[package]] +name = "memchr" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" + +[[package]] +name = "memmap2" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "714098028fe011992e1c3962653c96b2d578c4b4bce9036e15ff220319b1e0e3" +dependencies = [ + "libc", +] + +[[package]] +name = "memoffset" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4" +dependencies = [ + "autocfg", +] + +[[package]] +name = "memoffset" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" +dependencies = [ + "autocfg", +] + +[[package]] +name = "metal" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c43f73953f8cbe511f021b58f18c3ce1c3d1ae13fe953293e13345bf83217f25" +dependencies = [ + "bitflags 2.11.0", + "block", + "core-graphics-types", + "foreign-types", + "log", + "objc", + "paste", +] + +[[package]] +name = "miniz_oxide" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" +dependencies = [ + "adler2", + "simd-adler32", +] + +[[package]] +name = "mio" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc" +dependencies = [ + "libc", + "wasi", + "windows-sys 0.61.2", +] + +[[package]] +name = "naga" +version = "0.19.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50e3524642f53d9af419ab5e8dd29d3ba155708267667c2f3f06c88c9e130843" +dependencies = [ + "bit-set", + "bitflags 2.11.0", + "codespan-reporting", + "hexf-parse", + "indexmap", + "log", + "num-traits", + "rustc-hash 1.1.0", + "spirv", + "termcolor", + "thiserror 1.0.69", + "unicode-xid", +] + +[[package]] +name = "ndk" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2076a31b7010b17a38c01907c45b945e8f11495ee4dd588309718901b1f7a5b7" +dependencies = [ + "bitflags 2.11.0", + "jni-sys", + "log", + "ndk-sys", + "num_enum", + "raw-window-handle 0.5.2", + "raw-window-handle 0.6.2", + "thiserror 1.0.69", +] + +[[package]] +name = "ndk-context" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b" + +[[package]] +name = "ndk-sys" +version = "0.5.0+25.2.9519653" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c196769dd60fd4f363e11d948139556a344e79d451aeb2fa2fd040738ef7691" +dependencies = [ + "jni-sys", +] + +[[package]] +name = "nix" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "598beaf3cc6fdd9a5dfb1630c2800c7acd31df7aaf0f565796fba2b53ca1af1b" +dependencies = [ + "bitflags 1.3.2", + "cfg-if", + "libc", + "memoffset 0.7.1", +] + +[[package]] +name = "nix" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" +dependencies = [ + "bitflags 2.11.0", + "cfg-if", + "cfg_aliases 0.2.1", + "libc", + "memoffset 0.9.1", +] + +[[package]] +name = "nohash-hasher" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451" + +[[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_enum" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1207a7e20ad57b847bbddc6776b968420d38292bbfe2089accff5e19e82454c" +dependencies = [ + "num_enum_derive", + "rustversion", +] + +[[package]] +name = "num_enum_derive" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff32365de1b6743cb203b710788263c44a03de03802daf96092f2da4fe6ba4d7" +dependencies = [ + "proc-macro-crate 3.4.0", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "objc" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" +dependencies = [ + "malloc_buf", + "objc_exception", +] + +[[package]] +name = "objc-foundation" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1add1b659e36c9607c7aab864a76c7a4c2760cd0cd2e120f3fb8b952c7e22bf9" +dependencies = [ + "block", + "objc", + "objc_id", +] + +[[package]] +name = "objc-sys" +version = "0.2.0-beta.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b9834c1e95694a05a828b59f55fa2afec6288359cda67146126b3f90a55d7" + +[[package]] +name = "objc-sys" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb91bdd390c7ce1a8607f35f3ca7151b65afc0ff5ff3b34fa350f7d7c7e4310" + +[[package]] +name = "objc2" +version = "0.3.0-beta.3.patch-leaks.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e01640f9f2cb1220bbe80325e179e532cb3379ebcd1bf2279d703c19fe3a468" +dependencies = [ + "block2 0.2.0-alpha.6", + "objc-sys 0.2.0-beta.2", + "objc2-encode 2.0.0-pre.2", +] + +[[package]] +name = "objc2" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "559c5a40fdd30eb5e344fbceacf7595a81e242529fb4e21cf5f43fb4f11ff98d" +dependencies = [ + "objc-sys 0.3.5", + "objc2-encode 3.0.0", +] + +[[package]] +name = "objc2" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a12a8ed07aefc768292f076dc3ac8c48f3781c8f2d5851dd3d98950e8c5a89f" +dependencies = [ + "objc2-encode 4.1.0", +] + +[[package]] +name = "objc2-app-kit" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d49e936b501e5c5bf01fda3a9452ff86dc3ea98ad5f283e1455153142d97518c" +dependencies = [ + "bitflags 2.11.0", + "objc2 0.6.4", + "objc2-core-graphics", + "objc2-foundation", +] + +[[package]] +name = "objc2-core-foundation" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536" +dependencies = [ + "bitflags 2.11.0", + "dispatch2", + "objc2 0.6.4", +] + +[[package]] +name = "objc2-core-graphics" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e022c9d066895efa1345f8e33e584b9f958da2fd4cd116792e15e07e4720a807" +dependencies = [ + "bitflags 2.11.0", + "dispatch2", + "objc2 0.6.4", + "objc2-core-foundation", + "objc2-io-surface", +] + +[[package]] +name = "objc2-encode" +version = "2.0.0-pre.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abfcac41015b00a120608fdaa6938c44cb983fee294351cc4bac7638b4e50512" +dependencies = [ + "objc-sys 0.2.0-beta.2", +] + +[[package]] +name = "objc2-encode" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d079845b37af429bfe5dfa76e6d087d788031045b25cfc6fd898486fd9847666" + +[[package]] +name = "objc2-encode" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33" + +[[package]] +name = "objc2-foundation" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272" +dependencies = [ + "bitflags 2.11.0", + "objc2 0.6.4", + "objc2-core-foundation", +] + +[[package]] +name = "objc2-io-surface" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "180788110936d59bab6bd83b6060ffdfffb3b922ba1396b312ae795e1de9d81d" +dependencies = [ + "bitflags 2.11.0", + "objc2 0.6.4", + "objc2-core-foundation", +] + +[[package]] +name = "objc_exception" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad970fb455818ad6cba4c122ad012fae53ae8b4795f86378bce65e4f6bab2ca4" +dependencies = [ + "cc", +] + +[[package]] +name = "objc_id" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c92d4ddb4bd7b50d730c215ff871754d0da6b2178849f8a2a2ab69712d0c073b" +dependencies = [ + "objc", +] + +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + +[[package]] +name = "orbclient" +version = "0.3.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52ad2c6bae700b7aa5d1cc30c59bdd3a1c180b09dbaea51e2ae2b8e1cf211fdd" +dependencies = [ + "libc", + "libredox", +] + +[[package]] +name = "ordered-stream" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aa2b01e1d916879f73a53d01d1d6cee68adbb31d6d9177a8cfce093cced1d50" +dependencies = [ + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "owned_ttf_parser" +version = "0.25.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36820e9051aca1014ddc75770aab4d68bc1e9e632f0f5627c4086bc216fb583b" +dependencies = [ + "ttf-parser", +] + +[[package]] +name = "parking" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" + +[[package]] +name = "parking_lot" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall 0.5.18", + "smallvec", + "windows-link", +] + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "percent-encoding" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" + +[[package]] +name = "pin-project-lite" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" + +[[package]] +name = "piper" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c835479a4443ded371d6c535cbfd8d31ad92c5d23ae9770a61bc155e4992a3c1" +dependencies = [ + "atomic-waker", + "fastrand 2.3.0", + "futures-io", +] + +[[package]] +name = "pkg-config" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" + +[[package]] +name = "plain" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" + +[[package]] +name = "png" +version = "0.17.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82151a2fc869e011c153adc57cf2789ccb8d9906ce52c0b39a6b5697749d7526" +dependencies = [ + "bitflags 1.3.2", + "crc32fast", + "fdeflate", + "flate2", + "miniz_oxide", +] + +[[package]] +name = "polling" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b2d323e8ca7996b3e23126511a523f7e62924d93ecd5ae73b333815b0eb3dce" +dependencies = [ + "autocfg", + "bitflags 1.3.2", + "cfg-if", + "concurrent-queue", + "libc", + "log", + "pin-project-lite", + "windows-sys 0.48.0", +] + +[[package]] +name = "polling" +version = "3.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d0e4f59085d47d8241c88ead0f274e8a0cb551f3625263c05eb8dd897c34218" +dependencies = [ + "cfg-if", + "concurrent-queue", + "hermit-abi 0.5.2", + "pin-project-lite", + "rustix 1.1.4", + "windows-sys 0.61.2", +] + +[[package]] +name = "pollster" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22686f4785f02a4fcc856d3b3bb19bf6c8160d103f7a99cc258bddd0251dc7f2" + +[[package]] +name = "potential_utf" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b73949432f5e2a09657003c25bca5e19a0e9c84f8058ca374f49e0ebe605af77" +dependencies = [ + "zerovec", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "presser" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8cf8e6a8aa66ce33f63993ffc4ea4271eb5b0530a9002db8455ea6050c77bfa" + +[[package]] +name = "prettyplease" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" +dependencies = [ + "proc-macro2", + "syn 2.0.117", +] + +[[package]] +name = "proc-macro-crate" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" +dependencies = [ + "once_cell", + "toml_edit 0.19.15", +] + +[[package]] +name = "proc-macro-crate" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "219cb19e96be00ab2e37d6e299658a0cfa83e52429179969b0f0121b4ac46983" +dependencies = [ + "toml_edit 0.23.10+spec-1.0.0", +] + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "profiling" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3eb8486b569e12e2c32ad3e204dbaba5e4b5b216e9367044f25f1dba42341773" + +[[package]] +name = "quick-xml" +version = "0.38.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b66c2058c55a409d601666cffe35f04333cf1013010882cec174a7467cd4e21c" +dependencies = [ + "memchr", +] + +[[package]] +name = "quote" +version = "1.0.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21b2ebcf727b7760c461f091f9f0f539b77b8e87f2fd88131e7f1b433b3cece4" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.17", +] + +[[package]] +name = "raw-window-handle" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2ff9a1f06a88b01621b7ae906ef0211290d1c8a168a15542486a8f61c0833b9" + +[[package]] +name = "raw-window-handle" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20675572f6f24e9e76ef639bc5552774ed45f1c30e2951e1e99c59888861c539" + +[[package]] +name = "redox_syscall" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" +dependencies = [ + "bitflags 1.3.2", +] + +[[package]] +name = "redox_syscall" +version = "0.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" +dependencies = [ + "bitflags 2.11.0", +] + +[[package]] +name = "redox_syscall" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce70a74e890531977d37e532c34d45e9055d2409ed08ddba14529471ed0be16" +dependencies = [ + "bitflags 2.11.0", +] + +[[package]] +name = "regex" +version = "1.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" + +[[package]] +name = "renderdoc-sys" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19b30a45b0cd0bcca8037f3d0dc3421eaf95327a17cad11964fb8179b4fc4832" + +[[package]] +name = "rfd" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25a73a7337fc24366edfca76ec521f51877b114e42dab584008209cca6719251" +dependencies = [ + "ashpd", + "block", + "dispatch", + "js-sys", + "log", + "objc", + "objc-foundation", + "objc_id", + "pollster", + "raw-window-handle 0.6.2", + "urlencoding", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "windows-sys 0.48.0", +] + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustc-hash" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" + +[[package]] +name = "rustix" +version = "0.37.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "519165d378b97752ca44bbe15047d5d3409e875f39327546b42ac81d7e18c1b6" +dependencies = [ + "bitflags 1.3.2", + "errno", + "io-lifetimes", + "libc", + "linux-raw-sys 0.3.8", + "windows-sys 0.48.0", +] + +[[package]] +name = "rustix" +version = "0.38.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" +dependencies = [ + "bitflags 2.11.0", + "errno", + "libc", + "linux-raw-sys 0.4.15", + "windows-sys 0.59.0", +] + +[[package]] +name = "rustix" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" +dependencies = [ + "bitflags 2.11.0", + "errno", + "libc", + "linux-raw-sys 0.12.1", + "windows-sys 0.61.2", +] + +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[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 = "scoped-tls" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "sctk-adwaita" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70b31447ca297092c5a9916fc3b955203157b37c19ca8edde4f52e9843e602c7" +dependencies = [ + "ab_glyph", + "log", + "memmap2", + "smithay-client-toolkit 0.18.1", + "tiny-skia", +] + +[[package]] +name = "semver" +version = "1.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "serde_json" +version = "1.0.149" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "serde_repr" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "signal-hook-registry" +version = "1.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b" +dependencies = [ + "errno", + "libc", +] + +[[package]] +name = "simd-adler32" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e320a6c5ad31d271ad523dcf3ad13e2767ad8b1cb8f047f75a8aeaf8da139da2" + [[package]] name = "simple-multimidia-track-audio-editor" version = "0.1.0" +dependencies = [ + "anyhow", + "eframe", + "rfd", + "serde", + "serde_json", + "tokio", +] + +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + +[[package]] +name = "slotmap" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdd58c3c93c3d278ca835519292445cb4b0d4dc59ccfdf7ceadaab3f8aeb4038" +dependencies = [ + "version_check", +] + +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" + +[[package]] +name = "smithay-client-toolkit" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "922fd3eeab3bd820d76537ce8f582b1cf951eceb5475c28500c7457d9d17f53a" +dependencies = [ + "bitflags 2.11.0", + "calloop 0.12.4", + "calloop-wayland-source 0.2.0", + "cursor-icon", + "libc", + "log", + "memmap2", + "rustix 0.38.44", + "thiserror 1.0.69", + "wayland-backend", + "wayland-client", + "wayland-csd-frame", + "wayland-cursor", + "wayland-protocols 0.31.2", + "wayland-protocols-wlr 0.2.0", + "wayland-scanner", + "xkeysym", +] + +[[package]] +name = "smithay-client-toolkit" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0512da38f5e2b31201a93524adb8d3136276fa4fe4aafab4e1f727a82b534cc0" +dependencies = [ + "bitflags 2.11.0", + "calloop 0.14.4", + "calloop-wayland-source 0.4.1", + "cursor-icon", + "libc", + "log", + "memmap2", + "rustix 1.1.4", + "thiserror 2.0.18", + "wayland-backend", + "wayland-client", + "wayland-csd-frame", + "wayland-cursor", + "wayland-protocols 0.32.10", + "wayland-protocols-experimental", + "wayland-protocols-misc", + "wayland-protocols-wlr 0.3.10", + "wayland-scanner", + "xkeysym", +] + +[[package]] +name = "smithay-clipboard" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71704c03f739f7745053bde45fa203a46c58d25bc5c4efba1d9a60e9dba81226" +dependencies = [ + "libc", + "smithay-client-toolkit 0.20.0", + "wayland-backend", +] + +[[package]] +name = "smol_str" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd538fb6910ac1099850255cf94a94df6551fbdd602454387d0adb2d1ca6dead" +dependencies = [ + "serde", +] + +[[package]] +name = "socket2" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7916fc008ca5542385b89a3d3ce689953c143e9304a9bf8beec1de48994c0d" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "spirv" +version = "0.3.0+sdk-1.3.268.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eda41003dc44290527a59b13432d4a0379379fa074b70174882adfbdfd917844" +dependencies = [ + "bitflags 2.11.0", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "strict-num" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6637bab7722d379c8b41ba849228d680cc12d0a45ba1fa2b48f2a30577a06731" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "synstructure" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "tempfile" +version = "3.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82a72c767771b47409d2345987fda8628641887d5466101319899796367354a0" +dependencies = [ + "fastrand 2.3.0", + "getrandom 0.4.1", + "once_cell", + "rustix 1.1.4", + "windows-sys 0.61.2", +] + +[[package]] +name = "termcolor" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" +dependencies = [ + "thiserror-impl 2.0.18", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "tiny-skia" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83d13394d44dae3207b52a326c0c85a8bf87f1541f23b0d143811088497b09ab" +dependencies = [ + "arrayref", + "arrayvec", + "bytemuck", + "cfg-if", + "log", + "tiny-skia-path", +] + +[[package]] +name = "tiny-skia-path" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c9e7fc0c2e86a30b117d0462aa261b72b7a99b7ebd7deb3a14ceda95c5bdc93" +dependencies = [ + "arrayref", + "bytemuck", + "strict-num", +] + +[[package]] +name = "tinystr" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869" +dependencies = [ + "displaydoc", + "zerovec", +] + +[[package]] +name = "tokio" +version = "1.49.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72a2903cd7736441aac9df9d7688bd0ce48edccaadf181c3b90be801e81d3d86" +dependencies = [ + "bytes", + "libc", + "mio", + "pin-project-lite", + "signal-hook-registry", + "tokio-macros", + "windows-sys 0.61.2", +] + +[[package]] +name = "tokio-macros" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "toml_datetime" +version = "0.6.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" + +[[package]] +name = "toml_datetime" +version = "0.7.5+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92e1cfed4a3038bc5a127e35a2d360f145e1f4b971b551a2ba5fd7aedf7e1347" +dependencies = [ + "serde_core", +] + +[[package]] +name = "toml_edit" +version = "0.19.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" +dependencies = [ + "indexmap", + "toml_datetime 0.6.11", + "winnow 0.5.40", +] + +[[package]] +name = "toml_edit" +version = "0.23.10+spec-1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84c8b9f757e028cee9fa244aea147aab2a9ec09d5325a9b01e0a49730c2b5269" +dependencies = [ + "indexmap", + "toml_datetime 0.7.5+spec-1.1.0", + "toml_parser", + "winnow 0.7.14", +] + +[[package]] +name = "toml_parser" +version = "1.0.9+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "702d4415e08923e7e1ef96cd5727c0dfed80b4d2fa25db9647fe5eb6f7c5a4c4" +dependencies = [ + "winnow 0.7.14", +] + +[[package]] +name = "tracing" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" +dependencies = [ + "log", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "tracing-core" +version = "0.1.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" +dependencies = [ + "once_cell", +] + +[[package]] +name = "ttf-parser" +version = "0.25.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2df906b07856748fa3f6e0ad0cbaa047052d4a7dd609e231c4f72cee8c36f31" + +[[package]] +name = "type-map" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb30dbbd9036155e74adad6812e9898d03ec374946234fbcebd5dfc7b9187b90" +dependencies = [ + "rustc-hash 2.1.1", +] + +[[package]] +name = "typenum" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" + +[[package]] +name = "uds_windows" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89daebc3e6fd160ac4aa9fc8b3bf71e1f74fbf92367ae71fb83a037e8bf164b9" +dependencies = [ + "memoffset 0.9.1", + "tempfile", + "winapi", +] + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "unicode-segmentation" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" + +[[package]] +name = "unicode-width" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" + +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + +[[package]] +name = "url" +version = "2.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", + "serde", + "serde_derive", +] + +[[package]] +name = "urlencoding" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "waker-fn" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "317211a0dc0ceedd78fb2ca9a44aed3d7b9b26f81870d485c07122b4350673b7" + +[[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.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "wasip2" +version = "1.0.2+wasi-0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "wasip3" +version = "0.4.0+wasi-0.3.0-rc-2026-01-06" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.114" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6532f9a5c1ece3798cb1c2cfdba640b9b3ba884f5db45973a6f442510a87d38e" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9c5522b3a28661442748e09d40924dfb9ca614b21c00d3fd135720e48b67db8" +dependencies = [ + "cfg-if", + "futures-util", + "js-sys", + "once_cell", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.114" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18a2d50fcf105fb33bb15f00e7a77b772945a2ee45dcf454961fd843e74c18e6" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.114" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03ce4caeaac547cdf713d280eda22a730824dd11e6b8c3ca9e42247b25c631e3" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn 2.0.117", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.114" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75a326b8c223ee17883a4251907455a2431acc2791c98c26279376490c378c16" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "wasm-encoder" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" +dependencies = [ + "leb128fmt", + "wasmparser", +] + +[[package]] +name = "wasm-metadata" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" +dependencies = [ + "anyhow", + "indexmap", + "wasm-encoder", + "wasmparser", +] + +[[package]] +name = "wasmparser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" +dependencies = [ + "bitflags 2.11.0", + "hashbrown 0.15.5", + "indexmap", + "semver", +] + +[[package]] +name = "wayland-backend" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fee64194ccd96bf648f42a65a7e589547096dfa702f7cadef84347b66ad164f9" +dependencies = [ + "cc", + "downcast-rs", + "rustix 1.1.4", + "scoped-tls", + "smallvec", + "wayland-sys", +] + +[[package]] +name = "wayland-client" +version = "0.31.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e6faa537fbb6c186cb9f1d41f2f811a4120d1b57ec61f50da451a0c5122bec" +dependencies = [ + "bitflags 2.11.0", + "rustix 1.1.4", + "wayland-backend", + "wayland-scanner", +] + +[[package]] +name = "wayland-csd-frame" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "625c5029dbd43d25e6aa9615e88b829a5cad13b2819c4ae129fdbb7c31ab4c7e" +dependencies = [ + "bitflags 2.11.0", + "cursor-icon", + "wayland-backend", +] + +[[package]] +name = "wayland-cursor" +version = "0.31.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5864c4b5b6064b06b1e8b74ead4a98a6c45a285fe7a0e784d24735f011fdb078" +dependencies = [ + "rustix 1.1.4", + "wayland-client", + "xcursor", +] + +[[package]] +name = "wayland-protocols" +version = "0.31.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f81f365b8b4a97f422ac0e8737c438024b5951734506b0e1d775c73030561f4" +dependencies = [ + "bitflags 2.11.0", + "wayland-backend", + "wayland-client", + "wayland-scanner", +] + +[[package]] +name = "wayland-protocols" +version = "0.32.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baeda9ffbcfc8cd6ddaade385eaf2393bd2115a69523c735f12242353c3df4f3" +dependencies = [ + "bitflags 2.11.0", + "wayland-backend", + "wayland-client", + "wayland-scanner", +] + +[[package]] +name = "wayland-protocols-experimental" +version = "20250721.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40a1f863128dcaaec790d7b4b396cc9b9a7a079e878e18c47e6c2d2c5a8dcbb1" +dependencies = [ + "bitflags 2.11.0", + "wayland-backend", + "wayland-client", + "wayland-protocols 0.32.10", + "wayland-scanner", +] + +[[package]] +name = "wayland-protocols-misc" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "791c58fdeec5406aa37169dd815327d1e47f334219b523444bc26d70ceb4c34e" +dependencies = [ + "bitflags 2.11.0", + "wayland-backend", + "wayland-client", + "wayland-protocols 0.32.10", + "wayland-scanner", +] + +[[package]] +name = "wayland-protocols-plasma" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23803551115ff9ea9bce586860c5c5a971e360825a0309264102a9495a5ff479" +dependencies = [ + "bitflags 2.11.0", + "wayland-backend", + "wayland-client", + "wayland-protocols 0.31.2", + "wayland-scanner", +] + +[[package]] +name = "wayland-protocols-wlr" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad1f61b76b6c2d8742e10f9ba5c3737f6530b4c243132c2a2ccc8aa96fe25cd6" +dependencies = [ + "bitflags 2.11.0", + "wayland-backend", + "wayland-client", + "wayland-protocols 0.31.2", + "wayland-scanner", +] + +[[package]] +name = "wayland-protocols-wlr" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9597cdf02cf0c34cd5823786dce6b5ae8598f05c2daf5621b6e178d4f7345f3" +dependencies = [ + "bitflags 2.11.0", + "wayland-backend", + "wayland-client", + "wayland-protocols 0.32.10", + "wayland-scanner", +] + +[[package]] +name = "wayland-scanner" +version = "0.31.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5423e94b6a63e68e439803a3e153a9252d5ead12fd853334e2ad33997e3889e3" +dependencies = [ + "proc-macro2", + "quick-xml", + "quote", +] + +[[package]] +name = "wayland-sys" +version = "0.31.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e6dbfc3ac5ef974c92a2235805cc0114033018ae1290a72e474aa8b28cbbdfd" +dependencies = [ + "dlib", + "log", + "once_cell", + "pkg-config", +] + +[[package]] +name = "web-sys" +version = "0.3.91" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "854ba17bb104abfb26ba36da9729addc7ce7f06f5c0f90f3c391f8461cca21f9" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "web-time" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa30049b1c872b72c89866d458eae9f20380ab280ffd1b1e18df2d3e2d98cfe0" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webbrowser" +version = "0.8.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db67ae75a9405634f5882791678772c94ff5f16a66535aae186e26aa0841fc8b" +dependencies = [ + "core-foundation", + "home", + "jni", + "log", + "ndk-context", + "objc", + "raw-window-handle 0.5.2", + "url", + "web-sys", +] + +[[package]] +name = "wgpu" +version = "0.19.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbd7311dbd2abcfebaabf1841a2824ed7c8be443a0f29166e5d3c6a53a762c01" +dependencies = [ + "arrayvec", + "cfg-if", + "cfg_aliases 0.1.1", + "js-sys", + "log", + "parking_lot", + "profiling", + "raw-window-handle 0.6.2", + "smallvec", + "static_assertions", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "wgpu-core", + "wgpu-hal", + "wgpu-types", +] + +[[package]] +name = "wgpu-core" +version = "0.19.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28b94525fc99ba9e5c9a9e24764f2bc29bad0911a7446c12f446a8277369bf3a" +dependencies = [ + "arrayvec", + "bit-vec", + "bitflags 2.11.0", + "cfg_aliases 0.1.1", + "codespan-reporting", + "indexmap", + "log", + "naga", + "once_cell", + "parking_lot", + "profiling", + "raw-window-handle 0.6.2", + "rustc-hash 1.1.0", + "smallvec", + "thiserror 1.0.69", + "web-sys", + "wgpu-hal", + "wgpu-types", +] + +[[package]] +name = "wgpu-hal" +version = "0.19.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfabcfc55fd86611a855816326b2d54c3b2fd7972c27ce414291562650552703" +dependencies = [ + "android_system_properties", + "arrayvec", + "ash", + "bitflags 2.11.0", + "cfg_aliases 0.1.1", + "core-graphics-types", + "glow", + "glutin_wgl_sys", + "gpu-alloc", + "gpu-allocator", + "gpu-descriptor", + "hassle-rs", + "js-sys", + "khronos-egl", + "libc", + "libloading 0.8.9", + "log", + "metal", + "naga", + "ndk-sys", + "objc", + "once_cell", + "parking_lot", + "profiling", + "raw-window-handle 0.6.2", + "renderdoc-sys", + "rustc-hash 1.1.0", + "smallvec", + "thiserror 1.0.69", + "wasm-bindgen", + "web-sys", + "wgpu-types", + "winapi", +] + +[[package]] +name = "wgpu-types" +version = "0.19.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b671ff9fb03f78b46ff176494ee1ebe7d603393f42664be55b64dc8d53969805" +dependencies = [ + "bitflags 2.11.0", + "js-sys", + "web-sys", +] + +[[package]] +name = "widestring" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72069c3113ab32ab29e5584db3c6ec55d416895e60715417b5b883a357c3e471" + +[[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-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.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" +dependencies = [ + "windows-sys 0.61.2", +] + +[[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" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-targets 0.48.5", +] + +[[package]] +name = "windows" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be" +dependencies = [ + "windows-core", + "windows-targets 0.52.6", +] + +[[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-implement" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e2ee588991b9e7e6c8338edf3333fbe4da35dc72092643958ebb43f0ab2c49c" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "windows-interface" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6fb8df20c9bcaa8ad6ab513f7b40104840c8867d5751126e4df3b08388d0cc7" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets 0.42.2", +] + +[[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-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets 0.53.5", +] + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + +[[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 0.52.6", + "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-targets" +version = "0.53.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" +dependencies = [ + "windows-link", + "windows_aarch64_gnullvm 0.53.1", + "windows_aarch64_msvc 0.53.1", + "windows_i686_gnu 0.53.1", + "windows_i686_gnullvm 0.53.1", + "windows_i686_msvc 0.53.1", + "windows_x86_64_gnu 0.53.1", + "windows_x86_64_gnullvm 0.53.1", + "windows_x86_64_msvc 0.53.1", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + +[[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_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + +[[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_aarch64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + +[[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_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" + +[[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_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + +[[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_i686_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + +[[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_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + +[[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_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + +[[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 = "windows_x86_64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" + +[[package]] +name = "winit" +version = "0.29.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d59ad965a635657faf09c8f062badd885748428933dad8e8bdd64064d92e5ca" +dependencies = [ + "ahash", + "android-activity", + "atomic-waker", + "bitflags 2.11.0", + "bytemuck", + "calloop 0.12.4", + "cfg_aliases 0.1.1", + "core-foundation", + "core-graphics", + "cursor-icon", + "icrate", + "js-sys", + "libc", + "log", + "memmap2", + "ndk", + "ndk-sys", + "objc2 0.4.1", + "once_cell", + "orbclient", + "percent-encoding", + "raw-window-handle 0.5.2", + "raw-window-handle 0.6.2", + "redox_syscall 0.3.5", + "rustix 0.38.44", + "sctk-adwaita", + "smithay-client-toolkit 0.18.1", + "smol_str", + "unicode-segmentation", + "wasm-bindgen", + "wasm-bindgen-futures", + "wayland-backend", + "wayland-client", + "wayland-protocols 0.31.2", + "wayland-protocols-plasma", + "web-sys", + "web-time", + "windows-sys 0.48.0", + "x11-dl", + "x11rb", + "xkbcommon-dl", +] + +[[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.7.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5364e9d77fcdeeaa6062ced926ee3381faa2ee02d3eb83a5c27a8825540829" +dependencies = [ + "memchr", +] + +[[package]] +name = "wit-bindgen" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" +dependencies = [ + "wit-bindgen-rust-macro", +] + +[[package]] +name = "wit-bindgen-core" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" +dependencies = [ + "anyhow", + "heck", + "wit-parser", +] + +[[package]] +name = "wit-bindgen-rust" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" +dependencies = [ + "anyhow", + "heck", + "indexmap", + "prettyplease", + "syn 2.0.117", + "wasm-metadata", + "wit-bindgen-core", + "wit-component", +] + +[[package]] +name = "wit-bindgen-rust-macro" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a" +dependencies = [ + "anyhow", + "prettyplease", + "proc-macro2", + "quote", + "syn 2.0.117", + "wit-bindgen-core", + "wit-bindgen-rust", +] + +[[package]] +name = "wit-component" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" +dependencies = [ + "anyhow", + "bitflags 2.11.0", + "indexmap", + "log", + "serde", + "serde_derive", + "serde_json", + "wasm-encoder", + "wasm-metadata", + "wasmparser", + "wit-parser", +] + +[[package]] +name = "wit-parser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" +dependencies = [ + "anyhow", + "id-arena", + "indexmap", + "log", + "semver", + "serde", + "serde_derive", + "serde_json", + "unicode-xid", + "wasmparser", +] + +[[package]] +name = "writeable" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" + +[[package]] +name = "x11-dl" +version = "2.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38735924fedd5314a6e548792904ed8c6de6636285cb9fec04d5b1db85c1516f" +dependencies = [ + "libc", + "once_cell", + "pkg-config", +] + +[[package]] +name = "x11rb" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9993aa5be5a26815fe2c3eacfc1fde061fc1a1f094bf1ad2a18bf9c495dd7414" +dependencies = [ + "as-raw-xcb-connection", + "gethostname", + "libc", + "libloading 0.8.9", + "once_cell", + "rustix 1.1.4", + "x11rb-protocol", +] + +[[package]] +name = "x11rb-protocol" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea6fc2961e4ef194dcbfe56bb845534d0dc8098940c7e5c012a258bfec6701bd" + +[[package]] +name = "xcursor" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bec9e4a500ca8864c5b47b8b482a73d62e4237670e5b5f1d6b9e3cae50f28f2b" + +[[package]] +name = "xdg-home" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec1cdab258fb55c0da61328dc52c8764709b249011b2cad0454c72f0bf10a1f6" +dependencies = [ + "libc", + "windows-sys 0.59.0", +] + +[[package]] +name = "xkbcommon-dl" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d039de8032a9a8856a6be89cea3e5d12fdd82306ab7c94d74e6deab2460651c5" +dependencies = [ + "bitflags 2.11.0", + "dlib", + "log", + "once_cell", + "xkeysym", +] + +[[package]] +name = "xkeysym" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9cc00251562a284751c9973bace760d86c0276c471b4be569fe6b068ee97a56" + +[[package]] +name = "xml-rs" +version = "0.8.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ae8337f8a065cfc972643663ea4279e04e7256de865aa66fe25cec5fb912d3f" + +[[package]] +name = "yoke" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72d6e5c6afb84d73944e5cedb052c4680d5657337201555f9f2a16b7406d4954" +dependencies = [ + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", + "synstructure", +] + +[[package]] +name = "zbus" +version = "3.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "675d170b632a6ad49804c8cf2105d7c31eddd3312555cffd4b740e08e97c25e6" +dependencies = [ + "async-broadcast 0.5.1", + "async-executor", + "async-fs 1.6.0", + "async-io 1.13.0", + "async-lock 2.8.0", + "async-process 1.8.1", + "async-recursion", + "async-task", + "async-trait", + "blocking", + "byteorder", + "derivative", + "enumflags2", + "event-listener 2.5.3", + "futures-core", + "futures-sink", + "futures-util", + "hex", + "nix 0.26.4", + "once_cell", + "ordered-stream", + "rand", + "serde", + "serde_repr", + "sha1", + "static_assertions", + "tracing", + "uds_windows", + "winapi", + "xdg-home", + "zbus_macros 3.15.2", + "zbus_names 2.6.1", + "zvariant 3.15.2", +] + +[[package]] +name = "zbus" +version = "4.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb97012beadd29e654708a0fdb4c84bc046f537aecfde2c3ee0a9e4b4d48c725" +dependencies = [ + "async-broadcast 0.7.2", + "async-executor", + "async-fs 2.2.0", + "async-io 2.6.0", + "async-lock 3.4.2", + "async-process 2.5.0", + "async-recursion", + "async-task", + "async-trait", + "blocking", + "enumflags2", + "event-listener 5.4.1", + "futures-core", + "futures-sink", + "futures-util", + "hex", + "nix 0.29.0", + "ordered-stream", + "rand", + "serde", + "serde_repr", + "sha1", + "static_assertions", + "tracing", + "uds_windows", + "windows-sys 0.52.0", + "xdg-home", + "zbus_macros 4.4.0", + "zbus_names 3.0.0", + "zvariant 4.2.0", +] + +[[package]] +name = "zbus_macros" +version = "3.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7131497b0f887e8061b430c530240063d33bf9455fa34438f388a245da69e0a5" +dependencies = [ + "proc-macro-crate 1.3.1", + "proc-macro2", + "quote", + "regex", + "syn 1.0.109", + "zvariant_utils 1.0.1", +] + +[[package]] +name = "zbus_macros" +version = "4.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "267db9407081e90bbfa46d841d3cbc60f59c0351838c4bc65199ecd79ab1983e" +dependencies = [ + "proc-macro-crate 3.4.0", + "proc-macro2", + "quote", + "syn 2.0.117", + "zvariant_utils 2.1.0", +] + +[[package]] +name = "zbus_names" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "437d738d3750bed6ca9b8d423ccc7a8eb284f6b1d6d4e225a0e4e6258d864c8d" +dependencies = [ + "serde", + "static_assertions", + "zvariant 3.15.2", +] + +[[package]] +name = "zbus_names" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b9b1fef7d021261cc16cba64c351d291b715febe0fa10dc3a443ac5a5022e6c" +dependencies = [ + "serde", + "static_assertions", + "zvariant 4.2.0", +] + +[[package]] +name = "zerocopy" +version = "0.8.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a789c6e490b576db9f7e6b6d661bcc9799f7c0ac8352f56ea20193b2681532e5" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f65c489a7071a749c849713807783f70672b28094011623e200cb86dcb835953" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "zerofrom" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", + "synstructure", +] + +[[package]] +name = "zerotrie" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a59c17a5562d507e4b54960e8569ebee33bee890c70aa3fe7b97e85a9fd7851" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", +] + +[[package]] +name = "zerovec" +version = "0.11.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "zmij" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" + +[[package]] +name = "zvariant" +version = "3.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4eef2be88ba09b358d3b58aca6e41cd853631d44787f319a1383ca83424fb2db" +dependencies = [ + "byteorder", + "enumflags2", + "libc", + "serde", + "static_assertions", + "zvariant_derive 3.15.2", +] + +[[package]] +name = "zvariant" +version = "4.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2084290ab9a1c471c38fc524945837734fbf124487e105daec2bb57fd48c81fe" +dependencies = [ + "endi", + "enumflags2", + "serde", + "static_assertions", + "url", + "zvariant_derive 4.2.0", +] + +[[package]] +name = "zvariant_derive" +version = "3.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37c24dc0bed72f5f90d1f8bb5b07228cbf63b3c6e9f82d82559d4bae666e7ed9" +dependencies = [ + "proc-macro-crate 1.3.1", + "proc-macro2", + "quote", + "syn 1.0.109", + "zvariant_utils 1.0.1", +] + +[[package]] +name = "zvariant_derive" +version = "4.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73e2ba546bda683a90652bac4a279bc146adad1386f25379cf73200d2002c449" +dependencies = [ + "proc-macro-crate 3.4.0", + "proc-macro2", + "quote", + "syn 2.0.117", + "zvariant_utils 2.1.0", +] + +[[package]] +name = "zvariant_utils" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7234f0d811589db492d16893e3f21e8e2fd282e6d01b0cddee310322062cc200" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "zvariant_utils" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c51bcff7cc3dbb5055396bcf774748c3dab426b4b8659046963523cee4808340" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] diff --git a/Cargo.toml b/Cargo.toml index 748e520..ce37766 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,3 +4,9 @@ version = "0.1.0" edition = "2024" [dependencies] +eframe = "0.27" +anyhow = "1" +serde = { version = "1", features = ["derive"] } +serde_json = "1" +tokio = { version = "1", features = ["process", "rt-multi-thread", "macros", "io-util", "sync"] } +rfd = "0.14" diff --git a/src/adapters/ffmpeg/command_builder.rs b/src/adapters/ffmpeg/command_builder.rs new file mode 100644 index 0000000..78d072c --- /dev/null +++ b/src/adapters/ffmpeg/command_builder.rs @@ -0,0 +1,213 @@ +use crate::domain::entities::{Project, Track, TrackKind}; + +/// Constrói os argumentos do FFmpeg a partir de um Project. +/// +/// Invariantes: +/// - `-c copy` é **sempre** emitido (RNF-01 — nunca opcional) +/// - `SyncOffset(ms)` → `-itsoffset {seconds:.3}` (conversão feita somente aqui) +/// - `TrackId` → `-map N:tipo` (mapeamento feito somente aqui) +pub struct FfmpegCommandBuilder; + +impl FfmpegCommandBuilder { + /// Constrói o vetor de argumentos para o FFmpeg. Não inclui o binário "ffmpeg" no início. + pub fn build(project: &Project) -> Vec { + let mut args: Vec = Vec::new(); + + // ── Inputs ────────────────────────────────────────────────────────────── + // Input 0: arquivo fonte (sempre sem itsoffset próprio) + args.push("-i".to_string()); + args.push(project.source.path.to_string_lossy().to_string()); + + // Para faixas existentes com offset não-zero, adicionamos o source novamente + // como input extra com itsoffset. Agrupamos por valor de offset para minimizar inputs. + let mut offset_inputs: Vec<(i64, usize)> = Vec::new(); + let mut next_input_idx = 1usize; + + for track in &project.existing_tracks { + if !track.offset.is_zero() { + let ms = track.offset.as_ms(); + if !offset_inputs.iter().any(|(v, _)| *v == ms) { + let seconds = ms as f64 / 1000.0; + args.push("-itsoffset".to_string()); + args.push(format!("{:.3}", seconds)); + args.push("-i".to_string()); + args.push(project.source.path.to_string_lossy().to_string()); + offset_inputs.push((ms, next_input_idx)); + next_input_idx += 1; // contador de inputs usados + } + } + } + + // Inputs externos (faixas adicionadas pelo usuário) + let external_input_start = next_input_idx; + for track in &project.tracks { + let offset = track.offset(); + if !offset.is_zero() { + let seconds = offset.as_ms() as f64 / 1000.0; + args.push("-itsoffset".to_string()); + args.push(format!("{:.3}", seconds)); + } + args.push("-i".to_string()); + let path = match track { + Track::Audio(t) => t.path.to_string_lossy().to_string(), + Track::Subtitle(t) => t.path.to_string_lossy().to_string(), + }; + args.push(path); + next_input_idx += 1; + } + + // ── Maps ──────────────────────────────────────────────────────────────── + // Faixas existentes do source + for track in &project.existing_tracks { + args.push("-map".to_string()); + if track.offset.is_zero() { + args.push(format!("0:{}", track.stream_index)); + } else { + let ms = track.offset.as_ms(); + if let Some((_, idx)) = offset_inputs.iter().find(|(v, _)| *v == ms) { + args.push(format!("{}:{}", idx, track.stream_index)); + } + } + } + + // Se não há faixas existentes mapeadas, mapeia tudo do source + if project.existing_tracks.is_empty() { + args.push("-map".to_string()); + args.push("0".to_string()); + } + + // Faixas externas do usuário + let mut ext_idx = external_input_start; + for track in &project.tracks { + args.push("-map".to_string()); + match track { + Track::Audio(_) => args.push(format!("{}:a", ext_idx)), + Track::Subtitle(_) => args.push(format!("{}:s", ext_idx)), + } + ext_idx += 1; + } + + // ── -c copy (SEMPRE — invariante central do produto) ──────────────────── + args.push("-c".to_string()); + args.push("copy".to_string()); + + // ── Metadados de idioma ────────────────────────────────────────────────── + let existing_audio_count = project + .existing_tracks + .iter() + .filter(|t| matches!(t.kind, TrackKind::Audio)) + .count(); + let existing_sub_count = project + .existing_tracks + .iter() + .filter(|t| matches!(t.kind, TrackKind::Subtitle)) + .count(); + + let mut ext_audio_idx = existing_audio_count; + let mut ext_sub_idx = existing_sub_count; + + for track in &project.tracks { + match track { + Track::Audio(t) => { + args.push(format!("-metadata:s:a:{}", ext_audio_idx)); + args.push(format!("language={}", t.language)); + ext_audio_idx += 1; + } + Track::Subtitle(t) => { + args.push(format!("-metadata:s:s:{}", ext_sub_idx)); + args.push(format!("language={}", t.language)); + ext_sub_idx += 1; + } + } + } + + // ── Output ────────────────────────────────────────────────────────────── + args.push(project.output.path.to_string_lossy().to_string()); + + args + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::domain::entities::{MkvOutput, Project, VideoFile}; + use crate::domain::value_objects::{FilePath, SyncOffset, TrackLanguage}; + use crate::application::use_cases::add_audio_track::AddAudioTrack; + + fn base_project() -> Project { + Project::new( + VideoFile::new(FilePath::from("input.mkv")), + MkvOutput::new(FilePath::from("output.mkv")), + ) + .unwrap() + } + + #[test] + fn sempre_contem_c_copy() { + let project = base_project(); + let args = FfmpegCommandBuilder::build(&project); + let pos_c = args.iter().position(|a| a == "-c"); + assert!(pos_c.is_some(), "faltou -c"); + assert_eq!(args[pos_c.unwrap() + 1], "copy", "faltou copy após -c"); + } + + #[test] + fn output_e_o_ultimo_argumento() { + let project = base_project(); + let args = FfmpegCommandBuilder::build(&project); + assert_eq!(args.last().unwrap(), "output.mkv"); + } + + #[test] + fn itsoffset_formato_correto() { + let mut project = base_project(); + AddAudioTrack::execute( + &mut project, + FilePath::from("audio.aac"), + SyncOffset::from_ms(1200), + TrackLanguage::new("por").unwrap(), + ) + .unwrap(); + + let args = FfmpegCommandBuilder::build(&project); + let pos = args.iter().position(|a| a == "-itsoffset"); + assert!(pos.is_some(), "faltou -itsoffset"); + assert_eq!(args[pos.unwrap() + 1], "1.200"); + } + + #[test] + fn mapa_faixa_externa_de_audio() { + let mut project = base_project(); + AddAudioTrack::execute( + &mut project, + FilePath::from("audio.aac"), + SyncOffset::default(), + TrackLanguage::new("por").unwrap(), + ) + .unwrap(); + + let args = FfmpegCommandBuilder::build(&project); + assert!(args.contains(&"1:a".to_string()), "faltou map 1:a — args: {:?}", args); + } + + #[test] + fn metadata_idioma_audio() { + let mut project = base_project(); + AddAudioTrack::execute( + &mut project, + FilePath::from("audio.aac"), + SyncOffset::default(), + TrackLanguage::new("por").unwrap(), + ) + .unwrap(); + + let args = FfmpegCommandBuilder::build(&project); + assert!( + args.contains(&"-metadata:s:a:0".to_string()), + "faltou metadata — args: {:?}", + args + ); + assert!(args.contains(&"language=por".to_string())); + } +} diff --git a/src/adapters/ffmpeg/ffmpeg_gateway.rs b/src/adapters/ffmpeg/ffmpeg_gateway.rs new file mode 100644 index 0000000..9bf61af --- /dev/null +++ b/src/adapters/ffmpeg/ffmpeg_gateway.rs @@ -0,0 +1,26 @@ +use anyhow::{anyhow, Context, Result}; +use std::process::Command; +use crate::application::ports::MediaProcessorPort; + +/// Implementa MediaProcessorPort executando o binário `ffmpeg`. +pub struct FfmpegGateway; + +impl MediaProcessorPort for FfmpegGateway { + fn execute(&self, args: Vec) -> Result<()> { + let output = Command::new("ffmpeg") + .args(&args) + .output() + .context("Falha ao executar ffmpeg. Verifique se está instalado e no PATH.")?; + + if !output.status.success() { + let stderr = String::from_utf8_lossy(&output.stderr); + return Err(anyhow!( + "FFmpeg encerrou com erro (código {:?}):\n{}", + output.status.code(), + stderr + )); + } + + Ok(()) + } +} diff --git a/src/adapters/ffmpeg/ffprobe_gateway.rs b/src/adapters/ffmpeg/ffprobe_gateway.rs new file mode 100644 index 0000000..dc99c41 --- /dev/null +++ b/src/adapters/ffmpeg/ffprobe_gateway.rs @@ -0,0 +1,75 @@ +use anyhow::{anyhow, Context, Result}; +use serde::Deserialize; +use std::process::Command; +use crate::application::ports::MediaInfoPort; +use crate::domain::entities::{MediaTrackInfo, TrackKind}; +use crate::domain::value_objects::{FilePath, TrackId, TrackLanguage}; + +/// Implementa MediaInfoPort executando `ffprobe` e parseando a saída JSON. +pub struct FfprobeGateway; + +// ── Structs de deserialização do JSON do ffprobe ───────────────────────────── + +#[derive(Deserialize)] +struct FfprobeOutput { + streams: Vec, +} + +#[derive(Deserialize)] +struct FfprobeStream { + index: u32, + codec_type: Option, + codec_name: Option, + tags: Option, +} + +#[derive(Deserialize)] +struct FfprobeTags { + language: Option, +} + +impl MediaInfoPort for FfprobeGateway { + fn probe(&self, path: &FilePath) -> Result> { + let output = Command::new("ffprobe") + .args([ + "-v", "quiet", + "-print_format", "json", + "-show_streams", + path.to_str().ok_or_else(|| anyhow!("Caminho inválido"))?, + ]) + .output() + .context("Falha ao executar ffprobe. Verifique se está instalado e no PATH.")?; + + if !output.status.success() { + let stderr = String::from_utf8_lossy(&output.stderr); + return Err(anyhow!("ffprobe retornou erro:\n{}", stderr)); + } + + let json = String::from_utf8_lossy(&output.stdout); + let parsed: FfprobeOutput = + serde_json::from_str(&json).context("Falha ao interpretar saída do ffprobe")?; + + let tracks = parsed + .streams + .into_iter() + .filter_map(|s| { + let kind = match s.codec_type.as_deref() { + Some("video") => TrackKind::Video, + Some("audio") => TrackKind::Audio, + Some("subtitle") => TrackKind::Subtitle, + Some("data") => TrackKind::Data, + _ => return None, + }; + let codec = s.codec_name.unwrap_or_else(|| "unknown".to_string()); + let language = s + .tags + .and_then(|t| t.language) + .and_then(|l| TrackLanguage::new(l).ok()); + let id = TrackId::new(s.index); + Some(MediaTrackInfo::new(id, kind, codec, language, s.index)) + }) + .collect(); + + Ok(tracks) + } +} diff --git a/src/adapters/ffmpeg/mod.rs b/src/adapters/ffmpeg/mod.rs new file mode 100644 index 0000000..6499a75 --- /dev/null +++ b/src/adapters/ffmpeg/mod.rs @@ -0,0 +1,7 @@ +pub mod command_builder; +pub mod ffmpeg_gateway; +pub mod ffprobe_gateway; + +pub use command_builder::FfmpegCommandBuilder; +pub use ffmpeg_gateway::FfmpegGateway; +pub use ffprobe_gateway::FfprobeGateway; diff --git a/src/adapters/filesystem/file_picker.rs b/src/adapters/filesystem/file_picker.rs new file mode 100644 index 0000000..9eb65e1 --- /dev/null +++ b/src/adapters/filesystem/file_picker.rs @@ -0,0 +1,44 @@ +use std::path::PathBuf; +use crate::domain::value_objects::FilePath; + +/// Abre diálogos de seleção de arquivo usando a crate `rfd`. +pub struct FilePickerAdapter; + +impl FilePickerAdapter { + /// Abre diálogo de seleção de arquivo de vídeo. + pub fn pick_video() -> Option { + rfd::FileDialog::new() + .add_filter("Vídeo", &["mkv", "mp4", "avi", "mov", "ts"]) + .pick_file() + .map(|p: PathBuf| FilePath::from(p)) + } + + /// Abre diálogo de seleção de arquivo de áudio. + pub fn pick_audio() -> Option { + rfd::FileDialog::new() + .add_filter("Áudio", &["aac", "mp3", "flac", "ogg", "m4a", "wav", "ac3", "dts"]) + .pick_file() + .map(|p: PathBuf| FilePath::from(p)) + } + + /// Abre diálogo de seleção de arquivo de legenda. + pub fn pick_subtitle() -> Option { + rfd::FileDialog::new() + .add_filter("Legenda", &["srt", "ass", "ssa", "vtt", "sub"]) + .pick_file() + .map(|p: PathBuf| FilePath::from(p)) + } + + /// Abre diálogo de salvar arquivo MKV de saída. + pub fn pick_output() -> Option { + rfd::FileDialog::new() + .add_filter("MKV", &["mkv"]) + .set_file_name("output.mkv") + .save_file() + .map(|mut p: PathBuf| { + // Força extensão .mkv + p.set_extension("mkv"); + FilePath::from(p) + }) + } +} diff --git a/src/adapters/filesystem/mod.rs b/src/adapters/filesystem/mod.rs new file mode 100644 index 0000000..463f0a6 --- /dev/null +++ b/src/adapters/filesystem/mod.rs @@ -0,0 +1,14 @@ +use crate::application::ports::FileSystemPort; +use crate::domain::value_objects::FilePath; + +pub mod file_picker; +pub use file_picker::FilePickerAdapter; + +/// Implementação concreta do FileSystemPort. +pub struct RealFileSystem; + +impl FileSystemPort for RealFileSystem { + fn exists(&self, path: &FilePath) -> bool { + path.as_path().exists() + } +} diff --git a/src/adapters/mod.rs b/src/adapters/mod.rs new file mode 100644 index 0000000..e3f9c17 --- /dev/null +++ b/src/adapters/mod.rs @@ -0,0 +1,2 @@ +pub mod ffmpeg; +pub mod filesystem; diff --git a/src/application/mod.rs b/src/application/mod.rs new file mode 100644 index 0000000..7ca4f38 --- /dev/null +++ b/src/application/mod.rs @@ -0,0 +1,2 @@ +pub mod ports; +pub mod use_cases; diff --git a/src/application/ports/mod.rs b/src/application/ports/mod.rs new file mode 100644 index 0000000..510dd37 --- /dev/null +++ b/src/application/ports/mod.rs @@ -0,0 +1,18 @@ +use anyhow::Result; +use crate::domain::entities::MediaTrackInfo; +use crate::domain::value_objects::FilePath; + +/// Port para inspecionar faixas de um arquivo de mídia (implementado pelo FfprobeGateway). +pub trait MediaInfoPort { + fn probe(&self, path: &FilePath) -> Result>; +} + +/// Port para executar o processamento final de mídia (implementado pelo FfmpegGateway). +pub trait MediaProcessorPort { + fn execute(&self, args: Vec) -> Result<()>; +} + +/// Port para abstrair acesso ao sistema de arquivos. +pub trait FileSystemPort { + fn exists(&self, path: &FilePath) -> bool; +} diff --git a/src/application/use_cases/add_audio_track.rs b/src/application/use_cases/add_audio_track.rs new file mode 100644 index 0000000..cce0ab2 --- /dev/null +++ b/src/application/use_cases/add_audio_track.rs @@ -0,0 +1,48 @@ +use anyhow::Result; +use crate::domain::entities::{AudioTrack, Project, Track}; +use crate::domain::value_objects::{FilePath, SyncOffset, TrackLanguage}; + +/// Adiciona uma faixa de áudio externa ao projeto. +pub struct AddAudioTrack; + +impl AddAudioTrack { + pub fn execute( + project: &mut Project, + path: FilePath, + offset: SyncOffset, + language: TrackLanguage, + ) -> Result<()> { + let id = project.next_track_id(); + let track = AudioTrack::new(id, path, offset, language); + project.tracks.push(Track::Audio(track)); + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::domain::entities::{MkvOutput, VideoFile}; + use crate::domain::value_objects::FilePath; + + #[test] + fn adiciona_audio_no_projeto() { + let mut project = Project::new( + VideoFile::new(FilePath::from("input.mkv")), + MkvOutput::new(FilePath::from("output.mkv")), + ) + .unwrap(); + + let lang = TrackLanguage::new("por").unwrap(); + AddAudioTrack::execute( + &mut project, + FilePath::from("audio_pt.aac"), + SyncOffset::default(), + lang, + ) + .unwrap(); + + assert_eq!(project.tracks.len(), 1); + assert!(matches!(project.tracks[0], Track::Audio(_))); + } +} diff --git a/src/application/use_cases/add_subtitle.rs b/src/application/use_cases/add_subtitle.rs new file mode 100644 index 0000000..1c88e37 --- /dev/null +++ b/src/application/use_cases/add_subtitle.rs @@ -0,0 +1,47 @@ +use anyhow::Result; +use crate::domain::entities::{Project, SubtitleTrack, Track}; +use crate::domain::value_objects::{FilePath, SyncOffset, TrackLanguage}; + +/// Adiciona uma faixa de legenda externa ao projeto. +pub struct AddSubtitle; + +impl AddSubtitle { + pub fn execute( + project: &mut Project, + path: FilePath, + offset: SyncOffset, + language: TrackLanguage, + ) -> Result<()> { + let id = project.next_track_id(); + let track = SubtitleTrack::new(id, path, offset, language); + project.tracks.push(Track::Subtitle(track)); + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::domain::entities::{MkvOutput, VideoFile}; + + #[test] + fn adiciona_legenda_no_projeto() { + let mut project = Project::new( + VideoFile::new(FilePath::from("input.mkv")), + MkvOutput::new(FilePath::from("output.mkv")), + ) + .unwrap(); + + let lang = TrackLanguage::new("eng").unwrap(); + AddSubtitle::execute( + &mut project, + FilePath::from("sub_en.srt"), + SyncOffset::default(), + lang, + ) + .unwrap(); + + assert_eq!(project.tracks.len(), 1); + assert!(matches!(project.tracks[0], Track::Subtitle(_))); + } +} diff --git a/src/application/use_cases/adjust_sync.rs b/src/application/use_cases/adjust_sync.rs new file mode 100644 index 0000000..67dfcac --- /dev/null +++ b/src/application/use_cases/adjust_sync.rs @@ -0,0 +1,56 @@ +use anyhow::{anyhow, Result}; +use crate::domain::entities::Project; +use crate::domain::value_objects::{SyncOffset, TrackId}; + +/// Ajusta o offset de sincronização de uma faixa externa existente pelo TrackId. +pub struct AdjustSync; + +impl AdjustSync { + pub fn execute(project: &mut Project, id: TrackId, offset: SyncOffset) -> Result<()> { + let track = project + .find_track_mut(id) + .ok_or_else(|| anyhow!("Faixa não encontrada: {:?}", id))?; + track.set_offset(offset); + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::application::use_cases::add_audio_track::AddAudioTrack; + use crate::domain::entities::{MkvOutput, Project, VideoFile}; + use crate::domain::value_objects::{FilePath, TrackLanguage}; + + #[test] + fn ajusta_offset_existente() { + let mut project = Project::new( + VideoFile::new(FilePath::from("input.mkv")), + MkvOutput::new(FilePath::from("output.mkv")), + ) + .unwrap(); + let lang = TrackLanguage::new("por").unwrap(); + AddAudioTrack::execute( + &mut project, + FilePath::from("audio.aac"), + SyncOffset::default(), + lang, + ) + .unwrap(); + let id = project.tracks[0].id(); + + AdjustSync::execute(&mut project, id, SyncOffset::from_ms(1200)).unwrap(); + assert_eq!(project.tracks[0].offset().as_ms(), 1200); + } + + #[test] + fn erro_se_id_inexistente() { + let mut project = Project::new( + VideoFile::new(FilePath::from("input.mkv")), + MkvOutput::new(FilePath::from("output.mkv")), + ) + .unwrap(); + let result = AdjustSync::execute(&mut project, TrackId::new(99), SyncOffset::from_ms(500)); + assert!(result.is_err()); + } +} diff --git a/src/application/use_cases/edit_existing_track_sync.rs b/src/application/use_cases/edit_existing_track_sync.rs new file mode 100644 index 0000000..560891f --- /dev/null +++ b/src/application/use_cases/edit_existing_track_sync.rs @@ -0,0 +1,49 @@ +use anyhow::{anyhow, Result}; +use crate::domain::entities::Project; +use crate::domain::value_objects::{SyncOffset, TrackId}; + +/// Ajusta o offset de sincronização de uma faixa já presente no arquivo original (existing_tracks). +pub struct EditExistingTrackSync; + +impl EditExistingTrackSync { + pub fn execute(project: &mut Project, id: TrackId, offset: SyncOffset) -> Result<()> { + let track = project + .find_existing_track_mut(id) + .ok_or_else(|| anyhow!("Faixa existente não encontrada: {:?}", id))?; + track.offset = offset; + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::domain::entities::{MediaTrackInfo, MkvOutput, Project, TrackKind, VideoFile}; + use crate::domain::value_objects::{FilePath, TrackId}; + + #[test] + fn edita_offset_de_faixa_existente() { + let mut project = Project::new( + VideoFile::new(FilePath::from("input.mkv")), + MkvOutput::new(FilePath::from("output.mkv")), + ) + .unwrap(); + let id = TrackId::new(1); + project.existing_tracks.push(MediaTrackInfo::new(id, TrackKind::Audio, "aac", None, 1)); + + EditExistingTrackSync::execute(&mut project, id, SyncOffset::from_ms(-500)).unwrap(); + assert_eq!(project.existing_tracks[0].offset.as_ms(), -500); + } + + #[test] + fn erro_se_faixa_existente_nao_encontrada() { + let mut project = Project::new( + VideoFile::new(FilePath::from("input.mkv")), + MkvOutput::new(FilePath::from("output.mkv")), + ) + .unwrap(); + let result = + EditExistingTrackSync::execute(&mut project, TrackId::new(99), SyncOffset::from_ms(0)); + assert!(result.is_err()); + } +} diff --git a/src/application/use_cases/generate_output.rs b/src/application/use_cases/generate_output.rs new file mode 100644 index 0000000..b3149ab --- /dev/null +++ b/src/application/use_cases/generate_output.rs @@ -0,0 +1,65 @@ +use anyhow::Result; +use crate::application::ports::MediaProcessorPort; +use crate::domain::entities::Project; + +/// Constrói o comando FFmpeg a partir do Project e delega a execução ao MediaProcessorPort. +pub struct GenerateOutput; + +impl GenerateOutput { + pub fn execute(project: &Project, port: &dyn MediaProcessorPort) -> Result<()> { + use crate::adapters::ffmpeg::command_builder::FfmpegCommandBuilder; + let args = FfmpegCommandBuilder::build(project); + port.execute(args) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use anyhow::Result; + use crate::application::ports::MediaProcessorPort; + use crate::application::use_cases::add_audio_track::AddAudioTrack; + use crate::domain::entities::{MkvOutput, Project, VideoFile}; + use crate::domain::value_objects::{FilePath, SyncOffset, TrackLanguage}; + use std::cell::RefCell; + + struct MockProcessor { + captured_args: RefCell>, + } + + impl MediaProcessorPort for MockProcessor { + fn execute(&self, args: Vec) -> Result<()> { + *self.captured_args.borrow_mut() = args; + Ok(()) + } + } + + #[test] + fn sempre_inclui_c_copy() { + let mut project = Project::new( + VideoFile::new(FilePath::from("input.mkv")), + MkvOutput::new(FilePath::from("output.mkv")), + ) + .unwrap(); + + AddAudioTrack::execute( + &mut project, + FilePath::from("audio_pt.aac"), + SyncOffset::from_ms(1200), + TrackLanguage::new("por").unwrap(), + ) + .unwrap(); + + let mock = MockProcessor { + captured_args: RefCell::new(vec![]), + }; + GenerateOutput::execute(&project, &mock).unwrap(); + + let args = mock.captured_args.borrow(); + assert!( + args.contains(&"-c".to_string()) && args.contains(&"copy".to_string()), + "Comando não continha '-c copy': {:?}", + args + ); + } +} diff --git a/src/application/use_cases/load_media_info.rs b/src/application/use_cases/load_media_info.rs new file mode 100644 index 0000000..fc11361 --- /dev/null +++ b/src/application/use_cases/load_media_info.rs @@ -0,0 +1,55 @@ +use anyhow::Result; +use crate::application::ports::MediaInfoPort; +use crate::domain::entities::Project; + +/// Carrega as faixas existentes do arquivo de vídeo fonte e popula `Project::existing_tracks`. +pub struct LoadMediaInfo; + +impl LoadMediaInfo { + pub fn execute(project: &mut Project, port: &dyn MediaInfoPort) -> Result<()> { + let tracks = port.probe(&project.source.path)?; + project.existing_tracks = tracks; + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use anyhow::Result; + use crate::application::ports::MediaInfoPort; + use crate::domain::entities::{MediaTrackInfo, MkvOutput, Project, TrackKind, VideoFile}; + use crate::domain::value_objects::{FilePath, TrackId}; + + struct MockMediaInfoPort { + tracks: Vec, + } + + impl MediaInfoPort for MockMediaInfoPort { + fn probe(&self, _path: &FilePath) -> Result> { + Ok(self.tracks.clone()) + } + } + + #[test] + fn carrega_faixas_no_projeto() { + let mut project = Project::new( + VideoFile::new(FilePath::from("input.mkv")), + MkvOutput::new(FilePath::from("output.mkv")), + ) + .unwrap(); + + let mock = MockMediaInfoPort { + tracks: vec![MediaTrackInfo::new( + TrackId::new(1), + TrackKind::Audio, + "aac", + None, + 0, + )], + }; + + LoadMediaInfo::execute(&mut project, &mock).unwrap(); + assert_eq!(project.existing_tracks.len(), 1); + } +} diff --git a/src/application/use_cases/mod.rs b/src/application/use_cases/mod.rs new file mode 100644 index 0000000..6543083 --- /dev/null +++ b/src/application/use_cases/mod.rs @@ -0,0 +1,7 @@ +pub mod add_audio_track; +pub mod add_subtitle; +pub mod adjust_sync; +pub mod edit_existing_track_sync; +pub mod generate_output; +pub mod load_media_info; +pub mod set_track_language; diff --git a/src/application/use_cases/set_track_language.rs b/src/application/use_cases/set_track_language.rs new file mode 100644 index 0000000..0ac0bbf --- /dev/null +++ b/src/application/use_cases/set_track_language.rs @@ -0,0 +1,44 @@ +use anyhow::{anyhow, Result}; +use crate::domain::entities::Project; +use crate::domain::value_objects::{TrackId, TrackLanguage}; + +/// Altera o idioma de uma faixa externa pelo TrackId. +pub struct SetTrackLanguage; + +impl SetTrackLanguage { + pub fn execute(project: &mut Project, id: TrackId, language: TrackLanguage) -> Result<()> { + let track = project + .find_track_mut(id) + .ok_or_else(|| anyhow!("Faixa não encontrada: {:?}", id))?; + track.set_language(language); + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::application::use_cases::add_audio_track::AddAudioTrack; + use crate::domain::entities::{MkvOutput, Project, VideoFile}; + use crate::domain::value_objects::{FilePath, SyncOffset}; + + #[test] + fn altera_idioma_da_faixa() { + let mut project = Project::new( + VideoFile::new(FilePath::from("input.mkv")), + MkvOutput::new(FilePath::from("output.mkv")), + ) + .unwrap(); + AddAudioTrack::execute( + &mut project, + FilePath::from("audio.aac"), + SyncOffset::default(), + TrackLanguage::new("por").unwrap(), + ) + .unwrap(); + let id = project.tracks[0].id(); + + SetTrackLanguage::execute(&mut project, id, TrackLanguage::new("eng").unwrap()).unwrap(); + assert_eq!(project.tracks[0].language().as_str(), "eng"); + } +} diff --git a/src/domain/entities/audio_track.rs b/src/domain/entities/audio_track.rs new file mode 100644 index 0000000..ee28d77 --- /dev/null +++ b/src/domain/entities/audio_track.rs @@ -0,0 +1,17 @@ +use serde::{Deserialize, Serialize}; +use crate::domain::value_objects::{FilePath, SyncOffset, TrackId, TrackLanguage}; + +/// Faixa de áudio externa adicionada pelo usuário. +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct AudioTrack { + pub id: TrackId, + pub path: FilePath, + pub offset: SyncOffset, + pub language: TrackLanguage, +} + +impl AudioTrack { + pub fn new(id: TrackId, path: FilePath, offset: SyncOffset, language: TrackLanguage) -> Self { + AudioTrack { id, path, offset, language } + } +} diff --git a/src/domain/entities/media_track_info.rs b/src/domain/entities/media_track_info.rs new file mode 100644 index 0000000..78d66b5 --- /dev/null +++ b/src/domain/entities/media_track_info.rs @@ -0,0 +1,54 @@ +use serde::{Deserialize, Serialize}; +use crate::domain::value_objects::{SyncOffset, TrackId, TrackLanguage}; + +/// Tipo de stream de mídia. +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub enum TrackKind { + Video, + Audio, + Subtitle, + Data, +} + +impl std::fmt::Display for TrackKind { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + TrackKind::Video => write!(f, "Vídeo"), + TrackKind::Audio => write!(f, "Áudio"), + TrackKind::Subtitle => write!(f, "Legenda"), + TrackKind::Data => write!(f, "Dados"), + } + } +} + +/// Informação de uma faixa já presente no arquivo de mídia (lida via ffprobe). +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct MediaTrackInfo { + pub id: TrackId, + pub kind: TrackKind, + pub codec: String, + pub language: Option, + /// Offset de sincronização — pode ser ajustado via EditExistingTrackSync. + pub offset: SyncOffset, + /// Índice do stream no container original (usado internamente pelo adapter). + pub stream_index: u32, +} + +impl MediaTrackInfo { + pub fn new( + id: TrackId, + kind: TrackKind, + codec: impl Into, + language: Option, + stream_index: u32, + ) -> Self { + MediaTrackInfo { + id, + kind, + codec: codec.into(), + language, + offset: SyncOffset::default(), + stream_index, + } + } +} diff --git a/src/domain/entities/mkv_output.rs b/src/domain/entities/mkv_output.rs new file mode 100644 index 0000000..c5d3e61 --- /dev/null +++ b/src/domain/entities/mkv_output.rs @@ -0,0 +1,14 @@ +use serde::{Deserialize, Serialize}; +use crate::domain::value_objects::FilePath; + +/// Arquivo MKV de saída. +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct MkvOutput { + pub path: FilePath, +} + +impl MkvOutput { + pub fn new(path: FilePath) -> Self { + MkvOutput { path } + } +} diff --git a/src/domain/entities/mod.rs b/src/domain/entities/mod.rs new file mode 100644 index 0000000..017b1b4 --- /dev/null +++ b/src/domain/entities/mod.rs @@ -0,0 +1,15 @@ +pub mod audio_track; +pub mod media_track_info; +pub mod mkv_output; +pub mod project; +pub mod subtitle_track; +pub mod track; +pub mod video_file; + +pub use audio_track::AudioTrack; +pub use media_track_info::{MediaTrackInfo, TrackKind}; +pub use mkv_output::MkvOutput; +pub use project::Project; +pub use subtitle_track::SubtitleTrack; +pub use track::Track; +pub use video_file::VideoFile; diff --git a/src/domain/entities/project.rs b/src/domain/entities/project.rs new file mode 100644 index 0000000..7232e7d --- /dev/null +++ b/src/domain/entities/project.rs @@ -0,0 +1,81 @@ +use anyhow::{anyhow, Result}; +use serde::{Deserialize, Serialize}; +use crate::domain::entities::{MediaTrackInfo, MkvOutput, Track, VideoFile}; +use crate::domain::value_objects::TrackId; + +/// Entidade raiz do domínio — representa a sessão de edição completa do usuário. +/// É a única fonte de verdade do estado em memória. +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct Project { + pub source: VideoFile, + /// Faixas externas adicionadas pelo usuário. + pub tracks: Vec, + /// Faixas existentes lidas do arquivo via ffprobe. + pub existing_tracks: Vec, + pub output: MkvOutput, +} + +impl Project { + /// Cria um novo projeto. Retorna erro se output e source apontam para o mesmo arquivo. + pub fn new(source: VideoFile, output: MkvOutput) -> Result { + if source.path == output.path { + return Err(anyhow!( + "O arquivo de saída não pode ser o mesmo que o arquivo de origem: '{}'", + source.path + )); + } + Ok(Project { + source, + tracks: Vec::new(), + existing_tracks: Vec::new(), + output, + }) + } + + /// Retorna o próximo TrackId disponível para uso. + pub fn next_track_id(&self) -> TrackId { + let max = self.tracks.iter().map(|t| t.id().val()).max().unwrap_or(0); + TrackId::new(max + 1) + } + + /// Busca uma faixa externa pelo TrackId. + pub fn find_track_mut(&mut self, id: TrackId) -> Option<&mut Track> { + self.tracks.iter_mut().find(|t| t.id() == id) + } + + /// Busca uma faixa existente pelo TrackId. + pub fn find_existing_track_mut(&mut self, id: TrackId) -> Option<&mut MediaTrackInfo> { + self.existing_tracks.iter_mut().find(|t| t.id == id) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::domain::value_objects::FilePath; + + fn make_project(src: &str, out: &str) -> Result { + Project::new( + VideoFile::new(FilePath::from(src)), + MkvOutput::new(FilePath::from(out)), + ) + } + + #[test] + fn projeto_valido() { + assert!(make_project("input.mkv", "output.mkv").is_ok()); + } + + #[test] + fn projeto_invalido_mesmo_caminho() { + assert!(make_project("same.mkv", "same.mkv").is_err()); + } + + #[test] + fn track_id_opaco() { + let id = TrackId::new(42); + // TrackId não expõe indexação interna — apenas comparação e cópia + let id2 = id; + assert_eq!(id, id2); + } +} diff --git a/src/domain/entities/subtitle_track.rs b/src/domain/entities/subtitle_track.rs new file mode 100644 index 0000000..f6a8160 --- /dev/null +++ b/src/domain/entities/subtitle_track.rs @@ -0,0 +1,17 @@ +use serde::{Deserialize, Serialize}; +use crate::domain::value_objects::{FilePath, SyncOffset, TrackId, TrackLanguage}; + +/// Faixa de legenda externa adicionada pelo usuário. +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct SubtitleTrack { + pub id: TrackId, + pub path: FilePath, + pub offset: SyncOffset, + pub language: TrackLanguage, +} + +impl SubtitleTrack { + pub fn new(id: TrackId, path: FilePath, offset: SyncOffset, language: TrackLanguage) -> Self { + SubtitleTrack { id, path, offset, language } + } +} diff --git a/src/domain/entities/track.rs b/src/domain/entities/track.rs new file mode 100644 index 0000000..94b662e --- /dev/null +++ b/src/domain/entities/track.rs @@ -0,0 +1,47 @@ +use serde::{Deserialize, Serialize}; +use crate::domain::entities::{AudioTrack, SubtitleTrack}; +use crate::domain::value_objects::{SyncOffset, TrackId, TrackLanguage}; + +/// Faixa externa adicionada pelo usuário ao projeto. +#[derive(Clone, Debug, Serialize, Deserialize)] +pub enum Track { + Audio(AudioTrack), + Subtitle(SubtitleTrack), +} + +impl Track { + pub fn id(&self) -> TrackId { + match self { + Track::Audio(t) => t.id, + Track::Subtitle(t) => t.id, + } + } + + pub fn offset(&self) -> SyncOffset { + match self { + Track::Audio(t) => t.offset, + Track::Subtitle(t) => t.offset, + } + } + + pub fn language(&self) -> &TrackLanguage { + match self { + Track::Audio(t) => &t.language, + Track::Subtitle(t) => &t.language, + } + } + + pub fn set_offset(&mut self, offset: SyncOffset) { + match self { + Track::Audio(t) => t.offset = offset, + Track::Subtitle(t) => t.offset = offset, + } + } + + pub fn set_language(&mut self, language: TrackLanguage) { + match self { + Track::Audio(t) => t.language = language, + Track::Subtitle(t) => t.language = language, + } + } +} diff --git a/src/domain/entities/video_file.rs b/src/domain/entities/video_file.rs new file mode 100644 index 0000000..8b58688 --- /dev/null +++ b/src/domain/entities/video_file.rs @@ -0,0 +1,14 @@ +use serde::{Deserialize, Serialize}; +use crate::domain::value_objects::FilePath; + +/// Arquivo de vídeo base do projeto. +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct VideoFile { + pub path: FilePath, +} + +impl VideoFile { + pub fn new(path: FilePath) -> Self { + VideoFile { path } + } +} diff --git a/src/domain/mod.rs b/src/domain/mod.rs new file mode 100644 index 0000000..551b189 --- /dev/null +++ b/src/domain/mod.rs @@ -0,0 +1,2 @@ +pub mod entities; +pub mod value_objects; diff --git a/src/domain/value_objects/file_path.rs b/src/domain/value_objects/file_path.rs new file mode 100644 index 0000000..b56c3c4 --- /dev/null +++ b/src/domain/value_objects/file_path.rs @@ -0,0 +1,42 @@ +use std::path::PathBuf; +use serde::{Deserialize, Serialize}; + +/// Newtype sobre PathBuf representando um caminho de arquivo validado. +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct FilePath(PathBuf); + +impl FilePath { + pub fn new(path: impl Into) -> Self { + FilePath(path.into()) + } + + pub fn as_path(&self) -> &std::path::Path { + self.0.as_path() + } + + pub fn to_str(&self) -> Option<&str> { + self.0.to_str() + } + + pub fn to_string_lossy(&self) -> std::borrow::Cow { + self.0.to_string_lossy() + } +} + +impl std::fmt::Display for FilePath { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.0.display()) + } +} + +impl From for FilePath { + fn from(p: PathBuf) -> Self { + FilePath(p) + } +} + +impl From<&str> for FilePath { + fn from(s: &str) -> Self { + FilePath(PathBuf::from(s)) + } +} diff --git a/src/domain/value_objects/mod.rs b/src/domain/value_objects/mod.rs new file mode 100644 index 0000000..5416e31 --- /dev/null +++ b/src/domain/value_objects/mod.rs @@ -0,0 +1,9 @@ +pub mod file_path; +pub mod sync_offset; +pub mod track_id; +pub mod track_language; + +pub use file_path::FilePath; +pub use sync_offset::SyncOffset; +pub use track_id::TrackId; +pub use track_language::TrackLanguage; diff --git a/src/domain/value_objects/sync_offset.rs b/src/domain/value_objects/sync_offset.rs new file mode 100644 index 0000000..25551b8 --- /dev/null +++ b/src/domain/value_objects/sync_offset.rs @@ -0,0 +1,75 @@ +use anyhow::{anyhow, Result}; +use serde::{Deserialize, Serialize}; + +/// Offset de sincronização armazenado em milissegundos como inteiro. +/// Nunca usa f64 — a conversão de/para segundos com vírgula é feita aqui. +#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize, Default)] +pub struct SyncOffset(i64); + +impl SyncOffset { + /// Cria um SyncOffset a partir de milissegundos. + pub fn from_ms(ms: i64) -> Self { + SyncOffset(ms) + } + + /// Cria um SyncOffset a partir de uma string de segundos (ex: "1.2", "-0.5"). + pub fn from_seconds_str(s: &str) -> Result { + let trimmed = s.trim().trim_end_matches('s'); + let seconds: f64 = trimmed + .parse() + .map_err(|_| anyhow!("Offset inválido: '{}'", s))?; + let ms = (seconds * 1000.0).round() as i64; + Ok(SyncOffset(ms)) + } + + /// Retorna o offset em milissegundos. + pub fn as_ms(&self) -> i64 { + self.0 + } + + /// Retorna true se o offset é zero (sem deslocamento). + pub fn is_zero(&self) -> bool { + self.0 == 0 + } +} + +impl std::fmt::Display for SyncOffset { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let seconds = self.0 as f64 / 1000.0; + write!(f, "{:.3}s", seconds) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn from_seconds_str_positivo() { + let offset = SyncOffset::from_seconds_str("1.2").unwrap(); + assert_eq!(offset.as_ms(), 1200); + } + + #[test] + fn from_seconds_str_negativo() { + let offset = SyncOffset::from_seconds_str("-0.5").unwrap(); + assert_eq!(offset.as_ms(), -500); + } + + #[test] + fn from_seconds_str_com_sufixo_s() { + let offset = SyncOffset::from_seconds_str("2.0s").unwrap(); + assert_eq!(offset.as_ms(), 2000); + } + + #[test] + fn from_seconds_str_invalido() { + assert!(SyncOffset::from_seconds_str("abc").is_err()); + } + + #[test] + fn from_seconds_str_zero() { + let offset = SyncOffset::from_seconds_str("0").unwrap(); + assert!(offset.is_zero()); + } +} diff --git a/src/domain/value_objects/track_id.rs b/src/domain/value_objects/track_id.rs new file mode 100644 index 0000000..8e90345 --- /dev/null +++ b/src/domain/value_objects/track_id.rs @@ -0,0 +1,25 @@ +use serde::{Deserialize, Serialize}; + +/// Identificador opaco de uma faixa de mídia. +/// Não expõe indexação interna do FFmpeg — isso é responsabilidade do adapter. +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +pub struct TrackId(u32); + +impl TrackId { + pub fn new(id: u32) -> Self { + TrackId(id) + } +} + +impl TrackId { + /// Retorna o valor interno (uso restrito a crates internas). + pub(crate) fn val(self) -> u32 { + self.0 + } +} + +impl std::fmt::Display for TrackId { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "TrackId({})", self.0) + } +} diff --git a/src/domain/value_objects/track_language.rs b/src/domain/value_objects/track_language.rs new file mode 100644 index 0000000..77befd6 --- /dev/null +++ b/src/domain/value_objects/track_language.rs @@ -0,0 +1,57 @@ +use anyhow::{anyhow, Result}; +use serde::{Deserialize, Serialize}; + +/// Código de idioma ISO 639-2 (3 letras minúsculas), ex: "por", "eng". +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct TrackLanguage(String); + +impl TrackLanguage { + /// Cria um TrackLanguage validando o formato ISO 639-2 (3 letras minúsculas). + pub fn new(code: impl Into) -> Result { + let code = code.into(); + if code.len() == 3 && code.chars().all(|c| c.is_ascii_lowercase()) { + Ok(TrackLanguage(code)) + } else { + Err(anyhow!( + "Código de idioma inválido: '{}'. Esperado formato ISO 639-2 (3 letras minúsculas, ex: 'por', 'eng')", + code + )) + } + } + + pub fn as_str(&self) -> &str { + &self.0 + } +} + +impl std::fmt::Display for TrackLanguage { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.0) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn idioma_valido() { + assert!(TrackLanguage::new("por").is_ok()); + assert!(TrackLanguage::new("eng").is_ok()); + } + + #[test] + fn idioma_invalido_curto() { + assert!(TrackLanguage::new("pt").is_err()); + } + + #[test] + fn idioma_invalido_maiusculo() { + assert!(TrackLanguage::new("POR").is_err()); + } + + #[test] + fn idioma_invalido_longo() { + assert!(TrackLanguage::new("port").is_err()); + } +} diff --git a/src/infrastructure/mod.rs b/src/infrastructure/mod.rs new file mode 100644 index 0000000..80fe812 --- /dev/null +++ b/src/infrastructure/mod.rs @@ -0,0 +1 @@ +pub mod process; diff --git a/src/infrastructure/process/mod.rs b/src/infrastructure/process/mod.rs new file mode 100644 index 0000000..5559de6 --- /dev/null +++ b/src/infrastructure/process/mod.rs @@ -0,0 +1,59 @@ +use anyhow::{anyhow, Context, Result}; +use tokio::io::{AsyncBufReadExt, BufReader}; +use tokio::process::Command; +use tokio::sync::mpsc; + +/// Executa o FFmpeg de forma assíncrona, capturando stderr em tempo real. +/// As linhas de saída são enviadas pelo canal `progress_tx`. +pub async fn run_ffmpeg_async( + args: Vec, + progress_tx: mpsc::UnboundedSender, +) -> Result<()> { + let mut child = Command::new("ffmpeg") + .args(&args) + .stderr(std::process::Stdio::piped()) + .stdout(std::process::Stdio::null()) + .spawn() + .context("Falha ao iniciar ffmpeg")?; + + // Lê stderr em stream para exibir progresso em tempo real + if let Some(stderr) = child.stderr.take() { + let reader = BufReader::new(stderr); + let mut lines = reader.lines(); + let tx = progress_tx.clone(); + tokio::spawn(async move { + while let Ok(Some(line)) = lines.next_line().await { + let _ = tx.send(line); + } + }); + } + + let status = child.wait().await.context("Erro ao aguardar ffmpeg")?; + + if !status.success() { + return Err(anyhow!( + "FFmpeg encerrou com código de saída: {:?}", + status.code() + )); + } + + Ok(()) +} + +/// Verifica se um binário está disponível no PATH. +pub fn check_binary_available(binary: &str) -> Result<()> { + std::process::Command::new(binary) + .arg("-version") + .stdout(std::process::Stdio::null()) + .stderr(std::process::Stdio::null()) + .status() + .map_err(|_| anyhow!("'{}' não encontrado no PATH. Por favor, instale o FFmpeg.", binary))?; + Ok(()) +} + +/// Verifica se ffmpeg e ffprobe estão disponíveis. +pub fn validate_dependencies() -> Result<()> { + check_binary_available("ffmpeg")?; + check_binary_available("ffprobe")?; + Ok(()) +} diff --git a/src/main.rs b/src/main.rs index e7a11a9..04be7e9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,22 @@ -fn main() { - println!("Hello, world!"); +mod adapters; +mod application; +mod domain; +mod infrastructure; +mod ui; + +fn main() -> eframe::Result<()> { + // Configurações nativas da janela + let native_options = eframe::NativeOptions { + viewport: eframe::egui::ViewportBuilder::default() + .with_title("Editor de Faixas de Mídia") + .with_inner_size([900.0, 700.0]) + .with_min_inner_size([600.0, 400.0]), + ..Default::default() + }; + + eframe::run_native( + "Editor de Faixas de Mídia", + native_options, + Box::new(|cc| Box::new(ui::app::App::new(cc)) as Box), + ) } diff --git a/src/ui/app.rs b/src/ui/app.rs new file mode 100644 index 0000000..d3f4de6 --- /dev/null +++ b/src/ui/app.rs @@ -0,0 +1,247 @@ +use eframe::egui; +use std::sync::mpsc; +use crate::adapters::ffmpeg::{FfmpegCommandBuilder, FfmpegGateway, FfprobeGateway}; +use crate::application::ports::MediaProcessorPort; +use crate::application::use_cases::{ + add_audio_track::AddAudioTrack, add_subtitle::AddSubtitle, + edit_existing_track_sync::EditExistingTrackSync, load_media_info::LoadMediaInfo, +}; +use crate::domain::entities::{MkvOutput, Project, VideoFile}; +use crate::domain::value_objects::FilePath; +use crate::ui::components::{ + add_audio_track_form::AddAudioTrackForm, add_subtitle_form::AddSubtitleForm, + execution_panel::{ExecutionPanel, ExecutionState}, existing_track_list::ExistingTrackList, + output_selector::OutputSelector, video_selector::VideoSelector, +}; + +/// Mensagens enviadas do background thread para a UI. +enum BackgroundMsg { + LogLine(String), + Done, + Error(String), +} + +/// Estado da aplicação — contém o `Project` como única fonte de verdade. +pub struct App { + project: Option, + pending_source: Option, + pending_output: Option, + + // Componentes de UI com estado próprio + video_selector: VideoSelector, + output_selector: OutputSelector, + existing_track_list: ExistingTrackList, + add_audio_form: AddAudioTrackForm, + add_subtitle_form: AddSubtitleForm, + execution_panel: ExecutionPanel, + + // Canal de comunicação do background thread + bg_rx: Option>, + + // Mensagem de erro global (ex: dependências ausentes) + global_error: Option, +} + +impl App { + pub fn new(_cc: &eframe::CreationContext<'_>) -> Self { + // Verifica dependências na inicialização + let global_error = crate::infrastructure::process::validate_dependencies().err() + .map(|e| e.to_string()); + + App { + project: None, + pending_source: None, + pending_output: None, + video_selector: VideoSelector::new(), + output_selector: OutputSelector::new(), + existing_track_list: ExistingTrackList::new(), + add_audio_form: AddAudioTrackForm::new(), + add_subtitle_form: AddSubtitleForm::new(), + execution_panel: ExecutionPanel::new(), + bg_rx: None, + global_error, + } + } + + /// Tenta construir ou atualizar o Project quando source e output estão disponíveis. + fn try_build_project(&mut self) { + let source = match &self.pending_source { + Some(p) => p.clone(), + None => return, + }; + let output = match &self.pending_output { + Some(p) => p.clone(), + None => return, + }; + match Project::new(VideoFile::new(source.clone()), MkvOutput::new(output)) { + Ok(mut project) => { + // Carrega faixas existentes via ffprobe + let probe = FfprobeGateway; + match LoadMediaInfo::execute(&mut project, &probe) { + Ok(_) => { + self.existing_track_list.sync_tracks(&project.existing_tracks); + } + Err(e) => { + self.execution_panel.state = ExecutionState::Error(e.to_string()); + } + } + self.project = Some(project); + } + Err(e) => { + self.execution_panel.state = ExecutionState::Error(e.to_string()); + } + } + } + + /// Processa mensagens pendentes do background thread. + fn poll_background(&mut self) { + let mut done = false; + if let Some(rx) = &self.bg_rx { + loop { + match rx.try_recv() { + Ok(BackgroundMsg::LogLine(line)) => { + self.execution_panel.add_log(line); + } + Ok(BackgroundMsg::Done) => { + self.execution_panel.state = ExecutionState::Success; + done = true; + break; + } + Ok(BackgroundMsg::Error(e)) => { + self.execution_panel.state = ExecutionState::Error(e); + done = true; + break; + } + Err(mpsc::TryRecvError::Empty) => break, + Err(mpsc::TryRecvError::Disconnected) => { + done = true; + break; + } + } + } + } + if done { + self.bg_rx = None; + } + } + + /// Inicia geração do MKV em background thread. + fn start_generation(&mut self, ctx: egui::Context) { + let project = match &self.project { + Some(p) => p.clone(), + None => return, + }; + + let args = FfmpegCommandBuilder::build(&project); + let (tx, rx) = mpsc::channel::(); + self.bg_rx = Some(rx); + self.execution_panel.state = ExecutionState::Running; + self.execution_panel.log_lines.clear(); + + std::thread::spawn(move || { + let gateway = FfmpegGateway; + match gateway.execute(args) { + Ok(_) => { + let _ = tx.send(BackgroundMsg::Done); + } + Err(e) => { + let _ = tx.send(BackgroundMsg::Error(e.to_string())); + } + } + ctx.request_repaint(); + }); + } +} + +impl eframe::App for App { + fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) { + // Consome mensagens assíncronas + self.poll_background(); + if self.bg_rx.is_some() { + ctx.request_repaint_after(std::time::Duration::from_millis(100)); + } + + egui::CentralPanel::default().show(ctx, |ui| { + // Erro global de dependências + if let Some(ref err) = self.global_error { + ui.colored_label( + egui::Color32::RED, + format!("⚠ Dependência ausente: {}", err), + ); + ui.separator(); + } + + ui.heading("Editor de Faixas de Mídia"); + ui.add_space(8.0); + + // ── Seleção de vídeo ────────────────────────────────────────────── + if let Some(path) = self.video_selector.ui(ui) { + self.pending_source = Some(path); + if self.pending_output.is_some() { + self.try_build_project(); + } + } + + ui.add_space(4.0); + + // ── Seleção de saída ────────────────────────────────────────────── + if let Some(path) = self.output_selector.ui(ui) { + self.pending_output = Some(path); + if self.pending_source.is_some() { + self.try_build_project(); + } + } + + ui.add_space(8.0); + + if let Some(project) = &mut self.project { + // ── Faixas existentes ───────────────────────────────────────── + let events = + self.existing_track_list.ui(ui, &project.existing_tracks); + for event in events { + use crate::ui::components::existing_track_list::ExistingTrackEvent; + match event { + ExistingTrackEvent::OffsetChanged(id, offset) => { + let _ = EditExistingTrackSync::execute(project, id, offset); + } + } + } + + ui.add_space(8.0); + + // ── Adicionar áudio ─────────────────────────────────────────── + if let Some(req) = self.add_audio_form.ui(ui) { + let _ = AddAudioTrack::execute(project, req.path, req.offset, req.language); + } + + ui.add_space(4.0); + + // ── Adicionar legenda ───────────────────────────────────────── + if let Some(req) = self.add_subtitle_form.ui(ui) { + let _ = AddSubtitle::execute(project, req.path, req.offset, req.language); + } + + ui.add_space(8.0); + + // ── Painel de execução ──────────────────────────────────────── + let can_generate = + self.pending_source.is_some() && self.pending_output.is_some(); + if self.execution_panel.ui(ui, can_generate) { + let ctx_clone = ctx.clone(); + self.start_generation(ctx_clone); + } + } else { + ui.add_space(8.0); + ui.label( + egui::RichText::new( + "Selecione o arquivo de vídeo e o destino de saída para começar.", + ) + .italics(), + ); + ui.add_space(8.0); + let can_generate = self.pending_source.is_some() && self.pending_output.is_some(); + self.execution_panel.ui(ui, can_generate); + } + }); + } +} diff --git a/src/ui/components/add_audio_track_form.rs b/src/ui/components/add_audio_track_form.rs new file mode 100644 index 0000000..d36f63e --- /dev/null +++ b/src/ui/components/add_audio_track_form.rs @@ -0,0 +1,74 @@ +use eframe::egui; +use crate::adapters::filesystem::FilePickerAdapter; +use crate::domain::value_objects::{FilePath, SyncOffset, TrackLanguage}; +use crate::ui::components::{language_field::LanguageField, sync_offset_field::SyncOffsetField}; + +/// Evento emitido ao confirmar adição de faixa de áudio. +pub struct AddAudioTrackRequest { + pub path: FilePath, + pub offset: SyncOffset, + pub language: TrackLanguage, +} + +/// Formulário para adicionar uma faixa de áudio externa. +pub struct AddAudioTrackForm { + selected_path: Option, + offset_field: SyncOffsetField, + language_field: LanguageField, +} + +impl AddAudioTrackForm { + pub fn new() -> Self { + AddAudioTrackForm { + selected_path: None, + offset_field: SyncOffsetField::new(SyncOffset::default()), + language_field: LanguageField::new(None), + } + } + + /// Renderiza o formulário. Retorna `Some(request)` ao confirmar. + pub fn ui(&mut self, ui: &mut egui::Ui) -> Option { + let mut result = None; + + ui.group(|ui| { + ui.heading("Adicionar faixa de áudio"); + + ui.horizontal(|ui| { + let label = self + .selected_path + .as_ref() + .map(|p| p.to_string_lossy().to_string()) + .unwrap_or_else(|| "Nenhum arquivo".to_string()); + ui.label(&label); + if ui.button("Selecionar áudio…").clicked() { + self.selected_path = FilePickerAdapter::pick_audio(); + } + }); + + self.offset_field.ui(ui, "Atraso:"); + self.language_field.ui(ui); + + ui.add_space(4.0); + + let can_add = self.selected_path.is_some(); + if ui + .add_enabled(can_add, egui::Button::new("Adicionar")) + .clicked() + { + if let (Some(path), Some(offset), Some(language)) = ( + self.selected_path.clone(), + self.offset_field.parse(), + self.language_field.parse(), + ) { + result = Some(AddAudioTrackRequest { path, offset, language }); + // Reset form + self.selected_path = None; + self.offset_field = SyncOffsetField::new(SyncOffset::default()); + self.language_field = LanguageField::new(None); + } + } + }); + + result + } +} diff --git a/src/ui/components/add_subtitle_form.rs b/src/ui/components/add_subtitle_form.rs new file mode 100644 index 0000000..382f712 --- /dev/null +++ b/src/ui/components/add_subtitle_form.rs @@ -0,0 +1,73 @@ +use eframe::egui; +use crate::adapters::filesystem::FilePickerAdapter; +use crate::domain::value_objects::{FilePath, SyncOffset, TrackLanguage}; +use crate::ui::components::{language_field::LanguageField, sync_offset_field::SyncOffsetField}; + +/// Evento emitido ao confirmar adição de legenda. +pub struct AddSubtitleRequest { + pub path: FilePath, + pub offset: SyncOffset, + pub language: TrackLanguage, +} + +/// Formulário para adicionar uma faixa de legenda externa. +pub struct AddSubtitleForm { + selected_path: Option, + offset_field: SyncOffsetField, + language_field: LanguageField, +} + +impl AddSubtitleForm { + pub fn new() -> Self { + AddSubtitleForm { + selected_path: None, + offset_field: SyncOffsetField::new(SyncOffset::default()), + language_field: LanguageField::new(None), + } + } + + /// Renderiza o formulário. Retorna `Some(request)` ao confirmar. + pub fn ui(&mut self, ui: &mut egui::Ui) -> Option { + let mut result = None; + + ui.group(|ui| { + ui.heading("Adicionar legenda"); + + ui.horizontal(|ui| { + let label = self + .selected_path + .as_ref() + .map(|p| p.to_string_lossy().to_string()) + .unwrap_or_else(|| "Nenhum arquivo".to_string()); + ui.label(&label); + if ui.button("Selecionar legenda…").clicked() { + self.selected_path = FilePickerAdapter::pick_subtitle(); + } + }); + + self.offset_field.ui(ui, "Atraso:"); + self.language_field.ui(ui); + + ui.add_space(4.0); + + let can_add = self.selected_path.is_some(); + if ui + .add_enabled(can_add, egui::Button::new("Adicionar")) + .clicked() + { + if let (Some(path), Some(offset), Some(language)) = ( + self.selected_path.clone(), + self.offset_field.parse(), + self.language_field.parse(), + ) { + result = Some(AddSubtitleRequest { path, offset, language }); + self.selected_path = None; + self.offset_field = SyncOffsetField::new(SyncOffset::default()); + self.language_field = LanguageField::new(None); + } + } + }); + + result + } +} diff --git a/src/ui/components/execution_panel.rs b/src/ui/components/execution_panel.rs new file mode 100644 index 0000000..38def7d --- /dev/null +++ b/src/ui/components/execution_panel.rs @@ -0,0 +1,93 @@ +use eframe::egui; + +/// Estado da execução do FFmpeg. +#[derive(Default, PartialEq)] +pub enum ExecutionState { + #[default] + Idle, + Running, + Success, + Error(String), +} + +/// Painel de execução: botão "Gerar MKV", progresso e erros. +pub struct ExecutionPanel { + pub state: ExecutionState, + pub log_lines: Vec, +} + +impl ExecutionPanel { + pub fn new() -> Self { + ExecutionPanel { + state: ExecutionState::Idle, + log_lines: Vec::new(), + } + } + + pub fn add_log(&mut self, line: String) { + self.log_lines.push(line); + // Limita o histórico de log + if self.log_lines.len() > 200 { + self.log_lines.remove(0); + } + } + + /// Renderiza o painel. Retorna `true` se o botão "Gerar MKV" foi clicado. + pub fn ui(&mut self, ui: &mut egui::Ui, can_generate: bool) -> bool { + let mut clicked = false; + + ui.group(|ui| { + ui.heading("Gerar arquivo"); + + let is_running = self.state == ExecutionState::Running; + + if ui + .add_enabled( + can_generate && !is_running, + egui::Button::new(if is_running { + "Processando…" + } else { + "Gerar MKV" + }), + ) + .clicked() + { + clicked = true; + } + + match &self.state { + ExecutionState::Idle => {} + ExecutionState::Running => { + ui.spinner(); + ui.label("Processando..."); + } + ExecutionState::Success => { + ui.colored_label(egui::Color32::GREEN, "✓ Arquivo gerado com sucesso!"); + } + ExecutionState::Error(msg) => { + ui.colored_label(egui::Color32::RED, "✗ Erro durante o processamento:"); + ui.add( + egui::TextEdit::multiline(&mut msg.as_str()) + .desired_rows(4) + .desired_width(f32::INFINITY), + ); + } + } + + // Log de progresso + if !self.log_lines.is_empty() { + ui.collapsing("Log detalhado", |ui| { + egui::ScrollArea::vertical() + .max_height(150.0) + .show(ui, |ui| { + for line in &self.log_lines { + ui.label(egui::RichText::new(line).monospace().size(11.0)); + } + }); + }); + } + }); + + clicked + } +} diff --git a/src/ui/components/existing_track_list.rs b/src/ui/components/existing_track_list.rs new file mode 100644 index 0000000..837f0f3 --- /dev/null +++ b/src/ui/components/existing_track_list.rs @@ -0,0 +1,96 @@ +use eframe::egui; +use crate::domain::entities::{MediaTrackInfo, TrackKind}; +use crate::domain::value_objects::{SyncOffset, TrackId}; +use crate::ui::components::sync_offset_field::SyncOffsetField; +use std::collections::HashMap; + +/// Evento emitido por interações com a lista de faixas existentes. +pub enum ExistingTrackEvent { + OffsetChanged(TrackId, SyncOffset), +} + +/// Lista as faixas detectadas no arquivo de vídeo e permite editar o offset de cada uma. +pub struct ExistingTrackList { + offset_fields: HashMap, +} + +impl ExistingTrackList { + pub fn new() -> Self { + ExistingTrackList { + offset_fields: HashMap::new(), + } + } + + /// Sincroniza os campos de offset com as faixas atuais do projeto. + pub fn sync_tracks(&mut self, tracks: &[MediaTrackInfo]) { + for track in tracks { + self.offset_fields + .entry(track.id.val()) + .or_insert_with(|| SyncOffsetField::new(track.offset)); + } + } + + /// Renderiza a lista. Retorna eventos de alteração de offset. + pub fn ui( + &mut self, + ui: &mut egui::Ui, + tracks: &[MediaTrackInfo], + ) -> Vec { + let mut events = Vec::new(); + + ui.group(|ui| { + ui.heading("Faixas existentes"); + + if tracks.is_empty() { + ui.label("Nenhuma faixa detectada."); + return; + } + + egui::Grid::new("existing_tracks_grid") + .num_columns(4) + .striped(true) + .show(ui, |ui| { + ui.strong("#"); + ui.strong("Tipo"); + ui.strong("Codec"); + ui.strong("Atraso"); + ui.end_row(); + + for track in tracks { + let kind_label = match track.kind { + TrackKind::Video => "Vídeo", + TrackKind::Audio => "Áudio", + TrackKind::Subtitle => "Legenda", + TrackKind::Data => "Dados", + }; + let lang = track + .language + .as_ref() + .map(|l| format!(" ({})", l)) + .unwrap_or_default(); + + ui.label(format!("{}", track.stream_index)); + ui.label(format!("{}{}", kind_label, lang)); + ui.label(&track.codec); + + // Offset field + let field = self + .offset_fields + .entry(track.id.val()) + .or_insert_with(|| SyncOffsetField::new(track.offset)); + + field.ui(ui, ""); + if let Some(offset) = field.parse() { + if offset != track.offset { + events.push(ExistingTrackEvent::OffsetChanged(track.id, offset)); + } + } + + ui.end_row(); + } + }); + }); + + events + } +} diff --git a/src/ui/components/language_field.rs b/src/ui/components/language_field.rs new file mode 100644 index 0000000..9309c02 --- /dev/null +++ b/src/ui/components/language_field.rs @@ -0,0 +1,51 @@ +use eframe::egui; +use crate::domain::value_objects::TrackLanguage; + +/// Campo de seleção/entrada de idioma ISO 639-2. +pub struct LanguageField { + pub raw: String, + pub error: Option, +} + +impl LanguageField { + pub fn new(initial: Option<&TrackLanguage>) -> Self { + LanguageField { + raw: initial.map(|l| l.as_str().to_string()).unwrap_or_default(), + error: None, + } + } + + /// Renderiza o campo. Retorna true se o valor mudou. + pub fn ui(&mut self, ui: &mut egui::Ui) -> bool { + let mut changed = false; + ui.horizontal(|ui| { + ui.label("Idioma (ex: por, eng):"); + if ui + .add(egui::TextEdit::singleline(&mut self.raw).desired_width(60.0)) + .changed() + { + changed = true; + self.error = None; + } + }); + + if let Some(ref err) = self.error { + ui.colored_label(egui::Color32::RED, err); + } + changed + } + + /// Tenta converter para TrackLanguage. + pub fn parse(&mut self) -> Option { + match TrackLanguage::new(self.raw.trim().to_lowercase()) { + Ok(lang) => { + self.error = None; + Some(lang) + } + Err(e) => { + self.error = Some(format!("{}", e)); + None + } + } + } +} diff --git a/src/ui/components/mod.rs b/src/ui/components/mod.rs new file mode 100644 index 0000000..c3ebf92 --- /dev/null +++ b/src/ui/components/mod.rs @@ -0,0 +1,8 @@ +pub mod add_audio_track_form; +pub mod add_subtitle_form; +pub mod execution_panel; +pub mod existing_track_list; +pub mod language_field; +pub mod output_selector; +pub mod sync_offset_field; +pub mod video_selector; diff --git a/src/ui/components/output_selector.rs b/src/ui/components/output_selector.rs new file mode 100644 index 0000000..3d45789 --- /dev/null +++ b/src/ui/components/output_selector.rs @@ -0,0 +1,41 @@ +use eframe::egui; +use crate::adapters::filesystem::FilePickerAdapter; +use crate::domain::value_objects::FilePath; + +/// Componente para seleção do arquivo MKV de saída. +pub struct OutputSelector { + pub selected_path: Option, +} + +impl OutputSelector { + pub fn new() -> Self { + OutputSelector { selected_path: None } + } + + /// Renderiza o seletor. Retorna `Some(FilePath)` se um novo caminho foi selecionado. + pub fn ui(&mut self, ui: &mut egui::Ui) -> Option { + let mut selected = None; + + ui.group(|ui| { + ui.heading("Arquivo de saída (.mkv)"); + ui.horizontal(|ui| { + let label = self + .selected_path + .as_ref() + .map(|p| p.to_string_lossy().to_string()) + .unwrap_or_else(|| "Nenhum destino selecionado".to_string()); + + ui.label(&label); + + if ui.button("Salvar como…").clicked() { + if let Some(path) = FilePickerAdapter::pick_output() { + self.selected_path = Some(path.clone()); + selected = Some(path); + } + } + }); + }); + + selected + } +} diff --git a/src/ui/components/sync_offset_field.rs b/src/ui/components/sync_offset_field.rs new file mode 100644 index 0000000..7af737b --- /dev/null +++ b/src/ui/components/sync_offset_field.rs @@ -0,0 +1,51 @@ +use eframe::egui; +use crate::domain::value_objects::SyncOffset; + +/// Campo de entrada para offset de sincronização em segundos (ex: "-1.2"). +/// Converte para SyncOffset(ms) internamente ao confirmar. +pub struct SyncOffsetField { + pub raw: String, + pub error: Option, +} + +impl SyncOffsetField { + pub fn new(initial: SyncOffset) -> Self { + let seconds = initial.as_ms() as f64 / 1000.0; + SyncOffsetField { + raw: format!("{:.1}", seconds), + error: None, + } + } + + /// Renderiza o campo e retorna true se o valor mudou. + pub fn ui(&mut self, ui: &mut egui::Ui, label: &str) -> bool { + let mut changed = false; + ui.horizontal(|ui| { + ui.label(label); + if ui.text_edit_singleline(&mut self.raw).changed() { + changed = true; + self.error = None; + } + ui.label("s"); + }); + + if let Some(ref err) = self.error { + ui.colored_label(egui::Color32::RED, err); + } + changed + } + + /// Tenta converter o valor atual para SyncOffset. + pub fn parse(&mut self) -> Option { + match SyncOffset::from_seconds_str(&self.raw) { + Ok(offset) => { + self.error = None; + Some(offset) + } + Err(e) => { + self.error = Some(format!("Valor inválido: {}", e)); + None + } + } + } +} diff --git a/src/ui/components/video_selector.rs b/src/ui/components/video_selector.rs new file mode 100644 index 0000000..16e198f --- /dev/null +++ b/src/ui/components/video_selector.rs @@ -0,0 +1,42 @@ +use eframe::egui; +use crate::adapters::filesystem::FilePickerAdapter; +use crate::domain::value_objects::FilePath; + +/// Componente para seleção do arquivo de vídeo base. +/// Ao confirmar, dispara LoadMediaInfo via callback. +pub struct VideoSelector { + pub selected_path: Option, +} + +impl VideoSelector { + pub fn new() -> Self { + VideoSelector { selected_path: None } + } + + /// Renderiza o seletor. Retorna `Some(FilePath)` se um novo arquivo foi selecionado. + pub fn ui(&mut self, ui: &mut egui::Ui) -> Option { + let mut selected = None; + + ui.group(|ui| { + ui.heading("Arquivo de vídeo"); + ui.horizontal(|ui| { + let label = self + .selected_path + .as_ref() + .map(|p| p.to_string_lossy().to_string()) + .unwrap_or_else(|| "Nenhum arquivo selecionado".to_string()); + + ui.label(&label); + + if ui.button("Selecionar…").clicked() { + if let Some(path) = FilePickerAdapter::pick_video() { + self.selected_path = Some(path.clone()); + selected = Some(path); + } + } + }); + }); + + selected + } +} diff --git a/src/ui/mod.rs b/src/ui/mod.rs new file mode 100644 index 0000000..13545c4 --- /dev/null +++ b/src/ui/mod.rs @@ -0,0 +1,2 @@ +pub mod app; +pub mod components;