diff --git a/.gitignore b/.gitignore index ca52265..ebd1e64 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,6 @@ Cargo.lock # These are backup files generated by rustfmt **/*.rs.bk Cargo.lock + +*.blend1 +.vscode \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index 5a9eec2..bf459e2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,6 +8,15 @@ version = "0.11.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3" +[[package]] +name = "addr2line" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a76fd60b23679b7d19bd066031410fb7e458ccc5e958eb5c325888ce4baedc97" +dependencies = [ + "gimli", +] + [[package]] name = "adler" version = "1.0.2" @@ -25,15 +34,6 @@ dependencies = [ "version_check", ] -[[package]] -name = "aho-corasick" -version = "0.7.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac" -dependencies = [ - "memchr", -] - [[package]] name = "aliasable" version = "0.1.3" @@ -62,9 +62,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.66" +version = "1.0.68" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "216261ddc8289130e551ddcd5ce8a064710c0d064a4d2895c67151c92b5443f6" +checksum = "2cb2f989d18dd141ab8ae82f64d1a8cdd37e0840f73a406896cf5e99502fab61" [[package]] name = "arrayref" @@ -84,6 +84,21 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +[[package]] +name = "backtrace" +version = "0.3.67" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "233d376d6d185f2a3093e58f283f60f880315b6c60075b01f36b3b85154564ca" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide 0.6.2", + "object", + "rustc-demangle", +] + [[package]] name = "base64" version = "0.13.1" @@ -102,21 +117,6 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" -[[package]] -name = "buildstructor" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32e1c120c6c832d6101eeb3170eeaeec6aca98759b59c37a1f78b3becfa482a5" -dependencies = [ - "lazy_static", - "proc-macro2", - "quote", - "str_inflector", - "syn", - "thiserror", - "try_match", -] - [[package]] name = "bumpalo" version = "3.11.1" @@ -168,6 +168,43 @@ dependencies = [ "winapi", ] +[[package]] +name = "clap" +version = "4.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8d93d855ce6a0aa87b8473ef9169482f40abaa2e9e0993024c35c902cbd5920" +dependencies = [ + "bitflags", + "clap_derive", + "clap_lex", + "is-terminal", + "once_cell", + "strsim", + "termcolor", +] + +[[package]] +name = "clap_derive" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "684a277d672e91966334af371f1a7b5833f9aa00b07c84e92fbce95e00208ce8" +dependencies = [ + "heck", + "proc-macro-error", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "783fe232adfca04f90f56201b26d79682d4cd2625e0bc7290b95123afe558ade" +dependencies = [ + "os_str_bytes", +] + [[package]] name = "cluFlock" version = "1.2.7" @@ -188,6 +225,21 @@ dependencies = [ "unicode-width", ] +[[package]] +name = "color-eyre" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a667583cca8c4f8436db8de46ea8233c42a7d9ae424a82d338f2e4675229204" +dependencies = [ + "backtrace", + "color-spantrace", + "eyre", + "indenter", + "once_cell", + "owo-colors", + "tracing-error", +] + [[package]] name = "color-maps" version = "0.1.0" @@ -210,6 +262,18 @@ dependencies = [ "serde_derive", ] +[[package]] +name = "color-spantrace" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ba75b3d9449ecdccb27ecbc479fdc0b87fa2dd43d2f8298f9bf0e59aacc8dce" +dependencies = [ + "once_cell", + "owo-colors", + "tracing-core", + "tracing-error", +] + [[package]] name = "color_quant" version = "1.1.0" @@ -330,6 +394,15 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8d7439c3735f405729d52c3fbbe4de140eaf938a1fe47d227c27f8254d4302a5" +[[package]] +name = "directories" +version = "4.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f51c5d4ddabd36886dd3e1438cb358cdcb0d7c499cb99cb4ac2e38e18b5cb210" +dependencies = [ + "dirs-sys", +] + [[package]] name = "dirs" version = "4.0.0" @@ -356,6 +429,27 @@ version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797" +[[package]] +name = "errno" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1" +dependencies = [ + "errno-dragonfly", + "libc", + "winapi", +] + +[[package]] +name = "errno-dragonfly" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +dependencies = [ + "cc", + "libc", +] + [[package]] name = "exr" version = "1.5.2" @@ -371,6 +465,16 @@ dependencies = [ "threadpool", ] +[[package]] +name = "eyre" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c2b6b5a29c02cdc822728b7d7b8ae1bab3e3b05d44522770ddd49722eeac7eb" +dependencies = [ + "indenter", + "once_cell", +] + [[package]] name = "ez-pixmap" version = "0.2.2" @@ -501,6 +605,12 @@ dependencies = [ "weezl", ] +[[package]] +name = "gimli" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dec7af912d60cdbd3677c1af9352ebae6fb8394d165568a2234df0fa00f87793" + [[package]] name = "glam" version = "0.22.0" @@ -525,6 +635,12 @@ dependencies = [ "crunchy", ] +[[package]] +name = "heck" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" + [[package]] name = "hermit-abi" version = "0.1.19" @@ -534,6 +650,15 @@ dependencies = [ "libc", ] +[[package]] +name = "hermit-abi" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" +dependencies = [ + "libc", +] + [[package]] name = "iana-time-zone" version = "0.1.53" @@ -583,6 +708,12 @@ version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df19da1e92fbfec043ca97d622955381b1f3ee72a180ec999912df31b1ccd951" +[[package]] +name = "indenter" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" + [[package]] name = "instant" version = "0.1.12" @@ -592,6 +723,28 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "io-lifetimes" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7d6c6f8c91b4b9ed43484ad1a938e393caf35960fce7f82a040497207bd8e9e" +dependencies = [ + "libc", + "windows-sys", +] + +[[package]] +name = "is-terminal" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28dfb6c8100ccc63462345b67d1bbc3679177c75ee4bf59bf29c8b1d110b8189" +dependencies = [ + "hermit-abi 0.2.6", + "io-lifetimes", + "rustix", + "windows-sys", +] + [[package]] name = "jpeg-decoder" version = "0.3.0" @@ -646,6 +799,12 @@ dependencies = [ "cc", ] +[[package]] +name = "linux-raw-sys" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" + [[package]] name = "lock_api" version = "0.4.9" @@ -677,6 +836,15 @@ dependencies = [ "syn", ] +[[package]] +name = "matchers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" +dependencies = [ + "regex-automata", +] + [[package]] name = "memchr" version = "2.5.0" @@ -772,6 +940,16 @@ dependencies = [ "static_assertions", ] +[[package]] +name = "nu-ansi-term" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +dependencies = [ + "overload", + "winapi", +] + [[package]] name = "num-integer" version = "0.1.45" @@ -808,7 +986,7 @@ version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f6058e64324c71e02bc2b150e4f3bc8286db6c83092132ffa3f6b1eab0f9def5" dependencies = [ - "hermit-abi", + "hermit-abi 0.1.19", "libc", ] @@ -833,12 +1011,27 @@ dependencies = [ "syn", ] +[[package]] +name = "object" +version = "0.30.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b8c786513eb403643f2a88c244c2aaa270ef2153f55094587d0c48a3cf22a83" +dependencies = [ + "memchr", +] + [[package]] name = "once_cell" version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860" +[[package]] +name = "os_str_bytes" +version = "6.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee" + [[package]] name = "ouroboros" version = "0.15.5" @@ -862,6 +1055,18 @@ dependencies = [ "syn", ] +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + +[[package]] +name = "owo-colors" +version = "3.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f" + [[package]] name = "parking_lot" version = "0.11.2" @@ -1014,11 +1219,14 @@ dependencies = [ name = "protostar" version = "0.3.0" dependencies = [ - "anyhow", + "clap", + "color-eyre", + "directories", "dirs", "ez-pixmap", "glam", "image", + "lazy_static", "manifest-dir-macros", "mint", "nix", @@ -1027,6 +1235,7 @@ dependencies = [ "stardust-xr-molecules", "tempdir", "tokio", + "tracing-subscriber", "tween", "ustr", "walkdir", @@ -1158,12 +1367,19 @@ dependencies = [ [[package]] name = "regex" -version = "1.7.0" +version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e076559ef8e241f2ae3479e36f97bd5741c0330689e217ad51ce2c76808b868a" +checksum = "48aaa5748ba571fb95cd2c85c09f629215d3a6ece942baa100950af03a34f733" +dependencies = [ + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" dependencies = [ - "aho-corasick", - "memchr", "regex-syntax", ] @@ -1219,12 +1435,32 @@ dependencies = [ "xmlparser", ] +[[package]] +name = "rustc-demangle" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342" + [[package]] name = "rustc-hash" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" +[[package]] +name = "rustix" +version = "0.36.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4fdebc4b395b7fbb9ab11e462e20ed9051e7b16e42d24042c776eca0ac81b03" +dependencies = [ + "bitflags", + "errno", + "io-lifetimes", + "libc", + "linux-raw-sys", + "windows-sys", +] + [[package]] name = "rustybuzz" version = "0.6.0" @@ -1270,18 +1506,18 @@ checksum = "9c8132065adcfd6e02db789d9285a0deb2f3fcb04002865ab67d5fb103533898" [[package]] name = "serde" -version = "1.0.151" +version = "1.0.152" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97fed41fc1a24994d044e6db6935e69511a1153b52c15eb42493b26fa87feba0" +checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.151" +version = "1.0.152" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "255abe9a125a985c05190d687b320c12f9b1f0b99445e608c21ba0782c719ad8" +checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e" dependencies = [ "proc-macro2", "quote", @@ -1290,15 +1526,24 @@ dependencies = [ [[package]] name = "serde_repr" -version = "0.1.9" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fe39d9fbb0ebf5eb2c7cb7e2a47e4f462fad1379f1166b8ae49ad9eae89a7ca" +checksum = "9a5ec9fa74a20ebbe5d9ac23dac1fc96ba0ecfe9f50f2843b52e537b10fbcb4e" dependencies = [ "proc-macro2", "quote", "syn", ] +[[package]] +name = "sharded-slab" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "900fba806f70c630b0a382d0d825e17a0f19fcd059a2ade1ff237bcddf446b31" +dependencies = [ + "lazy_static", +] + [[package]] name = "signal-hook-registry" version = "1.4.0" @@ -1350,9 +1595,9 @@ dependencies = [ [[package]] name = "stardust-xr" -version = "0.10.0" +version = "0.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dd05e0962751e904acb73a3c96d09311b88658c186f6a15ced1786e059bb3eb" +checksum = "cd8541caad6c5c3e9c43ce4fa43a330f7a3df5ec293c88fd062fff46172ceb97" dependencies = [ "chrono", "cluFlock", @@ -1370,12 +1615,11 @@ dependencies = [ [[package]] name = "stardust-xr-fusion" -version = "0.28.1" +version = "0.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c12936fdb0f2a15e69e96e89dca15ef5aad5e43046ba58fa4697b562894395f" +checksum = "a95836cb0147824ee1be564ba104de65d022da86d50bdac526e29830e8e1cf7c" dependencies = [ "anyhow", - "buildstructor", "color-rs", "flagset", "glam", @@ -1394,9 +1638,9 @@ dependencies = [ [[package]] name = "stardust-xr-molecules" -version = "0.10.1" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab9a5aa267fe9489d4d38a0971572f96b218db6c59c2c0b6b1bbea002aac91ac" +checksum = "73d9a445dc6aef7010c17d972885cea278f96d5da70b030e6a39e3e041ca85df" dependencies = [ "color-rs", "flexbuffers", @@ -1407,6 +1651,7 @@ dependencies = [ "serde", "stardust-xr-fusion", "tokio", + "tracing", "xkbcommon", ] @@ -1431,16 +1676,6 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" -[[package]] -name = "str_inflector" -version = "0.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0b848d5a7695b33ad1be00f84a3c079fe85c9278a325ff9159e6c99cef4ef7" -dependencies = [ - "lazy_static", - "regex", -] - [[package]] name = "strict-num" version = "0.1.0" @@ -1450,6 +1685,12 @@ dependencies = [ "float-cmp", ] +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + [[package]] name = "svgfilters" version = "0.4.0" @@ -1501,24 +1742,33 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.37" +version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10deb33631e3c9018b9baf9dcbbc4f737320d2b576bac10f6aefa048fa407e3e" +checksum = "6a9cd18aa97d5c45c6603caea1da6628790b37f7a34b6ca89522331c5180fed0" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.37" +version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "982d17546b47146b28f7c22e3d08465f6b8903d0ea13c1660d9d84a6e7adcdbb" +checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f" dependencies = [ "proc-macro2", "quote", "syn", ] +[[package]] +name = "thread_local" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5516c27b78311c50bf42c071425c560ac799b11c30b31f87e3081965fe5e0180" +dependencies = [ + "once_cell", +] + [[package]] name = "threadpool" version = "1.8.1" @@ -1577,9 +1827,9 @@ dependencies = [ [[package]] name = "tokio" -version = "1.23.0" +version = "1.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eab6d665857cc6ca78d6e80303a02cea7a7851e85dfbd77cbdc09bd129f1ef46" +checksum = "1d9f76183f91ecfb55e1d7d5602bd1d979e38a3a522fe900241cf195624d67ae" dependencies = [ "autocfg", "bytes", @@ -1645,27 +1895,46 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" dependencies = [ "once_cell", + "valuable", ] [[package]] -name = "try_match" -version = "0.3.0" +name = "tracing-error" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "789f9cd474cc74c591dcc98669b846e158f2409ace4e6e342502ab44a41c584b" +checksum = "d686ec1c0f384b1277f097b2f279a2ecc11afe8c133c1aabf036a27cb4cd206e" dependencies = [ - "try_match_inner", + "tracing", + "tracing-subscriber", ] [[package]] -name = "try_match_inner" -version = "0.4.0" +name = "tracing-log" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "607e6b75bc1bdf1a60d4201c500ca965834c11f30e25921cfd28569315db49a9" +checksum = "78ddad33d2d10b1ed7eb9d1f518a5674713876e97e5bb9b7345a7984fbb4f922" dependencies = [ - "proc-macro-error", - "proc-macro2", - "quote", - "syn", + "lazy_static", + "log", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6176eae26dd70d0c919749377897b54a9276bd7061339665dd68777926b5a70" +dependencies = [ + "matchers", + "nu-ansi-term", + "once_cell", + "regex", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", ] [[package]] @@ -1676,9 +1945,9 @@ checksum = "375812fa44dab6df41c195cd2f7fecb488f6c09fbaafb62807488cefab642bff" [[package]] name = "tween" -version = "1.0.1" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63e9aab86b1958c0ff7f3d18d04abd6d8e583d237f92e88191ce5c7bf481c5ec" +checksum = "f114398da254e78168e12edec0ece6b4ca15a97262ecd8e5efd5025e3fc30204" [[package]] name = "unicode-bidi" @@ -1776,6 +2045,12 @@ dependencies = [ "usvg", ] +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + [[package]] name = "version_check" version = "0.9.4" diff --git a/Cargo.toml b/Cargo.toml index 506f7f8..a53c423 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,19 +4,23 @@ version = "0.3.0" edition = "2021" [dependencies] -anyhow = "1.0.66" +clap = { version = "4.1.3", features = ["derive"] } +color-eyre = "0.6.2" +directories = "4.0.1" dirs = "4.0.0" ez-pixmap = "0.2.2" glam = { version = "0.22.0", features = ["mint"] } image = "0.24.5" +lazy_static = "1.4.0" manifest-dir-macros = "0.1.16" mint = "0.5.9" nix = "0.26.1" resvg = "0.28.0" rustc-hash = "1.1.0" -stardust-xr-molecules = "0.10.1" -tokio = { version = "1.22.0", features = ["full"] } -tween = "1.0.1" +stardust-xr-molecules = "0.16.0" +tokio = { version = "1.24.1", features = ["full"] } +tracing-subscriber = { version = "0.3.16", features = ["env-filter"] } +tween = "2.0.0" ustr = "0.9.0" walkdir = "2.3.2" diff --git a/assets/cartridge.blend b/assets/cartridge.blend new file mode 100644 index 0000000..5321a8a Binary files /dev/null and b/assets/cartridge.blend differ diff --git a/assets/circuit.png b/assets/circuit.png new file mode 100644 index 0000000..466c860 Binary files /dev/null and b/assets/circuit.png differ diff --git a/assets/circuit_grayscale.png b/assets/circuit_grayscale.png new file mode 100644 index 0000000..94331af Binary files /dev/null and b/assets/circuit_grayscale.png differ diff --git a/assets/icon_test.png b/assets/icon_test.png new file mode 100644 index 0000000..68f4809 Binary files /dev/null and b/assets/icon_test.png differ diff --git a/examples/app_grid.rs b/examples/app_grid.rs new file mode 100644 index 0000000..9188085 --- /dev/null +++ b/examples/app_grid.rs @@ -0,0 +1,116 @@ +use color_eyre::eyre::Result; +use manifest_dir_macros::directory_relative_path; +use mint::Vector3; +use protostar::{ + protostar::ProtoStar, + xdg::{get_desktop_files, parse_desktop_file, DesktopFile}, +}; +use stardust_xr_molecules::fusion::{ + client::{Client, LifeCycleHandler, LogicStepInfo}, + spatial::Spatial, +}; + +const APP_LIMIT: usize = 50; +const APP_SIZE: f32 = 0.05; +const GRID_PADDING: f32 = 0.01; + +#[tokio::main(flavor = "current_thread")] +async fn main() -> Result<()> { + color_eyre::install().unwrap(); + tracing_subscriber::fmt() + .with_env_filter(tracing_subscriber::EnvFilter::from_default_env()) + .pretty() + .init(); + let (client, event_loop) = Client::connect_with_async_loop().await?; + client.set_base_prefixes(&[directory_relative_path!("res")]); + + let _root = client.wrap_root(AppGrid::new(&client)); + + tokio::select! { + _ = tokio::signal::ctrl_c() => (), + e = event_loop => e??, + }; + Ok(()) +} + +struct AppGrid { + apps: Vec, +} +impl AppGrid { + fn new(client: &Client) -> Self { + let apps = get_desktop_files() + .into_iter() + .filter_map(|d| parse_desktop_file(d).ok()) + .enumerate() + .filter(|(i, _)| *i <= APP_LIMIT) + .filter_map(|(i, a)| { + App::new( + client.get_root(), + [ + (i % 10) as f32 * (APP_SIZE + GRID_PADDING), + (i / 10) as f32 * (APP_SIZE + GRID_PADDING), + 0.0, + ], + a, + ) + }) + .collect::>(); + AppGrid { apps } + } +} +impl LifeCycleHandler for AppGrid { + fn logic_step(&mut self, info: LogicStepInfo) { + for app in &mut self.apps { + app.logic_step(info); + } + } +} +struct App { + // _text: Text, + _desktop_file: DesktopFile, + protostar: ProtoStar, +} +impl App { + fn new( + parent: &Spatial, + position: impl Into>, + desktop_file: DesktopFile, + ) -> Option { + let position = position.into(); + + let protostar = ProtoStar::create_from_desktop_file(parent, desktop_file.clone()).ok()?; + // let text = Text::create( + // protostar.content_parent(), + // Transform::from_position_rotation( + // [0.0, 0.0, APP_SIZE / 2.0], + // Quat::from_rotation_y(PI), + // ), + // desktop_file.name.as_deref().unwrap_or("Unknown"), + // TextStyle { + // character_height: APP_SIZE * 0.1, + // bounds: Some(Bounds { + // bounds: [APP_SIZE; 2].into(), + // fit: TextFit::Wrap, + // bounds_align: Alignment::XCenter | Alignment::YCenter, + // }), + // text_align: Alignment::XCenter | Alignment::YCenter, + // ..Default::default() + // }, + // ) + // .unwrap(); + protostar + .content_parent() + .set_position(None, position) + .unwrap(); + Some(App { + // _text: text, + _desktop_file: desktop_file, + protostar, + }) + } +} +impl LifeCycleHandler for App { + fn logic_step(&mut self, info: LogicStepInfo) { + self.protostar.logic_step(info); + } +} diff --git a/res/protostar/cartridge.glb b/res/protostar/cartridge.glb new file mode 100644 index 0000000..68abfd4 Binary files /dev/null and b/res/protostar/cartridge.glb differ diff --git a/src/args.rs b/src/args.rs deleted file mode 100644 index e69de29..0000000 diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..bda7f63 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,2 @@ +pub mod protostar; +pub mod xdg; diff --git a/src/main.rs b/src/main.rs index 268ad20..a47c509 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,29 +1,56 @@ -mod desktop_file; -mod protostar; - -use manifest_dir_macros::directory_relative_path; -use protostar::ProtoStar; -use stardust_xr_molecules::fusion::client::Client; -use std::{ - env::{args, current_dir}, - path::Path, +use clap::Parser; +use color_eyre::{ + eyre::{bail, Result}, + Report, }; +use manifest_dir_macros::directory_relative_path; +use protostar::{protostar::ProtoStar, xdg::parse_desktop_file}; +use stardust_xr_molecules::fusion::client::Client; +use std::path::PathBuf; + +#[derive(Debug, Parser)] +#[clap(author, version, about, long_about = None)] +struct Args { + #[clap(short, long, default_value_t = 0.1)] + size: f32, + #[clap( + short, + long, + conflicts_with = "command", + required_unless_present = "command", + conflicts_with = "icon", + required_unless_present = "icon" + )] + desktop_file: Option, + #[clap(short, long, conflicts_with = "desktop_file", requires = "command")] + icon: Option, + #[clap(short, long, conflicts_with = "desktop_file", requires = "icon")] + command: Option, +} #[tokio::main(flavor = "current_thread")] -async fn main() { - let (client, event_loop) = Client::connect_with_async_loop().await.unwrap(); +async fn main() -> Result<()> { + color_eyre::install()?; + let args = Args::parse(); + let (client, event_loop) = Client::connect_with_async_loop().await?; client.set_base_prefixes(&[directory_relative_path!("res")]); - let _root = client.wrap_root(ProtoStar::new( - client.clone(), - 0.1, - current_dir() - .unwrap() - .join(Path::new(&args().nth(1).unwrap())), - )); + let protostar = if let Some(desktop_file) = args.desktop_file { + ProtoStar::create_from_desktop_file( + client.get_root(), + parse_desktop_file(desktop_file).map_err(|e| Report::msg(e))?, + )? + } else if let Some(command) = args.command { + ProtoStar::new_raw(client.get_root(), None, command)? + } else { + bail!("No command or desktop file, nothing to launch."); + }; + + let _root = client.wrap_root(protostar); tokio::select! { _ = tokio::signal::ctrl_c() => (), - e = event_loop => e.unwrap().unwrap(), - } + e = event_loop => e??, + }; + Ok(()) } diff --git a/src/protostar.rs b/src/protostar.rs index 71d9138..acf556a 100644 --- a/src/protostar.rs +++ b/src/protostar.rs @@ -1,3 +1,5 @@ +use crate::xdg::{DesktopFile, Icon, RawIconType}; +use color_eyre::eyre::{eyre, Result}; use glam::Quat; use mint::Vector3; use nix::unistd::{execv, fork}; @@ -5,56 +7,110 @@ use stardust_xr_molecules::{ fusion::{ client::{Client, LifeCycleHandler, LogicStepInfo}, core::values::Transform, - drawable::Model, - fields::SphereField, + drawable::{MaterialParameter, Model, ResourceID}, + fields::BoxField, node::NodeType, - resource::NamespacedResource, + spatial::Spatial, startup_settings::StartupSettings, }, GrabData, Grabbable, }; -use std::{env::args, ffi::CString, path::PathBuf, sync::Arc}; +use std::{f32::consts::PI, ffi::CStr, sync::Arc}; use tween::{QuartInOut, Tweener}; use ustr::ustr; +fn model_from_icon(parent: &Spatial, icon: &Icon) -> Result { + let model = match icon { + Icon::Png(path) => { + let model = Model::create( + parent, + Transform::from_rotation(Quat::from_rotation_y(PI)), + &ResourceID::new_namespaced("protostar", "cartridge"), + )?; + model.set_material_parameter( + 0, + "diffuse", + MaterialParameter::Texture(ResourceID::Direct(path.clone())), + )?; + model + } + Icon::Gltf(path) => Model::create( + parent, + Transform::from_scale([0.05; 3]), + &ResourceID::new_direct(path)?, + )?, + }; + Ok(model) +} + pub struct ProtoStar { client: Arc, grabbable: Grabbable, - field: SphereField, + field: BoxField, icon: Model, - icon_shrink: Option>>, - size: f32, - executable_path: PathBuf, + icon_shrink: Option>, + execute_command: String, } impl ProtoStar { - pub fn new(client: Arc, size: f32, executable_path: PathBuf) -> Self { - let field = - SphereField::create(client.get_root(), Vector3::from([0.0; 3]), size * 0.5).unwrap(); + pub fn create_from_desktop_file(parent: &Spatial, desktop_file: DesktopFile) -> Result { + // dbg!(&desktop_file); + let mut raw_icons = desktop_file.get_raw_icons(); + let last_icon = raw_icons.pop(); + let icon = raw_icons + .into_iter() + .find(|i| match i { + RawIconType::Png(_) => false, + RawIconType::Svg(_) => false, + RawIconType::Gltf(_) => true, + }) + .or(last_icon) + .map(|i| dbg!(i.process(64)).ok()) + .ok_or_else(|| eyre!("No compatible icons found"))?; + Self::new_raw( + parent, + icon, + desktop_file.command.ok_or_else(|| eyre!("No command"))?, + ) + } + pub fn new_raw(parent: &Spatial, icon: Option, execute_command: String) -> Result { + let field = BoxField::create( + parent, + Transform::default(), + match icon.as_ref() { + Some(Icon::Png(_)) => [0.05, 0.0665, 0.005], + _ => [0.05; 3], + } + .into(), + )?; let grabbable = Grabbable::new( - client.get_root(), + parent, Transform::default(), &field, - GrabData { max_distance: 0.05 }, - ) - .unwrap(); - field - .set_spatial_parent(grabbable.content_parent()) - .unwrap(); - let icon = Model::create( - grabbable.content_parent(), - Transform::from_scale([size; 3]), - &NamespacedResource::new("protostar", "default_icon"), - ) - .unwrap(); - ProtoStar { - client, + GrabData { + max_distance: 0.025, + }, + )?; + field.set_spatial_parent(grabbable.content_parent())?; + let icon = icon + .map(|i| model_from_icon(grabbable.content_parent(), &i)) + .unwrap_or_else(|| { + Ok(Model::create( + grabbable.content_parent(), + Transform::from_scale([0.05; 3]), + &ResourceID::new_namespaced("protostar", "default_icon"), + )?) + })?; + Ok(ProtoStar { + client: parent.client()?, grabbable, field, icon, icon_shrink: None, - size, - executable_path, - } + execute_command, + }) + } + pub fn content_parent(&self) -> &Spatial { + self.grabbable.content_parent() } } impl LifeCycleHandler for ProtoStar { @@ -62,7 +118,8 @@ impl LifeCycleHandler for ProtoStar { self.grabbable.update(); if let Some(icon_shrink) = &mut self.icon_shrink { - if let Some(scale) = icon_shrink.update(info.delta) { + if !icon_shrink.is_finished() { + let scale = icon_shrink.move_by(info.delta); self.icon .set_scale(None, Vector3::from([scale; 3])) .unwrap(); @@ -84,19 +141,21 @@ impl LifeCycleHandler for ProtoStar { startup_settings .set_root(self.grabbable.content_parent()) .unwrap(); - self.icon_shrink = Some(Tweener::new(QuartInOut::new(self.size..=0.0, 0.25))); + self.icon_shrink = Some(Tweener::quart_in_out(1.0, 0.0, 0.25)); let future = startup_settings.generate_startup_token().unwrap(); - let executable = self.executable_path.clone(); + let executable = dbg!(self.execute_command.clone()); tokio::task::spawn(async move { std::env::set_var("STARDUST_STARTUP_TOKEN", future.await.unwrap()); if unsafe { fork() }.unwrap().is_parent() { - let executable = ustr(executable.to_str().unwrap()); - let args = args() - .skip(1) - .map(|arg| CString::new(arg)) - .collect::, _>>() - .unwrap(); - execv::(executable.as_cstr(), args.as_slice()).unwrap(); + execv::<&CStr>( + ustr("/bin/sh").as_cstr(), + &[ + ustr("/bin/sh").as_cstr(), + ustr("-c").as_cstr(), + ustr(&executable).as_cstr(), + ], + ) + .unwrap(); } }); } diff --git a/src/desktop_file.rs b/src/xdg.rs similarity index 57% rename from src/desktop_file.rs rename to src/xdg.rs index 546d18b..bd18044 100644 --- a/src/desktop_file.rs +++ b/src/xdg.rs @@ -1,16 +1,18 @@ -use std::ffi::OsString; -use std::io::{BufRead, BufReader}; -use std::path::{Path, PathBuf}; -use std::str::FromStr; -use std::{env, fs}; - -use anyhow::Result; +use color_eyre::eyre::Result; use resvg::render; use resvg::tiny_skia::{Pixmap, Transform}; use resvg::usvg::{FitTo, Tree}; +use std::ffi::OsString; +use std::fs::create_dir_all; +use std::io::{BufRead, BufReader, ErrorKind}; +use std::path::{Path, PathBuf}; +use std::str::FromStr; +use std::{env, fs}; use walkdir::WalkDir; -fn get_desktop_files() -> Vec { +const ICON_SIZES: &[&str] = &["128x128", "scalable", "256x256", "64x64", "32x32"]; + +pub fn get_desktop_files() -> Vec { // Get the XDG data directories let xdg_data_dirs = std::env::var("XDG_DATA_DIRS").unwrap_or("/usr/local/share:/usr/share".to_string()); @@ -55,9 +57,13 @@ fn test_get_desktop_files() { .any(|file| file.ends_with("gimp.desktop"))); } -fn parse_desktop_file(path: &Path) -> Result { +pub fn parse_desktop_file(path: PathBuf) -> Result { // Open the file in read-only mode - let file = match fs::File::open(path) { + let file = match fs::File::open( + env::current_dir() + .map_err(|e| e.to_string())? + .join(path.clone()), + ) { Ok(file) => file, Err(err) => return Err(format!("Failed to open file: {}", err)), }; @@ -93,7 +99,13 @@ fn parse_desktop_file(path: &Path) -> Result { match key { "Name" => name = Some(value.to_string()), "Exec" => command = Some(value.to_string()), - "Categories" => categories = value.split(';').map(|s| s.to_string()).collect(), + "Categories" => { + categories = value + .split(';') + .map(|s| s.to_string()) + .filter(|s| !s.is_empty()) + .collect() + } "Icon" => icon = Some(value.to_string()), _ => (), // Ignore unknown keys } @@ -101,6 +113,7 @@ fn parse_desktop_file(path: &Path) -> Result { // Create and return a new DesktopFile instance with the parsed values Ok(DesktopFile { + path, name, command, categories, @@ -117,7 +130,7 @@ fn test_parse_desktop_file() { fs::write(&file, data).unwrap(); // Parse the test desktop file - let desktop_file = parse_desktop_file(&file).unwrap(); + let desktop_file = parse_desktop_file(file).unwrap(); // Check the parsed values assert_eq!(desktop_file.name, Some("Test".to_string())); @@ -129,31 +142,130 @@ fn test_parse_desktop_file() { assert_eq!(desktop_file.icon, Some("test.png".to_string())); } +#[derive(Debug, Clone)] +pub struct DesktopFile { + path: PathBuf, + pub name: Option, + pub command: Option, + pub categories: Vec, + pub icon: Option, +} +impl DesktopFile { + pub fn get_raw_icons(&self) -> Vec { + // Get the name of the icon from the DesktopFile struct + let Some(icon_name) = self.icon.as_ref() else { return Vec::new(); }; + let test_icon_path = self.path.join(Path::new(icon_name)); + if test_icon_path.exists() { + return RawIconType::from_path(test_icon_path) + .map(|i| vec![i]) + .unwrap_or_default(); + } + + let Ok(icon_name) = OsString::from_str(icon_name) else { return Vec::new(); }; + + // Get the current icon theme from the XDG_ICON_THEME environment variable, or use "hicolor" as the default theme if the variable is not defined + let icon_theme = env::var_os("XDG_ICON_THEME").unwrap_or("hicolor".into()); + + // Get the XDG_DATA_HOME and XDG_DATA_DIRS environment variables, and split the XDG_DATA_DIRS variable into a list of directories + let Some(xdg_data_dirs) = env::var_os("XDG_DATA_DIRS") else { return Vec::new(); }; + let Ok(binding) = xdg_data_dirs.into_string() else { return Vec::new(); }; + let xdg_data_dirs = binding.split(":").map(Path::new); + + // Concatenate the XDG_DATA_HOME and XDG_DATA_DIRS directories with the default path for icon themes + xdg_data_dirs // XDG_DATA_DIRS directories + .flat_map(|dir| { + let icons_path = dir.join("icons").join(&icon_theme); + ICON_SIZES + .iter() + .map(|path| icons_path.join(path).join("apps")) + .collect::>() + }) + .filter_map(|dir| { + let dir = fs::read_dir(dir).ok()?; + Some( + dir.filter_map(|e| e.ok()) + .map(|file| file.path()) + .filter(|file| file.file_stem() == Some(&icon_name)), + ) + }) + .flatten() + .filter_map(RawIconType::from_path) + .collect() + } +} + #[derive(Debug, PartialEq, Eq)] -enum IconType { +pub enum RawIconType { Png(PathBuf), Svg(PathBuf), Gltf(PathBuf), } -impl IconType { - fn to_png(&self, size: u32) -> Option { - match self { - IconType::Png(path) => Some(path.clone()), - IconType::Svg(path) => { - let png_path = path.with_extension("png"); - render_svg_to_png(path, &png_path, size).ok()?; - Some(png_path) - } +impl RawIconType { + pub fn from_path(path: PathBuf) -> Option { + match path.extension().and_then(|ext| ext.to_str()) { + Some("png") => Some(RawIconType::Png(path)), + Some("svg") => Some(RawIconType::Svg(path)), + Some("glb") | Some("gltf") => Some(RawIconType::Gltf(path)), _ => None, } } + + pub fn process(self, size: u32) -> Result { + match self { + RawIconType::Png(path) => Ok(Icon::Png(path)), + RawIconType::Svg(path) => { + let png_path = path.with_extension("png"); + render_svg_to_png(path, &png_path, size)?; + Ok(Icon::Png(png_path)) + } + RawIconType::Gltf(path) => Ok(Icon::Gltf(path)), + } + } } -fn render_svg_to_png(svg_path: &PathBuf, png_path: &PathBuf, size: u32) -> Result<()> { +#[test] +fn test_get_icon_path() { + // Create an instance of the DesktopFile struct with some dummy data + let desktop_file = DesktopFile { + path: PathBuf::new(), + name: None, + command: None, + categories: vec![], + icon: Some("krita".into()), + }; + + // Call the get_icon_path() function with a size argument and store the result + let icon_paths = desktop_file.get_raw_icons(); + dbg!(&icon_paths); + + // Assert that the get_icon_path() function returns the expected result + assert!(icon_paths.contains(&RawIconType::Png(PathBuf::from( + "/usr/share/icons/hicolor/16x16/apps/krita.png" + )))); +} + +#[derive(Debug, PartialEq, Eq)] +pub enum Icon { + Png(PathBuf), + Gltf(PathBuf), +} + +pub fn render_svg_to_png( + cache_dir: impl AsRef, + svg_path: impl AsRef, + size: u32, +) -> Result { + let svg_path = fs::canonicalize(svg_path)?; let tree = Tree::from_data( - fs::read(svg_path)?.as_slice(), + fs::read(svg_path.as_path())?.as_slice(), &resvg::usvg::Options::default(), - )?; + ) + .map_err(|_| ErrorKind::InvalidData)?; + create_dir_all(cache_dir.as_ref())?; + let png_path = cache_dir + .as_ref() + .join(svg_path.file_name().unwrap()) + .with_extension("png"); let mut pixmap = Pixmap::new(size, size).unwrap(); render( &tree, @@ -161,101 +273,40 @@ fn render_svg_to_png(svg_path: &PathBuf, png_path: &PathBuf, size: u32) -> Resul Transform::identity(), pixmap.as_mut(), ); - pixmap.save_png(png_path)?; - Ok(()) + pixmap + .save_png(&png_path) + .map_err(|_| ErrorKind::InvalidData)?; + Ok(png_path) } #[test] fn test_render_svg_to_png() { use image::GenericImageView; // Create temporary input and output paths - let input_path = PathBuf::from("test_input.svg"); - let output_path = PathBuf::from("test_output.png"); + let svg_path = env::current_dir().unwrap().join("test_input.svg"); // Write some test SVG data to the input path let test_svg_data = " - - - - "; - fs::write(&input_path, test_svg_data).unwrap(); + + + +"; + fs::write(&svg_path, test_svg_data).unwrap(); // Call the function with the test input and output paths and a size of 200 - render_svg_to_png(&input_path, &output_path, 200).unwrap(); + let png_path = render_svg_to_png(".", &svg_path, 200).unwrap(); + dbg!(&png_path); // Check that the output file exists - assert!(output_path.exists()); + assert!(png_path.exists()); // Check that the output file is a PNG file - assert_eq!(output_path.extension().unwrap(), "png"); + assert_eq!(png_path.extension().unwrap(), "png"); // Check that the output file has the expected dimensions - let output_image = image::open(&output_path).unwrap(); + let output_image = image::open(&png_path).unwrap(); assert_eq!(output_image.dimensions(), (200, 200)); // Delete the temporary input and output files - fs::remove_file(&input_path).unwrap(); - fs::remove_file(&output_path).unwrap(); -} - -struct DesktopFile { - name: Option, - command: Option, - categories: Vec, - icon: Option, -} - -impl DesktopFile { - fn get_icon_path(&self, size: &str) -> Option { - // Get the name of the icon from the DesktopFile struct - let icon_name = OsString::from_str(self.icon.as_ref()?).ok()?; - - // Get the current icon theme from the XDG_ICON_THEME environment variable, or use "hicolor" as the default theme if the variable is not defined - let icon_theme = env::var_os("XDG_ICON_THEME").unwrap_or("hicolor".into()); - - // Get the XDG_DATA_HOME and XDG_DATA_DIRS environment variables, and split the XDG_DATA_DIRS variable into a list of directories - let xdg_data_dirs = env::var_os("XDG_DATA_DIRS")?; - let binding = xdg_data_dirs.into_string().ok()?; - let xdg_data_dirs = binding.split(":").map(Path::new); - - // Concatenate the XDG_DATA_HOME and XDG_DATA_DIRS directories with the default path for icon themes - let icon = xdg_data_dirs // XDG_DATA_DIRS directories - .map(|dir| dir.join("icons").join(&icon_theme).join(size).join("apps")) - .filter_map(|dir| { - fs::read_dir(dir) - .ok()? - .filter_map(|e| e.ok()) - .map(|file| file.path()) - .find(|file| file.file_stem() == Some(&icon_name)) - }) - .last()?; - - // Check the file extension of the icon file and return an option containing the path to the file in an instance of the IconType enum - match icon.extension().and_then(|ext| ext.to_str()) { - Some("png") => Some(IconType::Png(icon)), - Some("svg") => Some(IconType::Svg(icon)), - Some("glb") | Some("gltf") => Some(IconType::Gltf(icon)), - _ => None, - } - } -} -#[test] -fn test_get_icon_path() { - // Create an instance of the DesktopFile struct with some dummy data - let desktop_file = DesktopFile { - name: Some("test".into()), - command: Some("test-command".into()), - categories: vec!["test-category".into()], - icon: Some("krita".into()), - }; - - // Call the get_icon_path() function with a size argument and store the result - let icon_path = desktop_file.get_icon_path("16x16"); - - // Assert that the get_icon_path() function returns the expected result - assert_eq!( - icon_path, - Some(IconType::Png(PathBuf::from( - "/usr/share/icons/hicolor/16x16/apps/krita.png" - ))) - ); + fs::remove_file(&svg_path).unwrap(); + fs::remove_file(&png_path).unwrap(); }