diff --git a/delta_q/.gitignore b/delta_q/.gitignore new file mode 100644 index 0000000..a742227 --- /dev/null +++ b/delta_q/.gitignore @@ -0,0 +1,2 @@ +/target/ +/dist/ diff --git a/delta_q/Cargo.lock b/delta_q/Cargo.lock new file mode 100644 index 0000000..248e707 --- /dev/null +++ b/delta_q/Cargo.lock @@ -0,0 +1,2360 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "actix-codec" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f7b0a21988c1bf877cf4759ef5ddaac04c1c9fe808c9142ecb78ba97d97a28a" +dependencies = [ + "bitflags", + "bytes", + "futures-core", + "futures-sink", + "memchr", + "pin-project-lite", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "actix-http" +version = "3.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d48f96fc3003717aeb9856ca3d02a8c7de502667ad76eeacd830b48d2e91fac4" +dependencies = [ + "actix-codec", + "actix-rt", + "actix-service", + "actix-utils", + "ahash", + "base64", + "bitflags", + "brotli", + "bytes", + "bytestring", + "derive_more", + "encoding_rs", + "flate2", + "futures-core", + "h2", + "http", + "httparse", + "httpdate", + "itoa", + "language-tags", + "local-channel", + "mime", + "percent-encoding", + "pin-project-lite", + "rand", + "sha1", + "smallvec", + "tokio", + "tokio-util", + "tracing", + "zstd", +] + +[[package]] +name = "actix-macros" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e01ed3140b2f8d422c68afa1ed2e85d996ea619c988ac834d255db32138655cb" +dependencies = [ + "quote", + "syn 2.0.77", +] + +[[package]] +name = "actix-router" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13d324164c51f63867b57e73ba5936ea151b8a41a1d23d1031eeb9f70d0236f8" +dependencies = [ + "bytestring", + "cfg-if", + "http", + "regex", + "regex-lite", + "serde", + "tracing", +] + +[[package]] +name = "actix-rt" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24eda4e2a6e042aa4e55ac438a2ae052d3b5da0ecf83d7411e1a368946925208" +dependencies = [ + "futures-core", + "tokio", +] + +[[package]] +name = "actix-server" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ca2549781d8dd6d75c40cf6b6051260a2cc2f3c62343d761a969a0640646894" +dependencies = [ + "actix-rt", + "actix-service", + "actix-utils", + "futures-core", + "futures-util", + "mio", + "socket2", + "tokio", + "tracing", +] + +[[package]] +name = "actix-service" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b894941f818cfdc7ccc4b9e60fa7e53b5042a2e8567270f9147d5591893373a" +dependencies = [ + "futures-core", + "paste", + "pin-project-lite", +] + +[[package]] +name = "actix-utils" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88a1dcdff1466e3c2488e1cb5c36a71822750ad43839937f85d2f4d9f8b705d8" +dependencies = [ + "local-waker", + "pin-project-lite", +] + +[[package]] +name = "actix-web" +version = "4.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9180d76e5cc7ccbc4d60a506f2c727730b154010262df5b910eb17dbe4b8cb38" +dependencies = [ + "actix-codec", + "actix-http", + "actix-macros", + "actix-router", + "actix-rt", + "actix-server", + "actix-service", + "actix-utils", + "actix-web-codegen", + "ahash", + "bytes", + "bytestring", + "cfg-if", + "cookie", + "derive_more", + "encoding_rs", + "futures-core", + "futures-util", + "impl-more", + "itoa", + "language-tags", + "log", + "mime", + "once_cell", + "pin-project-lite", + "regex", + "regex-lite", + "serde", + "serde_json", + "serde_urlencoded", + "smallvec", + "socket2", + "time", + "url", +] + +[[package]] +name = "actix-web-codegen" +version = "4.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f591380e2e68490b5dfaf1dd1aa0ebe78d84ba7067078512b4ea6e4492d622b8" +dependencies = [ + "actix-router", + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "addr2line" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5fb1d8e4442bd405fdfd1dacb42792696b0cf9cb15882e5d097b742a676d375" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" + +[[package]] +name = "ahash" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +dependencies = [ + "cfg-if", + "getrandom", + "once_cell", + "version_check", + "zerocopy", +] + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "alloc-no-stdlib" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3" + +[[package]] +name = "alloc-stdlib" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94fb8275041c72129eb51b7d0322c29b8387a0386127718b096429201a5d6ece" +dependencies = [ + "alloc-no-stdlib", +] + +[[package]] +name = "allocator-api2" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" + +[[package]] +name = "anymap2" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d301b3b94cb4b2f23d7917810addbbaff90738e0ca2be692bd027e70d7e0330c" + +[[package]] +name = "arc-swap" +version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457" + +[[package]] +name = "autocfg" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" + +[[package]] +name = "backtrace" +version = "0.3.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" +dependencies = [ + "addr2line", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", + "windows-targets", +] + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + +[[package]] +name = "bitflags" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "boolinator" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfa8873f51c92e232f9bac4065cddef41b714152812bfc5f7672ba16d6ef8cd9" + +[[package]] +name = "brotli" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74f7971dbd9326d58187408ab83117d8ac1bb9c17b085fdacd1cf2f598719b6b" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", + "brotli-decompressor", +] + +[[package]] +name = "brotli-decompressor" +version = "4.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a45bd2e4095a8b518033b128020dd4a55aab1c0a381ba4404a472630f4bc362" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", +] + +[[package]] +name = "bumpalo" +version = "3.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "428d9aa8fbc0670b7b8d6030a7fadd0f86151cae55e4dbbece15f3780a3dfaf3" + +[[package]] +name = "bytestring" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74d80203ea6b29df88012294f62733de21cfeab47f17b41af3a38bc30a03ee72" +dependencies = [ + "bytes", +] + +[[package]] +name = "cc" +version = "1.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07b1695e2c7e8fc85310cde85aeaab7e3097f593c91d209d3f9df76c928100f0" +dependencies = [ + "jobserver", + "libc", + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "charts-rs" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d3115b05cc478e86d83d21d0560d0ed5023ce0a55bc59790c985555f1ba1972" +dependencies = [ + "ahash", + "arc-swap", + "charts-rs-derive", + "fontdue", + "once_cell", + "regex", + "serde", + "serde_json", + "snafu", + "substring", +] + +[[package]] +name = "charts-rs-derive" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "218a07f694e17fb902c2fe72ba31ab821f2d57c146d51761aae00ab5614a3727" +dependencies = [ + "quote", + "syn 2.0.77", +] + +[[package]] +name = "clone_dyn_types" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f4f0e34968641cc21b39c159b7d07e8c0f573cbc0ef9cd59e452fe6774c0579" + +[[package]] +name = "console_error_panic_hook" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc" +dependencies = [ + "cfg-if", + "wasm-bindgen", +] + +[[package]] +name = "convert_case" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" + +[[package]] +name = "cookie" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e859cd57d0710d9e06c381b550c06e76992472a8c6d527aecd2fc673dcc231fb" +dependencies = [ + "percent-encoding", + "time", + "version_check", +] + +[[package]] +name = "cpufeatures" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0" +dependencies = [ + "libc", +] + +[[package]] +name = "crc32fast" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "delta_q" +version = "0.1.0" +dependencies = [ + "actix-web", + "charts-rs", + "gloo-utils 0.2.0", + "include_dir", + "iter_tools", + "js-sys", + "maplit", + "parking_lot", + "serde", + "serde_json", + "tracing", + "tracing-subscriber", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "yew", +] + +[[package]] +name = "deranged" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +dependencies = [ + "powerfmt", +] + +[[package]] +name = "derive_more" +version = "0.99.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f33878137e4dafd7fa914ad4e259e18a4e8e532b9617a2d0150262bf53abfce" +dependencies = [ + "convert_case", + "proc-macro2", + "quote", + "rustc_version", + "syn 2.0.77", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + +[[package]] +name = "either" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" + +[[package]] +name = "encoding_rs" +version = "0.8.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "flate2" +version = "1.0.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "324a1be68054ef05ad64b861cc9eaf1d623d2d8cb25b4bf2cb9cdd902b4bf253" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "fontdue" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "efe23d02309319171d00d794c9ff48d4f903c0e481375b1b04b017470838af04" +dependencies = [ + "hashbrown", + "ttf-parser", +] + +[[package]] +name = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "futures" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" + +[[package]] +name = "futures-io" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" + +[[package]] +name = "futures-macro" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "futures-sink" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" + +[[package]] +name = "futures-task" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" + +[[package]] +name = "futures-util" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "wasi", + "wasm-bindgen", +] + +[[package]] +name = "gimli" +version = "0.31.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32085ea23f3234fc7846555e85283ba4de91e21016dc0455a16286d87a292d64" + +[[package]] +name = "gloo" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28999cda5ef6916ffd33fb4a7b87e1de633c47c0dc6d97905fee1cdaa142b94d" +dependencies = [ + "gloo-console 0.2.3", + "gloo-dialogs 0.1.1", + "gloo-events 0.1.2", + "gloo-file 0.2.3", + "gloo-history 0.1.5", + "gloo-net 0.3.1", + "gloo-render 0.1.1", + "gloo-storage 0.2.2", + "gloo-timers 0.2.6", + "gloo-utils 0.1.7", + "gloo-worker 0.2.1", +] + +[[package]] +name = "gloo" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd35526c28cc55c1db77aed6296de58677dbab863b118483a27845631d870249" +dependencies = [ + "gloo-console 0.3.0", + "gloo-dialogs 0.2.0", + "gloo-events 0.2.0", + "gloo-file 0.3.0", + "gloo-history 0.2.2", + "gloo-net 0.4.0", + "gloo-render 0.2.0", + "gloo-storage 0.3.0", + "gloo-timers 0.3.0", + "gloo-utils 0.2.0", + "gloo-worker 0.4.0", +] + +[[package]] +name = "gloo-console" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82b7ce3c05debe147233596904981848862b068862e9ec3e34be446077190d3f" +dependencies = [ + "gloo-utils 0.1.7", + "js-sys", + "serde", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "gloo-console" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a17868f56b4a24f677b17c8cb69958385102fa879418052d60b50bc1727e261" +dependencies = [ + "gloo-utils 0.2.0", + "js-sys", + "serde", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "gloo-dialogs" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67062364ac72d27f08445a46cab428188e2e224ec9e37efdba48ae8c289002e6" +dependencies = [ + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "gloo-dialogs" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf4748e10122b01435750ff530095b1217cf6546173459448b83913ebe7815df" +dependencies = [ + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "gloo-events" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68b107f8abed8105e4182de63845afcc7b69c098b7852a813ea7462a320992fc" +dependencies = [ + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "gloo-events" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27c26fb45f7c385ba980f5fa87ac677e363949e065a083722697ef1b2cc91e41" +dependencies = [ + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "gloo-file" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8d5564e570a38b43d78bdc063374a0c3098c4f0d64005b12f9bbe87e869b6d7" +dependencies = [ + "gloo-events 0.1.2", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "gloo-file" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97563d71863fb2824b2e974e754a81d19c4a7ec47b09ced8a0e6656b6d54bd1f" +dependencies = [ + "gloo-events 0.2.0", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "gloo-history" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85725d90bf0ed47063b3930ef28e863658a7905989e9929a8708aab74a1d5e7f" +dependencies = [ + "gloo-events 0.1.2", + "gloo-utils 0.1.7", + "serde", + "serde-wasm-bindgen 0.5.0", + "serde_urlencoded", + "thiserror", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "gloo-history" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "903f432be5ba34427eac5e16048ef65604a82061fe93789f2212afc73d8617d6" +dependencies = [ + "getrandom", + "gloo-events 0.2.0", + "gloo-utils 0.2.0", + "serde", + "serde-wasm-bindgen 0.6.5", + "serde_urlencoded", + "thiserror", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "gloo-net" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a66b4e3c7d9ed8d315fd6b97c8b1f74a7c6ecbbc2320e65ae7ed38b7068cc620" +dependencies = [ + "futures-channel", + "futures-core", + "futures-sink", + "gloo-utils 0.1.7", + "http", + "js-sys", + "pin-project", + "serde", + "serde_json", + "thiserror", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "gloo-net" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ac9e8288ae2c632fa9f8657ac70bfe38a1530f345282d7ba66a1f70b72b7dc4" +dependencies = [ + "futures-channel", + "futures-core", + "futures-sink", + "gloo-utils 0.2.0", + "http", + "js-sys", + "pin-project", + "serde", + "serde_json", + "thiserror", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "gloo-render" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fd9306aef67cfd4449823aadcd14e3958e0800aa2183955a309112a84ec7764" +dependencies = [ + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "gloo-render" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56008b6744713a8e8d98ac3dcb7d06543d5662358c9c805b4ce2167ad4649833" +dependencies = [ + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "gloo-storage" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d6ab60bf5dbfd6f0ed1f7843da31b41010515c745735c970e821945ca91e480" +dependencies = [ + "gloo-utils 0.1.7", + "js-sys", + "serde", + "serde_json", + "thiserror", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "gloo-storage" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbc8031e8c92758af912f9bc08fbbadd3c6f3cfcbf6b64cdf3d6a81f0139277a" +dependencies = [ + "gloo-utils 0.2.0", + "js-sys", + "serde", + "serde_json", + "thiserror", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "gloo-timers" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b995a66bb87bebce9a0f4a95aed01daca4872c050bfcb21653361c03bc35e5c" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "gloo-timers" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbb143cf96099802033e0d4f4963b19fd2e0b728bcf076cd9cf7f6634f092994" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "gloo-utils" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "037fcb07216cb3a30f7292bd0176b050b7b9a052ba830ef7d5d65f6dc64ba58e" +dependencies = [ + "js-sys", + "serde", + "serde_json", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "gloo-utils" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b5555354113b18c547c1d3a98fbf7fb32a9ff4f6fa112ce823a21641a0ba3aa" +dependencies = [ + "js-sys", + "serde", + "serde_json", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "gloo-worker" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13471584da78061a28306d1359dd0178d8d6fc1c7c80e5e35d27260346e0516a" +dependencies = [ + "anymap2", + "bincode", + "gloo-console 0.2.3", + "gloo-utils 0.1.7", + "js-sys", + "serde", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "gloo-worker" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76495d3dd87de51da268fa3a593da118ab43eb7f8809e17eb38d3319b424e400" +dependencies = [ + "bincode", + "futures", + "gloo-utils 0.2.0", + "gloo-worker-macros", + "js-sys", + "pinned", + "serde", + "thiserror", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "gloo-worker-macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "956caa58d4857bc9941749d55e4bd3000032d8212762586fa5705632967140e7" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "h2" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +dependencies = [ + "ahash", + "allocator-api2", +] + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hermit-abi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" + +[[package]] +name = "http" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "httparse" +version = "1.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9" + +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + +[[package]] +name = "idna" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "impl-more" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "206ca75c9c03ba3d4ace2460e57b189f39f43de612c2f85836e65c929701bb2d" + +[[package]] +name = "implicit-clone" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8a9aa791c7b5a71b636b7a68207fdebf171ddfc593d9c8506ec4cbc527b6a84" +dependencies = [ + "implicit-clone-derive", + "indexmap", +] + +[[package]] +name = "implicit-clone-derive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9311685eb9a34808bbb0608ad2fcab9ae216266beca5848613e95553ac914e3b" +dependencies = [ + "quote", + "syn 2.0.77", +] + +[[package]] +name = "include_dir" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "923d117408f1e49d914f1a379a309cffe4f18c05cf4e3d12e613a15fc81bd0dd" +dependencies = [ + "include_dir_macros", +] + +[[package]] +name = "include_dir_macros" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cab85a7ed0bd5f0e76d93846e0147172bed2e2d3f859bcc33a8d9699cad1a75" +dependencies = [ + "proc-macro2", + "quote", +] + +[[package]] +name = "indexmap" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5" +dependencies = [ + "equivalent", + "hashbrown", +] + +[[package]] +name = "iter_tools" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27812bb0a056539d62930a899759af39dfab17ac73a17d5caf58365762657891" +dependencies = [ + "clone_dyn_types", + "itertools", +] + +[[package]] +name = "itertools" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" + +[[package]] +name = "jobserver" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0" +dependencies = [ + "libc", +] + +[[package]] +name = "js-sys" +version = "0.3.70" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1868808506b929d7b0cfa8f75951347aa71bb21144b7791bae35d9bccfcfe37a" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "language-tags" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4345964bb142484797b161f473a503a434de77149dd8c7427788c6e13379388" + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "libc" +version = "0.2.158" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" + +[[package]] +name = "local-channel" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6cbc85e69b8df4b8bb8b89ec634e7189099cea8927a276b7384ce5488e53ec8" +dependencies = [ + "futures-core", + "futures-sink", + "local-waker", +] + +[[package]] +name = "local-waker" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d873d7c67ce09b42110d801813efbc9364414e356be9935700d368351657487" + +[[package]] +name = "lock_api" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" + +[[package]] +name = "maplit" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" + +[[package]] +name = "matchers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" +dependencies = [ + "regex-automata 0.1.10", +] + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "miniz_oxide" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" +dependencies = [ + "adler2", +] + +[[package]] +name = "mio" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" +dependencies = [ + "hermit-abi", + "libc", + "log", + "wasi", + "windows-sys", +] + +[[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-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + +[[package]] +name = "num_cpus" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "object" +version = "0.36.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "084f1a5821ac4c651660a94a7153d27ac9d8a53736203f58b31945ded098070a" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + +[[package]] +name = "parking_lot" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets", +] + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[package]] +name = "pin-project" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pinned" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a829027bd95e54cfe13e3e258a1ae7b645960553fb82b75ff852c29688ee595b" +dependencies = [ + "futures", + "rustversion", + "thiserror", +] + +[[package]] +name = "pkg-config" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "ppv-lite86" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "prettyplease" +version = "0.2.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479cf940fbbb3426c32c5d5176f62ad57549a0bb84773423ba8be9d089f5faba" +dependencies = [ + "proc-macro2", + "syn 2.0.77", +] + +[[package]] +name = "proc-macro-crate" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" +dependencies = [ + "once_cell", + "toml_edit", +] + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn 1.0.109", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro2" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "prokio" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03b55e106e5791fa5a13abd13c85d6127312e8e09098059ca2bc9b03ca4cf488" +dependencies = [ + "futures", + "gloo 0.8.1", + "num_cpus", + "once_cell", + "pin-project", + "pinned", + "tokio", + "tokio-stream", + "wasm-bindgen-futures", +] + +[[package]] +name = "quote" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "redox_syscall" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0884ad60e090bf1345b93da0a5de8923c93884cd03f40dfcfddd3b4bee661853" +dependencies = [ + "bitflags", +] + +[[package]] +name = "regex" +version = "1.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata 0.4.7", + "regex-syntax 0.8.4", +] + +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" +dependencies = [ + "regex-syntax 0.6.29", +] + +[[package]] +name = "regex-automata" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax 0.8.4", +] + +[[package]] +name = "regex-lite" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53a49587ad06b26609c52e423de037e7f57f20d53535d66e08c695f347df952a" + +[[package]] +name = "regex-syntax" +version = "0.6.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" + +[[package]] +name = "regex-syntax" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" + +[[package]] +name = "rustc-demangle" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + +[[package]] +name = "rustversion" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" + +[[package]] +name = "ryu" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "semver" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" + +[[package]] +name = "serde" +version = "1.0.210" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde-wasm-bindgen" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3b143e2833c57ab9ad3ea280d21fd34e285a42837aeb0ee301f4f41890fa00e" +dependencies = [ + "js-sys", + "serde", + "wasm-bindgen", +] + +[[package]] +name = "serde-wasm-bindgen" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8302e169f0eddcc139c70f139d19d6467353af16f9fce27e8c30158036a1e16b" +dependencies = [ + "js-sys", + "serde", + "wasm-bindgen", +] + +[[package]] +name = "serde_derive" +version = "1.0.210" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "serde_json" +version = "1.0.128" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "signal-hook-registry" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" +dependencies = [ + "libc", +] + +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + +[[package]] +name = "smallvec" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + +[[package]] +name = "snafu" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b835cb902660db3415a672d862905e791e54d306c6e8189168c7f3d9ae1c79d" +dependencies = [ + "snafu-derive", +] + +[[package]] +name = "snafu-derive" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38d1e02fca405f6280643174a50c942219f0bbf4dbf7d480f1dd864d6f211ae5" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "socket2" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" +dependencies = [ + "libc", + "windows-sys", +] + +[[package]] +name = "substring" +version = "1.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ee6433ecef213b2e72f587ef64a2f5943e7cd16fbd82dbe8bc07486c534c86" +dependencies = [ + "autocfg", +] + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thiserror" +version = "1.0.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "thread_local" +version = "1.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" +dependencies = [ + "cfg-if", + "once_cell", +] + +[[package]] +name = "time" +version = "0.3.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" +dependencies = [ + "deranged", + "itoa", + "num-conv", + "powerfmt", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" + +[[package]] +name = "time-macros" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" +dependencies = [ + "num-conv", + "time-core", +] + +[[package]] +name = "tinyvec" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tokio" +version = "1.40.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2b070231665d27ad9ec9b8df639893f46727666c6767db40317fbe920a5d998" +dependencies = [ + "backtrace", + "bytes", + "libc", + "mio", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "windows-sys", +] + +[[package]] +name = "tokio-stream" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f4e6ce100d0eb49a2734f8c0812bcd324cf357d21810932c5df6b96ef2b86f1" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61e7c3654c13bcd040d4a03abee2c75b1d14a37b423cf5a813ceae1cc903ec6a" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "toml_datetime" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" + +[[package]] +name = "toml_edit" +version = "0.19.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" +dependencies = [ + "indexmap", + "toml_datetime", + "winnow", +] + +[[package]] +name = "tracing" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +dependencies = [ + "log", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "tracing-core" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" +dependencies = [ + "matchers", + "nu-ansi-term", + "once_cell", + "regex", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", +] + +[[package]] +name = "ttf-parser" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c591d83f69777866b9126b24c6dd9a18351f177e49d625920d19f989fd31cf8" + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "unicode-bidi" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" + +[[package]] +name = "unicode-ident" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" + +[[package]] +name = "unicode-normalization" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "url" +version = "2.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[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.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a82edfc16a6c469f5f44dc7b571814045d60404b55a0ee849f9bcfa2e63dd9b5" +dependencies = [ + "cfg-if", + "once_cell", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9de396da306523044d3302746f1208fa71d7532227f15e347e2d93e4145dd77b" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn 2.0.77", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61e9300f63a621e96ed275155c108eb6f843b6a26d053f122ab69724559dc8ed" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484" + +[[package]] +name = "web-sys" +version = "0.3.70" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26fdeaafd9bd129f65e7c031593c24d62186301e0c72c8978fa1678be7d532c0" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "winnow" +version = "0.5.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" +dependencies = [ + "memchr", +] + +[[package]] +name = "yew" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f1a03f255c70c7aa3e9c62e15292f142ede0564123543c1cc0c7a4f31660cac" +dependencies = [ + "console_error_panic_hook", + "futures", + "gloo 0.10.0", + "implicit-clone", + "indexmap", + "js-sys", + "prokio", + "rustversion", + "serde", + "slab", + "thiserror", + "tokio", + "tracing", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "yew-macro", +] + +[[package]] +name = "yew-macro" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02fd8ca5166d69e59f796500a2ce432ff751edecbbb308ca59fd3fe4d0343de2" +dependencies = [ + "boolinator", + "once_cell", + "prettyplease", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "zerocopy" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +dependencies = [ + "byteorder", + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "zstd" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcf2b778a664581e31e389454a7072dab1647606d44f7feea22cd5abb9c9f3f9" +dependencies = [ + "zstd-safe", +] + +[[package]] +name = "zstd-safe" +version = "7.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54a3ab4db68cea366acc5c897c7b4d4d1b8994a9cd6e6f841f8964566a419059" +dependencies = [ + "zstd-sys", +] + +[[package]] +name = "zstd-sys" +version = "2.0.13+zstd.1.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38ff0f21cfee8f97d94cef41359e0c89aa6113028ab0291aa8ca0038995a95aa" +dependencies = [ + "cc", + "pkg-config", +] diff --git a/delta_q/Cargo.toml b/delta_q/Cargo.toml new file mode 100644 index 0000000..71fb97d --- /dev/null +++ b/delta_q/Cargo.toml @@ -0,0 +1,51 @@ +[package] +name = "delta_q" +version = "0.1.0" +edition = "2021" + +[[bin]] +name = "editor" +required-features = ["main"] + +[[bin]] +name = "editor-web" +required-features = ["web"] + +[features] +main = ["actix-web", "include_dir", "parking_lot", "tracing-subscriber"] +web = [ + "yew", + "wasm-bindgen", + "wasm-bindgen-futures", + "js-sys", + "web-sys", + "gloo-utils", + "serde_json", + "charts-rs", +] + +[dependencies] +actix-web = { version = "4.9.0", optional = true } +charts-rs = { version = "0.3.17", optional = true } +gloo-utils = { version = "0.2.0", optional = true } +include_dir = { version = "0.7.4", optional = true } +iter_tools = "0.21.0" +js-sys = { version = "0.3.70", optional = true } +parking_lot = { version = "0.12.3", optional = true } +serde = { version = "1.0.210", features = ["derive"] } +serde_json = { version = "1.0.128", optional = true } +tracing = "0.1.40" +tracing-subscriber = { version = "0.3.18", features = [ + "env-filter", +], optional = true } +wasm-bindgen = { version = "0.2.93", optional = true } +wasm-bindgen-futures = { version = "0.4.43", optional = true } +yew = { version = "0.21.0", features = ["csr"], optional = true } + +[dependencies.web-sys] +version = "0.3.70" +optional = true +features = ["Window", "Request", "Location"] + +[dev-dependencies] +maplit = "1.0.2" diff --git a/delta_q/README.md b/delta_q/README.md new file mode 100644 index 0000000..0b9ce78 --- /dev/null +++ b/delta_q/README.md @@ -0,0 +1,51 @@ +# Delta-Q utilities for Rust + +the paper: + +The goal of this project is to provide tooling that is easy to use for designers of decentralised systems to model their network and assess communication timeliness and resource usage. +This should be supported by a web UI that allows modelling CDFs (cumulative distribution function), named outcome expressions, and constraints/expectations. +The underlying ΔQ expressions shall be used for export/import of the model, complemented with a library for CDF constructors (like step function etc.). + +## Implementation plan + +The first step is to provide a library for manipulating CDFs, offering all operations required by the theory; the internal representation will be discrete numerical [DONE], with later optimizations for constant parts at the beginning and end of the vector. + +The second step yields an internal DSL for creating ΔQ expressions and printing them. [DONE] + +The third step provides evaluation of ΔQ expressions as defined in the paper. [DONE] +This will later be expanded to include some exponentiation-like operator that simplifies expressing a randomly chosen repetition count for some sub-expression (as frequently occurs in gossip protocols). + +The fourth step adds a web UI to expose the internal DSL to no-code users. [DONE] +The interaction with a ΔQ expression shall closely resemble the refinement approach for system modelling as defined in the paper. [DONE] +It will allow the system designer to see immediately the result of the current model [DONE] and how its computed attenuation compares to the expectation or constraints. + +In addition and in parallel to the above, the theory shall be better understood and where required enhanced to support not only timeliness analysis but also load prediction. +It is expected that while the same system model can be used for both aspects, the inputs for the load analysis need to be somewhat different from CDFs, i.e. they will likely require more information. + +## Caveats + +Since the timing CDFs don't model load dependence, they are only representative of the unloaded system. +The results of the load analysis will indicate where and under which conditions this assumption will be broken, but it isn't obvious how to feed that information back into a changed CDF to adapt the timeliness analysis to those circumstances. + +## Building and Running + +The build comprises two steps: + +- `trunk build` (i.e. you’ll need to `cargo install --locked trunk` first) +- `cargo run --bin editor` + +The first one builds the web app in the `dist/` folder, which the second one then integrates into the single-binary application that will serve HTTP resources on port 8080 when run. + +When developing the web UI part you can leave `cargo run --bin editor` running while using `trunk serve` to serve the UI with change detection. +Requests to the `delta_q/*` endpoints will be proxied. + +## Known Shortcomings + +- not optimised at all, especially regarding memory usage (need to make cloning cheap for CDF, DeltaQ, etc.) and web assembly size +- functional but ugly +- duplicates state management in web app and backend, not yet decided what to put where (currently ΔQ expression evaluation is done in the backend, could easily move to a web worker) +- no editing of CDFs yet +- should have export (probably as JSON) and matching import +- should allow editing the formulas as text, which requires making the syntax more accessible via normal keyboards +- fixed samples and width for CDF, should be changed to support any step function and compute its width dynamically (with upper bound on details to avoid memory explosion) +- should add “exponentiation” operator to wrap an expression in another one with a hole for a specified number of times diff --git a/delta_q/Trunk.toml b/delta_q/Trunk.toml new file mode 100644 index 0000000..f605d8a --- /dev/null +++ b/delta_q/Trunk.toml @@ -0,0 +1,5 @@ +[serve] +port = 8081 + +[[proxy]] +backend = "http://localhost:8080/delta_q" diff --git a/delta_q/index.html b/delta_q/index.html new file mode 100644 index 0000000..2a1e0cf --- /dev/null +++ b/delta_q/index.html @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/delta_q/src/bin/editor-web.rs b/delta_q/src/bin/editor-web.rs new file mode 100644 index 0000000..f1b7cb0 --- /dev/null +++ b/delta_q/src/bin/editor-web.rs @@ -0,0 +1,221 @@ +macro_rules! cloned { + ($($name:ident),*; $e:expr) => {{ + $(let $name = $name.clone();)* + $e + }}; +} + +use delta_q::{cdf_to_svg, DeltaQ, DeltaQComponent, DeltaQContext, EvaluationContext, CDF}; +use html::RenderResult; +use std::rc::Rc; +use wasm_bindgen::{JsCast, JsValue}; +use wasm_bindgen_futures::JsFuture; +use web_sys::{HtmlInputElement, RequestInit}; +use yew::{platform, prelude::*, suspense::use_future_with}; + +#[hook] +fn use_json serde::Deserialize<'a>>( + dep: D, + url: impl Fn(Rc) -> Result + 'static, +) -> RenderResult> { + let window = web_sys::window().unwrap(); + let json = use_future_with(dep, move |dep| async move { + match url(dep) { + Ok(url) => { + JsFuture::from( + JsFuture::from(window.fetch_with_str(&url)) + .await? + .dyn_into::()? + .text()?, + ) + .await + } + Err(e) => Err(e), + } + })?; + Ok(match &*json { + Ok(cdf) => match serde_json::from_str::(&cdf.as_string().unwrap()) { + Ok(cdf) => Ok(cdf), + Err(e) => Err(format!("{cdf:?} Deserialisation error: {}", e)), + }, + Err(e) => Err(format!("Error: {e:?}")), + }) +} + +async fn put_json(url: &str, value: T) -> Result { + let window = web_sys::window().unwrap(); + let value = serde_json::to_string(&value).unwrap(); + let init = RequestInit::new(); + init.set_method("PUT"); + { + let headers = web_sys::Headers::new().unwrap(); + headers.set("Content-Type", "application/json").unwrap(); + init.set_headers(&headers); + } + init.set_body(&value.into()); + JsFuture::from( + JsFuture::from(window.fetch_with_str_and_init(url, &init)) + .await? + .dyn_into::()? + .text()?, + ) + .await +} + +async fn delete_path(url: &str) -> Result { + let window = web_sys::window().unwrap(); + let init = RequestInit::new(); + init.set_method("DELETE"); + JsFuture::from( + JsFuture::from(window.fetch_with_str_and_init(url, &init)) + .await? + .dyn_into::()? + .text()?, + ) + .await +} + +#[function_component(AppMain)] +fn app_main() -> HtmlResult { + let location = web_sys::window().unwrap().location().href().unwrap(); + let location2 = location.clone(); + + let ctx = + match use_json::<_, EvaluationContext>((), move |_| Ok(format!("{location2}delta_q")))? { + Ok(ctx) => ctx, + Err(e) => return Ok(html! {

{ e }

}), + }; + + let selected = use_state(|| Some("out".to_owned())); + let onclick = cloned!(selected; Callback::from(move |n| selected.set(Some(n)))); + + // epoch counter to trigger recomputation when the context changes + let epoch = use_state(|| 0); + + let cdf = use_json::<_, CDF>( + (selected.clone(), epoch.clone()), + cloned!(location; move |selected| { + (*selected) + .0 + .as_ref() + .ok_or(JsValue::NULL) + .map(|s| format!("{location}delta_q/{}", s)) + }), + )?; + + let ctx = use_reducer(move || ctx); + let on_change = cloned!(ctx, epoch, location; + Callback::from(move |(name, dq): (String, Option)| { + ctx.dispatch((name.clone(), dq.clone())); + platform::spawn_local(cloned!(epoch, location; async move { + if let Some(dq) = dq { + put_json(&format!("{location}delta_q/{name}"), dq).await.unwrap(); + } else { + delete_path(&format!("{location}delta_q/{name}")).await.unwrap(); + } + epoch.set(*epoch + 1); + })); + }) + ); + + let mut sel_found = false; + let list_items = ctx + .iter() + .map(|(k, v)| { + let name = k.clone(); + let onclick = onclick.clone(); + let mut h = html! { +
  • + + { format!("{k}: {v}") } +
  • + }; + if selected.as_ref() == Some(k) { + sel_found = true; + h = html! { { h } }; + } + h + }) + .collect::(); + if selected.is_some() && !sel_found { + selected.set(None); + // this only takes effect on the next render! + } + + let dq = selected.as_ref().and_then(|name| ctx.get(name)); + web_sys::console::log_1(&JsValue::from_str(&format!("{dq:?}"))); + + let cdf = match cdf { + Ok(cdf) => cdf_to_svg(&cdf), + Err(e) => html! {

    { "no CDF result: " }{ e }

    }, + }; + + let add_on_change = on_change.reform(cloned!(selected; move |x: (String, Option)| { + selected.set(Some(x.0.clone())); + x + })); + + Ok(html! { +
    +

    { "context:" }

    +
      + { list_items } +
    + + if let (Some(name), Some(dq)) = (selected.as_ref(), dq) { +

    { "selected: " } { name }

    +
    + context={DeltaQContext::new(&ctx, &name)}> + + > +
    + { cdf } + } +
    + }) +} + +#[derive(Properties, PartialEq, Clone)] +struct AddExpressionProps { + on_change: Callback<(String, Option)>, +} + +#[function_component(AddExpression)] +fn add_expression(props: &AddExpressionProps) -> HtmlResult { + let name = use_state(|| "".to_owned()); + let value = (*name).clone(); + let on_change = props.on_change.clone(); + let on_submit = cloned!(name, on_change; Callback::from(move |e: SubmitEvent| { + e.prevent_default(); + name.set("".to_owned()); + on_change.emit(((*name).clone(), Some(DeltaQ::BlackBox))); + })); + let on_input = Callback::from(move |e: InputEvent| { + name.set(e.target_unchecked_into::().value()) + }); + + Ok(html! { +
    + + +
    + }) +} + +#[function_component(App)] +fn app() -> Html { + let waiting = html! {

    { "Waiting for DeltaQ..." }

    }; + + html! { +
    +

    { "DeltaQ Editor" }

    + + + +
    + } +} + +fn main() { + yew::Renderer::::new().render(); +} diff --git a/delta_q/src/bin/editor.rs b/delta_q/src/bin/editor.rs new file mode 100644 index 0000000..f0cc49a --- /dev/null +++ b/delta_q/src/bin/editor.rs @@ -0,0 +1,142 @@ +use actix_web::{delete, get, put, web, App, HttpRequest, HttpResponse, HttpServer, Responder}; +use delta_q::{DeltaQ, EvaluationContext, CDF}; +use include_dir::{include_dir, Dir}; +use parking_lot::Mutex; +use std::io; +use tracing_subscriber::EnvFilter; + +static ASSETS: Dir = include_dir!("$CARGO_MANIFEST_DIR/dist"); + +struct Data { + ctx: Mutex, +} + +#[get("/")] +async fn index() -> impl Responder { + tracing::info!("GET /"); + HttpResponse::Ok().content_type("text/html").body( + ASSETS + .get_file("index.html") + .expect("no index.html in dist/") + .contents(), + ) +} + +async fn assets(req: HttpRequest) -> impl Responder { + tracing::info!("GET {}", req.path()); + let path = &req.path()[1..]; + let mime = if let Some(pos) = path.rfind('.') { + match &path[pos + 1..] { + "html" => "text/html", + "js" => "application/javascript", + "css" => "text/css", + "wasm" => "application/wasm", + "txt" => "text/plain", + _ => "application/octet-stream", + } + } else { + "application/octet-stream" + }; + Ok::( + HttpResponse::Ok().content_type(mime).body( + ASSETS + .get_file(path) + .ok_or_else(|| io::Error::new(io::ErrorKind::NotFound, "not found"))? + .contents(), + ), + ) +} + +#[get("/delta_q")] +async fn all_delta_q(data: web::Data) -> impl Responder { + tracing::info!("GET /delta_q"); + HttpResponse::Ok() + .insert_header(("Cache-Control", "no-store")) + .json(&*data.ctx.lock()) +} + +#[get("/delta_q/{name}")] +async fn get_delta_q(data: web::Data, name: web::Path) -> impl Responder { + tracing::info!("GET /delta_q/{}", name); + let mut ctx = data.ctx.lock(); + match ctx.eval(&name) { + Ok(dq) => HttpResponse::Ok() + .insert_header(("Cache-Control", "no-store")) + .json(dq), + Err(e) => HttpResponse::NotFound() + .insert_header(("Cache-Control", "no-store")) + .body(e.to_string()), + } +} + +#[put("/delta_q/{name}")] +async fn put_delta_q( + data: web::Data, + name: web::Path, + dq: web::Json, +) -> impl Responder { + tracing::info!("PUT /delta_q/{}", name); + let mut ctx = data.ctx.lock(); + ctx.put(name.into_inner(), dq.into_inner()); + HttpResponse::Ok().finish() +} + +#[delete("/delta_q/{name}")] +async fn delete_delta_q(data: web::Data, name: web::Path) -> impl Responder { + tracing::info!("DELETE /delta_q/{}", name); + let mut ctx = data.ctx.lock(); + if ctx.remove(&name).is_some() { + HttpResponse::Ok().finish() + } else { + HttpResponse::NotFound().finish() + } +} + +#[actix_web::main] +async fn main() -> io::Result<()> { + tracing_subscriber::fmt() + .with_env_filter(EnvFilter::from_default_env()) + .init(); + + let data = web::Data::new(Data { + ctx: Mutex::new(EvaluationContext::default()), + }); + + // add two delta_q to the context + let mut ctx = data.ctx.lock(); + ctx.put("black".to_owned(), DeltaQ::BlackBox); + ctx.put( + "cdf".to_owned(), + DeltaQ::cdf(CDF::step(&[(0.1, 0.33), (0.2, 0.66), (0.4, 1.0)], 0.01, 300).unwrap()), + ); + ctx.put( + "out".to_owned(), + DeltaQ::seq( + DeltaQ::name("cdf"), + DeltaQ::choice( + DeltaQ::name("cdf"), + 0.5, + DeltaQ::for_all( + DeltaQ::name("cdf"), + DeltaQ::seq(DeltaQ::name("cdf"), DeltaQ::name("cdf")), + ), + 3.0, + ), + ), + ); + drop(ctx); + + let server = HttpServer::new(move || { + App::new() + .app_data(data.clone()) + .service(index) + .service(all_delta_q) + .service(get_delta_q) + .service(put_delta_q) + .service(delete_delta_q) + .route("/{f:.*}", web::get().to(assets)) + }) + .workers(1); + println!("Listening on http://localhost:8080"); + server.bind(("localhost", 8080))?.run().await +} diff --git a/delta_q/src/cdf.rs b/delta_q/src/cdf.rs new file mode 100644 index 0000000..62e0227 --- /dev/null +++ b/delta_q/src/cdf.rs @@ -0,0 +1,396 @@ +use std::cmp::Ordering; + +#[derive(Debug, PartialEq)] +pub enum CDFError { + InvalidDataRange, + NonMonotonicData, + BinSizeMismatch, + LengthMismatch, + InvalidFraction, +} + +impl std::fmt::Display for CDFError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + CDFError::InvalidDataRange => { + write!(f, "Data vector must contain values between 0 and 1") + } + CDFError::NonMonotonicData => write!( + f, + "Data vector must contain monotonically increasing values" + ), + CDFError::BinSizeMismatch => write!(f, "CDFs must have the same bin size"), + CDFError::LengthMismatch => write!(f, "CDFs must have the same length"), + CDFError::InvalidFraction => write!(f, "Fraction must be between 0 and 1"), + } + } +} + +impl std::error::Error for CDFError {} + +/// A Cumulative Distribution Function (CDF) is a representation of a probability +/// distribution that can be manipulated in various ways. +#[derive(Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct CDF { + data: Vec, + bin_size: f32, +} + +impl std::fmt::Debug for CDF { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("CDF") + .field("data", &self.to_string()) + .field("bin_size", &self.bin_size) + .field("len", &self.data.len()) + .finish() + } +} + +impl std::fmt::Display for CDF { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let mut last_value = 0; + write!(f, "CDF[")?; + for (i, &value) in self.data.iter().enumerate() { + let value_f32 = (value as f32 / 65535.0).min(1.0); + if value != last_value { + if last_value != 0 { + write!(f, ", ")?; + } + let x = i as f32 * self.bin_size; + write!(f, "({:.4}, {:.4})", x, value_f32)?; + last_value = value; + } + } + write!(f, "]")?; + Ok(()) + } +} + +pub struct CDFIterator<'a> { + cdf: &'a CDF, + index: usize, + last_value: u16, + first: bool, + last: bool, +} + +impl<'a> Iterator for CDFIterator<'a> { + type Item = (f32, f32); + + fn next(&mut self) -> Option { + if self.first { + self.first = false; + return Some((0.0, 0.0)); + } + while self.index < self.cdf.data.len() { + let value = self.cdf.data[self.index]; + let value_f32 = (value as f32 / 65535.0).min(1.0); + if value != self.last_value { + let x = self.index as f32 * self.cdf.bin_size; + self.last_value = value; + self.index += 1; + return Some((x, value_f32)); + } + self.index += 1; + } + if !self.last { + self.last = true; + Some(( + self.cdf.width(), + (self.last_value as f32 / 65535.0).min(1.0), + )) + } else { + None + } + } +} + +impl CDF { + /// Create a new CDF from a vector of data and a bin size. + /// The data vector must contain values between 0 and 1, and must be + /// monotonically increasing. + pub fn new(data: &[f32], bin_size: f32) -> Result { + if !data.iter().all(|&x| x >= 0.0 && x <= 1.0) { + return Err(CDFError::InvalidDataRange); + } + if !data.windows(2).all(|w| w[0] <= w[1]) { + return Err(CDFError::NonMonotonicData); + } + let converted_data: Vec = data.iter().map(|&x| (x * 65535.0) as u16).collect(); + Ok(Self { + data: converted_data, + bin_size, + }) + } + + pub fn iter(&self) -> CDFIterator { + CDFIterator { + cdf: self, + index: 0, + last_value: 0, + first: true, + last: false, + } + } + + /// Get the width of the CDF. + pub fn width(&self) -> f32 { + self.data.len() as f32 * self.bin_size + } + + /// Create a step function CDF from a vector of (x, y) pairs. + /// The x values must be greater than 0 and must be strictly monotonically increasing. + /// The y values must be from (0, 1] and must be strictly monotonically increasing. + pub fn step(points: &[(f32, f32)], bin_size: f32, bins: usize) -> Result { + if !points.iter().all(|&(x, y)| x >= 0.0 && y > 0.0 && y <= 1.0) { + return Err(CDFError::InvalidDataRange); + } + if !points + .windows(2) + .all(|w| w[0].0 < w[1].0 && w[0].1 < w[1].1) + { + return Err(CDFError::NonMonotonicData); + } + let mut data = vec![0u16; bins]; + for &(x, y) in points { + let index = (x / bin_size).floor() as usize; + data[index] = to_int(y); + } + for i in 1..data.len() { + if data[i] == 0 { + data[i] = data[i - 1]; + } + } + Ok(Self { data, bin_size }) + } + + /// Combine two CDFs by choosing between them, using the given fraction as the probability for + /// the first CDF. + pub fn choice(&self, fraction: f32, other: &CDF) -> Result { + if self.bin_size != other.bin_size { + return Err(CDFError::BinSizeMismatch); + } + if self.data.len() != other.data.len() { + return Err(CDFError::LengthMismatch); + } + if fraction < 0.0 || fraction > 1.0 { + return Err(CDFError::InvalidFraction); + } + let my_fraction = to_int(fraction); + let fraction = 65535 - my_fraction; + let combined_data: Vec = self + .data + .iter() + .zip(&other.data) + .map(|(&x, &y)| { + mul(x, my_fraction) + .checked_add(mul(y, fraction)) + .expect("addition overflow") + }) + .collect(); + Ok(CDF { + data: combined_data, + bin_size: self.bin_size, + }) + } + + /// Combine two CDFs by universal quantification, meaning that both outcomes must occur. + pub fn for_all(&self, other: &CDF) -> Result { + if self.bin_size != other.bin_size { + return Err(CDFError::BinSizeMismatch); + } + if self.data.len() != other.data.len() { + return Err(CDFError::LengthMismatch); + } + let multiplied_data: Vec = self + .data + .iter() + .zip(&other.data) + .map(|(&x, &y)| mul(x, y)) + .collect(); + Ok(CDF { + data: multiplied_data, + bin_size: self.bin_size, + }) + } + + /// Combine two CDFs by existential quantification, meaning that at least one of the outcomes + pub fn for_some(&self, other: &CDF) -> Result { + if self.bin_size != other.bin_size { + return Err(CDFError::BinSizeMismatch); + } + if self.data.len() != other.data.len() { + return Err(CDFError::LengthMismatch); + } + let multiplied_data: Vec = self + .data + .iter() + .zip(&other.data) + .map(|(&x, &y)| { + u16::try_from( + (x as u32 + y as u32) + .checked_sub(mul(x, y) as u32) + .expect("subtraction underflow during for_some"), + ) + .expect("overflow during for_some") + }) + .collect(); + Ok(CDF { + data: multiplied_data, + bin_size: self.bin_size, + }) + } + + /// Convolve two CDFs, which is equivalent to taking the sum of all possible outcomes of the + /// two CDFs. This describes the distribution of the sum of two independent random variables. + pub fn convolve(&self, other: &CDF) -> Result { + if self.bin_size != other.bin_size { + return Err(CDFError::BinSizeMismatch); + } + if self.data.len() != other.data.len() { + return Err(CDFError::LengthMismatch); + } + let len = self.data.len(); + let mut convolved_data: Vec = vec![0; len]; + for i in 0..len { + for j in 0..len - i { + let other = if j == 0 { + other.data[j] + } else { + other.data[j] - other.data[j - 1] + }; + convolved_data[i + j] += mul(self.data[i], other); + } + } + Ok(CDF { + data: convolved_data, + bin_size: self.bin_size, + }) + } +} + +impl PartialOrd for CDF { + fn partial_cmp(&self, other: &Self) -> Option { + if self.bin_size != other.bin_size { + return None; + } + let mut ret = None; + for (l, r) in self.data.iter().zip(&other.data) { + if l < r { + if ret == Some(Ordering::Greater) { + return None; + } + ret = Some(Ordering::Less); + } else if l > r { + if ret == Some(Ordering::Less) { + return None; + } + ret = Some(Ordering::Greater); + } + } + ret.or(Some(Ordering::Equal)) + } +} + +fn mul(x: u16, y: u16) -> u16 { + ((x as u32 * y as u32 + 65535) >> 16) as u16 +} + +fn to_int(x: f32) -> u16 { + (x * 65536.0 + 0.5).min(65535.0) as u16 +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_new() { + let cdf = CDF::new(&[0.0, 0.25, 0.5, 0.75, 1.0], 0.25).unwrap(); + assert_eq!(cdf.data, vec![0, 16383, 32767, 49151, 65535]); + assert_eq!(cdf.bin_size, 0.25); + + let cdf = CDF::new(&[0.0, 0.25, 0.5, 0.75, 1.1], 0.25); + assert_eq!(cdf, Err(CDFError::InvalidDataRange)); + + let cdf = CDF::new(&[0.0, 0.25, 0.5, 0.75, 0.5], 0.25); + assert_eq!(cdf, Err(CDFError::NonMonotonicData)); + } + + #[test] + fn test_choice() { + let left = CDF::new(&[0.0, 0.0, 0.5, 1.0, 1.0], 0.25).unwrap(); + let right = CDF::new(&[0.0, 1.0, 1.0, 1.0, 1.0], 0.25).unwrap(); + let added = left.choice(0.7, &right).unwrap(); + assert_eq!(added, CDF::new(&[0.0, 0.3, 0.65, 1.0, 1.0], 0.25).unwrap()); + let added = left.choice(1.0, &right).unwrap(); + assert_eq!(added, CDF::new(&[0.0, 0.0, 0.5, 1.0, 1.0], 0.25).unwrap()); + } + + #[test] + fn test_convolve_step() { + let left = CDF::new(&[0.0, 1.0, 1.0, 1.0, 1.0], 1.0).unwrap(); + let right = CDF::new(&[0.0, 0.0, 1.0, 1.0, 1.0], 1.0).unwrap(); + let convolved = left.convolve(&right).unwrap(); + assert_eq!( + convolved, + CDF::new(&[0.0, 0.0, 0.0, 1.0, 1.0], 1.0).unwrap() + ); + } + + #[test] + fn test_convolve_two() { + let left = CDF::new(&[0.0, 0.3, 0.3, 1.0, 1.0, 1.0, 1.0], 1.0).unwrap(); + let right = CDF::new(&[0.0, 0.0, 0.6, 1.0, 1.0, 1.0, 1.0], 1.0).unwrap(); + let convolved = left.convolve(&right).unwrap(); + assert_eq!( + convolved, + CDF::new(&[0.0, 0.0, 0.0, 0.18, 0.3, 0.72, 1.0], 1.0).unwrap() + ); + } + + #[test] + fn test_for_all() { + let left = CDF::new(&[0.0, 0.5, 0.75, 1.0], 0.25).unwrap(); + let right = CDF::new(&[0.0, 0.25, 0.5, 1.0], 0.25).unwrap(); + let result = left.for_all(&right).unwrap(); + assert_eq!(result, CDF::new(&[0.0, 0.12501, 0.375, 1.0], 0.25).unwrap()); + } + + #[test] + fn test_for_some() { + let left = CDF::new(&[0.0, 0.5, 0.75, 1.0], 0.25).unwrap(); + let right = CDF::new(&[0.0, 0.25, 0.5, 1.0], 0.25).unwrap(); + let result = left.for_some(&right).unwrap(); + assert_eq!(result, CDF::new(&[0.0, 0.62499, 0.875, 1.0], 0.25).unwrap()); + } + + #[test] + fn partial_ord() { + let left = CDF::new(&[0.0, 0.3, 0.3, 1.0], 1.0).unwrap(); + let right = CDF::new(&[0.0, 0.0, 0.6, 1.0], 1.0).unwrap(); + let top = CDF::new(&[0.0, 0.3, 0.6, 1.0], 1.0).unwrap(); + let bottom = CDF::new(&[0.0, 0.0, 0.3, 1.0], 1.0).unwrap(); + assert_ne!(left, right); + assert!(!(left < right)); + assert!(!(right > left)); + assert!(!(left <= right)); + assert!(!(right >= left)); + assert!(left < top); + assert!(top > left); + assert!(left <= top); + assert!(top >= left); + assert!(right < top); + assert!(top > right); + assert!(right <= top); + assert!(top >= right); + assert!(left > bottom); + assert!(bottom < left); + assert!(left >= bottom); + assert!(bottom <= left); + assert!(right > bottom); + assert!(bottom < right); + assert!(right >= bottom); + assert!(bottom <= right); + } +} diff --git a/delta_q/src/delta_q.rs b/delta_q/src/delta_q.rs new file mode 100644 index 0000000..3823c40 --- /dev/null +++ b/delta_q/src/delta_q.rs @@ -0,0 +1,457 @@ +use crate::{CDFError, CDF}; +use std::{ + collections::{BTreeMap, BTreeSet}, + fmt::{self, Display}, +}; + +#[derive(Debug, PartialEq)] +pub enum DeltaQError { + CDFError(CDFError), + NameError(String), + BlackBox, +} + +impl std::error::Error for DeltaQError {} + +impl Display for DeltaQError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + DeltaQError::CDFError(e) => write!(f, "CDF error: {}", e), + DeltaQError::NameError(name) => write!(f, "Name error: {}", name), + DeltaQError::BlackBox => write!(f, "Black box encountered"), + } + } +} + +impl From for DeltaQError { + fn from(e: CDFError) -> DeltaQError { + DeltaQError::CDFError(e) + } +} + +#[derive(Debug, Clone, PartialEq, Default, serde::Serialize, serde::Deserialize)] +#[serde(from = "BTreeMap", into = "BTreeMap")] +pub struct EvaluationContext { + ctx: BTreeMap)>, + deps: BTreeMap>, +} + +impl EvaluationContext { + pub fn put(&mut self, name: String, delta_q: DeltaQ) { + // first remove all computed values that depend on this name + let mut to_remove = vec![name.clone()]; + while let Some(name) = to_remove.pop() { + if self.ctx.get_mut(&name).and_then(|x| x.1.take()).is_some() { + tracing::info!("Removing computed value for {}", name); + for (k, v) in self.deps.iter() { + if v.contains(&name) { + to_remove.push(k.clone()); + } + } + } + } + self.deps.insert(name.clone(), delta_q.deps()); + self.ctx.insert(name, (delta_q, None)); + } + + pub fn remove(&mut self, name: &str) -> Option { + // first remove all computed values that depend on this name + let mut to_remove = vec![name.to_owned()]; + while let Some(name) = to_remove.pop() { + if self.ctx.get_mut(&name).and_then(|x| x.1.take()).is_some() { + tracing::info!("Removing computed value for {}", name); + for (k, v) in self.deps.iter() { + if v.contains(&name) { + to_remove.push(k.clone()); + } + } + } + } + self.deps.remove(name); + self.ctx.remove(name).map(|(dq, _)| dq) + } + + pub fn get(&self, name: &str) -> Option<&DeltaQ> { + self.ctx.get(name).map(|(dq, _)| dq) + } + + pub fn eval(&mut self, name: &str) -> Result { + DeltaQ::name(name).eval(self) + } + + pub fn iter(&self) -> impl Iterator { + self.ctx.iter().map(|(k, (v, _))| (k, v)) + } +} + +impl From> for EvaluationContext { + fn from(value: BTreeMap) -> Self { + let deps = value.iter().map(|(k, v)| (k.clone(), v.deps())).collect(); + Self { + ctx: value.into_iter().map(|(k, v)| (k, (v, None))).collect(), + deps, + } + } +} + +impl Into> for EvaluationContext { + fn into(self) -> BTreeMap { + self.ctx.into_iter().map(|(k, (v, _))| (k, v)).collect() + } +} + +/// A DeltaQ is a representation of a probability distribution that can be +/// manipulated in various ways. +/// +/// The Display implementation prints out the expression using the syntax from the paper: +/// - Names are printed as-is. +/// - CDFs are printed as-is. +/// - Sequences are printed as `A •->-• B`. +/// - Choices are printed as `A a⇌b B`. +/// - Universal quantifications are printed as `∀(A|B)`. +/// - Existential quantifications are printed as `∃(A|B)`. +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub enum DeltaQ { + /// Un unelaborated and unknown DeltaQ. + BlackBox, + /// A named DeltaQ that can be referenced elsewhere. + Name(String), + /// A CDF that is used as a DeltaQ. + CDF(CDF), + /// The convolution of two DeltaQs, describing the sequential execution of two outcomes. + Seq(Box, Box), + /// A choice between two DeltaQs (i.e. their outcomes), with a given weight of each. + Choice(Box, f32, Box, f32), + /// A DeltaQ that is the result of a universal quantification over two DeltaQs, + /// meaning that both outcomes must occur. + ForAll(Box, Box), + /// A DeltaQ that is the result of an existential quantification over two DeltaQs, + /// meaning that at least one of the outcomes must occur. + ForSome(Box, Box), +} + +impl Display for DeltaQ { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.display(f, false) + } +} + +impl DeltaQ { + /// Create a new DeltaQ from a name, referencing a variable. + pub fn name(name: &str) -> DeltaQ { + DeltaQ::Name(name.to_string()) + } + + /// Create a new DeltaQ from a CDF. + pub fn cdf(cdf: CDF) -> DeltaQ { + DeltaQ::CDF(cdf) + } + + /// Create a new DeltaQ from the convolution of two DeltaQs. + pub fn seq(first: DeltaQ, second: DeltaQ) -> DeltaQ { + DeltaQ::Seq(Box::new(first), Box::new(second)) + } + + /// Create a new DeltaQ from a choice between two DeltaQs. + pub fn choice(first: DeltaQ, first_weight: f32, second: DeltaQ, second_weight: f32) -> DeltaQ { + DeltaQ::Choice( + Box::new(first), + first_weight, + Box::new(second), + second_weight, + ) + } + + /// Create a new DeltaQ from a universal quantification over two DeltaQs. + pub fn for_all(first: DeltaQ, second: DeltaQ) -> DeltaQ { + DeltaQ::ForAll(Box::new(first), Box::new(second)) + } + + /// Create a new DeltaQ from an existential quantification over two DeltaQs. + pub fn for_some(first: DeltaQ, second: DeltaQ) -> DeltaQ { + DeltaQ::ForSome(Box::new(first), Box::new(second)) + } + + pub fn deps(&self) -> BTreeSet { + match self { + DeltaQ::BlackBox => BTreeSet::new(), + DeltaQ::Name(name) => { + let mut deps = BTreeSet::new(); + deps.insert(name.clone()); + deps + } + DeltaQ::CDF(_) => BTreeSet::new(), + DeltaQ::Seq(first, second) => { + let mut deps = first.deps(); + deps.extend(second.deps()); + deps + } + DeltaQ::Choice(first, _, second, _) => { + let mut deps = first.deps(); + deps.extend(second.deps()); + deps + } + DeltaQ::ForAll(first, second) => { + let mut deps = first.deps(); + deps.extend(second.deps()); + deps + } + DeltaQ::ForSome(first, second) => { + let mut deps = first.deps(); + deps.extend(second.deps()); + deps + } + } + } + + fn display(&self, f: &mut fmt::Formatter<'_>, parens: bool) -> fmt::Result { + match self { + DeltaQ::BlackBox => { + write!(f, "■") + } + DeltaQ::Name(name) => { + write!(f, "{}", name) + } + DeltaQ::CDF(cdf) => { + write!(f, "{:?}", cdf) + } + DeltaQ::Seq(first, second) => { + if parens { + write!(f, "(")?; + } + first.display(f, true)?; + write!(f, " •->-• ")?; + second.display(f, true)?; + if parens { + write!(f, ")")?; + } + Ok(()) + } + DeltaQ::Choice(first, first_weight, second, second_weight) => { + if parens { + write!(f, "(")?; + } + first.display(f, true)?; + write!(f, " {}⇌{} ", first_weight, second_weight)?; + second.display(f, true)?; + if parens { + write!(f, ")")?; + } + Ok(()) + } + DeltaQ::ForAll(first, second) => { + write!(f, "∀({} | {})", first, second) + } + DeltaQ::ForSome(first, second) => { + write!(f, "∃({} | {})", first, second) + } + } + } + + pub fn eval(&self, ctx: &mut EvaluationContext) -> Result { + match self { + DeltaQ::BlackBox => Err(DeltaQError::BlackBox), + DeltaQ::Name(n) => { + if let Some((_, Some(cdf))) = ctx.ctx.get(n) { + Ok(cdf.clone()) + } else if let Some((dq, _)) = ctx.ctx.remove(n) { + match dq.eval(ctx) { + Ok(cdf) => { + ctx.ctx.insert(n.to_owned(), (dq, Some(cdf.clone()))); + Ok(cdf) + } + Err(e) => { + ctx.ctx.insert(n.to_owned(), (dq, None)); + Err(e) + } + } + } else { + Err(DeltaQError::NameError(n.to_owned())) + } + } + DeltaQ::CDF(cdf) => Ok(cdf.clone()), + DeltaQ::Seq(first, second) => { + let first_cdf = first.eval(ctx)?; + let second_cdf = second.eval(ctx)?; + first_cdf + .convolve(&second_cdf) + .map_err(DeltaQError::CDFError) + } + DeltaQ::Choice(first, first_fraction, second, second_fraction) => { + let first_cdf = first.eval(ctx)?; + let second_cdf = second.eval(ctx)?; + first_cdf + .choice( + *first_fraction / (*first_fraction + *second_fraction), + &second_cdf, + ) + .map_err(DeltaQError::CDFError) + } + DeltaQ::ForAll(first, second) => { + let first_cdf = first.eval(ctx)?; + let second_cdf = second.eval(ctx)?; + first_cdf + .for_all(&second_cdf) + .map_err(DeltaQError::CDFError) + } + DeltaQ::ForSome(first, second) => { + let first_cdf = first.eval(ctx)?; + let second_cdf = second.eval(ctx)?; + first_cdf + .for_some(&second_cdf) + .map_err(DeltaQError::CDFError) + } + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use maplit::btreemap; + + #[test] + fn test_display_name() { + let dq = DeltaQ::name("A"); + assert_eq!(dq.to_string(), "A"); + } + + #[test] + fn test_display_cdf() { + let cdf = CDF::new(&[0.0, 0.2, 0.9], 1.0).unwrap(); + let dq = DeltaQ::cdf(cdf.clone()); + assert_eq!(dq.to_string(), format!("{:?}", cdf)); + } + + #[test] + fn test_display_seq() { + let dq1 = DeltaQ::name("A"); + let dq2 = DeltaQ::name("B"); + let seq = DeltaQ::seq(dq1, dq2); + assert_eq!(seq.to_string(), "A •->-• B"); + } + + #[test] + fn test_display_choice() { + let dq1 = DeltaQ::name("A"); + let dq2 = DeltaQ::name("B"); + let choice = DeltaQ::choice(dq1, 0.3, dq2, 0.7); + assert_eq!(choice.to_string(), "A 0.3⇌0.7 B"); + } + + #[test] + fn test_display_for_all() { + let dq1 = DeltaQ::name("A"); + let dq2 = DeltaQ::name("B"); + let for_all = DeltaQ::for_all(dq1, dq2); + assert_eq!(for_all.to_string(), "∀(A | B)"); + } + + #[test] + fn test_display_for_some() { + let dq1 = DeltaQ::name("A"); + let dq2 = DeltaQ::name("B"); + let for_some = DeltaQ::for_some(dq1, dq2); + assert_eq!(for_some.to_string(), "∃(A | B)"); + } + + #[test] + fn test_display_nested_seq() { + let dq1 = DeltaQ::name("A"); + let dq2 = DeltaQ::name("B"); + let dq3 = DeltaQ::name("C"); + let nested_seq = DeltaQ::seq(DeltaQ::seq(dq1, dq2), dq3); + assert_eq!(nested_seq.to_string(), "(A •->-• B) •->-• C"); + } + + #[test] + fn test_display_nested_choice() { + let dq1 = DeltaQ::name("A"); + let dq2 = DeltaQ::name("B"); + let dq3 = DeltaQ::name("C"); + let nested_choice = DeltaQ::choice(DeltaQ::choice(dq1, 0.3, dq2, 0.7), 0.5, dq3, 0.5); + assert_eq!(nested_choice.to_string(), "(A 0.3⇌0.7 B) 0.5⇌0.5 C"); + } + + #[test] + fn test_display_nested_for_all() { + let dq1 = DeltaQ::name("A"); + let dq2 = DeltaQ::name("B"); + let dq3 = DeltaQ::name("C"); + let dq4 = DeltaQ::name("D"); + let nested_for_all = DeltaQ::for_all(DeltaQ::for_all(dq1, dq2), DeltaQ::seq(dq3, dq4)); + assert_eq!(nested_for_all.to_string(), "∀(∀(A | B) | C •->-• D)"); + } + + #[test] + fn test_display_nested_for_some() { + let dq1 = DeltaQ::name("A"); + let dq2 = DeltaQ::name("B"); + let dq3 = DeltaQ::name("C"); + let dq4 = DeltaQ::name("D"); + let nested_for_some = DeltaQ::for_some( + DeltaQ::for_some(dq1, dq2), + DeltaQ::choice(dq3, 1.0, dq4, 2.0), + ); + assert_eq!(nested_for_some.to_string(), "∃(∃(A | B) | C 1⇌2 D)"); + } + + #[test] + fn test_scenario_from_paper_64k() { + let ctx = btreemap! { + "single".to_owned() => + DeltaQ::cdf(CDF::step( + &[(0.024, 1.0 / 3.0), (0.143, 2.0 / 3.0), (0.531, 1.0)], + 0.01, + 300, + ) + .unwrap()), + "model2".to_owned() => + DeltaQ::choice( + DeltaQ::name("single"), + 1.0, + DeltaQ::seq(DeltaQ::name("single"), DeltaQ::name("single")), + 100.0, + ), + "model3".to_owned() => + DeltaQ::choice( + DeltaQ::name("single"), + 1.0, + DeltaQ::seq(DeltaQ::name("single"), DeltaQ::name("model2")), + 100.0, + ), + "model4".to_owned() => + DeltaQ::choice( + DeltaQ::name("single"), + 1.0, + DeltaQ::seq(DeltaQ::name("single"), DeltaQ::name("model3")), + 100.0, + ), + "model5".to_owned() => + DeltaQ::choice( + DeltaQ::name("single"), + 1.0, + DeltaQ::seq(DeltaQ::name("single"), DeltaQ::name("model4")), + 100.0, + ), + }; + let result = DeltaQ::name("model5").eval(&mut ctx.into()).unwrap(); + assert_eq!(result.to_string(), "CDF[(0.0200, 0.0033), (0.0400, 0.0044), (0.0600, 0.0048), (0.0800, 0.0049), (0.1000, 0.0089), (0.1400, 0.0122), (0.1600, 0.0144), (0.1800, 0.0155), (0.2000, 0.0159), (0.2200, 0.0357), (0.2800, 0.0368), (0.3000, 0.0379), (0.3200, 0.0386), (0.3400, 0.0782), (0.4200, 0.0786), (0.4400, 0.0791), (0.4600, 0.1187), (0.5300, 0.1220), (0.5500, 0.1241), (0.5600, 0.1242), (0.5700, 0.1253), (0.5800, 0.1451), (0.5900, 0.1456), (0.6100, 0.1654), (0.6700, 0.1676), (0.6900, 0.1697), (0.7000, 0.1737), (0.7100, 0.1751), (0.7300, 0.2542), (0.8100, 0.2553), (0.8300, 0.2567), (0.8500, 0.3753), (0.9500, 0.3758), (0.9700, 0.4549), (1.0600, 0.4560), (1.0800, 0.4570), (1.0900, 0.4768), (1.1000, 0.4775), (1.1200, 0.5171), (1.2000, 0.5181), (1.2200, 0.5195), (1.2400, 0.6381), (1.3400, 0.6388), (1.3600, 0.7575), (1.4800, 0.7970), (1.5900, 0.7974), (1.6100, 0.7978), (1.6300, 0.8374), (1.7300, 0.8378), (1.7500, 0.9169), (1.8700, 0.9564), (2.1200, 0.9565), (2.1400, 0.9763), (2.2600, 0.9960), (2.6500, 1.0000)]"); + } + + #[test] + fn test_recursive_deltaq() { + let ctx = btreemap! { + "recursive".to_owned() => + DeltaQ::choice( + DeltaQ::name("base"), + 1.0, + DeltaQ::seq(DeltaQ::name("base"), DeltaQ::name("recursive")), + 1.0, + ), + "base".to_owned() => DeltaQ::cdf(CDF::new(&[0.0, 0.5, 1.0], 1.0).unwrap()), + }; + let result = DeltaQ::name("recursive").eval(&mut ctx.into()).unwrap_err(); + assert_eq!(result, DeltaQError::NameError("recursive".to_owned())); + } +} diff --git a/delta_q/src/lib.rs b/delta_q/src/lib.rs new file mode 100644 index 0000000..11f212e --- /dev/null +++ b/delta_q/src/lib.rs @@ -0,0 +1,17 @@ +#[allow(unused_macros)] +macro_rules! cloned { + ($($name:ident),*; $e:expr) => {{ + $(let $name = $name.clone();)* + $e + }}; +} + +mod cdf; +mod delta_q; +#[cfg(feature = "web")] +mod render; + +pub use cdf::{CDFError, CDF}; +pub use delta_q::{DeltaQ, EvaluationContext}; +#[cfg(feature = "web")] +pub use render::{cdf_to_svg, DeltaQComponent, DeltaQContext}; diff --git a/delta_q/src/render.rs b/delta_q/src/render.rs new file mode 100644 index 0000000..88d5e0b --- /dev/null +++ b/delta_q/src/render.rs @@ -0,0 +1,461 @@ +use crate::{delta_q::DeltaQ, EvaluationContext, CDF}; +use charts_rs::{Axis, Canvas, Color, Point, Polyline}; +use iter_tools::Itertools; +use std::{rc::Rc, sync::Arc}; +use web_sys::HtmlInputElement; +use yew::{prelude::*, virtual_dom::VNode}; + +/// A piece of context that tells the DeltaQ rendering components to which named expression they belong. +#[derive(Clone, PartialEq)] +pub struct DeltaQContext { + pub eval_ctx: Rc, + pub name: String, +} + +impl DeltaQContext { + pub fn new(eval_ctx: &EvaluationContext, name: &str) -> Self { + Self { + eval_ctx: Rc::new(eval_ctx.clone()), + name: name.to_owned(), + } + } +} + +#[derive(Properties, Clone, PartialEq, Debug)] +pub struct Props { + pub delta_q: DeltaQ, + pub on_change: Callback<(String, Option)>, +} + +#[function_component(DeltaQComponent)] +pub fn delta_q_component(props: &Props) -> Html { + web_sys::console::log_1(&wasm_bindgen::JsValue::from_str(&format!("{props:?}"))); + let on_change = props.on_change.clone(); + match &props.delta_q { + DeltaQ::BlackBox => { + html! { } + } + DeltaQ::Name(name) => { + html! { } + } + DeltaQ::CDF(cdf) => { + html! {
    { format!("{}", cdf) }
    } + } + DeltaQ::Seq(first, second) => { + html!() + } + DeltaQ::Choice(first, first_weight, second, second_weight) => { + html!() + } + DeltaQ::ForAll(first, second) => { + html!() + } + DeltaQ::ForSome(first, second) => { + html!() + } + } +} + +#[derive(Properties, Clone, PartialEq)] +pub struct BlackBoxProps { + pub on_change: Callback<(String, Option)>, +} + +#[function_component(BlackBox)] +pub fn black_box(props: &BlackBoxProps) -> Html { + let on_change = props.on_change.clone(); + let popup = use_state(|| false); + let ctx = use_context::().unwrap(); + + let name = use_state(|| "".to_string()); + let oninput = Callback::from(cloned!(name; move |e: InputEvent| { + name.set(e.target_unchecked_into::().value()); + })); + let onsubmit = Callback::from(cloned!(name, on_change, ctx; + move |e: SubmitEvent| { + e.prevent_default(); + on_change.emit((ctx.name.clone(), Some(DeltaQ::name(&name)))); + } + )); + + html! { +
    + if *popup { +
    + +
    + + +
    +
    + } +
    + } +} + +#[derive(Properties, Clone, PartialEq)] +pub struct NameProps { + pub name: String, + pub on_change: Callback<(String, Option)>, +} + +#[function_component(NameComponent)] +pub fn name_component(props: &NameProps) -> Html { + let on_change = props.on_change.clone(); + let popup = use_state(|| false); + let name = props.name.clone(); + let ctx = use_context::().unwrap(); + + let new_name = use_state(|| props.name.clone()); + let oninput = Callback::from(cloned!(new_name; move |e: InputEvent| { + new_name.set(e.target_unchecked_into::().value()); + })); + let onsubmit = Callback::from(cloned!(new_name, on_change, ctx, popup; + move |e: SubmitEvent| { + e.prevent_default(); + popup.set(false); + on_change.emit((ctx.name.clone(), Some(DeltaQ::Name((*new_name).clone())))); + } + )); + + html! { +
    + { &props.name } + if *popup { +
    + +
    + + +
    + + + + + + +
    + } +
    + } +} + +#[derive(Properties, Clone, PartialEq)] +pub struct SeqProps { + pub first: DeltaQ, + pub second: DeltaQ, + pub on_change: Callback<(String, Option)>, +} + +#[function_component(Seq)] +pub fn seq(props: &SeqProps) -> Html { + let on_change = props.on_change.clone(); + let first = props.first.clone(); + let second = props.second.clone(); + let ctx = use_context::().unwrap(); + + let on_first_change = Callback::from(cloned!(second, on_change, ctx; + move |(name, delta_q)| { + // if the name matches our context, edit the DeltaQ; otherwise just bubble up + if name != ctx.name { + on_change.emit((name, delta_q)); + } else if let Some(delta_q) = delta_q { + on_change.emit((name, Some(DeltaQ::seq(delta_q, second.clone())))); + } + } + )); + + let on_second_change = Callback::from(cloned!(first, on_change, ctx; + move |(name, delta_q)| { + // if the name matches our context, edit the DeltaQ; otherwise just bubble up + if name != ctx.name { + on_change.emit((name, delta_q)); + } else if let Some(delta_q) = delta_q { + on_change.emit((name, Some(DeltaQ::seq(first.clone(), delta_q)))); + } + } + )); + + let popup = use_state(|| false); + let name = use_state(|| "".to_string()); + let oninput = Callback::from(cloned!(name; move |e: InputEvent| { + name.set(e.target_unchecked_into::().value()); + })); + let onsubmit = Callback::from(cloned!(name, on_change, ctx, first, second; + move |e: SubmitEvent| { + e.prevent_default(); + on_change.emit((ctx.name.clone(), Some(DeltaQ::name(&name)))); + on_change.emit(((*name).clone(), Some(DeltaQ::seq(first.clone(), second.clone())))); + } + )); + + html! { +
    + +
    + if *popup { +
    + + + + + + + + +
    + + +
    +
    + } +
    + +
    + } +} + +#[derive(Properties, Clone, PartialEq)] +pub struct BranchProps { + pub top: DeltaQ, + pub bottom: DeltaQ, + pub on_change: Callback<(String, Option)>, + pub kind: BranchKind, +} + +#[derive(Clone, Copy, PartialEq)] +pub enum BranchKind { + Choice(f32, f32), + ForAll, + ForSome, +} + +impl BranchKind { + pub fn choice_frac(&self) -> (f32, f32) { + match self { + BranchKind::Choice(l, r) => (*l, *r), + _ => (1.0, 1.0), + } + } +} + +#[function_component(BranchKindComponent)] +pub fn branch_kind_component(props: &BranchProps) -> Html { + let kind = match &props.kind { + BranchKind::Choice(first_weight, second_weight) => html! { +
    +
    {first_weight}
    +
    {"⇌"}
    +
    {second_weight}
    +
    + }, + BranchKind::ForAll => html! {
    { "∀" }
    }, + BranchKind::ForSome => html! {
    { "∃" }
    }, + }; + + let popup = use_state(|| false); + let on_change = props.on_change.clone(); + let top = props.top.clone(); + let bottom = props.bottom.clone(); + let ctx = use_context::().unwrap(); + + let top_frac = use_state(|| props.kind.choice_frac().0); + let bottom_frac = use_state(|| props.kind.choice_frac().1); + let top_input = Callback::from(cloned!(top_frac; + move |e: InputEvent| top_frac.set(e.target_unchecked_into::().value_as_number() as f32))); + let bottom_input = Callback::from(cloned!(bottom_frac; + move |e: InputEvent| bottom_frac.set(e.target_unchecked_into::().value_as_number() as f32))); + let frac_submit = Callback::from( + cloned!(popup, on_change, top, bottom, top_frac, bottom_frac, ctx; + move |e: SubmitEvent| { + e.prevent_default(); + popup.set(false); + on_change.emit((ctx.name.clone(), Some(DeltaQ::choice(top.clone(), *top_frac, bottom.clone(), *bottom_frac)))); + }), + ); + + let abstract_name = use_state(|| "".to_string()); + let abstract_input = Callback::from(cloned!(abstract_name; + move |e: InputEvent| abstract_name.set(e.target_unchecked_into::().value()))); + let abstract_submit = Callback::from( + cloned!(abstract_name, on_change, ctx, bottom, bottom_frac, top, top_frac; + move |e: SubmitEvent| { + e.prevent_default(); + on_change.emit((ctx.name.clone(), Some(DeltaQ::name(&abstract_name)))); + on_change.emit(((*abstract_name).clone(), Some(DeltaQ::choice(top.clone(), *top_frac, bottom.clone(), *bottom_frac)))); + } + ), + ); + + html!( +
    + { kind } + if *popup { +
    + +
    + + + +
    + + + + + + +
    + + +
    +
    + } +
    ) +} + +/// A component that renders a branch of a DeltaQ tree. +/// +/// The HTML representation consists of two DIV, with the left showing the branch kind and the right showing the branch content. +/// The branch content is rendered in two DIV, the top branch at the top and the bottom branch at the bottom. +/// There is a border between the two branches and to the right of the branch kind. +#[function_component(Branch)] +fn branch(props: &BranchProps) -> Html { + let on_change = props.on_change.clone(); + let top = props.top.clone(); + let bottom = props.bottom.clone(); + let kind = props.kind; + let constructor: Arc, Box) -> DeltaQ> = match kind { + BranchKind::Choice(l, r) => Arc::new(move |dql, dqr| DeltaQ::Choice(dql, l, dqr, r)), + BranchKind::ForAll => Arc::new(DeltaQ::ForAll), + BranchKind::ForSome => Arc::new(DeltaQ::ForSome), + }; + let ctx = use_context::().unwrap(); + + let on_top_change = Callback::from(cloned!(bottom, on_change, constructor, ctx; + move |(name, delta_q)| { + // if the name matches our context, edit the DeltaQ; otherwise just bubble up + if name != ctx.name { + on_change.emit((name, delta_q)); + } else if let Some(delta_q) = delta_q { + on_change.emit((name, Some(constructor(Box::new(delta_q), Box::new(bottom.clone()))))); + } + } + )); + + let on_bottom_change = Callback::from(cloned!(top, on_change, ctx; + move |(name, delta_q)| { + // if the name matches our context, edit the DeltaQ; otherwise just bubble up + if name != ctx.name { + on_change.emit((name, delta_q)); + } else if let Some(delta_q) = delta_q { + on_change.emit((name, Some(constructor(Box::new(top.clone()), Box::new(delta_q))))); + } + } + )); + + html! { +
    + +
    +
    + +
    +
    +
    + +
    +
    +
    + } +} + +pub fn cdf_to_svg(cdf: &CDF) -> Html { + let mut canvas = Canvas::new(310.0, 110.0); + let x_scale = 300.0 / cdf.width(); + canvas.polyline(Polyline { + color: Some(Color::black()), + stroke_width: 1.0, + points: cdf + .iter() + .tuple_windows() + .flat_map(|((x, y), (x2, _))| { + vec![ + Point { + x: x * x_scale + 10.0, + y: (1.0 - y) * 100.0 + 1.0, + }, + Point { + x: x2 * x_scale + 10.0, + y: (1.0 - y) * 100.0 + 1.0, + }, + ] + }) + .collect(), + }); + canvas.axis(Axis { + stroke_color: Some(Color::black()), + left: 10.0, + top: 101.0, + width: 300.0, + split_number: 300, + tick_interval: x_scale as usize, + ..Default::default() + }); + canvas.axis(Axis { + stroke_color: Some(Color::black()), + position: charts_rs::Position::Left, + top: 1.0, + left: 10.0, + height: 100.0, + split_number: 1, + ..Default::default() + }); + let svg = VNode::from_html_unchecked(canvas.svg().unwrap().into()); + html! { + <> +

    { "result: " }{cdf.to_string()}

    + { svg } + + } +} + +impl Reducible for EvaluationContext { + type Action = (String, Option); + + fn reduce(self: Rc, (name, dq): Self::Action) -> Rc { + let mut ctx = (*self).clone(); + if let Some(dq) = dq { + ctx.put(name, dq); + } else { + ctx.remove(&name); + } + Rc::new(ctx) + } +} diff --git a/delta_q/styles.css b/delta_q/styles.css new file mode 100644 index 0000000..ff1348c --- /dev/null +++ b/delta_q/styles.css @@ -0,0 +1,25 @@ +.anchor { position: relative; } +.blackBox { background-color: black; border-radius: 50%; margin: 4px; padding: 16px; cursor: pointer; } +.cdf { border: 4px solid orange; margin: 4px; padding: 4px; } +.popup { + position: absolute; top: 0; left: 0; + background-color: white; border: 1px solid black; + padding: 4px; + display: flex; flex-direction: column; + white-space: nowrap; + z-index: 1; +} +.popup form { margin: 0px; } +.name { border: 4px solid orange; border-radius: 50%; margin: 4px; padding: 8px; cursor: pointer; } +.frame { margin: 4px; border: 1px solid grey; } +.seqSymbol { width: 10px; height: 10px; border: 2px solid black; margin: 4px; cursor: pointer; } +.branchKind { padding: 8px; cursor: pointer; } +.expression { background-color: rgb(206, 236, 254); padding: 4px; display: inline-block; margin: 4px; cursor: pointer; } + +.column { display: flex; flex-direction: column; } +.row { display: flex; flex-direction: row; } +.center { align-items: center; justify-content: center; } +.left { align-items: left; } + +input[type="number"] { width: 5em; } +input[type="text"] { width: 7em; }