diff --git a/Cargo.lock b/Cargo.lock index 9356261..c35f3b4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -173,7 +173,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef6978589202a00cd7e118380c448a08b6ed394c3a8df3a430d0898e3a42d046" dependencies = [ "android-properties", - "bitflags 2.6.0", + "bitflags 2.7.0", "cc", "cesu8", "jni", @@ -253,9 +253,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.94" +version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1fd03a028ef38ba2276dce7e33fcd6369c158a1bca17946c4b1b701891c1ff7" +checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04" [[package]] name = "arbitrary" @@ -292,9 +292,9 @@ checksum = "175571dd1d178ced59193a6fc02dde1b972eb0bc56c892cde9beeceac5bf0f6b" [[package]] name = "async-broadcast" -version = "0.7.1" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20cd0e2e25ea8e5f7e9df04578dc6cf5c83577fd09b1a46aaf5c85e1c33f2a7e" +checksum = "435a87a52755b8f27fcf321ac4f04b2802e337c8c4872923137471ec39c37532" dependencies = [ "event-listener", "event-listener-strategy", @@ -424,9 +424,9 @@ checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" [[package]] name = "async-trait" -version = "0.1.83" +version = "0.1.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd" +checksum = "3f934833b4b7233644e5848f235df3f57ed8c80f1528a26c3dfa13d2147fa056" dependencies = [ "proc-macro2", "quote", @@ -506,7 +506,7 @@ dependencies = [ "cfg-if", "libc", "miniz_oxide", - "object 0.36.5", + "object 0.36.7", "rustc-demangle", "windows-targets 0.52.6", ] @@ -534,9 +534,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.6.0" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" +checksum = "1be3f42a67d6d345ecd59f675f3f012d6974981560836e938c22b424b85ce1be" dependencies = [ "serde", ] @@ -605,18 +605,18 @@ checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" [[package]] name = "bytemuck" -version = "1.20.0" +version = "1.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b37c88a63ffd85d15b406896cc343916d7cf57838a847b3a6f2ca5d39a5695a" +checksum = "ef657dfab802224e671f5818e9a4935f9b1957ed18e58292690cc39e7a4092a3" dependencies = [ "bytemuck_derive", ] [[package]] name = "bytemuck_derive" -version = "1.8.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcfcc3cd946cb52f0bbfdbbcfa2f4e24f75ebb6c0e1002f7c25904fada18b9ec" +checksum = "3fa76293b4f7bb636ab88fd78228235b5248b4d05cc589aed610f954af5d7c7a" dependencies = [ "proc-macro2", "quote", @@ -647,7 +647,7 @@ version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b99da2f8558ca23c71f4fd15dc57c906239752dd27ff3c00a1d56b685b7cbfec" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.7.0", "log", "polling", "rustix", @@ -746,9 +746,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.3" +version = "1.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27f657647bcff5394bf56c7317665bbf790a137a50eaaa5c6bfbb9e27a518f2d" +checksum = "c8293772165d9345bdaaa39b45b2109591e63fe5e6fbc23c6ff930a048aa310b" dependencies = [ "jobserver", "libc", @@ -1172,9 +1172,9 @@ dependencies = [ [[package]] name = "crossbeam-deque" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" +checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" dependencies = [ "crossbeam-epoch", "crossbeam-utils", @@ -1191,9 +1191,9 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.20" +version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" [[package]] name = "crunchy" @@ -1211,29 +1211,6 @@ dependencies = [ "typenum", ] -[[package]] -name = "cssparser" -version = "0.34.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7c66d1cd8ed61bf80b38432613a7a2f09401ab8d0501110655f8b341484a3e3" -dependencies = [ - "cssparser-macros", - "dtoa-short", - "itoa", - "phf", - "smallvec", -] - -[[package]] -name = "cssparser-macros" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13b588ba4ac1a99f7f2964d24b3d896ddc6bf847ee3855dbd4366f058cfcd331" -dependencies = [ - "quote", - "syn", -] - [[package]] name = "cursor-icon" version = "1.1.0" @@ -1303,17 +1280,6 @@ dependencies = [ "powerfmt", ] -[[package]] -name = "derive_more" -version = "0.99.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f33878137e4dafd7fa914ad4e259e18a4e8e532b9617a2d0150262bf53abfce" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "digest" version = "0.10.7" @@ -1412,21 +1378,6 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f25c0e292a7ca6d6498557ff1df68f32c99850012b6ea401cf8daf771f22ff53" -[[package]] -name = "dtoa" -version = "1.0.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcbb2bf8e87535c23f7a8a321e364ce21462d0ff10cb6407820e8e96dfff6653" - -[[package]] -name = "dtoa-short" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd1511a7b6a56299bd043a9c167a6d2bfb37bf84a6dfceaba651168adfb43c87" -dependencies = [ - "dtoa", -] - [[package]] name = "ecolor" version = "0.29.1" @@ -1475,12 +1426,6 @@ dependencies = [ "winit", ] -[[package]] -name = "ego-tree" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2972feb8dffe7bc8c5463b1dacda1b0dfbed3710e50f977d965429692d74cd8" - [[package]] name = "egui" version = "0.29.1" @@ -1609,9 +1554,9 @@ dependencies = [ [[package]] name = "env_filter" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f2c92ceda6ceec50f43169f9ee8424fe2db276791afde7b2cd8bc084cb376ab" +checksum = "186e05a59d4c50738528153b83b0b0194d3a29507dfec16eccd4b342903397d0" dependencies = [ "log", "regex", @@ -1619,9 +1564,9 @@ dependencies = [ [[package]] name = "env_logger" -version = "0.11.5" +version = "0.11.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13fa619b91fb2381732789fc5de83b45675e882f66623b7d8cb4f643017018d" +checksum = "dcaee3d8e3cfc3fd92428d477bc97fc29ec8716d180c0d74c643bb26166660e0" dependencies = [ "anstream", "anstyle", @@ -1678,9 +1623,9 @@ checksum = "a5d9305ccc6942a704f4335694ecd3de2ea531b114ac2d51f5f843750787a92f" [[package]] name = "event-listener" -version = "5.3.1" +version = "5.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6032be9bd27023a771701cc49f9f053c751055f71efb2e0ae5c15809093675ba" +checksum = "3492acde4c3fc54c845eaab3eed8bd00c7a7d881f78bfc801e43a93dec1331ae" dependencies = [ "concurrent-queue", "parking", @@ -1705,9 +1650,9 @@ checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649" [[package]] name = "fastrand" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "486f806e73c5707928240ddc295403b1b93c96a02038563881c4a2fd84b81ac4" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" [[package]] name = "fd-lock" @@ -1747,9 +1692,9 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "foldhash" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f81ec6369c545a7d40e4589b5597581fa1c441fe1cce96dd1de43159910a36a2" +checksum = "a0d2fde1f7b3d48b8395d5f2de76c18a528bd6a9cdde438df747bfcba3e05d6f" [[package]] name = "foreign-types" @@ -1858,9 +1803,9 @@ checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" [[package]] name = "futures-lite" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cef40d21ae2c515b51041df9ed313ed21e572df340ea58a922a0aefe7e8891a1" +checksum = "f5edaec856126859abb19ed65f39e90fea3a9574b9707f13539acf4abf7eb532" dependencies = [ "fastrand", "futures-core", @@ -1925,7 +1870,7 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "27d12c0aed7f1e24276a241aadc4cb8ea9f83000f34bc062b7cc2d51e3b0fabd" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.7.0", "debugid", "fxhash", "serde", @@ -2023,11 +1968,11 @@ dependencies = [ [[package]] name = "glutin" -version = "0.32.1" +version = "0.32.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec69412a0bf07ea7607e638b415447857a808846c2b685a43c8aa18bc6d5e499" +checksum = "03642b8b0cce622392deb0ee3e88511f75df2daac806102597905c3ea1974848" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.7.0", "cfg_aliases", "cgl", "core-foundation 0.9.4", @@ -2060,9 +2005,9 @@ dependencies = [ [[package]] name = "glutin_egl_sys" -version = "0.7.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cae99fff4d2850dbe6fb8c1fa8e4fead5525bab715beaacfccf3fb994e01c827" +checksum = "4c4680ba6195f424febdc3ba46e7a42a0e58743f2edb115297b86d7f8ecc02d2" dependencies = [ "gl_generator", "windows-sys 0.52.0", @@ -2070,9 +2015,9 @@ dependencies = [ [[package]] name = "glutin_glx_sys" -version = "0.6.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c2b2d3918e76e18e08796b55eb64e8fe6ec67d5a6b2e2a7e2edce224ad24c63" +checksum = "8a7bb2938045a88b612499fbcba375a77198e01306f52272e692f8c1f3751185" dependencies = [ "gl_generator", "x11-dl", @@ -2080,9 +2025,9 @@ dependencies = [ [[package]] name = "glutin_wgl_sys" -version = "0.6.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a4e1951bbd9434a81aa496fe59ccc2235af3820d27b85f9314e279609211e2c" +checksum = "2c4ee00b289aba7a9e5306d57c2d05499b2e5dc427f84ac708bd2c090212cf3e" dependencies = [ "gl_generator", ] @@ -2133,11 +2078,11 @@ checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" [[package]] name = "home" -version = "0.5.9" +version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" +checksum = "589533453244b0995c858700322199b2becb13b627df2851f64a2775d024abcf" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -2172,9 +2117,9 @@ dependencies = [ [[package]] name = "html5ever" -version = "0.29.0" +version = "0.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e15626aaf9c351bc696217cbe29cb9b5e86c43f8a46b5e2f5c6c5cf7cb904ce" +checksum = "c13771afe0e6e846f1e67d038d4cb29998a6779f93c809212e4e9c32efd244d4" dependencies = [ "log", "mac", @@ -2504,9 +2449,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.76" +version = "0.3.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6717b6b5b077764fb5966237269cb3c64edddde4b14ce42647430a78ced9e7b7" +checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" dependencies = [ "once_cell", "wasm-bindgen", @@ -2548,9 +2493,9 @@ checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67" [[package]] name = "libc" -version = "0.2.167" +version = "0.2.169" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09d6582e104315a817dff97f75133544b2e094ee22447d2acf4a74e189ba06fc" +checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" [[package]] name = "libloading" @@ -2574,16 +2519,16 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.7.0", "libc", - "redox_syscall 0.5.7", + "redox_syscall 0.5.8", ] [[package]] name = "linux-raw-sys" -version = "0.4.14" +version = "0.4.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" +checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" [[package]] name = "litemap" @@ -2637,9 +2582,9 @@ dependencies = [ [[package]] name = "markup5ever" -version = "0.14.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82c88c6129bd24319e62a0359cb6b958fa7e8be6e19bb1663bc396b90883aca5" +checksum = "16ce3abbeba692c8b8441d036ef91aea6df8da2c6b6e21c7e14d3c18e526be45" dependencies = [ "log", "phf", @@ -2649,6 +2594,18 @@ dependencies = [ "tendril", ] +[[package]] +name = "markup5ever_rcdom" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edaa21ab3701bfee5099ade5f7e1f84553fd19228cf332f13cd6e964bf59be18" +dependencies = [ + "html5ever", + "markup5ever", + "tendril", + "xml5ever", +] + [[package]] name = "matchers" version = "0.1.0" @@ -2699,9 +2656,9 @@ dependencies = [ [[package]] name = "miniz_oxide" -version = "0.8.0" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" +checksum = "4ffbe83022cedc1d264172192511ae958937694cd57ce297164951b8b3568394" dependencies = [ "adler2", "simd-adler32", @@ -2724,7 +2681,7 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3f42e7bbe13d351b6bead8286a43aac9534b82bd3cc43e47037f012ebfd62d4" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.7.0", "jni-sys", "log", "ndk-sys", @@ -2760,7 +2717,7 @@ version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.7.0", "cfg-if", "cfg_aliases", "libc", @@ -2850,7 +2807,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e4e89ad9e3d7d297152b17d39ed92cd50ca8063a89a9fa569046d41568891eff" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.7.0", "block2", "libc", "objc2", @@ -2866,7 +2823,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74dd3b56391c7a0596a295029734d3c1c5e7e510a4cb30245f8221ccea96b009" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.7.0", "block2", "objc2", "objc2-core-location", @@ -2890,7 +2847,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "617fbf49e071c178c0b24c080767db52958f716d9eabdf0890523aeae54773ef" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.7.0", "block2", "objc2", "objc2-foundation", @@ -2932,7 +2889,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.7.0", "block2", "dispatch", "libc", @@ -2957,7 +2914,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd0cba1276f6023976a406a14ffa85e1fdd19df6b0f737b063b95f6c8c7aadd6" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.7.0", "block2", "objc2", "objc2-foundation", @@ -2969,7 +2926,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e42bee7bff906b14b167da2bac5efe6b6a07e6f7c0a21a7308d40c960242dc7a" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.7.0", "block2", "objc2", "objc2-foundation", @@ -2992,7 +2949,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8bb46798b20cd6b91cbd113524c490f1686f4c4e8f49502431415f3512e2b6f" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.7.0", "block2", "objc2", "objc2-cloud-kit", @@ -3024,7 +2981,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "76cfcbf642358e8689af64cee815d139339f3ed8ad05103ed5eaf73db8d84cb3" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.7.0", "block2", "objc2", "objc2-core-location", @@ -3045,9 +3002,9 @@ dependencies = [ [[package]] name = "object" -version = "0.36.5" +version = "0.36.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aedf0a2d09c573ed1d8d85b30c119153926a2b36dce0ab28322c09a117a4683e" +checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" dependencies = [ "crc32fast", "hashbrown 0.15.2", @@ -3122,7 +3079,7 @@ checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.5.7", + "redox_syscall 0.5.8", "smallvec", "windows-targets 0.52.6", ] @@ -3145,7 +3102,6 @@ version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078" dependencies = [ - "phf_macros", "phf_shared 0.11.3", ] @@ -3179,19 +3135,6 @@ dependencies = [ "rand", ] -[[package]] -name = "phf_macros" -version = "0.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f84ac04429c13a7ff43785d75ad27569f2951ce0ffd30a3321230db2fc727216" -dependencies = [ - "phf_generator 0.11.3", - "phf_shared 0.11.3", - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "phf_shared" version = "0.10.0" @@ -3212,18 +3155,18 @@ dependencies = [ [[package]] name = "pin-project" -version = "1.1.7" +version = "1.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be57f64e946e500c8ee36ef6331845d40a93055567ec57e8fae13efd33759b95" +checksum = "1e2ec53ad785f4d35dac0adea7f7dc6f1bb277ad84a680c7afefeae05d1f5916" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.1.7" +version = "1.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c0f5fad0874fc7abcd4d750e76917eaebbecaa2c20bde22e1dbeeba8beb758c" +checksum = "d56a66c0c55993aa927429d0f8a0abfd74f084e4d9c192cffed01e418d83eefb" dependencies = [ "proc-macro2", "quote", @@ -3232,9 +3175,9 @@ dependencies = [ [[package]] name = "pin-project-lite" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "915a1e146535de9163f3987b8944ed8cf49a18bb0056bcebcdcece385cece4ff" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" [[package]] name = "pin-utils" @@ -3261,9 +3204,9 @@ checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" [[package]] name = "png" -version = "0.17.15" +version = "0.17.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b67582bd5b65bdff614270e2ea89a1cf15bef71245cc1e5f7ea126977144211d" +checksum = "82151a2fc869e011c153adc57cf2789ccb8d9906ce52c0b39a6b5697749d7526" dependencies = [ "bitflags 1.3.2", "crc32fast", @@ -3328,9 +3271,9 @@ checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" [[package]] name = "prettyplease" -version = "0.2.25" +version = "0.2.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64d1ec885c64d0457d564db4ec299b2dae3f9c02808b8ad9c3a089c591b18033" +checksum = "6924ced06e1f7dfe3fa48d57b9f74f55d8915f5036121bef647ef4b204895fac" dependencies = [ "proc-macro2", "syn", @@ -3347,9 +3290,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.92" +version = "1.0.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" +checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99" dependencies = [ "unicode-ident", ] @@ -3395,9 +3338,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.37" +version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" dependencies = [ "proc-macro2", ] @@ -3471,6 +3414,7 @@ dependencies = [ name = "rdx" version = "0.2.1" dependencies = [ + "ahash", "anyhow", "async-trait", "eframe", @@ -3480,14 +3424,17 @@ dependencies = [ "getrandom", "html-egui-bindgen", "html-to-egui", + "html5ever", "js_wasm_runtime_layer", "log", + "markup5ever", + "markup5ever_rcdom", "parking_lot", "rand", "rhai", - "scraper", "send_wrapper", "serde", + "test-log", "thiserror", "time", "tokio", @@ -3513,11 +3460,11 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.7" +version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f" +checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.7.0", ] [[package]] @@ -3615,7 +3562,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0277a46f29fe3b3eb10821ca2c65a4751b686b6c84422aae31695ba167b0fbc" dependencies = [ "ahash", - "bitflags 2.6.0", + "bitflags 2.7.0", "getrandom", "instant", "no-std-compat", @@ -3646,7 +3593,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b91f7eff05f748767f183df4320a63d6936e9c6107d97c9e6bdd9784f4289c94" dependencies = [ "base64", - "bitflags 2.6.0", + "bitflags 2.7.0", "serde", "serde_derive", ] @@ -3665,17 +3612,17 @@ checksum = "c7fb8039b3032c191086b10f11f319a6e99e1e82889c5cc6046f515c9db1d497" [[package]] name = "rustix" -version = "0.38.41" +version = "0.38.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7f649912bc1495e167a6edee79151c84b1bad49748cb4f1f1167f459f6224f6" +checksum = "a78891ee6bf2340288408954ac787aa063d8e8817e9f53abb37c695c6d834ef6" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.7.0", "errno", "itoa", "libc", "linux-raw-sys", "once_cell", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -3711,44 +3658,11 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" -[[package]] -name = "scraper" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc3d051b884f40e309de6c149734eab57aa8cc1347992710dc80bcc1c2194c15" -dependencies = [ - "cssparser", - "ego-tree", - "html5ever", - "precomputed-hash", - "selectors", - "tendril", -] - -[[package]] -name = "selectors" -version = "0.26.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd568a4c9bb598e291a08244a5c1f5a8a6650bee243b5b0f8dbb3d9cc1d87fe8" -dependencies = [ - "bitflags 2.6.0", - "cssparser", - "derive_more", - "fxhash", - "log", - "new_debug_unreachable", - "phf", - "phf_codegen", - "precomputed-hash", - "servo_arc", - "smallvec", -] - [[package]] name = "semver" -version = "1.0.23" +version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" +checksum = "3cb6eb87a131f756572d7fb904f6e7b68633f09cca868c5df1c4b8d1a694bbba" dependencies = [ "serde", ] @@ -3764,18 +3678,18 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.215" +version = "1.0.217" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6513c1ad0b11a9376da888e3e0baa0077f1aed55c17f50e7b2397136129fb88f" +checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.215" +version = "1.0.217" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0" +checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" dependencies = [ "proc-macro2", "quote", @@ -3784,9 +3698,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.133" +version = "1.0.135" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7fceb2473b9166b2294ef05efcb65a3db80803f0b03ef86a5fc88a2b85ee377" +checksum = "2b0d7ba2887406110130a978386c4e1befb98c674b4fba677954e4db976630d9" dependencies = [ "itoa", "memchr", @@ -3814,15 +3728,6 @@ dependencies = [ "serde", ] -[[package]] -name = "servo_arc" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae65c4249478a2647db249fb43e23cec56a2c8974a427e7bd8cb5a1d0964921a" -dependencies = [ - "stable_deref_trait", -] - [[package]] name = "sha1" version = "0.10.6" @@ -3947,7 +3852,7 @@ version = "0.19.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3457dea1f0eb631b4034d61d4d8c32074caa6cd1ab2d59f2327bd8461e2c0016" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.7.0", "calloop", "calloop-wayland-source", "cursor-icon", @@ -3998,9 +3903,9 @@ dependencies = [ [[package]] name = "spdx" -version = "0.10.7" +version = "0.10.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bae30cc7bfe3656d60ee99bf6836f472b0c53dddcbf335e253329abb16e535a2" +checksum = "58b69356da67e2fc1f542c71ea7e654a361a79c938e4424392ecf4fa065d2193" dependencies = [ "smallvec", ] @@ -4063,9 +3968,9 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "syn" -version = "2.0.90" +version = "2.0.96" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31" +checksum = "d5d0adab1ae378d7f53bdebc67a39f1f151407ef230f0ce2883572f5d8985c80" dependencies = [ "proc-macro2", "quote", @@ -4089,7 +3994,7 @@ version = "0.27.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cc4592f674ce18521c2a81483873a49596655b179f71c5e05d10c1fe66c78745" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.7.0", "cap-fs-ext", "cap-std", "fd-lock", @@ -4107,12 +4012,13 @@ checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" [[package]] name = "tempfile" -version = "3.14.0" +version = "3.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28cce251fcbc87fac86a866eeb0d6c2d536fc16d06f184bb61aeae11aa4cee0c" +checksum = "9a8a559c81686f576e8cd0290cd2a24a2a9ad80c98b3478856500fcbd7acd704" dependencies = [ "cfg-if", "fastrand", + "getrandom", "once_cell", "rustix", "windows-sys 0.59.0", @@ -4138,6 +4044,28 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "test-log" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3dffced63c2b5c7be278154d76b479f9f9920ed34e7574201407f0b14e2bbb93" +dependencies = [ + "env_logger", + "test-log-macros", + "tracing-subscriber", +] + +[[package]] +name = "test-log-macros" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5999e24eaa32083191ba4e425deb75cdf25efefabe5aaccb7446dd0d4122a3f5" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "thin-vec" version = "0.2.13" @@ -4217,9 +4145,9 @@ dependencies = [ [[package]] name = "tokio" -version = "1.42.0" +version = "1.43.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cec9b21b0450273377fc97bd4c33a8acffc8c996c987a7c5b319a0083707551" +checksum = "3d61fa4ffa3de412bfea335c6ecff681de2b609ba3c77ef3e00e521813a9ed9e" dependencies = [ "backtrace", "bytes", @@ -4235,9 +4163,9 @@ dependencies = [ [[package]] name = "tokio-macros" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" +checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" dependencies = [ "proc-macro2", "quote", @@ -4424,9 +4352,9 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.11.0" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8c5f0a0af699448548ad1a2fbf920fb4bee257eae39953ba95cb84891a0446a" +checksum = "b913a3b5fe84142e269d63cc62b64319ccaf89b748fc31fe025177f767a756c4" [[package]] name = "valuable" @@ -4458,20 +4386,21 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.99" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a474f6281d1d70c17ae7aa6a613c87fce69a127e2624002df63dcb39d6cf6396" +checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" dependencies = [ "cfg-if", "once_cell", + "rustversion", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.99" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f89bb38646b4f81674e8f5c3fb81b562be1fd936d84320f3264486418519c79" +checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" dependencies = [ "bumpalo", "log", @@ -4483,9 +4412,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.49" +version = "0.4.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38176d9b44ea84e9184eff0bc34cc167ed044f816accfe5922e54d84cf48eca2" +checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" dependencies = [ "cfg-if", "js-sys", @@ -4496,9 +4425,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.99" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2cc6181fd9a7492eef6fef1f33961e3695e4579b9872a6f7c83aee556666d4fe" +checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -4506,9 +4435,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.99" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30d7a95b763d3c45903ed6c81f156801839e5ee968bb07e534c44df0fcd330c2" +checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" dependencies = [ "proc-macro2", "quote", @@ -4519,9 +4448,12 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.99" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "943aab3fdaaa029a6e0271b35ea10b72b943135afe9bffca82384098ad0e06a6" +checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +dependencies = [ + "unicode-ident", +] [[package]] name = "wasm-encoder" @@ -4561,12 +4493,12 @@ dependencies = [ [[package]] name = "wasm-encoder" -version = "0.221.2" +version = "0.223.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c17a3bd88f2155da63a1f2fcb8a56377a24f0b6dfed12733bb5f544e86f690c5" +checksum = "7e636076193fa68103e937ac951b5f2f587624097017d764b8984d9c0f149464" dependencies = [ "leb128", - "wasmparser 0.221.2", + "wasmparser 0.223.0", ] [[package]] @@ -4632,7 +4564,7 @@ version = "0.119.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c35daf77afb4f9b14016625144a391085ec2ca99ca9cc53ed291bb53ab5278d" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.7.0", "indexmap", "semver", ] @@ -4643,7 +4575,7 @@ version = "0.121.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9dbe55c8f9d0dbd25d9447a5a889ff90c0cc3feaa7395310d3d826b2c703eaab" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.7.0", "indexmap", "semver", ] @@ -4655,7 +4587,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ca917a21307d3adf2b9857b94dd05ebf8496bdcff4437a9b9fb3899d3e6c74e7" dependencies = [ "ahash", - "bitflags 2.6.0", + "bitflags 2.7.0", "hashbrown 0.14.5", "indexmap", "semver", @@ -4669,7 +4601,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b09e46c7fceceaa72b2dd1a8a137ea7fd8f93dfaa69806010a709918e496c5dc" dependencies = [ "ahash", - "bitflags 2.6.0", + "bitflags 2.7.0", "hashbrown 0.14.5", "indexmap", "semver", @@ -4682,13 +4614,24 @@ version = "0.221.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9845c470a2e10b61dd42c385839cdd6496363ed63b5c9e420b5488b77bd22083" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.7.0", "hashbrown 0.15.2", "indexmap", "semver", "serde", ] +[[package]] +name = "wasmparser" +version = "0.223.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5a99faceb1a5a84dd6084ec4bfa4b2ab153b5793b43fd8f58b89232634afc35" +dependencies = [ + "bitflags 2.7.0", + "indexmap", + "semver", +] + [[package]] name = "wasmprinter" version = "0.2.80" @@ -4730,7 +4673,7 @@ dependencies = [ "addr2line 0.22.0", "anyhow", "async-trait", - "bitflags 2.6.0", + "bitflags 2.7.0", "bumpalo", "cc", "cfg-if", @@ -4745,7 +4688,7 @@ dependencies = [ "log", "mach2", "memfd", - "object 0.36.5", + "object 0.36.7", "once_cell", "paste", "postcard", @@ -4784,7 +4727,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51e762e163fd305770c6c341df3290f0cabb3c264e7952943018e9a1ced8d917" dependencies = [ "anyhow", - "bitflags 2.6.0", + "bitflags 2.7.0", "bumpalo", "cc", "cfg-if", @@ -4795,7 +4738,7 @@ dependencies = [ "log", "mach2", "memfd", - "object 0.36.5", + "object 0.36.7", "once_cell", "paste", "postcard", @@ -4920,7 +4863,7 @@ dependencies = [ "cranelift-wasm", "gimli 0.29.0", "log", - "object 0.36.5", + "object 0.36.7", "smallvec", "target-lexicon", "thiserror", @@ -4945,7 +4888,7 @@ dependencies = [ "gimli 0.31.1", "itertools", "log", - "object 0.36.5", + "object 0.36.7", "smallvec", "target-lexicon", "thiserror", @@ -4991,7 +4934,7 @@ dependencies = [ "gimli 0.29.0", "indexmap", "log", - "object 0.36.5", + "object 0.36.7", "postcard", "rustc-demangle", "semver", @@ -5017,7 +4960,7 @@ dependencies = [ "gimli 0.31.1", "indexmap", "log", - "object 0.36.5", + "object 0.36.7", "postcard", "serde", "serde_derive", @@ -5049,7 +4992,7 @@ version = "25.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "106731c6ebe1d551362ee8c876d450bdc2d517988b20eb3653dc4837b1949437" dependencies = [ - "object 0.36.5", + "object 0.36.7", "once_cell", "rustix", "wasmtime-versioned-export-macros 25.0.3", @@ -5148,7 +5091,7 @@ checksum = "d042ea66b2834fb03b8a6968ef1a99a4b537211b00f7502a4d6a37f4eb2049b2" dependencies = [ "anyhow", "async-trait", - "bitflags 2.6.0", + "bitflags 2.7.0", "bytes", "cap-fs-ext", "cap-net-ext", @@ -5180,7 +5123,7 @@ dependencies = [ "anyhow", "cranelift-codegen 0.112.3", "gimli 0.29.0", - "object 0.36.5", + "object 0.36.7", "target-lexicon", "wasmparser 0.217.0", "wasmtime-cranelift 25.0.3", @@ -5237,24 +5180,24 @@ dependencies = [ [[package]] name = "wast" -version = "221.0.2" +version = "223.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcc4470b9de917ba199157d1f0ae104f2ae362be728c43e68c571c7715bd629e" +checksum = "d59b2ba8a2ff9f06194b7be9524f92e45e70149f4dacc0d0c7ad92b59ac875e4" dependencies = [ "bumpalo", "leb128", "memchr", "unicode-width", - "wasm-encoder 0.221.2", + "wasm-encoder 0.223.0", ] [[package]] name = "wat" -version = "1.221.2" +version = "1.223.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b1f3c6d82af47286494c6caea1d332037f5cbeeac82bbf5ef59cb8c201c466e" +checksum = "662786915c427e4918ff01eabb3c4756d4d947cd8f635761526b4cc9da2eaaad" dependencies = [ - "wast 221.0.2", + "wast 223.0.0", ] [[package]] @@ -5277,7 +5220,7 @@ version = "0.31.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b66249d3fc69f76fd74c82cc319300faa554e9d865dab1f7cd66cc20db10b280" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.7.0", "rustix", "wayland-backend", "wayland-scanner", @@ -5289,7 +5232,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "625c5029dbd43d25e6aa9615e88b829a5cad13b2819c4ae129fdbb7c31ab4c7e" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.7.0", "cursor-icon", "wayland-backend", ] @@ -5311,7 +5254,7 @@ version = "0.32.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7cd0ade57c4e6e9a8952741325c30bf82f4246885dca8bf561898b86d0c1f58e" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.7.0", "wayland-backend", "wayland-client", "wayland-scanner", @@ -5323,7 +5266,7 @@ version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b31cab548ee68c7eb155517f2212049dc151f7cd7910c2b66abfd31c3ee12bd" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.7.0", "wayland-backend", "wayland-client", "wayland-protocols", @@ -5336,7 +5279,7 @@ version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "782e12f6cd923c3c316130d56205ebab53f55d6666b7faddfad36cecaeeb4022" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.7.0", "wayland-backend", "wayland-client", "wayland-protocols", @@ -5368,9 +5311,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.76" +version = "0.3.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04dd7223427d52553d3702c004d3b2fe07c148165faa56313cb00211e31c12bc" +checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" dependencies = [ "js-sys", "wasm-bindgen", @@ -5412,7 +5355,7 @@ checksum = "4c8fdcd81702e0f46a8ab2ed28a5bf824aabf4a1af1673af496a020aacd0b6f9" dependencies = [ "anyhow", "async-trait", - "bitflags 2.6.0", + "bitflags 2.7.0", "thiserror", "tracing", "wasmtime 25.0.3", @@ -5774,14 +5717,14 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winit" -version = "0.30.5" +version = "0.30.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0be9e76a1f1077e04a411f0b989cbd3c93339e1771cb41e71ac4aee95bfd2c67" +checksum = "f5d74280aabb958072864bff6cfbcf9025cf8bfacdde5e32b5e12920ef703b0f" dependencies = [ "ahash", "android-activity", "atomic-waker", - "bitflags 2.6.0", + "bitflags 2.7.0", "block2", "bytemuck", "calloop", @@ -5825,9 +5768,9 @@ dependencies = [ [[package]] name = "winnow" -version = "0.6.20" +version = "0.6.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b" +checksum = "c8d71a593cc5c42ad7876e2c1fda56f314f3754c084128833e64f1345ff8a03a" dependencies = [ "memchr", ] @@ -5838,7 +5781,7 @@ version = "0.36.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f3fd376f71958b862e7afb20cfe5a22830e1963462f3a17f49d82a6c1d1f42d" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.7.0", "windows-sys 0.59.0", ] @@ -5848,7 +5791,7 @@ version = "0.34.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "744845cde309b8fa32408d6fb67456449278c66ea4dcd96de29797b302721f02" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.7.0", ] [[package]] @@ -5857,7 +5800,7 @@ version = "0.35.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "605d5562e971a823cf5a550a9d69cb7144e9b4969a810043127175517a4e1865" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.7.0", ] [[package]] @@ -5867,7 +5810,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "429e3c06fba3a7566aab724ae3ffff3152ede5399d44789e7dd11f5421292859" dependencies = [ "anyhow", - "bitflags 2.6.0", + "bitflags 2.7.0", "indexmap", "log", "serde", @@ -6010,7 +5953,7 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d039de8032a9a8856a6be89cea3e5d12fdd82306ab7c94d74e6deab2460651c5" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.7.0", "dlib", "log", "once_cell", @@ -6025,9 +5968,20 @@ checksum = "b9cc00251562a284751c9973bace760d86c0276c471b4be569fe6b068ee97a56" [[package]] name = "xml-rs" -version = "0.8.24" +version = "0.8.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5b940ebc25896e71dd073bad2dbaa2abfe97b0a391415e22ad1326d9c54e3c4" + +[[package]] +name = "xml5ever" +version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea8b391c9a790b496184c29f7f93b9ed5b16abb306c05415b68bcc16e4d06432" +checksum = "9bbb26405d8e919bc1547a5aa9abc95cbfa438f04844f5fdd9dc7596b748bf69" +dependencies = [ + "log", + "mac", + "markup5ever", +] [[package]] name = "yoke" diff --git a/Cargo.toml b/Cargo.toml index 0bbd3a9..353a38c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,6 @@ members = [ ".", "crates/html-egui-bindgen", "crates/html-to-egui", - "crates/validate_rhai_proc_macro", "examples/counter", "examples/datetime", "examples/login", @@ -16,7 +15,7 @@ version = "0.2.1" authors = ["Doug Anderson "] edition = "2021" include = ["LICENSE-APACHE", "LICENSE-MIT", "**/*.rs", "Cargo.toml"] -rust-version = "1.78" # Minimum version required to build this project, for wasm-wsip1 target, 1.78 minimum is required. +rust-version = "1.78" default-run = "rdx" [package.metadata.docs.rs] @@ -52,8 +51,11 @@ anyhow = "1.0.93" futures-lite = "2.5.0" send_wrapper = "0.6" parking_lot = "0.12" -scraper = { version = "0.22", default-features = false } +html5ever = "0.27" +markup5ever_rcdom = "0.3.0" +markup5ever = "0.12.0" html-to-egui = { path = "crates/html-to-egui" } +ahash = "0.8.11" # For native builds: [target.'cfg(not(target_arch = "wasm32"))'.dependencies] @@ -74,13 +76,13 @@ js_wasm_runtime_layer = "0.4.0" anyhow = "1.0" send_wrapper = { version = "0.6.0", features = ["futures"] } web-time = "1.1.0" -lol_alloc = "0.4.1" [build-dependencies] wasmparser = "0.221" [dev-dependencies] html-egui-bindgen = { path = "crates/html-egui-bindgen" } +test-log = "0.2.16" [profile.release] opt-level = 2 # fast and small wasm diff --git a/crates/html-to-egui/src/action.rs b/crates/html-to-egui/src/action.rs index c074f5a..ec670d9 100644 --- a/crates/html-to-egui/src/action.rs +++ b/crates/html-to-egui/src/action.rs @@ -1,3 +1,5 @@ +use std::fmt::{self, Formatter}; + use super::*; /// Enumerate the action handlers, such as on-click, on-change, etc. @@ -9,11 +11,18 @@ use super::*; /// This enum enumerates the 'on-click', 'on-change', etc. so that /// the html crate can use this enum to build the html text programmatically /// in a type safe way, without typo errors. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum Action { OnClick, OnChange, } +impl Display for Action { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.as_str()) + } +} + impl Action { // Define constants for the attribute strings const ON_CLICK: &'static str = "on-click"; @@ -28,6 +37,18 @@ impl Action { } } +impl TryFrom<&str> for Action { + type Error = Error; + + fn try_from(s: &str) -> Result { + match s { + Action::ON_CLICK => Ok(Action::OnClick), + Action::ON_CHANGE => Ok(Action::OnChange), + _ => Err(Error::InvalidConversion(s.to_string())), + } + } +} + impl From for Attribute { fn from(val: Action) -> Self { match val { diff --git a/crates/html-to-egui/src/attribute.rs b/crates/html-to-egui/src/attribute.rs index 2bc3c99..6606312 100644 --- a/crates/html-to-egui/src/attribute.rs +++ b/crates/html-to-egui/src/attribute.rs @@ -1,6 +1,7 @@ use super::*; /// These are the data attibutes asscoiated with the action handlers. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum Attribute { DataOnClick, DataOnChange, @@ -20,6 +21,36 @@ impl Attribute { } } +impl From for Action { + fn from(attr: Attribute) -> Self { + match attr { + Attribute::DataOnClick => Action::OnClick, + Attribute::DataOnChange => Action::OnChange, + } + } +} + +impl From<&Attribute> for Action { + fn from(attr: &Attribute) -> Self { + match attr { + Attribute::DataOnClick => Action::OnClick, + Attribute::DataOnChange => Action::OnChange, + } + } +} + +impl TryFrom<&str> for Attribute { + type Error = Error; + + fn try_from(s: &str) -> Result { + match s { + Attribute::DATA_ON_CLICK => Ok(Attribute::DataOnClick), + Attribute::DATA_ON_CHANGE => Ok(Attribute::DataOnChange), + _ => Err(Error::InvalidConversion(s.to_string())), + } + } +} + impl From for &'static str { fn from(val: Attribute) -> Self { val.as_str() diff --git a/crates/html-to-egui/src/error.rs b/crates/html-to-egui/src/error.rs new file mode 100644 index 0000000..7312f7f --- /dev/null +++ b/crates/html-to-egui/src/error.rs @@ -0,0 +1,8 @@ +//! Crate Errors + +pub enum Error { + /// Parsing error + Parse(String), + /// Invalid action + InvalidConversion(String), +} diff --git a/crates/html-to-egui/src/lib.rs b/crates/html-to-egui/src/lib.rs index f2a5485..0a54cce 100644 --- a/crates/html-to-egui/src/lib.rs +++ b/crates/html-to-egui/src/lib.rs @@ -31,3 +31,6 @@ pub use selectors::*; use std::fmt::Display; use std::ops::{Deref, DerefMut}; + +mod error; +pub use error::Error; diff --git a/crates/html-to-egui/src/selectors.rs b/crates/html-to-egui/src/selectors.rs index 0442079..ed541ae 100644 --- a/crates/html-to-egui/src/selectors.rs +++ b/crates/html-to-egui/src/selectors.rs @@ -1,6 +1,7 @@ use super::*; /// Slectors available to use with the Div element. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum DivSelectors { /// No selector. None, diff --git a/examples/datetime/src/lib.rs b/examples/datetime/src/lib.rs index f419f4e..a2784a1 100644 --- a/examples/datetime/src/lib.rs +++ b/examples/datetime/src/lib.rs @@ -1,3 +1,7 @@ +#[allow(warnings)] +#[cfg_attr(rustfmt, rustfmt_skip)] +mod bindings; + mod reactor; use reactor::Reactor; @@ -6,9 +10,6 @@ pub use block_on::{block_on, noop_waker}; mod polling; -#[allow(warnings)] -mod bindings; - use bindings::component::plugin::host::{emit, now, subscribe_duration}; use bindings::component::plugin::types::Event; use bindings::exports::component::plugin::run::Guest; @@ -29,7 +30,6 @@ impl Guest for Component { render(`
Seconds since unix was invented: {{datetime}} -
`) "# @@ -46,6 +46,7 @@ impl Guest for Component { } /// This function calls now() every second by + /// Only works in native, breaks in wasm fn ticker() { block_on(|reactor| async move { // we use sleep to wait for 1 second in between updates to datetime. diff --git a/examples/login/rustfmt.toml b/examples/login/rustfmt.toml deleted file mode 100644 index d15e6bb..0000000 --- a/examples/login/rustfmt.toml +++ /dev/null @@ -1 +0,0 @@ -ignore = ["src/bindings.rs"] diff --git a/examples/login/src/lib.rs b/examples/login/src/lib.rs index 6e4e6f7..475599a 100644 --- a/examples/login/src/lib.rs +++ b/examples/login/src/lib.rs @@ -1,4 +1,5 @@ #[allow(warnings)] +#[cfg_attr(rustfmt, rustfmt_skip)] mod bindings; use bindings::component::plugin::host::emit; @@ -51,7 +52,7 @@ impl Guest for Component {
- +
`) "# diff --git a/examples/random/rustfmt.toml b/examples/random/rustfmt.toml deleted file mode 100644 index d15e6bb..0000000 --- a/examples/random/rustfmt.toml +++ /dev/null @@ -1 +0,0 @@ -ignore = ["src/bindings.rs"] diff --git a/examples/random/src/lib.rs b/examples/random/src/lib.rs index 6a5d584..226c190 100644 --- a/examples/random/src/lib.rs +++ b/examples/random/src/lib.rs @@ -1,4 +1,5 @@ #[allow(warnings)] +#[cfg_attr(rustfmt, rustfmt_skip)] mod bindings; use bindings::component::plugin::host::{emit, random_byte}; diff --git a/justfile b/justfile index 3222c07..1d03435 100644 --- a/justfile +++ b/justfile @@ -1,20 +1,25 @@ # for each dir in crates which has a `wit` directory in it, AND has src/bindings.rs, build it build-wits: - for dir in crates/*; do \ - if ([ -d $dir/wit ] && [ -f $dir/src/bindings.rs ]); then \ - cargo component build --manifest-path=$dir/Cargo.toml --release; \ - fi \ - done + for dir in crates/*; do \ + if [ -d $dir/wit ] && [ -f $dir/src/bindings.rs ]; then \ + echo "Processing $dir"; \ + (cd $dir && cargo component build); \ + (cd $dir && cargo component build --release); \ + fi; \ + done # build all wit examples in examples/ directory build-examples: for dir in examples/*; do \ - if ([ -d $dir/wit ] && [ -f $dir/src/bindings.rs ]); then \ - cargo component build --manifest-path=$dir/Cargo.toml --target wasm32-unknown-unknown --release; \ - fi \ + if [ -d $dir/wit ] && [ -f $dir/src/bindings.rs ]; then \ + echo "Processing $dir"; \ + (cd $dir && cargo component build --target wasm32-unknown-unknown); \ + (cd $dir && cargo component build --target wasm32-unknown-unknown --release); \ + fi; \ done -build: build-wits build-examples +build: build-examples + cargo build test: build cargo test @@ -32,7 +37,7 @@ check32: RUSTFLAGS="--deny warnings" cargo check --target wasm32-unknown-unknown build32: - cargo +nightly build -Z build-std --release--target wasm32-unknown-unknown + cargo +nightly build -Z build-std --target wasm32-unknown-unknown force: cargo run --bin force-build-wasm-bins diff --git a/rust-toolchain b/rust-toolchain index 67f4a11..69dfc07 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -5,6 +5,9 @@ # to the user in the error, instead of "error: invalid channel name '[toolchain]'". [toolchain] -# channel = "1.82" # Avoid specifying a patch version here; see https://github.com/emilk/eframe_template/issues/145 +# wasm-bindgen works with rust 1.78 + wasm-bindgen 2.92 onward. see: https://github.com/prisma/prisma-engines/pull/4867 +# after v1.78 there appears to be a dlmalloc issue with wasm32-unknown-unknown with [scraper] crate +# so we are sticking with 1.78 for now +# channel = "1.80" # Avoid specifying a patch version here; see https://github.com/emilk/eframe_template/issues/145. components = [ "rustfmt", "clippy" ] targets = [ "wasm32-unknown-unknown" ] diff --git a/src/error.rs b/src/error.rs index 36a5d87..9412f07 100644 --- a/src/error.rs +++ b/src/error.rs @@ -19,10 +19,6 @@ pub enum Error { #[error("Anyhow Error: {0}")] Anyhow(#[from] anyhow::Error), - /// Selector Kind error - #[error("Selector Kind Error: {0}")] - Selector(#[from] scraper::error::SelectorErrorKind<'static>), - /// Parse error #[error("Parse Error: {0}")] Parse(String), diff --git a/src/hteg.rs b/src/hteg.rs index 650562a..26d2cfb 100644 --- a/src/hteg.rs +++ b/src/hteg.rs @@ -1,144 +1,40 @@ //! HTML to egui (HTEG) converter.and renderer in egui. +mod element_parser; +mod types; -use html_to_egui::{Action, Attribute, DivSelectors}; +use element_parser::Parser; +use html_to_egui::{Action, DivSelectors}; +use types::{FuncAndArgs, HtmlElement}; use std::sync::{Arc, Mutex}; -use scraper::{ElementRef, Html, Selector}; use wasm_component_layer::Value; use crate::layer::{Inner, Instantiator}; -use crate::template::{Template, TemplatePart}; +use crate::template::TemplatePart; use crate::Error; -/// Parses the html text into a Vec of [scraper::html::Select] elements. -/// Then renders the elements into egui UI components. -pub fn parse_and_render( - ui: &mut egui::Ui, - html: &str, - plugin: Arc>>, -) -> Result<(), Error> { - let fragment = Html::parse_fragment(html); - let top_selector = Selector::parse("html")?; - for element in fragment.select(&top_selector) { - render_element(ui, element, plugin.clone())?; - } - Ok(()) +/// Parses the html and renders to egui for us. +#[derive(Default, Clone)] +pub struct HtmlToEgui { + parser: Parser, } -/// Wrapper struct to hold the [scraper::ElementRef] and the [HtmlElement] that -/// is being rendered into egui UI components. -struct ElementWrapper<'a> { - html_element: HtmlElement, - element_ref: ElementRef<'a>, -} - -impl<'a> ElementWrapper<'a> { - /// Creates a new [ElementWrapper] from the given [scraper::ElementRef]. - fn new(element_ref: ElementRef<'a>) -> Self { - let html_element = HtmlElement::from_element(&element_ref); - ElementWrapper { - html_element, - element_ref, - } - } - - ///// Determines if this element matches the given selector. - //fn matches(&self, selector: &str) -> Result { - // let selectors = Selector::parse(selector).map_err(|e| Error::Parse(e.to_string()))?; - // let m = selectors.matches(&self.element_ref); - // Ok(m) - //} - - /// Returns teh [HtmlElement] - fn html_element(&self) -> &HtmlElement { - &self.html_element - } - - /// Returns a tuple of the functions and arguments for the given attribute. - /// - /// The attribute value is expected to be in the format `function_name(arg1, arg2, arg3)`. - /// - /// # Example - /// ```ignore - /// # use crate::hteg::ElementWrapper; - /// # use scraper::{Html, Selector}; - /// let html = r#"
...
"#; - /// let fragment = Html::parse_fragment(html); - /// let div_selector = Selector::parse("div").unwrap(); - /// let element = fragment.select(&div_selector).next().unwrap(); - /// let elw = ElementWrapper::new(element); - /// let attr = "data-on-click"; - /// let (func, args) = elw.func_and_args(attr).unwrap(); - /// assert_eq!(func, "increment"); - /// assert_eq!(args, vec!["name"]); - /// ``` - fn func_and_args(&self, attr: impl Into) -> Option<(&str, Vec<&str>)> { - let attr: Attribute = attr.into(); - let attr = self.element_ref.value().attr(&attr)?; - let splits = attr.split('(').collect::>(); - - let func_name = splits[0]; - let func_args = splits[1] - .trim_end_matches(')') - .split(',') - // mao on trim for whitespace, and filter on non-empty strings - .filter_map(|v| { - let v_trimmed = v.trim(); - if v_trimmed.is_empty() { - None - } else { - Some(v_trimmed) - } - }) - .collect::>(); - - Some((func_name, func_args)) - } -} - -/// Enum to represent the HTML elements that can be rendered into egui UI components. -enum HtmlElement { - /// Represents a div element. Divs are converted to [egui::Ui::vertical] by default. - Div(DivSelectors), - /// Represents a button element. Buttons are converted to [egui::Button]. - Button, - /// Represents an input element. Inputs are converted to [egui::TextEdit]. - Input, - /// Represents a label element. Labels are converted to [egui::RichText]. - Label, - /// Represents a span element. Spans are converted to [egui::RichText]. - Span, - /// Paragraph element. Paragraphs are converted to [egui::RichText]. - Paragraph, -} - -impl HtmlElement { - /// Creates a new [HtmlElement] from the given [scraper::ElementRef]. - pub fn from_element(element: &ElementRef) -> Self { - let tag_name = element.value().name(); - - let class_selectors = element.value().attr("class").unwrap_or_default(); - - match tag_name { - "div" => { - if class_selectors.contains(&*DivSelectors::FlexRow) - || Selector::parse(&DivSelectors::FlexRow) - .unwrap() - .matches(element) - { - HtmlElement::Div(DivSelectors::FlexRow) - } else { - HtmlElement::Div(DivSelectors::None) - } - } - "button" => HtmlElement::Button, - "input" => HtmlElement::Input, - "label" => HtmlElement::Label, - "span" => HtmlElement::Span, - "p" => HtmlElement::Paragraph, - _ => HtmlElement::Div(DivSelectors::None), +impl HtmlToEgui { + /// Parses the html text into a Vec of [scraper::html::Select] elements. + /// Then renders the elements into egui UI components. + pub fn parse_and_render( + &mut self, + ui: &mut egui::Ui, + html: &str, + plugin: Arc>>, + ) -> Result<(), Error> { + if let Ok(html_ast) = self.parser.parse(html) { + render_element(ui, &html_ast, plugin.clone())?; + } else { + tracing::error!("Failed to parse html: {}", html); } + Ok(()) } } @@ -146,61 +42,66 @@ impl HtmlElement { /// HTML into egui UI components. fn render_element( ui: &mut egui::Ui, - element: ElementRef, + element: &HtmlElement, plugin: Arc>>, ) -> Result<(), Error> { - // fill the content template with scope values - let content = { - let content = element.text().collect::(); - let template = Template::new(&content); + // get the content scope values + // converts the rhai::Dynamic value to a string + let entries = { let lock = plugin.lock().unwrap(); let state = lock.store().data(); let scope = state.clone().into_scope(); - // converts the rhai::Dynamic value to a string - let entries = scope + scope .iter() - .map(|(k, _c, v)| (k, v.to_string())) - .collect::>(); - - template.render(entries) + .map(|(k, _c, v)| (k.to_string(), v.to_string())) + .collect::>() }; - let elw = ElementWrapper::new(element); - match elw.html_element() { - // TODO: Add selectors to Div - HtmlElement::Div(DivSelectors::FlexRow) => { - ui.horizontal(|ui| { - for child in element.child_elements() { - if let Err(e) = render_element(ui, child, plugin.clone()) { - tracing::error!("Error rendering child element: {:?}", e); - } - } - }); + match element { + HtmlElement::Html { children, .. } => { + for child in children { + render_element(ui, child, plugin.clone())?; + } } - HtmlElement::Div(DivSelectors::None) => { - ui.vertical(|ui| { - for child in element.child_elements() { - if let Err(e) = render_element(ui, child, plugin.clone()) { - tracing::error!("Error rendering child element: {:?}", e); + HtmlElement::Div { + template: _, style, .. + } => { + let inner = |ui: &mut egui::Ui| { + if element.child_elements().is_some() { + for child in element.child_elements().unwrap() { + if let Err(e) = render_element(ui, child, plugin.clone()) { + tracing::error!("Error rendering child element: {:?}", e); + } } } - }); - } - HtmlElement::Button => { - let color = match element.value().attr("color") { - Some("green") => egui::Color32::from_rgb(100, 200, 100), - Some("red") => egui::Color32::from_rgb(200, 100, 100), - _ => ui.style().visuals.widgets.active.bg_fill, }; + match style { + DivSelectors::FlexRow => ui.horizontal(|ui| { + inner(ui); + }), + DivSelectors::None => ui.vertical(|ui| { + inner(ui); + }), + }; + } + HtmlElement::Button(button) => { + let color = ui.style().visuals.widgets.active.bg_fill; + + let template = button.template(); + + let content = template.render(entries); - let text = element.text().collect::(); - if ui.add(egui::Button::new(&text).fill(color)).clicked() { - if let Some((on_click, func_args)) = elw.func_and_args(Action::OnClick) { + if ui.add(egui::Button::new(content).fill(color)).clicked() { + // get button.evt_handlers Vec entry which matches EvtHandler.ty == OnClick + if let Some(FuncAndArgs { + function: on_click, + args, + }) = button.func_and_args(Action::OnClick) + { let args = { let mut lock = plugin.lock().unwrap(); let scope = lock.store_mut().data_mut().scope_mut(); - func_args - .iter() + args.iter() // ONLY use non-empty args, filter everything else out // there can be zero arg ie) increment() where the return vec is zero // length. That's ok. @@ -220,7 +121,7 @@ fn render_element( ); let mut lock = plugin.lock().unwrap(); - match lock.call(on_click, args.as_slice()) { + match lock.call(&on_click, args.as_slice()) { Ok(res) => { tracing::info!("on_click response {:?}", res); } @@ -232,14 +133,14 @@ fn render_element( } ui.add_space(4.0); } - HtmlElement::Input => { - let is_password = element.value().attr("password") == Some("true"); + HtmlElement::Input(input) => { + let is_password = input.is_password(); // get the first TemplatPart::Dynamic from template.parts.iter() // otherwisse return early // since input doesn't have a closing tag, we need to take the template from elsewhere // - let template = Template::new(element.value().attr("value").unwrap_or_default()); + let template = input.template(); let Some(TemplatePart::Dynamic(var_name)) = template.parts.first() else { // nowhere to save the input, returning early return Err(Error::Parse("No variable name found".to_string())); @@ -261,7 +162,11 @@ fn render_element( scope.set_value(var_name.as_str(), val.clone()); // also call the on_change function if it exists - if let Some((on_change, func_args)) = elw.func_and_args(Action::OnClick) { + if let Some(FuncAndArgs { + function: on_change, + args: func_args, + }) = input.func_and_args(Action::OnChange) + { // if on_change is not empty, call the function if !on_change.is_empty() { let args = func_args @@ -278,7 +183,7 @@ fn render_element( let mut lock = plugin.lock().unwrap(); - if let Ok(value) = lock.call(on_change, args.as_slice()) { + if let Ok(value) = lock.call(&on_change, args.as_slice()) { match value { Some(Value::String(_s)) => { // TODO: act on return value(s)? @@ -296,12 +201,12 @@ fn render_element( scope.set_value(var_name.as_str(), var_name.to_string()); } } - HtmlElement::Label | HtmlElement::Span | HtmlElement::Paragraph => { - let size = match element.value().attr("size") { - Some("small") => 14.0, - Some("large") => 18.0, - _ => 16.0, - }; + HtmlElement::Label { template } + | HtmlElement::Span { template } + | HtmlElement::Paragraph { template } + | HtmlElement::Text { contents: template } => { + let size = 16.0; + let content = template.render(entries); ui.label(egui::RichText::new(content).size(size)); ui.add_space(4.0); @@ -311,209 +216,4 @@ fn render_element( } #[cfg(test)] -mod tests { - use super::*; - - use html_to_egui::Handler; - - #[test] - fn test_parse_and_render() { - let html = r#" -
- - -

Click to Start counting!

-
- "#; - - let fragment = Html::parse_fragment(html); - let top_selector = Selector::parse("*").unwrap(); - for element in fragment.select(&top_selector) { - tracing::info!("Rendering element {:?}", element); - - let tag_name = element.value().name(); - - match tag_name { - "div" => { - eprintln!("
"); - } - "button" => { - eprintln!("\n - - -

Click to Start counting!

-
- "#; - - let fragment = Html::parse_fragment(html); - let button_1p_selector = Selector::parse("button#button1").unwrap(); - - for element in fragment.select(&button_1p_selector) { - let elw = ElementWrapper::new(element); - let (func, args) = elw.func_and_args(Action::OnClick).unwrap(); - assert_eq!(func, "increment"); - assert_eq!(args, vec!["key"]); - } - - let button_2p_selector = Selector::parse("button#button2").unwrap(); - - for element in fragment.select(&button_2p_selector) { - let elw = ElementWrapper::new(element); - let (func, args) = elw.func_and_args(Action::OnClick).unwrap(); - assert_eq!(func, "decrement"); - assert_eq!(args, vec!["key", "value"]); - } - - let button_3p_selector = Selector::parse("button#button3").unwrap(); - - for element in fragment.select(&button_3p_selector) { - let elw = ElementWrapper::new(element); - let (func, args) = elw.func_and_args(Action::OnClick).unwrap(); - assert_eq!(func, "reset"); - assert!(args.is_empty()); - } - } - - // We should be able to use the html crate and the types in hteg to build type safe tests, same - // as above. - #[test] - fn test_render_element() { - use html_egui_bindgen::{Button, Division, Paragraph}; - - let html_expected = r#"

Click to Start counting!

"#; - - let html = Division::builder() - .push( - Button::builder() - .id("button1") - .data(Action::OnClick, "increment(key)") - .text("Increment") - .build(), - ) - .push( - Button::builder() - .id("button2") - .data(Action::OnClick, "decrement(key, value)") - .text("Decrement") - .build(), - ) - .push( - Paragraph::builder() - .text("Click to Start counting!") - .build(), - ) - .build(); - - assert_eq!(html.to_string(), html_expected); - - let fragment = Html::parse_fragment(&html.to_string()); - - let div_selector = Selector::parse("button#button1").unwrap(); - - for element in fragment.select(&div_selector) { - let elw = ElementWrapper::new(element); - let (func, args) = elw.func_and_args(Attribute::DataOnClick).unwrap(); - assert_eq!(func, "increment"); - assert_eq!(args, vec!["key"]); - } - } - - #[test] - fn test_render_element_builders() { - use html_egui_bindgen::{Button, Division, Paragraph}; - - let html_expected = r#"

Click to Start counting!

"#; - - // We save 1 line and enforce type safety by using new_with_func() - let button = Button::new_with_func( - Action::OnClick, - Handler::builder() - .named("increment".to_owned()) - .args(vec!["key".to_owned()]) - .build(), - ) - .id("button1") - .text("Increment") - .build(); - - let html = Division::builder() - .push(button) - .push( - Button::builder() - .data( - Action::OnClick, - Handler::builder() - .named("decrement".to_owned()) - .args(vec!["key".to_owned(), "value".to_owned()]) - .build(), - ) - .id("button2") - .text("Decrement") - .build(), - ) - .push( - Button::builder() - .id("button3") - .data( - Action::OnClick, - Handler::builder().named("reset".to_owned()).build(), - ) - .text("Reset") - .build(), - ) - .push( - Paragraph::builder() - .text("Click to Start counting!") - .build(), - ) - .class(DivSelectors::FlexRow) - .build(); - - assert_eq!(html.to_string(), html_expected); - - let fragment = Html::parse_fragment(&html.to_string()); - - let div_selector = Selector::parse("button#button1").unwrap(); - - for element in fragment.select(&div_selector) { - let elw = ElementWrapper::new(element); - let (func, args) = elw.func_and_args(Action::OnClick).unwrap(); - assert_eq!(func, "increment"); - assert_eq!(args, vec!["key"]); - } - - let div_selector = Selector::parse("button#button2").unwrap(); - - let element = fragment.select(&div_selector).next().unwrap(); - - let elw = ElementWrapper::new(element); - let (func, args) = elw.func_and_args(Action::OnClick).unwrap(); - assert_eq!(func, "decrement"); - assert_eq!(args, vec!["key", "value"]); - - let div_selector = Selector::parse("button#button3").unwrap(); - - let element = fragment.select(&div_selector).next().unwrap(); - - let elw = ElementWrapper::new(element); - // We can also use Attribute::DataOnClick here - let (func, args) = elw.func_and_args(Attribute::DataOnClick).unwrap(); - assert_eq!(func, "reset"); - assert!(args.is_empty()); - } -} +mod tests {} diff --git a/src/hteg/element_parser.rs b/src/hteg/element_parser.rs new file mode 100644 index 0000000..d287930 --- /dev/null +++ b/src/hteg/element_parser.rs @@ -0,0 +1,58 @@ +//! Parses and ElementRef into HtmlElement +#![allow(dead_code)] + +use super::types::HtmlElement; +use crate::Error; +use ahash::AHashMap; + +use html5ever::tendril::TendrilSink; +use html5ever::{local_name, ns, parse_fragment, QualName}; +use markup5ever::namespace_url; +use markup5ever_rcdom::RcDom; + +/// A Parser that will parse an html string into a Vec +/// Holds a cache to avoid parsing the same html multiple times. +#[derive(Default, Clone)] +pub struct Parser { + cache: AHashMap, +} + +impl Parser { + /// Parse an html string into a Vec + pub fn parse(&mut self, html: &str) -> Result { + // Before usign scraper, we check the cache to see if we have already parsed this html + // and if so, we return the cached value. This is a simple way to avoid parsing the same + // html multiple times, which eats memory and cpu. + if let Some(elements) = self.cache.get(&html.to_string()) { + return Ok(elements.clone()); + } + + tracing::info!("No cache found. Parsing html: {}", html); + + let elements = parse(html)?; + // Map this html to Vec in a cache so that we can return + // any subsequent calls to this same html without having to reparse it. + self.cache.insert(html.to_string(), elements.clone()); + + Ok(elements) + } +} + +pub(crate) fn parse(html: &str) -> Result { + // Parse the HTML + let dom = parse_fragment( + RcDom::default(), + Default::default(), + QualName::new(None, ns!(), local_name!("div")), // Context element + vec![], + ) + .from_utf8() + .read_from(&mut html.as_bytes()) + .unwrap(); + + let ast = HtmlElement::from_node(&dom.document).unwrap(); + Ok(ast) +} + +#[cfg(test)] +mod tests {} diff --git a/src/hteg/types.rs b/src/hteg/types.rs new file mode 100644 index 0000000..f9b3ae8 --- /dev/null +++ b/src/hteg/types.rs @@ -0,0 +1,487 @@ +#![allow(dead_code)] + +use std::{cell::RefCell, ops::Deref}; + +use html_to_egui::{Action, Attribute, DivSelectors}; +use markup5ever_rcdom::{Handle, NodeData}; + +use crate::{ + template::{Template, TemplatePart}, + Error, +}; + +/// Event Handler including the [Action] type and the function and arguments [FuncAndArgs] +#[derive(Debug, Clone)] +struct EvtHandler { + ty: Action, + details: FuncAndArgs, +} + +impl EvtHandler { + fn new(ty: Action, func_and_args: FuncAndArgs) -> Self { + Self { + ty, + details: func_and_args, + } + } + + /// From an [Action] value + fn new_from(ty: Action, val: &str) -> Result { + tracing::info!("Parsing event handler: {}", val); + // trim end ')' ')' and split on '(' + let val = val.trim_end_matches(')'); + let mut parts = val.split('('); + let Some(function) = parts.next() else { + return Err(Error::Parse(format!( + "No {} function found in : {}", + ty, val + ))); + }; + let args = parts + .next() + // only keep existant, filter out empty / non existant args + // and trim any whitespace + .map(|args| { + args.split(',') + .filter(|arg| !arg.is_empty()) + .map(|arg| arg.trim().to_string()) + .collect() + }) + .unwrap_or_default(); + + Ok(Self::new( + Action::OnClick, + FuncAndArgs { + function: function.to_string(), + args, + }, + )) + } + + /// Gets the function and args associated with an Action ty. + fn func_and_args(&self) -> FuncAndArgs { + FuncAndArgs { + function: self.details.function.clone(), + args: self.details.args.clone(), + } + } +} + +/// Enum to represent the HTML elements that can be rendered into egui UI components. +#[derive(Debug, Clone)] +pub enum HtmlElement { + /// Represents the root element of the HTML document. + Html { + /// The [HtmlElement] children of the html element. + children: Vec, + }, + /// Represents a div element. Divs are converted to [egui::Ui::vertical] by default. + Div { + /// The [HtmlElement] children of the div element. + children: Vec, + /// The text of this Div + template: Template, + /// The style of the div element. + style: DivSelectors, + }, + /// Represents a button element. Buttons are converted to [egui::Button]. + Button(Button), + /// Represents an input element. Inputs are converted to [egui::TextEdit]. + Input(Input), + /// Represents a label element. Labels are converted to [egui::RichText]. + Label { + /// The text of the label, expressed as a [Template]. + template: Template, + }, + /// Represents a span element. Spans are converted to [egui::RichText]. + Span { + /// The text of the span, expressed as a [Template]. + template: Template, + }, + /// Paragraph element. Paragraphs are converted to [egui::RichText]. + Paragraph { + /// The text of the paragraph, expressed as a [Template]. + template: Template, + }, + /// The inner text of an element. + Text { + /// The text content of the element. + contents: Template, + }, +} + +/// Button varaint details +#[derive(Debug, Clone)] +pub struct Button { + /// The [Action] function, its type, and associated function arguments. + evt_handlers: Vec, + /// The text of the button, expressed as a [Template]. + template: Template, + /// Button style + style: Style, +} + +/// Sturct to hold func and arg types +#[derive(Debug, Clone)] +pub struct FuncAndArgs { + /// The function name + pub function: String, + /// The function arguments + pub args: Vec, +} + +impl Button { + /// Gets the function and args associated withthe given Action ty. + pub fn func_and_args(&self, ty: Action) -> Option { + self.evt_handlers.iter().find_map(|evt_handler| { + if evt_handler.ty == ty { + Some(evt_handler.details.clone()) + } else { + None + } + }) + } +} + +impl Button { + pub fn style(&self) -> &Style { + &self.style + } + + pub fn template(&self) -> &Template { + &self.template + } +} + +/// Input variant details +#[derive(Debug, Clone)] +pub struct Input { + /// Whether the input is a password field or not. + is_password: bool, + /// The contents of the Input, expressed as a [Template]. + value: Template, + /// The [Action] function, its type, and associated function arguments. + evt_handlers: Vec, + /// The variable name of the [rhaii:Scope] the imput is bound to + var_name: String, +} + +impl Input { + /// Gets the function and args associated with the given Action ty. + pub fn func_and_args(&self, ty: Action) -> Option { + self.evt_handlers.iter().find_map(|evt_handler| { + if evt_handler.ty == ty { + Some(evt_handler.details.clone()) + } else { + None + } + }) + } + + pub fn is_password(&self) -> bool { + self.is_password + } + + pub fn value(&self) -> &Template { + &self.value + } + + pub fn var_name(&self) -> &str { + &self.var_name + } + + /// The input element's template value as [Template] + pub fn template(&self) -> &Template { + &self.value + } +} + +#[derive(Debug, Clone)] +pub struct Style { + /// Color + color: Option, +} + +impl Style { + fn new(color: Option) -> Self { + Self { color } + } + + pub fn color(&self) -> Option<&Color> { + self.color.as_ref() + } +} + +#[derive(Debug, Clone)] +pub struct Color { + /// The color of the button + color: egui::Color32, + /// The color of the button when hovered + hovered: egui::Color32, + /// The color of the button when clicked + clicked: egui::Color32, +} + +impl HtmlElement { + // Define constants for the tag names + const HTML: &'static str = "html"; + const DIV: &'static str = "div"; + const BUTTON: &'static str = "button"; + const INPUT: &'static str = "input"; + const LABEL: &'static str = "label"; + const SPAN: &'static str = "span"; + const PARAGRAPH: &'static str = "p"; + + // Method to get the string representation + pub(crate) fn as_str(&self) -> &'static str { + match self { + HtmlElement::Html { .. } => Self::HTML, + HtmlElement::Div { .. } => Self::DIV, + HtmlElement::Button { .. } => Self::BUTTON, + HtmlElement::Input { .. } => Self::INPUT, + HtmlElement::Label { .. } => Self::LABEL, + HtmlElement::Span { .. } => Self::SPAN, + HtmlElement::Paragraph { .. } => Self::PARAGRAPH, + // No need + HtmlElement::Text { .. } => unreachable!(), + } + } + + /// Recursive From Handle to [HtmlElement] + pub(crate) fn from_node(node: &Handle) -> Option { + match &node.data { + NodeData::Document => { + // Document node + let children = node + .children + .borrow() + .iter() + .filter_map(HtmlElement::from_node) + .collect(); + Some(HtmlElement::Html { children }) + } + NodeData::Element { name, attrs, .. } => { + // Element node + let tag = name.local.to_string(); + + tracing::info!("Tag: {}", tag); + + let children: Vec = node + .children + .borrow() + .iter() + .filter_map(HtmlElement::from_node) + .collect(); + + let text = children + .iter() + .filter_map(|child| { + if let HtmlElement::Text { contents } = child { + Some(contents.to_string()) + } else { + None + } + }) + .collect::>() + .concat(); + + match tag.as_str() { + Self::HTML => Some(HtmlElement::Html { children }), + Self::DIV => { + let text = children + .iter() + .filter_map(|child| { + if let HtmlElement::Text { contents } = child { + Some(contents.to_string()) + } else { + None + } + }) + .collect::>() + .join(" "); + Some(HtmlElement::Div { + children, + template: Template::new(&text), + style: DivSelectors::None, + }) + } + Self::BUTTON => { + let text = children + .iter() + .filter_map(|child| { + if let HtmlElement::Text { contents } = child { + Some(contents.to_string()) + } else { + None + } + }) + .collect::>() + .concat(); + let evt_handlers = parse_evt_handlers(attrs); + Some(HtmlElement::Button(Button { + evt_handlers, + template: Template::new(&text), + style: Style::new(None), + })) + } + Self::INPUT => { + let is_password = attrs + .borrow() + .iter() + .any(|attr| *attr.name.local == *"password"); + + let value = attrs + .borrow() + .iter() + .find(|attr| *attr.name.local == *"value") + .map(|attr| attr.value.to_string()) + .unwrap_or_default(); + + let template = Template::new(&value); + let template_clone = template.clone(); + let Some(TemplatePart::Dynamic(var_name)) = template_clone.parts.first() + else { + // nowhere to save the input, returning early + return None; + }; + let evt_handlers = parse_evt_handlers(attrs); + + Some(HtmlElement::Input(Input { + is_password, + value: template, + evt_handlers, + var_name: var_name.to_string(), + })) + } + Self::LABEL => { + let text = Template::new(&text); + Some(HtmlElement::Label { template: text }) + } + Self::SPAN => { + let text = Template::new(&text); + Some(HtmlElement::Span { template: text }) + } + Self::PARAGRAPH => { + tracing::info!("Paragraph: {}", text); + let text = Template::new(&text); + tracing::info!("Paragraph template: {:?}", text); + Some(HtmlElement::Paragraph { template: text }) + } + _ => None, + } + } + NodeData::Text { contents } => { + // skip any contents that are only comprised of \n, whitespace, and no text + if contents.borrow().trim().is_empty() { + return None; + } + // Text node + Some(HtmlElement::Text { + contents: Template::new(&contents.borrow()), + }) + } + _ => None, + } + } + + /// The child elements + pub(crate) fn child_elements(&self) -> Option<&Vec> { + match self { + HtmlElement::Html { children, .. } => Some(children), + HtmlElement::Div { children, .. } => Some(children), + _ => None, + } + } + + /// Adds a child to the element. + pub(crate) fn add_child(&mut self, child: HtmlElement) { + match self { + HtmlElement::Html { children, .. } => children.push(child), + HtmlElement::Div { children, .. } => children.push(child), + _ => {} + } + } + + /// Returns the [Template] of the element, if any + pub(crate) fn template(&self) -> &Template { + match self { + HtmlElement::Html { .. } => unreachable!(), + HtmlElement::Label { template } => template, + HtmlElement::Span { template } => template, + HtmlElement::Paragraph { template } => template, + HtmlElement::Div { template, .. } => template, + HtmlElement::Button(Button { template, .. }) => template, + HtmlElement::Input(Input { value, .. }) => value, + HtmlElement::Text { contents } => contents, + } + } +} + +impl TryFrom for HtmlElement { + type Error = Error; + + fn try_from(value: Handle) -> Result { + HtmlElement::from_node(&value).ok_or(Error::Parse("Unknown tag name".to_string())) + } +} + +impl From for &'static str { + fn from(val: HtmlElement) -> Self { + val.as_str() + } +} + +impl From for String { + fn from(val: HtmlElement) -> Self { + val.as_str().to_string() + } +} + +// impl into std::borrow::Cow<'static, str>> +impl From for std::borrow::Cow<'static, str> { + fn from(val: HtmlElement) -> Self { + val.as_str().into() + } +} + +impl Deref for HtmlElement { + type Target = str; + + fn deref(&self) -> &Self::Target { + self.as_str() + } +} + +// parse event handlers +fn parse_evt_handlers(attrs: &RefCell>) -> Vec { + tracing::info!("Parsing event handlers: {:?}", attrs); + let mut evt_handlers = Vec::new(); + for attr in attrs.borrow().iter() { + if let Ok(attribute) = Attribute::try_from(attr.name.local.to_string().as_str()) { + match EvtHandler::new_from(attribute.into(), &attr.value) { + Ok(evt_handler) => evt_handlers.push(evt_handler), + Err(e) => { + tracing::error!("Invalid data-on-click function: {}", e); + } + } + } + } + evt_handlers +} + +#[cfg(test)] +mod tests { + use super::*; + + // test EvtHandler + #[test] + fn test_evt_handler() { + // test EvtHandler::new_from + let evt_handler = + EvtHandler::new_from(Action::OnClick, "function_name(arg1, arg2)").unwrap(); + + assert_eq!(evt_handler.ty, Action::OnClick); + assert_eq!(evt_handler.details.function, "function_name"); + assert_eq!(evt_handler.details.args, vec!["arg1", "arg2"]); + } +} diff --git a/src/main.rs b/src/main.rs index 7106a9b..4bd3636 100644 --- a/src/main.rs +++ b/src/main.rs @@ -36,13 +36,13 @@ async fn main() -> eframe::Result { // talc was mentioned as an alternative to lol_alloc. Sadly I couldn't get talc to work with my // wasm32 setup. // See: // See: https://github.com/rustwasm/wee_alloc/issues/107#issuecomment-1820233830 -#[cfg(target_arch = "wasm32")] -use lol_alloc::{AssumeSingleThreaded, FreeListAllocator}; - -#[cfg(target_arch = "wasm32")] -#[global_allocator] -static ALLOCATOR: AssumeSingleThreaded = - unsafe { AssumeSingleThreaded::new(FreeListAllocator::new()) }; +//#[cfg(target_arch = "wasm32")] +//use lol_alloc::{AssumeSingleThreaded, FreeListAllocator}; +// +//#[cfg(target_arch = "wasm32")] +//#[global_allocator] +//static ALLOCATOR: AssumeSingleThreaded = +// unsafe { AssumeSingleThreaded::new(FreeListAllocator::new()) }; // When compiling to web using trunk: #[cfg(target_arch = "wasm32")] diff --git a/src/rdx.rs b/src/rdx.rs index cd6e0c8..7664df3 100644 --- a/src/rdx.rs +++ b/src/rdx.rs @@ -3,7 +3,7 @@ use std::collections::HashMap; use std::sync::{Arc, Mutex}; -use crate::hteg::parse_and_render; +use crate::hteg::HtmlToEgui; use crate::layer::{Inner, Instantiator, LayerPlugin, ScopeRef, ScopeRefMut}; use rhai::{Dynamic, Scope}; @@ -32,6 +32,7 @@ impl RdxApp { for (name, wasm_bytes) in crate::BUILTIN_PLUGINS.iter() { // TODO: init from wasm logic somehow! // scope.set_or_push("count", 0); + tracing::info!("Loading plugin: {}", name); let mut plugin = LayerPlugin::new(wasm_bytes, State::new(ctx.clone())); let rdx_source = plugin.call("load", &[]).unwrap(); @@ -146,6 +147,9 @@ impl PluginDeets { return; }; + let html_to_egui = Arc::new(parking_lot::Mutex::new(HtmlToEgui::default())); + tracing::info!("CREATED HTML TO EGUI Struct"); + self.engine.register_fn("render", move |html: &str| { // Options are only Window, Area, CentralPanel, SidePanel, TopBottomPanel egui::Window::new(name.clone()) @@ -155,7 +159,11 @@ impl PluginDeets { #[cfg(target_arch = "wasm32")] let plugin_clone = plugin_clone.deref(); - if let Err(e) = parse_and_render(ui, html, plugin_clone.clone()) { + if let Err(e) = + html_to_egui + .lock() + .parse_and_render(ui, html, plugin_clone.clone()) + { tracing::error!( "Failed to parse RDX source for plugin: {}, source {} with error: {:?}", name, diff --git a/src/template.rs b/src/template.rs index 9c847cc..4b6e812 100644 --- a/src/template.rs +++ b/src/template.rs @@ -1,3 +1,5 @@ +use std::fmt::Display; + /// Holds the template parts (Static and Dynamic). /// /// Renders the template with the provided values. @@ -16,6 +18,19 @@ pub enum TemplatePart { Dynamic(String), } +// asemble the parts in sequence to return the string template literal +impl Display for Template { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + for part in &self.parts { + match part { + TemplatePart::Static(s) => write!(f, "{}", s)?, + TemplatePart::Dynamic(key) => write!(f, "{{{{{}}}}}", key)?, + } + } + Ok(()) + } +} + impl Template { /// Create a new Template from the provided string. pub(crate) fn new(template: &str) -> Self { @@ -55,9 +70,9 @@ impl Template { /// /// Pass it an object that implements Iterator so it can perform lookups on the values given /// the key. - pub(crate) fn render<'a>( + pub(crate) fn render( &self, - values: impl IntoIterator + Clone, + values: impl IntoIterator + Clone, ) -> String { let mut result = String::new(); @@ -68,7 +83,7 @@ impl Template { TemplatePart::Dynamic(key) => { if let Some(value) = vals.into_iter() - .find_map(|(k, v)| if k == key { Some(v) } else { None }) + .find_map(|(k, v)| if k == *key { Some(v) } else { None }) { result.push_str(&value); } else { @@ -91,9 +106,9 @@ mod tests { #[test] fn test_no_replacements() { let template = Template::new("This is a template"); - let values: HashMap<&str, &str> = HashMap::<&str, &str>::new(); + let values: HashMap = HashMap::::new(); - let result = template.render(values.iter().map(|(k, v)| (*k, v.to_string()))); + let result = template.render(values.iter().map(|(k, v)| (k.to_string(), v.to_string()))); assert_eq!(result, "This is a template"); } @@ -102,10 +117,10 @@ mod tests { fn test_template() { let template = Template::new("This {{word_var}} is replaced with {{a_value}}"); let mut values = HashMap::new(); - values.insert("word_var", "template".to_string()); - values.insert("a_value", "content".to_string()); + values.insert("word_var".to_string(), "template".to_string()); + values.insert("a_value".to_string(), "content".to_string()); - let result = template.render(values.iter().map(|(k, v)| (*k, v.clone()))); + let result = template.render(values.iter().map(|(k, v)| (k.to_string(), v.clone()))); assert_eq!(result, "This template is replaced with content"); } @@ -118,11 +133,11 @@ mod tests { eprintln!("{:?}", template); let mut values = HashMap::new(); - values.insert("word_var", "template".to_string()); - values.insert("a_value", "content".to_string()); - values.insert("words_here", "other words".to_string()); + values.insert("word_var".to_string(), "template".to_string()); + values.insert("a_value".to_string(), "content".to_string()); + values.insert("words_here".to_string(), "other words".to_string()); - let result = template.render(values.iter().map(|(k, v)| (*k, v.clone()))); + let result = template.render(values.iter().map(|(k, v)| (k.to_string(), v.clone()))); assert_eq!( result, "This template is replaced with content or these other words" @@ -132,10 +147,20 @@ mod tests { #[test] fn test_missing_value() { let template = Template::new("This {{word_var}} is replaced with {{a_value}}"); - let values: HashMap<&str, &str> = HashMap::<&str, &str>::new(); + let values: HashMap = HashMap::::new(); - let result = template.render(values.iter().map(|(k, v)| (*k, v.to_string()))); + let result = template.render(values.iter().map(|(k, v)| (k.to_string(), v.to_string()))); assert_eq!(result, "This {{word_var}} is replaced with {{a_value}}"); } + + // test Display + #[test] + fn test_display() { + let template = Template::new("This {{word_var}} is replaced with {{a_value}}"); + assert_eq!( + format!("{}", template), + "This {{word_var}} is replaced with {{a_value}}" + ); + } }