diff --git a/Cargo.lock b/Cargo.lock index bbeef2e..4a73809 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,39 +8,15 @@ version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" -[[package]] -name = "aead" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" -dependencies = [ - "crypto-common 0.1.7", - "generic-array", -] - [[package]] name = "aes" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" -dependencies = [ - "cfg-if", - "cipher", - "cpufeatures 0.2.17", -] - -[[package]] -name = "aes-gcm" -version = "0.10.3" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "831010a0f742e1209b3bcea8fab6a8e149051ba6099432c8cb2cc117dec3ead1" +checksum = "66bd29a732b644c0431c6140f370d097879203d79b80c94a6747ba0872adaef8" dependencies = [ - "aead", - "aes", "cipher", - "ctr", - "ghash 0.5.1", - "subtle", + "cpubits", + "cpufeatures 0.3.0", ] [[package]] @@ -147,20 +123,20 @@ checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" [[package]] name = "block-buffer" -version = "0.10.4" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +checksum = "cdd35008169921d80bc60d3d0ab416eecb028c4cd653352907921d95084790be" dependencies = [ - "generic-array", + "hybrid-array", ] [[package]] name = "block-padding" -version = "0.3.3" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8894febbff9f758034a5b8e12d87918f56dfc64a8e1fe757d65e29041538d93" +checksum = "710f1dd022ef4e93f8a438b4ba958de7f64308434fa6a87104481645cc30068b" dependencies = [ - "generic-array", + "hybrid-array", ] [[package]] @@ -180,15 +156,21 @@ name = "bytes" version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" + +[[package]] +name = "castaway" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dec551ab6e7578819132c713a93c022a05d60159dc86e7a7050223577484c55a" dependencies = [ - "serde", + "rustversion", ] [[package]] name = "cbc" -version = "0.1.2" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26b52a9543ae338f279b96b0b9fed9c8093744685043739079ce85cd58f289a6" +checksum = "98db6aeaef0eeef2c1e3ce9a27b739218825dae116076352ac3777076aa22225" dependencies = [ "cipher", ] @@ -236,11 +218,12 @@ dependencies = [ [[package]] name = "cipher" -version = "0.4.4" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +checksum = "e34d8227fe1ba289043aeb13792056ff80fd6de1a9f49137a5f499de8e8c78ea" dependencies = [ - "crypto-common 0.1.7", + "block-buffer", + "crypto-common", "inout", ] @@ -251,41 +234,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "de0758edba32d61d1fd9f4d69491b47604b91ee2f7e6b33de7e54ca4ebe55dc3" [[package]] -name = "concurrent-queue" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" -dependencies = [ - "crossbeam-utils", -] - -[[package]] -name = "cookie" -version = "0.18.1" +name = "compact_str" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ddef33a339a91ea89fb53151bd0a4689cfce27055c291dfa69945475d22c747" +checksum = "3fdb1325a1cece981e8a296ab8f0f9b63ae357bd0784a9faaf548cc7b480707a" dependencies = [ - "percent-encoding", - "time", - "version_check", + "castaway", + "cfg-if", + "itoa", + "rustversion", + "ryu", + "serde", + "static_assertions", ] [[package]] -name = "cookie_store" -version = "0.22.1" +name = "concurrent-queue" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15b2c103cf610ec6cae3da84a766285b42fd16aad564758459e6ecf128c75206" +checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" dependencies = [ - "cookie", - "document-features", - "idna", - "indexmap", - "log", - "serde", - "serde_derive", - "serde_json", - "time", - "url", + "crossbeam-utils", ] [[package]] @@ -342,16 +311,6 @@ version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" -[[package]] -name = "crypto-common" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" -dependencies = [ - "generic-array", - "typenum", -] - [[package]] name = "crypto-common" version = "0.2.1" @@ -363,9 +322,9 @@ dependencies = [ [[package]] name = "ctr" -version = "0.9.2" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" +checksum = "17469f8eb9bdbfad10f71f4cfddfd38b01143520c0e717d8796ccb4d44d44e42" dependencies = [ "cipher", ] @@ -388,7 +347,6 @@ dependencies = [ "cfg-if", "cpufeatures 0.2.17", "curve25519-dalek-derive", - "digest", "fiat-crypto", "rustc_version", "subtle", @@ -520,13 +478,13 @@ dependencies = [ [[package]] name = "digest" -version = "0.10.7" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +checksum = "4850db49bf08e663084f7fb5c87d202ef91a3907271aff24a94eb97ff039153c" dependencies = [ "block-buffer", - "crypto-common 0.1.7", - "subtle", + "crypto-common", + "ctutils", ] [[package]] @@ -540,15 +498,6 @@ dependencies = [ "syn", ] -[[package]] -name = "document-features" -version = "0.2.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4b8a88685455ed29a21542a33abd9cb6510b6b129abadabdcef0f4c55bc8f61" -dependencies = [ - "litrs", -] - [[package]] name = "downcast-rs" version = "2.0.2" @@ -631,12 +580,6 @@ dependencies = [ "pin-project-lite", ] -[[package]] -name = "fastrand" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" - [[package]] name = "fiat-crypto" version = "0.2.9" @@ -649,12 +592,6 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" -[[package]] -name = "fixedbitset" -version = "0.5.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99" - [[package]] name = "flate2" version = "1.1.9" @@ -683,15 +620,6 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" -[[package]] -name = "form_urlencoded" -version = "1.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" -dependencies = [ - "percent-encoding", -] - [[package]] name = "futures" version = "0.3.32" @@ -768,16 +696,6 @@ dependencies = [ "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.17" @@ -803,23 +721,13 @@ dependencies = [ "wasip3", ] -[[package]] -name = "ghash" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0d8a4362ccb29cb0b265253fb0a2728f592895ee6854fd9bc13f2ffda266ff1" -dependencies = [ - "opaque-debug", - "polyval 0.6.2", -] - [[package]] name = "ghash" version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2eecf2d5dc9b66b732b97707a0210906b1d30523eb773193ab777c0c84b3e8d5" dependencies = [ - "polyval 0.7.1", + "polyval", ] [[package]] @@ -840,6 +748,18 @@ dependencies = [ "foldhash 0.2.0", ] +[[package]] +name = "hashify" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd1246c0e5493286aeb2dde35b1f4eb9c4ce00e628641210a5e553fc001a1f26" +dependencies = [ + "indexmap", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "heck" version = "0.5.0" @@ -854,18 +774,18 @@ checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" [[package]] name = "hkdf" -version = "0.12.4" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" +checksum = "4aaa26c720c68b866f2c96ef5c1264b3e6f473fe5d4ce61cd44bbe913e553018" dependencies = [ "hmac", ] [[package]] name = "hmac" -version = "0.12.1" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +checksum = "6303bc9732ae41b04cb554b844a762b4115a61bfaa81e3e83050991eeb56863f" dependencies = [ "digest", ] @@ -919,87 +839,6 @@ dependencies = [ "cc", ] -[[package]] -name = "icu_collections" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c6b649701667bbe825c3b7e6388cb521c23d88644678e83c0c4d0a621a34b43" -dependencies = [ - "displaydoc", - "potential_utf", - "yoke", - "zerofrom", - "zerovec", -] - -[[package]] -name = "icu_locale_core" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edba7861004dd3714265b4db54a3c390e880ab658fec5f7db895fae2046b5bb6" -dependencies = [ - "displaydoc", - "litemap", - "tinystr", - "writeable", - "zerovec", -] - -[[package]] -name = "icu_normalizer" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f6c8828b67bf8908d82127b2054ea1b4427ff0230ee9141c54251934ab1b599" -dependencies = [ - "icu_collections", - "icu_normalizer_data", - "icu_properties", - "icu_provider", - "smallvec", - "zerovec", -] - -[[package]] -name = "icu_normalizer_data" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a" - -[[package]] -name = "icu_properties" -version = "2.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "020bfc02fe870ec3a66d93e677ccca0562506e5872c650f893269e08615d74ec" -dependencies = [ - "icu_collections", - "icu_locale_core", - "icu_properties_data", - "icu_provider", - "zerotrie", - "zerovec", -] - -[[package]] -name = "icu_properties_data" -version = "2.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "616c294cf8d725c6afcd8f55abc17c56464ef6211f9ed59cccffe534129c77af" - -[[package]] -name = "icu_provider" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85962cf0ce02e1e0a629cc34e7ca3e373ce20dda4c4d7294bbd0bf1fdb59e614" -dependencies = [ - "displaydoc", - "icu_locale_core", - "writeable", - "yoke", - "zerofrom", - "zerotrie", - "zerovec", -] - [[package]] name = "id-arena" version = "2.3.0" @@ -1012,27 +851,6 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" -[[package]] -name = "idna" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" -dependencies = [ - "idna_adapter", - "smallvec", - "utf8_iter", -] - -[[package]] -name = "idna_adapter" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" -dependencies = [ - "icu_normalizer", - "icu_properties", -] - [[package]] name = "indexmap" version = "2.13.0" @@ -1047,12 +865,12 @@ dependencies = [ [[package]] name = "inout" -version = "0.1.4" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01" +checksum = "4250ce6452e92010fdf7268ccc5d14faa80bb12fc741938534c58f16804e03c7" dependencies = [ "block-padding", - "generic-array", + "hybrid-array", ] [[package]] @@ -1100,33 +918,15 @@ checksum = "b5b646652bf6661599e1da8901b3b9522896f01e736bad5f723fe7a3a27f899d" [[package]] name = "libsqlite3-sys" -version = "0.35.0" +version = "0.36.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "133c182a6a2c87864fe97778797e46c7e999672690dc9fa3ee8e241aa4a9c13f" +checksum = "95b4103cffefa72eb8428cb6b47d6627161e51c2739fc5e3b734584157bc642a" dependencies = [ "cc", "pkg-config", "vcpkg", ] -[[package]] -name = "linux-raw-sys" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" - -[[package]] -name = "litemap" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77" - -[[package]] -name = "litrs" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11d3d7f243d5c5a8b9bb5d6dd2b1602c0cb0b9db1621bafc7ed66e35ff9fe092" - [[package]] name = "lock_api" version = "0.4.14" @@ -1225,12 +1025,6 @@ dependencies = [ "uuid", ] -[[package]] -name = "multimap" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d87ecb2933e8aeadb3e3a02b828fed80a7528047e68b4f424523a0981a3a084" - [[package]] name = "nu-ansi-term" version = "0.50.3" @@ -1261,12 +1055,6 @@ version = "1.21.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" -[[package]] -name = "opaque-debug" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" - [[package]] name = "parking" version = "2.2.1" @@ -1296,71 +1084,12 @@ dependencies = [ "windows-link", ] -[[package]] -name = "pbkdf2" -version = "0.12.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2" -dependencies = [ - "digest", - "hmac", -] - [[package]] name = "percent-encoding" version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" -[[package]] -name = "petgraph" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8701b58ea97060d5e5b155d383a69952a60943f0e6dfe30b04c287beb0b27455" -dependencies = [ - "fixedbitset", - "hashbrown 0.15.5", - "indexmap", -] - -[[package]] -name = "phf" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1562dc717473dbaa4c1f85a36410e03c047b2e7df7f45ee938fbef64ae7fadf" -dependencies = [ - "phf_shared", -] - -[[package]] -name = "phf_codegen" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49aa7f9d80421bca176ca8dbfebe668cc7a2684708594ec9f3c0db0805d5d6e1" -dependencies = [ - "phf_generator", - "phf_shared", -] - -[[package]] -name = "phf_generator" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "135ace3a761e564ec88c03a77317a7c6b80bb7f7135ef2544dbe054243b89737" -dependencies = [ - "fastrand", - "phf_shared", -] - -[[package]] -name = "phf_shared" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e57fef6bc5981e38c2ce2d63bfa546861309f875b8a75f092d1d54ae2d64f266" -dependencies = [ - "siphasher", -] - [[package]] name = "pin-project-lite" version = "0.2.17" @@ -1373,18 +1102,6 @@ version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" -[[package]] -name = "polyval" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d1fe60d06143b2430aa532c94cfe9e29783047f06c0d7fd359a9a51b729fa25" -dependencies = [ - "cfg-if", - "cpufeatures 0.2.17", - "opaque-debug", - "universal-hash 0.5.1", -] - [[package]] name = "polyval" version = "0.7.1" @@ -1393,7 +1110,7 @@ checksum = "7dfc63250416fea14f5749b90725916a6c903f599d51cb635aa7a52bfd03eede" dependencies = [ "cpubits", "cpufeatures 0.3.0", - "universal-hash 0.6.1", + "universal-hash", ] [[package]] @@ -1402,15 +1119,6 @@ version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49" -[[package]] -name = "potential_utf" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b73949432f5e2a09657003c25bca5e19a0e9c84f8058ca374f49e0ebe605af77" -dependencies = [ - "zerovec", -] - [[package]] name = "powerfmt" version = "0.2.0" @@ -1446,23 +1154,6 @@ dependencies = [ "prost-derive", ] -[[package]] -name = "prost-build" -version = "0.14.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "343d3bd7056eda839b03204e68deff7d1b13aba7af2b2fd16890697274262ee7" -dependencies = [ - "heck", - "itertools", - "log", - "multimap", - "petgraph", - "prost", - "prost-types", - "regex", - "tempfile", -] - [[package]] name = "prost-derive" version = "0.14.3" @@ -1476,15 +1167,6 @@ dependencies = [ "syn", ] -[[package]] -name = "prost-types" -version = "0.14.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8991c4cbdb8bc5b11f0b074ffe286c30e523de90fee5ba8132f1399f23cb3dd7" -dependencies = [ - "prost", -] - [[package]] name = "pyo3" version = "0.28.2" @@ -1627,18 +1309,6 @@ dependencies = [ "bitflags", ] -[[package]] -name = "regex" -version = "1.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" -dependencies = [ - "aho-corasick", - "memchr", - "regex-automata", - "regex-syntax", -] - [[package]] name = "regex-automata" version = "0.4.14" @@ -1689,19 +1359,6 @@ dependencies = [ "semver", ] -[[package]] -name = "rustix" -version = "1.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" -dependencies = [ - "bitflags", - "errno", - "libc", - "linux-raw-sys", - "windows-sys 0.61.2", -] - [[package]] name = "rustls" version = "0.23.37" @@ -1743,6 +1400,12 @@ version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" +[[package]] +name = "ryu" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" + [[package]] name = "scheduled-thread-pool" version = "0.2.7" @@ -1827,23 +1490,23 @@ dependencies = [ [[package]] name = "sha1" -version = "0.10.6" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +checksum = "aacc4cc499359472b4abe1bf11d0b12e688af9a805fa5e3016f9a386dc2d0214" dependencies = [ "cfg-if", - "cpufeatures 0.2.17", + "cpufeatures 0.3.0", "digest", ] [[package]] name = "sha2" -version = "0.10.9" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" +checksum = "446ba717509524cb3f22f17ecc096f10f4822d76ab5c0b9822c5f9c284e825f4" dependencies = [ "cfg-if", - "cpufeatures 0.2.17", + "cpufeatures 0.3.0", "digest", ] @@ -1884,12 +1547,6 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" -[[package]] -name = "siphasher" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2aa850e253778c88a04c3d7323b043aeda9d3e30d5971937c1855769763678e" - [[package]] name = "slab" version = "0.4.12" @@ -1930,6 +1587,12 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + [[package]] name = "strsim" version = "0.11.1" @@ -1976,19 +1639,6 @@ version = "0.13.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "adb6935a6f5c20170eeceb1a3835a49e12e19d792f6dd344ccc76a985ca5a6ca" -[[package]] -name = "tempfile" -version = "3.27.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" -dependencies = [ - "fastrand", - "getrandom 0.4.2", - "once_cell", - "rustix", - "windows-sys 0.61.2", -] - [[package]] name = "thiserror" version = "2.0.18" @@ -2049,16 +1699,6 @@ dependencies = [ "time-core", ] -[[package]] -name = "tinystr" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869" -dependencies = [ - "displaydoc", - "zerovec", -] - [[package]] name = "tokio" version = "1.50.0" @@ -2224,7 +1864,7 @@ dependencies = [ [[package]] name = "tryx" -version = "0.5.0" +version = "0.5.1" dependencies = [ "async-trait", "chrono", @@ -2281,23 +1921,13 @@ version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" -[[package]] -name = "universal-hash" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" -dependencies = [ - "crypto-common 0.1.7", - "subtle", -] - [[package]] name = "universal-hash" version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f4987bdc12753382e0bec4a65c50738ffaabc998b9cdd1f952fb5f39b0048a96" dependencies = [ - "crypto-common 0.2.1", + "crypto-common", "ctutils", ] @@ -2320,13 +1950,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dea7109cdcd5864d4eeb1b58a1648dc9bf520360d7af16ec26d0a9354bafcfc0" dependencies = [ "base64", - "cookie_store", "log", "percent-encoding", "rustls", "rustls-pki-types", - "serde", - "serde_json", "ureq-proto", "utf8-zero", "webpki-roots", @@ -2344,30 +1971,12 @@ dependencies = [ "log", ] -[[package]] -name = "url" -version = "2.5.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed" -dependencies = [ - "form_urlencoded", - "idna", - "percent-encoding", - "serde", -] - [[package]] name = "utf8-zero" version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8c0a043c9540bae7c578c88f91dda8bd82e59ae27c21baca69c8b191aaf5a6e" -[[package]] -name = "utf8_iter" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" - [[package]] name = "uuid" version = "1.22.0" @@ -2391,12 +2000,6 @@ version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" -[[package]] -name = "version_check" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" - [[package]] name = "virtue" version = "0.0.18" @@ -2408,7 +2011,6 @@ name = "wacore" version = "0.5.0" dependencies = [ "aes", - "aes-gcm", "anyhow", "async-channel", "async-lock", @@ -2423,10 +2025,9 @@ dependencies = [ "hex", "hkdf", "hmac", + "itoa", "log", "md5", - "once_cell", - "pbkdf2", "portable-atomic", "prost", "rand", @@ -2469,11 +2070,15 @@ dependencies = [ name = "wacore-binary" version = "0.5.0" dependencies = [ + "bytes", + "compact_str", "flate2", - "phf", - "phf_codegen", + "hashify", + "itoa", "serde", "serde_json", + "stable_deref_trait", + "yoke", ] [[package]] @@ -2490,16 +2095,16 @@ name = "wacore-libsignal" version = "0.5.0" dependencies = [ "aes", - "aes-gcm", "arrayref", "async-trait", + "bytes", "cbc", "chrono", "ctr", "curve25519-dalek", "derive_more", "displaydoc", - "ghash 0.6.0", + "ghash", "hex", "hkdf", "hmac", @@ -2520,7 +2125,6 @@ dependencies = [ name = "wacore-noise" version = "0.5.0" dependencies = [ - "aes-gcm", "anyhow", "bytes", "hkdf", @@ -2539,7 +2143,6 @@ name = "waproto" version = "0.5.0" dependencies = [ "prost", - "prost-build", "serde", ] @@ -2670,6 +2273,7 @@ dependencies = [ "event-listener", "futures", "hex", + "itoa", "log", "moka", "portable-atomic", @@ -2694,6 +2298,7 @@ version = "0.5.0" dependencies = [ "async-trait", "bincode", + "bytes", "diesel", "diesel_migrations", "libsqlite3-sys", @@ -2701,7 +2306,6 @@ dependencies = [ "serde_json", "tokio", "wacore", - "wacore-binary", ] [[package]] @@ -2975,12 +2579,6 @@ dependencies = [ "wasmparser", ] -[[package]] -name = "writeable" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" - [[package]] name = "x25519-dalek" version = "2.0.1" @@ -2995,9 +2593,9 @@ dependencies = [ [[package]] name = "yoke" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72d6e5c6afb84d73944e5cedb052c4680d5657337201555f9f2a16b7406d4954" +checksum = "abe8c5fda708d9ca3df187cae8bfb9ceda00dd96231bed36e445a1a48e66f9ca" dependencies = [ "stable_deref_trait", "yoke-derive", @@ -3006,9 +2604,9 @@ dependencies = [ [[package]] name = "yoke-derive" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" +checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e" dependencies = [ "proc-macro2", "quote", @@ -3057,39 +2655,6 @@ dependencies = [ "syn", ] -[[package]] -name = "zerotrie" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a59c17a5562d507e4b54960e8569ebee33bee890c70aa3fe7b97e85a9fd7851" -dependencies = [ - "displaydoc", - "yoke", - "zerofrom", -] - -[[package]] -name = "zerovec" -version = "0.11.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002" -dependencies = [ - "yoke", - "zerofrom", - "zerovec-derive", -] - -[[package]] -name = "zerovec-derive" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "zlib-rs" version = "0.6.3" diff --git a/libs/whatsapp-rust b/libs/whatsapp-rust index f48983d..4dd4994 160000 --- a/libs/whatsapp-rust +++ b/libs/whatsapp-rust @@ -1 +1 @@ -Subproject commit f48983d18110da5b26adff9028b6948cfb4a8522 +Subproject commit 4dd4994bc2cfd3982b2e85c8929c46dd47a9a422 diff --git a/src/clients/event_callbacks.rs b/src/clients/event_callbacks.rs index 02c5407..a83aed2 100644 --- a/src/clients/event_callbacks.rs +++ b/src/clients/event_callbacks.rs @@ -22,6 +22,7 @@ pub struct EventCallbacks { pub presence: CallbackList, pub picture_update: CallbackList, pub user_about_update: CallbackList, + #[allow(dead_code)] // Reserved for future JoinedGroup event variant pub joined_group: CallbackList, pub group_info_update: CallbackList, pub contact_update: CallbackList, diff --git a/src/clients/tryx.rs b/src/clients/tryx.rs index 72f7d23..75e8bc0 100644 --- a/src/clients/tryx.rs +++ b/src/clients/tryx.rs @@ -32,7 +32,7 @@ use crate::clients::chat_actions::ChatActionsClient; use crate::log::init_logging; use crate::backend::{SqliteBackend, BackendBase}; use crate::events::types::{ - EvArchiveUpdate, EvBusinessStatusUpdate, EvChatPresence, EvClientOutDated, EvConnectFailure, EvConnected, EvContactNumberChanged, EvContactSyncRequested, EvContactUpdate, EvContactUpdated, EvDeleteChatUpdate, EvDeleteMessageForMeUpdate, EvDeviceListUpdate, EvDisappearingModeChanged, EvDisconnected, EvGroupUpdate, EvHistorySync, EvJoinedGroup, EvLoggedOut, EvMarkChatAsReadUpdate, EvMessage, EvMuteUpdate, EvNewsletterLiveUpdate, EvNotification, EvOfflineSyncCompleted, EvOfflineSyncPreview, EvPairError, EvPairSuccess, EvPairingCode, EvPairingQrCode, EvPictureUpdate, EvPinUpdate, EvPresence, EvPushNameUpdate, EvQrScannedWithoutMultidevice, EvReceipt, EvSelfPushNameUpdated, EvStarUpdate, EvStreamError, EvStreamReplaced, EvTemporaryBan, EvUndecryptableMessage, EvUserAboutUpdate + EvArchiveUpdate, EvBusinessStatusUpdate, EvChatPresence, EvClientOutDated, EvConnectFailure, EvConnected, EvContactNumberChanged, EvContactSyncRequested, EvContactUpdate, EvContactUpdated, EvDeleteChatUpdate, EvDeleteMessageForMeUpdate, EvDeviceListUpdate, EvDisappearingModeChanged, EvDisconnected, EvGroupUpdate, EvHistorySync, EvLoggedOut, EvMarkChatAsReadUpdate, EvMessage, EvMuteUpdate, EvNewsletterLiveUpdate, EvNotification, EvOfflineSyncCompleted, EvOfflineSyncPreview, EvPairError, EvPairSuccess, EvPairingCode, EvPairingQrCode, EvPictureUpdate, EvPinUpdate, EvPresence, EvPushNameUpdate, EvQrScannedWithoutMultidevice, EvReceipt, EvSelfPushNameUpdated, EvStarUpdate, EvStreamError, EvStreamReplaced, EvTemporaryBan, EvUndecryptableMessage, EvUserAboutUpdate }; use crate::exceptions::{EventDispatchError, FailedBuildClient, UnsupportedBackend}; use crate::events::dispatcher::Dispatcher; @@ -325,7 +325,7 @@ impl Tryx { let tryx_client = Python::attach(|py| tryx_client.clone_ref(py)); async move { - match event { + match event.as_ref() { Event::Connected(_) => { Self::emit_built_event(&tryx_client, &callbacks.connected, locals.clone(), "Connected", |py| { Py::new(py, EvConnected {}).map(|event| event.into_any()) @@ -337,16 +337,19 @@ impl Tryx { }).await; } Event::LoggedOut(logout) => { + let logout = logout.clone(); Self::emit_built_event(&tryx_client, &callbacks.logout, locals.clone(), "LoggedOut", |py| { Py::new(py, EvLoggedOut::new(logout)).map(|event| event.into_any()) }).await; } Event::PairSuccess(pair_success) => { + let pair_success = pair_success.clone(); Self::emit_built_event(&tryx_client, &callbacks.pair_success, locals.clone(), "PairSuccess", |py| { Py::new(py, EvPairSuccess::from(pair_success)).map(|event| event.into_any()) }).await; } Event::PairError(pair_error) => { + let pair_error = pair_error.clone(); Self::emit_built_event( &tryx_client, &callbacks.pair_error, @@ -369,16 +372,21 @@ impl Tryx { .await; } Event::PairingQrCode { code, timeout } => { + let code = code.clone(); + let timeout_secs = timeout.as_secs(); Self::emit_built_event(&tryx_client, &callbacks.pairing_qr, locals.clone(), "PairingQrCode", |py| { - Py::new(py, EvPairingQrCode::new(code, timeout.as_secs())).map(|event| event.into_any()) + Py::new(py, EvPairingQrCode::new(code, timeout_secs)).map(|event| event.into_any()) }).await; } Event::PairingCode { code, timeout } => { + let code = code.clone(); + let timeout_secs = timeout.as_secs(); Self::emit_built_event(&tryx_client, &callbacks.pairing_code, locals.clone(), "PairingCode", |py| { - Py::new(py, EvPairingCode::new(code, timeout.as_secs())).map(|event| event.into_any()) + Py::new(py, EvPairingCode::new(code, timeout_secs)).map(|event| event.into_any()) }).await; } Event::QrScannedWithoutMultidevice(scanned) => { + let scanned = scanned.clone(); Self::emit_built_event(&tryx_client, &callbacks.qr_scanned_without_multidevice, locals.clone(), "QrScannedWithoutMultidevice", |py| { Py::new(py, EvQrScannedWithoutMultidevice::from(scanned)).map(|event| event.into_any()) }).await; @@ -389,11 +397,14 @@ impl Tryx { }).await; } Event::Message(msg, info) => { + let msg = (**msg).clone(); + let info = (**info).clone(); Self::emit_built_event(&tryx_client, &callbacks.message, locals.clone(), "Message", |py| { - Py::new(py, EvMessage::new(*msg, info)).map(|event| event.into_any()) + Py::new(py, EvMessage::new(msg, info)).map(|event| event.into_any()) }).await; } Event::Receipt(receipt) => { + let receipt = receipt.clone(); Self::emit_built_event( &tryx_client, &callbacks.receipt, @@ -405,7 +416,6 @@ impl Tryx { receipt.message_ids, receipt.timestamp, receipt.r#type, - receipt.message_sender, ) .into_any()) }, @@ -413,101 +423,123 @@ impl Tryx { .await; } Event::UndecryptableMessage(undecryptable_message) => { + let info = (*undecryptable_message.info).clone(); + let is_unavailable = undecryptable_message.is_unavailable; + let unavailable_type = undecryptable_message.unavailable_type.clone(); + let decrypt_fail_mode = undecryptable_message.decrypt_fail_mode.clone(); Self::emit_built_event(&tryx_client, &callbacks.undecryptable_message, locals.clone(), "UndecryptableMessage", |py| { - Py::new(py, EvUndecryptableMessage::new(undecryptable_message.info, undecryptable_message.is_unavailable, undecryptable_message.unavailable_type, undecryptable_message.decrypt_fail_mode)).map(|event| event.into_any()) + Py::new(py, EvUndecryptableMessage::new(info, is_unavailable, unavailable_type, decrypt_fail_mode)).map(|event| event.into_any()) }).await; } Event::Notification(notification) => { + let notification = notification.clone(); Self::emit_built_event(&tryx_client, &callbacks.notification, locals.clone(), "Notification", |py| { - Py::new(py, EvNotification::new(notification)).map(|event| event.into_any()) + Py::new(py, EvNotification::new(notification.to_owned_node())).map(|event| event.into_any()) }).await; } Event::ChatPresence(chat_presence) => { + let chat_presence = chat_presence.clone(); Self::emit_built_event(&tryx_client, &callbacks.chat_presence, locals.clone(), "ChatPresence", |py| { Py::new(py, EvChatPresence::from(chat_presence)).map(|event| event.into_any()) }).await; } Event::Presence(presence) => { + let presence = presence.clone(); Self::emit_built_event(&tryx_client, &callbacks.presence, locals.clone(), "Presence", |py| { Py::new(py, EvPresence::from(presence)).map(|event| event.into_any()) }).await; } Event::PictureUpdate(picture_update) => { + let picture_update = picture_update.clone(); Self::emit_built_event(&tryx_client, &callbacks.picture_update, locals.clone(), "PictureUpdate", |py| { Py::new(py, EvPictureUpdate::new(picture_update)).map(|event| event.into_any()) }).await; } Event::UserAboutUpdate(user_about) => { + let user_about = user_about.clone(); Self::emit_built_event(&tryx_client, &callbacks.user_about_update, locals.clone(), "UserAboutUpdate", |py| { Py::new(py, EvUserAboutUpdate::new(user_about)).map(|event| event.into_any()) }).await; } - Event::JoinedGroup(joined_group) => { - Self::emit_built_event(&tryx_client, &callbacks.joined_group, locals.clone(), "JoinedGroup", |py| { - Py::new(py, EvJoinedGroup::new(joined_group)).map(|event| event.into_any()) - }).await; - } + // JoinedGroup is not a current Event variant; reserved for future use. Event::GroupUpdate(group_info) => { + let group_info = group_info.clone(); Self::emit_built_event(&tryx_client, &callbacks.group_info_update, locals.clone(), "GroupUpdate", |py| { Py::new(py, EvGroupUpdate::new(group_info)).map(|event| event.into_any()) }).await; } Event::ContactUpdate(contact_update) => { + let contact_update = contact_update.clone(); Self::emit_built_event(&tryx_client, &callbacks.contact_update, locals.clone(), "ContactUpdate", |py| { Py::new(py, EvContactUpdate::new(contact_update)).map(|event| event.into_any()) }).await; } Event::PushNameUpdate(pushname) => { + let pushname = pushname.clone(); Self::emit_built_event(&tryx_client, &callbacks.push_name_update, locals.clone(), "PushNameUpdate", |py| { Py::new(py, EvPushNameUpdate::from(pushname)).map(|event| event.into_any()) }).await; } Event::SelfPushNameUpdated(self_push_name_update) => { + let self_push_name_update = self_push_name_update.clone(); Self::emit_built_event(&tryx_client, &callbacks.self_push_name_updated, locals.clone(), "SelfPushNameUpdated", |py| { Py::new(py, EvSelfPushNameUpdated::from(self_push_name_update)).map(|event| event.into_any()) }).await; } Event::PinUpdate(pin_update) => { + let pin_update = pin_update.clone(); Self::emit_built_event(&tryx_client, &callbacks.pin_update, locals.clone(), "PinUpdate", |py| { Py::new(py, EvPinUpdate::new(pin_update)).map(|event| event.into_any()) }).await; } Event::MuteUpdate(mute_update) => { + let mute_update = mute_update.clone(); Self::emit_built_event(&tryx_client, &callbacks.mute_update, locals.clone(), "MuteUpdate", |py| { Py::new(py, EvMuteUpdate::from(mute_update)).map(|event| event.into_any()) }).await; } Event::ArchiveUpdate(archived) => { + let archived = archived.clone(); Self::emit_built_event(&tryx_client, &callbacks.archive_update, locals.clone(), "ArchiveUpdate", |py| { Py::new(py, EvArchiveUpdate::from(archived)).map(|event| event.into_any()) }).await; } Event::MarkChatAsReadUpdate(mark_chat_as_read_update) => { + let mark_chat_as_read_update = mark_chat_as_read_update.clone(); Self::emit_built_event(&tryx_client, &callbacks.mark_chat_as_read_update, locals.clone(), "MarkChatAsReadUpdate", |py| { Py::new(py, EvMarkChatAsReadUpdate::from(mark_chat_as_read_update)).map(|event| event.into_any()) }).await; } Event::HistorySync(history_sync) => { + let history_sync = history_sync.clone(); Self::emit_built_event(&tryx_client, &callbacks.history_sync, locals.clone(), "HistorySync", |py| { - Py::new(py, EvHistorySync::from(history_sync)).map(|event| event.into_any()) + if let Some(decoded) = history_sync.get() { + Py::new(py, EvHistorySync::from(decoded.clone())).map(|event| event.into_any()) + } else { + Err(pyo3::exceptions::PyRuntimeError::new_err("Failed to decode HistorySync")) + } }).await; } Event::OfflineSyncPreview(offline_sync_preview) => { + let offline_sync_preview = offline_sync_preview.clone(); Self::emit_built_event(&tryx_client, &callbacks.offline_sync_preview, locals.clone(), "OfflineSyncPreview", |py| { Py::new(py, EvOfflineSyncPreview::from(offline_sync_preview)).map(|event| event.into_any()) }).await; } Event::OfflineSyncCompleted(offline_sync_complete) => { + let offline_sync_complete = offline_sync_complete.clone(); Self::emit_built_event(&tryx_client, &callbacks.offline_sync_completed, locals.clone(), "OfflineSyncCompleted", |py| { Py::new(py, EvOfflineSyncCompleted::from(offline_sync_complete)).map(|event| event.into_any()) }).await; } Event::DeviceListUpdate(device_list_update) => { + let device_list_update = device_list_update.clone(); Self::emit_built_event(&tryx_client, &callbacks.device_list_update, locals.clone(), "DeviceListUpdate", |py| { Py::new(py, EvDeviceListUpdate::from(device_list_update)).map(|event| event.into_any()) }).await; } Event::BusinessStatusUpdate(business_status_update) => { + let business_status_update = business_status_update.clone(); Self::emit_built_event(&tryx_client, &callbacks.business_status_update, locals.clone(), "BusinessStatusUpdate", |py| { Py::new(py, EvBusinessStatusUpdate::from(business_status_update)).map(|event| event.into_any()) }).await; @@ -518,56 +550,67 @@ impl Tryx { }).await; } Event::TemporaryBan(temporary_ban) => { + let temporary_ban = temporary_ban.clone(); Self::emit_built_event(&tryx_client, &callbacks.temporary_ban, locals.clone(), "TemporaryBan", |py| { Py::new(py, EvTemporaryBan::from(temporary_ban)).map(|event| event.into_any()) }).await; } Event::ConnectFailure(connect_failure) => { + let connect_failure = connect_failure.clone(); Self::emit_built_event(&tryx_client, &callbacks.connect_failure, locals.clone(), "ConnectFailure", |py| { Py::new(py, EvConnectFailure::new(connect_failure.reason, connect_failure.message, connect_failure.raw)).map(|event| event.into_any()) }).await; } Event::StreamError(stream_error) => { + let stream_error = stream_error.clone(); Self::emit_built_event(&tryx_client, &callbacks.stream_error, locals.clone(), "StreamError", |py| { Py::new(py, EvStreamError::new(stream_error.code, stream_error.raw)).map(|event| event.into_any()) }).await; } Event::ContactNumberChanged(contact_number_changed) => { + let contact_number_changed = contact_number_changed.clone(); Self::emit_built_event(&tryx_client, &callbacks.contact_number_changed, locals.clone(), "ContactNumberChanged", |py| { Py::new(py, EvContactNumberChanged::from(contact_number_changed)).map(|event| event.into_any()) }).await; } Event::ContactSyncRequested(contact_sync_requested) => { + let contact_sync_requested = contact_sync_requested.clone(); Self::emit_built_event(&tryx_client, &callbacks.contact_sync_requested, locals.clone(), "ContactSyncRequested", |py| { Py::new(py, EvContactSyncRequested::from(contact_sync_requested)).map(|event| event.into_any()) }).await; } Event::ContactUpdated(contact_updated) => { + let contact_updated = contact_updated.clone(); Self::emit_built_event(&tryx_client, &callbacks.contact_updated, locals.clone(), "ContactUpdated", |py| { Py::new(py, EvContactUpdated::from(contact_updated)).map(|event| event.into_any()) }).await; } Event::StarUpdate(star_update) => { + let star_update = star_update.clone(); Self::emit_built_event(&tryx_client, &callbacks.star_update, locals.clone(), "StarUpdate", |py| { Py::new(py, EvStarUpdate::from(star_update)).map(|event| event.into_any()) }).await; } Event::DisappearingModeChanged(disappearing_mode_changed) => { + let disappearing_mode_changed = disappearing_mode_changed.clone(); Self::emit_built_event(&tryx_client, &callbacks.disappearing_mode_changed, locals.clone(), "DisappearingModeChanged", |py| { Py::new(py, EvDisappearingModeChanged::from(disappearing_mode_changed)).map(|event| event.into_any()) }).await; } Event::NewsletterLiveUpdate(newsletter_live_update) => { + let newsletter_live_update = newsletter_live_update.clone(); Self::emit_built_event(&tryx_client, &callbacks.newsletter_live_update, locals.clone(), "NewsletterLiveUpdate", |py| { Py::new(py, EvNewsletterLiveUpdate::from(newsletter_live_update)).map(|event| event.into_any()) }).await; } Event::DeleteChatUpdate(delete_chat_update) => { + let delete_chat_update = delete_chat_update.clone(); Self::emit_built_event(&tryx_client, &callbacks.delete_chat_update, locals.clone(), "DeleteChatUpdate", |py| { Py::new(py, EvDeleteChatUpdate::from(delete_chat_update)).map(|event| event.into_any()) }).await; } Event::DeleteMessageForMeUpdate(delete_message_for_me_update) => { + let delete_message_for_me_update = delete_message_for_me_update.clone(); Self::emit_built_event(&tryx_client, &callbacks.delete_message_for_me_update, locals.clone(), "DeleteMessageForMeUpdate", |py| { Py::new(py, EvDeleteMessageForMeUpdate::from(delete_message_for_me_update)).map(|event| event.into_any()) }).await; diff --git a/src/clients/tryx_client.rs b/src/clients/tryx_client.rs index bd3bd07..170b2c6 100644 --- a/src/clients/tryx_client.rs +++ b/src/clients/tryx_client.rs @@ -237,16 +237,15 @@ impl TryxClient { Ok(result) }) } - fn upload<'py>(&self, py: Python<'py>, data: &[u8], media_type: Py) -> PyResult> { + fn upload<'py>(&self, py: Python<'py>, data: Vec, media_type: Py) -> PyResult> { let client = self.client_rx.borrow().clone().ok_or_else(|| { PyErr::new::("Client is not running. Call Tryx.run() or Tryx.run_blocking() first.") })?; - let data_vec = data.to_vec(); let mtype = media_type.bind(py).borrow_mut().to_wacore_enum(); let locals = get_current_locals(py)?; future_into_py_with_locals::<_, UploadResponse>(py, locals, async move { let url = client - .upload(data_vec, mtype, UploadOptions::default()) + .upload(data, mtype, UploadOptions::default()) .await .map_err(|e| PyErr::new::(e.to_string()))?; let result= UploadResponse { @@ -329,17 +328,16 @@ impl TryxClient { }) } #[pyo3(signature = (to, photo_data, caption=None, quoted=None))] - fn send_photo<'py>(&self, py: Python<'py>, to: Py, photo_data: &[u8], caption: Option, quoted: Option>) -> PyResult> { + fn send_photo<'py>(&self, py: Python<'py>, to: Py, photo_data: Vec, caption: Option, quoted: Option>) -> PyResult> { let client = self.client_rx.borrow().clone().ok_or_else(|| { PyErr::new::("Client is not running. Call Tryx.run() or Tryx.run_blocking() first.") })?; let jid = to.bind(py).borrow().as_whatsapp_jid(); - let photo_clone = photo_data.to_vec(); let locals = get_current_locals(py)?; let context_info = Self::quote_context(py, quoted.as_ref()); future_into_py_with_locals::<_, Py>(py, locals, async move { let upload = client - .upload(photo_clone, wacore::download::MediaType::Image, UploadOptions::default()) + .upload(photo_data, wacore::download::MediaType::Image, UploadOptions::default()) .await .map_err(|e| PyErr::new::(e.to_string()))?; let message = WhatsappMessage { @@ -369,7 +367,7 @@ impl TryxClient { &self, py: Python<'py>, to: Py, - document_data: &[u8], + document_data: Vec, mimetype: String, file_name: Option, caption: Option, @@ -379,13 +377,12 @@ impl TryxClient { PyErr::new::("Client is not running. Call Tryx.run() or Tryx.run_blocking() first.") })?; let jid = to.bind(py).borrow().as_whatsapp_jid(); - let data = document_data.to_vec(); let locals = get_current_locals(py)?; let context_info = Self::quote_context(py, quoted.as_ref()); future_into_py_with_locals::<_, Py>(py, locals, async move { let upload = client - .upload(data, wacore::download::MediaType::Document, UploadOptions::default()) + .upload(document_data, wacore::download::MediaType::Document, UploadOptions::default()) .await .map_err(|e| PyErr::new::(e.to_string()))?; @@ -420,7 +417,7 @@ impl TryxClient { &self, py: Python<'py>, to: Py, - audio_data: &[u8], + audio_data: Vec, mimetype: Option, ptt: bool, seconds: Option, @@ -430,13 +427,12 @@ impl TryxClient { PyErr::new::("Client is not running. Call Tryx.run() or Tryx.run_blocking() first.") })?; let jid = to.bind(py).borrow().as_whatsapp_jid(); - let data = audio_data.to_vec(); let locals = get_current_locals(py)?; let context_info = Self::quote_context(py, quoted.as_ref()); future_into_py_with_locals::<_, Py>(py, locals, async move { let upload = client - .upload(data, wacore::download::MediaType::Audio, UploadOptions::default()) + .upload(audio_data, wacore::download::MediaType::Audio, UploadOptions::default()) .await .map_err(|e| PyErr::new::(e.to_string()))?; @@ -470,7 +466,7 @@ impl TryxClient { &self, py: Python<'py>, to: Py, - video_data: &[u8], + video_data: Vec, mimetype: Option, caption: Option, seconds: Option, @@ -481,13 +477,12 @@ impl TryxClient { PyErr::new::("Client is not running. Call Tryx.run() or Tryx.run_blocking() first.") })?; let jid = to.bind(py).borrow().as_whatsapp_jid(); - let data = video_data.to_vec(); let locals = get_current_locals(py)?; let context_info = Self::quote_context(py, quoted.as_ref()); future_into_py_with_locals::<_, Py>(py, locals, async move { let upload = client - .upload(data, wacore::download::MediaType::Video, UploadOptions::default()) + .upload(video_data, wacore::download::MediaType::Video, UploadOptions::default()) .await .map_err(|e| PyErr::new::(e.to_string()))?; @@ -522,7 +517,7 @@ impl TryxClient { &self, py: Python<'py>, to: Py, - gif_data: &[u8], + gif_data: Vec, caption: Option, seconds: Option, quoted: Option>, @@ -535,7 +530,7 @@ impl TryxClient { &self, py: Python<'py>, to: Py, - sticker_data: &[u8], + sticker_data: Vec, is_animated: bool, quoted: Option>, ) -> PyResult> { @@ -543,13 +538,12 @@ impl TryxClient { PyErr::new::("Client is not running. Call Tryx.run() or Tryx.run_blocking() first.") })?; let jid = to.bind(py).borrow().as_whatsapp_jid(); - let data = sticker_data.to_vec(); let locals = get_current_locals(py)?; let context_info = Self::quote_context(py, quoted.as_ref()); future_into_py_with_locals::<_, Py>(py, locals, async move { let upload = client - .upload(data, wacore::download::MediaType::Sticker, UploadOptions::default()) + .upload(sticker_data, wacore::download::MediaType::Sticker, UploadOptions::default()) .await .map_err(|e| PyErr::new::(e.to_string()))?; diff --git a/src/events/types.rs b/src/events/types.rs index a4a96f1..9e2e4f0 100644 --- a/src/events/types.rs +++ b/src/events/types.rs @@ -1,3 +1,4 @@ +use std::sync::Arc; use std::sync::OnceLock; use prost::Message as ProstMessage; @@ -173,12 +174,10 @@ pub struct EvReceipt { timestamp: Py, #[pyo3(get)] receipt_type: Py, - #[pyo3(get)] - message_sender: Py } impl EvReceipt { - pub fn new(inner: wacore::types::message::MessageSource, message_ids: Vec, timestamp: DateTime, r#type: wacore::types::presence::ReceiptType, message_sender: Jid) -> Py { + pub fn new(inner: wacore::types::message::MessageSource, message_ids: Vec, timestamp: DateTime, r#type: wacore::types::presence::ReceiptType) -> Py { let receipt_type = match r#type { wacore::types::presence::ReceiptType::Delivered => ReceiptType::Delivered, wacore::types::presence::ReceiptType::Sender => ReceiptType::Sender, @@ -201,7 +200,6 @@ impl EvReceipt { message_ids, timestamp: PyDateTime::from_timestamp(py, timestamp.naive_utc().and_utc().timestamp_millis() as f64 / 1000.0, None).unwrap().into(), receipt_type: Py::new(py, receipt_type).unwrap(), - message_sender: Py::new(py, JID::from(message_sender.clone())).unwrap(), }) }) .unwrap() @@ -536,11 +534,11 @@ impl EvUserAboutUpdate { #[pyclass] pub struct LazyConversation { - inner: Box, + inner: Box, parsed: Option>, } impl LazyConversation { - pub fn new(inner: wacore::types::events::LazyConversation) -> Self { + pub fn new(inner: waproto::whatsapp::Conversation) -> Self { Self { inner: Box::new(inner), parsed: None } } } @@ -552,7 +550,7 @@ impl LazyConversation { Ok(Some(parsed.clone_ref(py))) } else { let proto_type = get_lazy_conversation_proto_type(py)?; - let proto = self.inner.get().ok_or_else(|| PyErr::new::("LazyConversation does not contain conversation data"))?; + let proto = &*self.inner; let mut proto_bytes = Vec::with_capacity(proto.encoded_len()); proto.encode(&mut proto_bytes).map_err(|e| PyErr::new::(format!("Failed to encode conversation proto: {}", e)))?; let parsed_proto = from_string_to_python_proto(py, proto_type, &proto_bytes)?; @@ -564,11 +562,11 @@ impl LazyConversation { #[pyclass] pub struct EvJoinedGroup{ - inner: wacore::types::events::LazyConversation, + inner: waproto::whatsapp::Conversation, conversation_cached: Option>, } impl EvJoinedGroup { - pub fn new(inner: wacore::types::events::LazyConversation) -> Self { + pub fn new(inner: waproto::whatsapp::Conversation) -> Self { Self { inner, conversation_cached: None } } } diff --git a/src/events/types/message_and_updates.rs b/src/events/types/message_and_updates.rs index 49afda6..15c6b7b 100644 --- a/src/events/types/message_and_updates.rs +++ b/src/events/types/message_and_updates.rs @@ -221,14 +221,14 @@ impl EvStreamError { } #[pyclass] pub struct MessageData { - inner: Box, - inner_message_info: Box, + inner: Arc, + inner_message_info: Arc, message_info: OnceLock>, message_proto: OnceLock>, } impl MessageData { - pub fn new(inner: Box, inner_message_info: Box) -> Self { - Self { inner: inner, inner_message_info: inner_message_info, message_info: OnceLock::new(), message_proto: OnceLock::new() } + pub fn new(inner: Arc, inner_message_info: Arc) -> Self { + Self { inner, inner_message_info, message_info: OnceLock::new(), message_proto: OnceLock::new() } } } #[pymethods] @@ -259,7 +259,7 @@ impl MessageData { if let Some(info) = self.message_info.get() { Ok(info.clone_ref(py)) } else { - let info = MessageInfo::from(*self.inner_message_info.clone()); + let info = MessageInfo::from((*self.inner_message_info).clone()); let py_info = Py::new(py, info)?; self.message_info.set(py_info.clone_ref(py)).ok(); Ok(py_info) @@ -291,13 +291,13 @@ impl MessageData { #[pyclass] pub struct EvMessage { - pub inner: Box, - pub inner_message_info: Box, + pub inner: Arc, + pub inner_message_info: Arc, pub data_cache: OnceLock>, } impl EvMessage { pub fn new(inner: waproto::whatsapp::Message, message_info: WhatsappMessageInfo) -> Self { - Self { inner: Box::new(inner), inner_message_info: Box::new(message_info), data_cache: OnceLock::new() } + Self { inner: Arc::new(inner), inner_message_info: Arc::new(message_info), data_cache: OnceLock::new() } } } #[pymethods] @@ -307,7 +307,7 @@ impl EvMessage { if let Some(ref data) = self.data_cache.get() { Ok(data.clone_ref(py)) } else { - let new_data = MessageData::new(Box::clone(&self.inner), Box::clone(&self.inner_message_info)); + let new_data = MessageData::new(Arc::clone(&self.inner), Arc::clone(&self.inner_message_info)); let py_data = Py::new(py, new_data)?; self.data_cache.set(py_data.clone_ref(py)).ok(); Ok(py_data) @@ -449,7 +449,7 @@ impl EvDisappearingModeChanged { let data = EvDisappearingModeChangedData { from: Py::new(py, JID::from(self.inner.from.clone())).unwrap(), duration: self.inner.duration, - setting_timestamp: self.inner.setting_timestamp, + setting_timestamp: self.inner.setting_timestamp.timestamp() as u64, }; let py_data = Py::new(py, data)?; self.data_cache.set(py_data.clone_ref(py)).ok(); @@ -738,6 +738,18 @@ pub enum GroupNotificationAction { MembershipApprovalMode { enabled: bool, }, + MembershipApprovalRequest { + request_method: String, + parent_group_jid: Option>, + }, + CreatedMembershipRequests { + request_method: String, + parent_group_jid: Option>, + requests: Vec>, + }, + RevokedMembershipRequests { + participants: Vec>, + }, MemberAddMode { mode: String, }, @@ -885,6 +897,27 @@ impl EvGroupUpdate { GroupNotificationAction::Unlink { unlink_type: unlink_type.clone(), unlink_reason: unlink_reason.clone(), raw: py_raw } }, wacore::stanza::groups::GroupNotificationAction::Unknown { tag } => GroupNotificationAction::Unknown { tag: tag.clone() }, + wacore::stanza::groups::GroupNotificationAction::MembershipApprovalRequest { request_method, parent_group_jid } => { + let method_str = format!("{:?}", request_method); + let py_parent = parent_group_jid.as_ref().map(|j| Py::new(py, JID::from(j.clone())).unwrap()); + GroupNotificationAction::MembershipApprovalRequest { request_method: method_str, parent_group_jid: py_parent } + }, + wacore::stanza::groups::GroupNotificationAction::CreatedMembershipRequests { request_method, parent_group_jid, requests } => { + let method_str = format!("{:?}", request_method); + let py_parent = parent_group_jid.as_ref().map(|j| Py::new(py, JID::from(j.clone())).unwrap()); + let py_requests = requests.iter().map(|p| { + let gp = GroupParticipant { + jid: Py::new(py, JID::from(p.jid.clone())).unwrap(), + phone_number: p.phone_number.as_ref().map(|pn| Py::new(py, JID::from(pn.clone())).unwrap()), + }; + Py::new(py, gp).unwrap() + }).collect(); + GroupNotificationAction::CreatedMembershipRequests { request_method: method_str, parent_group_jid: py_parent, requests: py_requests } + }, + wacore::stanza::groups::GroupNotificationAction::RevokedMembershipRequests { participants } => { + let py_participants = participants.iter().map(|j| Py::new(py, JID::from(j.clone())).unwrap()).collect(); + GroupNotificationAction::RevokedMembershipRequests { participants: py_participants } + }, }; let action = Py::new(py, action)?; let data = GroupUpdateData { diff --git a/src/events/types/profile_sync.rs b/src/events/types/profile_sync.rs index c29631c..197cfdb 100644 --- a/src/events/types/profile_sync.rs +++ b/src/events/types/profile_sync.rs @@ -540,7 +540,7 @@ impl EvBusinessStatusUpdate { wacore::types::events::BusinessUpdateType::SubscriptionsUpdated => BusinessStatusUpdateType::SubscriptionsUpdated, wacore::types::events::BusinessUpdateType::Unknown => BusinessStatusUpdateType::Unknown, }; - let new_data = BusinessStatusUpdateData::new(self.inner.jid.clone().into(), update_type, self.inner.timestamp, self.inner.target_jid.clone().map(|j| j.into()), self.inner.hash.clone(), self.inner.product_ids.clone(), self.inner.collection_ids.clone(), self.inner.subscriptions.iter().map(|s| s.clone().into()).collect()); + let new_data = BusinessStatusUpdateData::new(self.inner.jid.clone().into(), update_type, self.inner.timestamp.timestamp(), self.inner.target_jid.clone().map(|j| j.into()), self.inner.hash.clone(), self.inner.product_ids.clone(), self.inner.collection_ids.clone(), self.inner.subscriptions.iter().map(|s| s.clone().into()).collect()); let py_data = Py::new(py, new_data).unwrap(); self.data_cached.set(py_data.clone_ref(py)).ok(); py_data diff --git a/src/main.rs b/src/main.rs index cddaea4..6d68685 100644 --- a/src/main.rs +++ b/src/main.rs @@ -17,14 +17,14 @@ async fn main() -> Result<(), Box> { .with_transport_factory(TokioWebSocketTransportFactory::new()) .with_http_client(UreqHttpClient::new()) .on_event(|event, _client| async move { - match event { + match &*event { Event::PairingQrCode { code, .. } => { println!("Scan this QR code with WhatsApp:\n{}", code); } Event::Message(msg, info) => { println!("Message from {}: {:?}", info.source.sender, msg); } - Event::Connected(_e)=> { + Event::Connected(_e) => { println!("Connected to WhatsApp"); } _ => {} diff --git a/src/types.rs b/src/types.rs index be56539..68d50f6 100644 --- a/src/types.rs +++ b/src/types.rs @@ -31,18 +31,23 @@ impl JID { (*self.inner).clone() } + pub fn as_whatsapp_jid_ref(&self) -> &WhatsAppJID { + &self.inner + } } #[pymethods] impl JID { #[new] fn new(user: String, server: String) -> PyResult { - let inner = WhatsAppJID::new(&user, &server); + let server_enum = wacore_binary::jid::Server::try_from(server.as_str()) + .map_err(|e| pyo3::exceptions::PyValueError::new_err(format!("Invalid server: {e}")))?; + let inner = WhatsAppJID::new(&user, server_enum); Ok(JID { inner: Arc::new(inner) }) } #[getter] fn user(&self) -> String { - self.inner.user.clone() + self.inner.user.to_string() } #[getter] fn server(&self) -> String { diff --git a/src/wacore/iq/community.rs b/src/wacore/iq/community.rs index 0b7a284..2ab2aaa 100644 --- a/src/wacore/iq/community.rs +++ b/src/wacore/iq/community.rs @@ -195,13 +195,14 @@ pub struct GroupParticipant { impl GroupParticipant { pub fn from_inner(py: Python<'_>, value: WaGroupParticipant) -> PyResult { + let is_admin = value.is_admin(); Ok(Self { jid: Py::new(py, JID::from(value.jid))?, phone_number: value .phone_number .map(|jid| Py::new(py, JID::from(jid))) .transpose()?, - is_admin: value.is_admin, + is_admin, }) } } diff --git a/src/wacore/iq/groups.rs b/src/wacore/iq/groups.rs index 41b72fc..aece2da 100644 --- a/src/wacore/iq/groups.rs +++ b/src/wacore/iq/groups.rs @@ -300,7 +300,7 @@ impl GroupInfo { let lid_to_pn_map = value .lid_to_pn_map() .iter() - .map(|(lid, jid)| Ok((lid.clone(), Py::new(py, JID::from(jid.clone()))?))) + .map(|(lid, jid)| Ok((lid.to_string(), Py::new(py, JID::from(jid.clone()))?))) .collect::>>()?; let participants = value diff --git a/src/wacore/node.rs b/src/wacore/node.rs index f0ec628..1703a2a 100644 --- a/src/wacore/node.rs +++ b/src/wacore/node.rs @@ -1,9 +1,27 @@ +use std::collections::HashSet; +use std::sync::Mutex; + use pyo3::types::PyString; use pyo3::{Python, pyclass, pymethods}; use pyo3::{IntoPyObjectExt, prelude::*}; use whatsapp_rust::NodeBuilder; use crate::types::JID; +/// Intern pool for attribute keys to avoid `Box::leak` on every call. +/// Keys are interned once and reused for the lifetime of the process. +static INTERNED_KEYS: std::sync::LazyLock>> = + std::sync::LazyLock::new(|| Mutex::new(HashSet::new())); + +fn intern_key(key: &str) -> &'static str { + let mut set = INTERNED_KEYS.lock().unwrap(); + if let Some(&existing) = set.get(key) { + return existing; + } + let leaked: &'static str = Box::leak(key.to_owned().into_boxed_str()); + set.insert(leaked); + leaked +} + pub enum NodeValueEnum { String(String), Jid(pyo3::Py), @@ -44,16 +62,20 @@ impl NodeValue { } #[setter] - pub fn set_value(&mut self, value: Py) { + pub fn set_value(&mut self, value: Py) -> PyResult<()> { Python::attach(|py| { if let Ok(s) = value.extract::(py) { self.inner = NodeValueEnum::String(s); + Ok(()) } else if let Ok(jid) = value.extract::>(py) { self.inner = NodeValueEnum::Jid(jid); + Ok(()) } else { - panic!("Invalid type for NodeValue. Expected String or JID."); + Err(pyo3::exceptions::PyTypeError::new_err( + "Invalid type for NodeValue. Expected String or JID.", + )) } - }); + }) } pub fn __repr__(&self, py: Python<'_>) -> String { @@ -83,11 +105,11 @@ pub struct NodeContent { #[pymethods] impl NodeContent { #[getter] - fn value(&self, py: Python<'_>) -> Py { + fn value(&self, py: Python<'_>) -> PyResult> { match &self.inner { - NodeContentEnum::Bytes(b) => b.into_py_any(py).unwrap(), - NodeContentEnum::String(s) => s.into_py_any(py).unwrap(), - NodeContentEnum::Nodes(n) => n.into_py_any(py).unwrap(), + NodeContentEnum::Bytes(b) => b.into_py_any(py).map_err(|e| e.into()), + NodeContentEnum::String(s) => s.into_py_any(py).map_err(|e| e.into()), + NodeContentEnum::Nodes(n) => n.into_py_any(py).map_err(|e| e.into()), } } @@ -177,10 +199,7 @@ impl Node { let attr_ref = attr.bind(py).borrow(); let value_ref = attr_ref.value.bind(py).borrow(); - // Ensure the key lives long enough for the builder which expects '&'static str'. - // Minimal overhead: leak the owned String (intentional small leak for static lifetime). - let key_owned = attr_ref.key.clone(); - let key_static: &'static str = Box::leak(key_owned.into_boxed_str()); + let key_static: &'static str = intern_key(&attr_ref.key); builder = match &value_ref.inner { NodeValueEnum::String(s) => builder.attr(key_static, s.clone()), @@ -195,35 +214,36 @@ impl Node { } pub fn from_node(node: &wacore_binary::node::Node) -> Self { - let (attrs, content) = Python::attach(|py| { + let (attrs, content) = Python::attach(|py| -> (Vec>, Option>) { let attrs = node .attrs .iter() - .map(|(k, v)| { + .filter_map(|(k, v)| { let value = match v { - wacore_binary::node::NodeValue::String(s) => NodeValue::new_string(s.clone()), + wacore_binary::node::NodeValue::String(s) => NodeValue::new_string(s.to_string()), wacore_binary::node::NodeValue::Jid(jid) => { - NodeValue::jid(Py::new(py, JID::from(jid.clone())).unwrap()) + NodeValue::jid(Py::new(py, JID::from(jid.clone())).ok()?) } }; - Py::new(py, Attrs::new(k.to_string(), Py::new(py, value).unwrap())).unwrap() + let py_value = Py::new(py, value).ok()?; + Py::new(py, Attrs::new(k.to_string(), py_value)).ok() }) .collect::>(); - let content = node.content.as_ref().map(|c| { + let content = node.content.as_ref().and_then(|c| { let content_enum = match c { wacore_binary::node::NodeContent::Bytes(b) => NodeContentEnum::Bytes(b.clone()), - wacore_binary::node::NodeContent::String(s) => NodeContentEnum::String(s.clone()), + wacore_binary::node::NodeContent::String(s) => NodeContentEnum::String(s.to_string()), wacore_binary::node::NodeContent::Nodes(n) => { let nodes = n .iter() .map(Self::from_node) - .map(|node| Py::new(py, node).unwrap()) + .filter_map(|node| Py::new(py, node).ok()) .collect(); NodeContentEnum::Nodes(nodes) } }; - Py::new(py, NodeContent { inner: content_enum }).unwrap() + Py::new(py, NodeContent { inner: content_enum }).ok() }); (attrs, content)