├── .gitignore ├── README.md ├── src ├── lib.rs ├── error.rs ├── options.rs ├── status.rs ├── call.rs └── client.rs ├── examples ├── normal_client.rs └── batch_client.rs ├── LICENSE-MIT ├── Cargo.toml ├── LICENSE-APACHE └── Cargo.lock /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Aria2-rs 2 | [![Crates.io](https://img.shields.io/crates/v/aria2-rs.svg)](https://crates.io/crates/aria2-rs) 3 | 4 | Yet Another Aria2 JSON-RPC Client. 5 | 6 | ## Features 7 | - [x] Simple direct call via websocket. 8 | - [x] Batch call via websocket. 9 | - [x] Automatically multiplex. 10 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod call; 2 | mod client; 3 | mod error; 4 | pub mod options; 5 | pub mod status; 6 | 7 | pub type SmallMap = small_map::FxSmallMap<4, K, V>; 8 | pub type SmallVec = smallvec::SmallVec<[T; 4]>; 9 | pub type SmallString = smol_str::SmolStr; 10 | pub type Result = std::result::Result; 11 | 12 | pub use call::{Call, MultiResponse, Reply, OK}; 13 | pub use client::{BatchClient, Client, ConnectionMeta, NotificationCallback}; 14 | pub use error::{Error, RpcError}; 15 | -------------------------------------------------------------------------------- /src/error.rs: -------------------------------------------------------------------------------- 1 | #[derive(thiserror::Error, Debug)] 2 | pub enum Error { 3 | #[error("Encode error: {0}")] 4 | Encode(#[from] serde_json::Error), 5 | #[error("Decode error: {0}")] 6 | Decode(serde_json::Error), 7 | #[error("Websocket error: {0}")] 8 | Websocket(#[from] tokio_tungstenite::tungstenite::Error), 9 | #[error("Rpc error: {0}")] 10 | Rpc(#[from] RpcError), 11 | #[error("Request send error")] 12 | ChannelSend, 13 | #[error("Response recv error: {0}")] 14 | ChannelRecv(#[from] tokio::sync::oneshot::error::RecvError), 15 | } 16 | 17 | #[derive(serde::Deserialize, Debug, Clone)] 18 | pub struct RpcError { 19 | pub code: u32, 20 | pub message: String, 21 | } 22 | 23 | impl std::fmt::Display for RpcError { 24 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 25 | write!( 26 | f, 27 | "RpcError: {{\"code\": {}, \"message\": \"{}\"}}", 28 | self.code, self.message 29 | ) 30 | } 31 | } 32 | impl std::error::Error for RpcError {} 33 | -------------------------------------------------------------------------------- /examples/normal_client.rs: -------------------------------------------------------------------------------- 1 | use aria2_rs::{ 2 | call::{AddUriCall, MultiCall}, 3 | Client, ConnectionMeta, SmallVec, 4 | }; 5 | 6 | const WS_RPC_ADDRESS: &str = "wss://TEST/jsonrpc"; 7 | const TOKEN: &str = "token:TEST"; 8 | 9 | #[tokio::main] 10 | async fn main() { 11 | let client = Client::connect( 12 | ConnectionMeta { 13 | url: WS_RPC_ADDRESS.to_string(), 14 | token: Some(TOKEN.to_string()), 15 | }, 16 | 10, 17 | ) 18 | .await 19 | .unwrap(); 20 | let r = client 21 | .call(&AddUriCall { 22 | uris: SmallVec::from_iter(["http://example.org/file".to_string()]), 23 | options: None, 24 | }) 25 | .await 26 | .unwrap(); 27 | println!("response: {r:?}"); 28 | 29 | let add_uri = AddUriCall { 30 | uris: SmallVec::from_iter(["http://example.org/file".to_string()]), 31 | options: None, 32 | }; 33 | let mut multi = MultiCall::new(); 34 | multi.push(add_uri); 35 | let r = client.call(&multi).await.unwrap(); 36 | println!("response: {r:?}"); 37 | } 38 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2023 ihciah 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | People who habitually steal code are prohibited from using it. 13 | 14 | The above copyright notice and this permission notice 15 | shall be included in all copies or substantial portions 16 | of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 19 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 20 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 21 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 22 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 23 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 24 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 25 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 26 | DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /examples/batch_client.rs: -------------------------------------------------------------------------------- 1 | use std::time::Duration; 2 | 3 | use aria2_rs::{call::AddUriCall, BatchClient, ConnectionMeta, SmallVec}; 4 | 5 | const WS_RPC_ADDRESS: &str = "wss://TEST/jsonrpc"; 6 | const TOKEN: &str = "token:TEST"; 7 | 8 | #[tokio::main] 9 | async fn main() { 10 | let client = BatchClient::connect( 11 | ConnectionMeta { 12 | url: WS_RPC_ADDRESS.to_string(), 13 | token: Some(TOKEN.to_string()), 14 | }, 15 | 10, 16 | Duration::from_secs(5), 17 | ) 18 | .await 19 | .unwrap(); 20 | 21 | // The following 3 requests will be sent in one batch(multicall). 22 | let r = tokio::join!( 23 | client.call(AddUriCall { 24 | uris: SmallVec::from_iter(["http://example.org/file".to_string()]), 25 | options: None, 26 | }), 27 | client.call(AddUriCall { 28 | uris: SmallVec::from_iter(["http://example.org/file".to_string()]), 29 | options: None, 30 | }), 31 | client.call(AddUriCall { 32 | uris: SmallVec::from_iter(["http://example.org/file".to_string()]), 33 | options: None, 34 | }) 35 | ); 36 | println!("response: {r:?}"); 37 | } 38 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ["ihciah "] 3 | description = "Yet Another Aria2 JSON-RPC Client." 4 | edition = "2021" 5 | keywords = ["aria2c", "websocket"] 6 | license = "MIT/Apache-2.0" 7 | name = "aria2-rs" 8 | readme = "README.md" 9 | repository = "https://github.com/ihciah/aria2-rs" 10 | version = "0.3.3" 11 | 12 | [dependencies] 13 | tokio = { version = "1", features = ["sync", "time", "macros", "rt"] } 14 | tokio-tungstenite = { version = "0.27" } 15 | futures-util = { version = "0.3", features = ["sink"] } 16 | 17 | parking_lot = { version = "0.12", features = ["hardware-lock-elision"] } 18 | uuid = { version = "1", features = ["fast-rng", "v4"] } 19 | serde = { version = "1", features = ["derive"] } 20 | serde_json = "1" 21 | serde_with = "3" 22 | thiserror = "2" 23 | base64 = "0.22" 24 | tracing = "0.1" 25 | 26 | smallvec = { version = "1", features = ["serde"] } 27 | small-map = { version = "0.1.3", features = ["fxhash", "serde"] } 28 | smol_str = { version = "0.3", features = ["serde"] } 29 | 30 | [dev-dependencies] 31 | tokio = { version = "1", features = [ 32 | "sync", 33 | "time", 34 | "macros", 35 | "rt-multi-thread", 36 | ] } 37 | 38 | [features] 39 | default = ["tokio-tungstenite-native-tls-vendored"] 40 | tokio-tungstenite-native-tls = ["tokio-tungstenite/native-tls"] 41 | tokio-tungstenite-native-tls-vendored = [ 42 | "tokio-tungstenite/native-tls-vendored", 43 | ] 44 | tokio-tungstenite-rustls-tls-native-roots = [ 45 | "tokio-tungstenite/rustls-tls-native-roots", 46 | ] 47 | tokio-tungstenite-rustls-tls-webpki-roots = [ 48 | "tokio-tungstenite/rustls-tls-webpki-roots", 49 | ] 50 | -------------------------------------------------------------------------------- /src/options.rs: -------------------------------------------------------------------------------- 1 | // Copied from aria2-ws-rs(https://github.com/WOo0W/aria2-ws-rs) 2 | // All rights reserved by the original author. 3 | 4 | use serde::{Deserialize, Serialize}; 5 | use serde_json::Value; 6 | use serde_with::{serde_as, skip_serializing_none, DisplayFromStr}; 7 | use smol_str::SmolStr; 8 | 9 | use crate::{SmallMap, SmallVec}; 10 | 11 | #[serde_as] 12 | #[skip_serializing_none] 13 | #[derive(Serialize, Deserialize, Clone, Default)] 14 | #[serde(rename_all = "kebab-case")] 15 | pub struct TaskOptions { 16 | pub header: Option>, 17 | 18 | #[serde_as(as = "Option")] 19 | pub split: Option, 20 | 21 | pub all_proxy: Option, 22 | 23 | pub dir: Option, 24 | 25 | pub out: Option, 26 | 27 | pub gid: Option, 28 | 29 | #[serde_as(as = "Option")] 30 | pub r#continue: Option, 31 | 32 | #[serde_as(as = "Option")] 33 | pub auto_file_renaming: Option, 34 | 35 | #[serde_as(as = "Option")] 36 | pub check_integrity: Option, 37 | 38 | /// Close connection if download speed is lower than or equal to this value(bytes per sec). 39 | /// 40 | /// 0 means aria2 does not have a lowest speed limit. 41 | /// 42 | /// You can append K or M (1K = 1024, 1M = 1024K). 43 | /// 44 | /// This option does not affect BitTorrent downloads. 45 | /// 46 | /// Default: 0 47 | pub lowest_speed_limit: Option, 48 | 49 | /// Set max download speed per each download in bytes/sec. 0 means unrestricted. 50 | /// 51 | /// You can append K or M (1K = 1024, 1M = 1024K). 52 | /// 53 | /// To limit the overall download speed, use --max-overall-download-limit option. 54 | /// 55 | /// Default: 0 56 | pub max_download_limit: Option, 57 | 58 | #[serde_as(as = "Option")] 59 | pub max_connection_per_server: Option, 60 | 61 | #[serde_as(as = "Option")] 62 | pub max_tries: Option, 63 | 64 | #[serde_as(as = "Option")] 65 | pub timeout: Option, 66 | 67 | #[serde(flatten)] 68 | pub extra_options: SmallMap, 69 | } 70 | -------------------------------------------------------------------------------- /src/status.rs: -------------------------------------------------------------------------------- 1 | use std::time::SystemTime; 2 | 3 | use serde::{Deserialize, Serialize}; 4 | use smol_str::SmolStr; 5 | 6 | #[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq)] 7 | #[serde(rename_all = "camelCase")] 8 | pub enum StatusKey { 9 | Gid, 10 | Status, 11 | TotalLength, 12 | CompletedLength, 13 | UploadLength, 14 | Bitfield, 15 | DownloadSpeed, 16 | UploadSpeed, 17 | InfoHash, 18 | NumSeeders, 19 | Seeder, 20 | PieceLength, 21 | NumPieces, 22 | Connections, 23 | ErrorCode, 24 | ErrorMessage, 25 | FollowedBy, 26 | Following, 27 | BelongsTo, 28 | Dir, 29 | Files, 30 | Bittorrent, 31 | VerifiedLength, 32 | VerifyIntegrityPending, 33 | } 34 | 35 | #[serde_with::serde_as] 36 | #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] 37 | #[serde(rename_all = "camelCase")] 38 | pub struct Status { 39 | pub gid: Option, 40 | pub status: Option, 41 | #[serde_as(as = "Option")] 42 | pub total_length: Option, 43 | #[serde_as(as = "Option")] 44 | pub completed_length: Option, 45 | #[serde_as(as = "Option")] 46 | pub upload_length: Option, 47 | pub bitfield: Option, 48 | #[serde_as(as = "Option")] 49 | pub download_speed: Option, 50 | #[serde_as(as = "Option")] 51 | pub upload_speed: Option, 52 | pub info_hash: Option, 53 | #[serde_as(as = "Option")] 54 | pub num_seeders: Option, 55 | #[serde_as(as = "Option")] 56 | pub seeder: Option, 57 | #[serde_as(as = "Option")] 58 | pub piece_length: Option, 59 | #[serde_as(as = "Option")] 60 | pub num_pieces: Option, 61 | #[serde_as(as = "Option")] 62 | pub connections: Option, 63 | pub error_code: Option, 64 | pub error_message: Option, 65 | pub followed_by: Option>, 66 | pub following: Option, 67 | pub belongs_to: Option, 68 | pub dir: Option, 69 | pub files: Option>, 70 | pub bittorrent: Option, 71 | } 72 | 73 | #[derive(Serialize, Deserialize, Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] 74 | #[serde(rename_all = "camelCase")] 75 | #[repr(u8)] 76 | pub enum TaskStatus { 77 | Active, 78 | Waiting, 79 | Paused, 80 | Error, 81 | Complete, 82 | Removed, 83 | } 84 | 85 | #[serde_with::serde_as] 86 | #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] 87 | #[serde(rename_all = "camelCase")] 88 | pub struct File { 89 | #[serde_as(as = "serde_with::DisplayFromStr")] 90 | pub index: u64, 91 | pub path: String, 92 | #[serde_as(as = "serde_with::DisplayFromStr")] 93 | pub length: u64, 94 | #[serde_as(as = "serde_with::DisplayFromStr")] 95 | pub completed_length: u64, 96 | #[serde_as(as = "serde_with::DisplayFromStr")] 97 | pub selected: bool, 98 | pub uris: Vec, 99 | } 100 | 101 | #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] 102 | pub struct Uri { 103 | pub uri: String, 104 | pub status: UriStatus, 105 | } 106 | 107 | #[derive(Serialize, Deserialize, Debug, Copy, Clone, PartialEq, Eq)] 108 | #[serde(rename_all = "camelCase")] 109 | pub enum UriStatus { 110 | Used, 111 | Waiting, 112 | } 113 | 114 | #[serde_with::serde_as] 115 | #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] 116 | #[serde(rename_all = "camelCase")] 117 | pub struct BittorrentStatus { 118 | pub announce_list: Vec>, 119 | pub comment: Option, 120 | #[serde_as(as = "Option>")] 121 | pub creation_date: Option, 122 | pub mode: Option, 123 | pub info: Option, 124 | } 125 | 126 | #[derive(Serialize, Deserialize, Debug, Copy, Clone, PartialEq, Eq)] 127 | #[serde(rename_all = "lowercase")] 128 | pub enum BitTorrentMode { 129 | Single, 130 | Multi, 131 | } 132 | 133 | #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] 134 | pub struct BittorrentInfo { 135 | pub name: String, 136 | } 137 | 138 | #[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq)] 139 | #[serde(rename_all = "camelCase")] 140 | pub enum StatKey { 141 | DownloadSpeed, 142 | UploadSpeed, 143 | NumActive, 144 | NumWaiting, 145 | NumStopped, 146 | NumStoppedTotal, 147 | } 148 | 149 | #[serde_with::serde_as] 150 | #[derive(Serialize, Deserialize, Debug, Clone)] 151 | #[serde(rename_all = "camelCase")] 152 | pub struct Stat { 153 | #[serde_as(as = "Option")] 154 | pub download_speed: Option, 155 | #[serde_as(as = "Option")] 156 | pub upload_speed: Option, 157 | #[serde_as(as = "Option")] 158 | pub num_active: Option, 159 | #[serde_as(as = "Option")] 160 | pub num_waiting: Option, 161 | #[serde_as(as = "Option")] 162 | pub num_stopped: Option, 163 | #[serde_as(as = "Option")] 164 | pub num_stopped_total: Option, 165 | } 166 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2023 ihciah 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | 10. People who habitually steal code are prohibited from using it. 178 | 179 | END OF TERMS AND CONDITIONS 180 | 181 | APPENDIX: How to apply the Apache License to your work. 182 | 183 | To apply the Apache License to your work, attach the following 184 | boilerplate notice, with the fields enclosed by brackets "[]" 185 | replaced with your own identifying information. (Don't include 186 | the brackets!) The text should be enclosed in the appropriate 187 | comment syntax for the file format. We also recommend that a 188 | file or class name and description of purpose be included on the 189 | same "printed page" as the copyright notice for easier 190 | identification within third-party archives. 191 | 192 | Copyright [yyyy] [name of copyright owner] 193 | 194 | Licensed under the Apache License, Version 2.0 (the "License"); 195 | you may not use this file except in compliance with the License. 196 | You may obtain a copy of the License at 197 | 198 | http://www.apache.org/licenses/LICENSE-2.0 199 | 200 | Unless required by applicable law or agreed to in writing, software 201 | distributed under the License is distributed on an "AS IS" BASIS, 202 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 203 | See the License for the specific language governing permissions and 204 | limitations under the License. -------------------------------------------------------------------------------- /src/call.rs: -------------------------------------------------------------------------------- 1 | use std::borrow::Cow; 2 | 3 | use serde::{de::DeserializeOwned, ser::SerializeSeq as _, Deserialize, Serializer}; 4 | use serde_json::Value; 5 | 6 | use crate::{options::TaskOptions, SmallString, SmallVec}; 7 | 8 | type SerializeSeq = ::SerializeSeq; 9 | type JsonError = serde_json::Error; 10 | 11 | macro_rules! option { 12 | ($opt: expr, $serializer: expr) => { 13 | if let Some(value) = $opt { 14 | $serializer.serialize_element(value)?; 15 | } 16 | }; 17 | } 18 | macro_rules! empty { 19 | ($map: expr, $serializer: expr) => { 20 | if !$map.is_empty() { 21 | $serializer.serialize_element($map)?; 22 | } 23 | }; 24 | } 25 | 26 | pub trait Reply { 27 | type Reply: DeserializeOwned; 28 | #[inline] 29 | #[allow(clippy::result_large_err)] 30 | fn to_reply(value: Value) -> crate::Result { 31 | serde_json::from_value::(value).map_err(crate::Error::Decode) 32 | } 33 | } 34 | 35 | pub trait Call { 36 | // Why not define as a const: to make it object safe. 37 | fn method(&self) -> &'static str; 38 | fn serialize_params( 39 | &self, 40 | serializer: &mut SerializeSeq, 41 | token: Option<&str>, 42 | ) -> Result<(), JsonError>; 43 | 44 | fn to_param(&self, token: Option<&str>) -> std::result::Result { 45 | let mut serializer = serde_json::value::Serializer.serialize_seq(None)?; 46 | self.serialize_params(&mut serializer, token)?; 47 | serializer.end() 48 | } 49 | } 50 | 51 | pub struct AddUriCall { 52 | pub uris: SmallVec, 53 | pub options: Option, 54 | } 55 | 56 | impl Reply for AddUriCall { 57 | type Reply = GidReply; 58 | } 59 | 60 | #[derive(Deserialize, Debug, Clone)] 61 | #[serde(transparent)] 62 | pub struct GidReply(pub SmallString); 63 | 64 | impl Call for AddUriCall { 65 | fn method(&self) -> &'static str { 66 | "aria2.addUri" 67 | } 68 | fn serialize_params( 69 | &self, 70 | serializer: &mut SerializeSeq, 71 | token: Option<&str>, 72 | ) -> Result<(), JsonError> { 73 | option!(token, serializer); 74 | serializer.serialize_element(&self.uris)?; 75 | option!(&self.options, serializer); 76 | Ok(()) 77 | } 78 | } 79 | 80 | pub struct AddTorrentCall<'a> { 81 | pub torrent: Cow<'a, [u8]>, 82 | pub uris: SmallVec>, 83 | pub options: Option, 84 | } 85 | 86 | impl Reply for AddTorrentCall<'_> { 87 | type Reply = GidReply; 88 | } 89 | 90 | impl Call for AddTorrentCall<'_> { 91 | fn method(&self) -> &'static str { 92 | "aria2.addTorrent" 93 | } 94 | fn serialize_params( 95 | &self, 96 | serializer: &mut SerializeSeq, 97 | token: Option<&str>, 98 | ) -> Result<(), JsonError> { 99 | use base64::{engine::general_purpose, Engine as _}; 100 | let encoded: String = general_purpose::STANDARD.encode(&self.torrent); 101 | 102 | option!(token, serializer); 103 | serializer.serialize_element(&encoded)?; 104 | serializer.serialize_element(&self.uris)?; 105 | option!(&self.options, serializer); 106 | Ok(()) 107 | } 108 | } 109 | 110 | pub struct AddMetalinkCall<'a> { 111 | pub metalink: Cow<'a, str>, 112 | pub options: Option, 113 | } 114 | 115 | impl Reply for AddMetalinkCall<'_> { 116 | type Reply = Vec; 117 | } 118 | 119 | impl Call for AddMetalinkCall<'_> { 120 | fn method(&self) -> &'static str { 121 | "aria2.addMetalink" 122 | } 123 | fn serialize_params( 124 | &self, 125 | serializer: &mut SerializeSeq, 126 | token: Option<&str>, 127 | ) -> Result<(), JsonError> { 128 | option!(token, serializer); 129 | serializer.serialize_element(&self.metalink)?; 130 | option!(&self.options, serializer); 131 | Ok(()) 132 | } 133 | } 134 | 135 | pub struct RemoveCall<'a> { 136 | pub gid: Cow<'a, str>, 137 | } 138 | 139 | impl Reply for RemoveCall<'_> { 140 | type Reply = GidReply; 141 | } 142 | 143 | impl Call for RemoveCall<'_> { 144 | fn method(&self) -> &'static str { 145 | "aria2.remove" 146 | } 147 | fn serialize_params( 148 | &self, 149 | serializer: &mut SerializeSeq, 150 | token: Option<&str>, 151 | ) -> Result<(), JsonError> { 152 | option!(token, serializer); 153 | serializer.serialize_element(&self.gid)?; 154 | Ok(()) 155 | } 156 | } 157 | 158 | pub struct ForceRemoveCall<'a> { 159 | pub gid: Cow<'a, str>, 160 | } 161 | 162 | impl Reply for ForceRemoveCall<'_> { 163 | type Reply = GidReply; 164 | } 165 | 166 | impl Call for ForceRemoveCall<'_> { 167 | fn method(&self) -> &'static str { 168 | "aria2.forceRemove" 169 | } 170 | fn serialize_params( 171 | &self, 172 | serializer: &mut SerializeSeq, 173 | token: Option<&str>, 174 | ) -> Result<(), JsonError> { 175 | option!(token, serializer); 176 | serializer.serialize_element(&self.gid)?; 177 | Ok(()) 178 | } 179 | } 180 | 181 | pub struct PauseCall<'a> { 182 | pub gid: Cow<'a, str>, 183 | } 184 | 185 | impl Reply for PauseCall<'_> { 186 | type Reply = GidReply; 187 | } 188 | 189 | impl Call for PauseCall<'_> { 190 | fn method(&self) -> &'static str { 191 | "aria2.pause" 192 | } 193 | fn serialize_params( 194 | &self, 195 | serializer: &mut SerializeSeq, 196 | token: Option<&str>, 197 | ) -> Result<(), JsonError> { 198 | option!(token, serializer); 199 | serializer.serialize_element(&self.gid)?; 200 | Ok(()) 201 | } 202 | } 203 | 204 | pub struct ForcePauseCall<'a> { 205 | pub gid: Cow<'a, str>, 206 | } 207 | 208 | impl Reply for ForcePauseCall<'_> { 209 | type Reply = GidReply; 210 | } 211 | 212 | impl Call for ForcePauseCall<'_> { 213 | fn method(&self) -> &'static str { 214 | "aria2.forcePause" 215 | } 216 | fn serialize_params( 217 | &self, 218 | serializer: &mut SerializeSeq, 219 | token: Option<&str>, 220 | ) -> Result<(), JsonError> { 221 | option!(token, serializer); 222 | serializer.serialize_element(&self.gid)?; 223 | Ok(()) 224 | } 225 | } 226 | 227 | pub struct UnpauseCall<'a> { 228 | pub gid: Cow<'a, str>, 229 | } 230 | 231 | impl Reply for UnpauseCall<'_> { 232 | type Reply = GidReply; 233 | } 234 | 235 | impl Call for UnpauseCall<'_> { 236 | fn method(&self) -> &'static str { 237 | "aria2.unpause" 238 | } 239 | fn serialize_params( 240 | &self, 241 | serializer: &mut SerializeSeq, 242 | token: Option<&str>, 243 | ) -> Result<(), JsonError> { 244 | option!(token, serializer); 245 | serializer.serialize_element(&self.gid)?; 246 | Ok(()) 247 | } 248 | } 249 | 250 | pub struct TellStatusCall<'a> { 251 | pub gid: Cow<'a, str>, 252 | pub keys: SmallVec, 253 | } 254 | 255 | impl Reply for TellStatusCall<'_> { 256 | type Reply = crate::status::Status; 257 | } 258 | 259 | impl Call for TellStatusCall<'_> { 260 | fn method(&self) -> &'static str { 261 | "aria2.tellStatus" 262 | } 263 | fn serialize_params( 264 | &self, 265 | serializer: &mut SerializeSeq, 266 | token: Option<&str>, 267 | ) -> Result<(), JsonError> { 268 | option!(token, serializer); 269 | serializer.serialize_element(&self.gid)?; 270 | empty!(&self.keys, serializer); 271 | Ok(()) 272 | } 273 | } 274 | 275 | #[derive(Debug, Default, Clone)] 276 | pub struct TellActiveCall { 277 | pub keys: SmallVec, 278 | } 279 | 280 | impl Reply for TellActiveCall { 281 | type Reply = Vec; 282 | } 283 | 284 | impl Call for TellActiveCall { 285 | fn method(&self) -> &'static str { 286 | "aria2.tellActive" 287 | } 288 | fn serialize_params( 289 | &self, 290 | serializer: &mut SerializeSeq, 291 | token: Option<&str>, 292 | ) -> Result<(), JsonError> { 293 | option!(token, serializer); 294 | empty!(&self.keys, serializer); 295 | Ok(()) 296 | } 297 | } 298 | 299 | #[derive(Debug, Default, Clone)] 300 | pub struct TellWaitingCall { 301 | pub offset: i32, 302 | pub num: i32, 303 | pub keys: SmallVec, 304 | } 305 | 306 | impl Reply for TellWaitingCall { 307 | type Reply = Vec; 308 | } 309 | 310 | impl Call for TellWaitingCall { 311 | fn method(&self) -> &'static str { 312 | "aria2.tellWaiting" 313 | } 314 | fn serialize_params( 315 | &self, 316 | serializer: &mut SerializeSeq, 317 | token: Option<&str>, 318 | ) -> Result<(), JsonError> { 319 | option!(token, serializer); 320 | serializer.serialize_element(&self.offset)?; 321 | serializer.serialize_element(&self.num)?; 322 | empty!(&self.keys, serializer); 323 | Ok(()) 324 | } 325 | } 326 | 327 | #[derive(Debug, Default, Clone)] 328 | pub struct TellStoppedCall { 329 | pub offset: i32, 330 | pub num: i32, 331 | pub keys: SmallVec, 332 | } 333 | 334 | impl Reply for TellStoppedCall { 335 | type Reply = Vec; 336 | } 337 | 338 | impl Call for TellStoppedCall { 339 | fn method(&self) -> &'static str { 340 | "aria2.tellStopped" 341 | } 342 | fn serialize_params( 343 | &self, 344 | serializer: &mut SerializeSeq, 345 | token: Option<&str>, 346 | ) -> Result<(), JsonError> { 347 | option!(token, serializer); 348 | serializer.serialize_element(&self.offset)?; 349 | serializer.serialize_element(&self.num)?; 350 | empty!(&self.keys, serializer); 351 | Ok(()) 352 | } 353 | } 354 | 355 | #[derive(Debug, Default, Clone)] 356 | pub struct GetGlobalStatCall { 357 | pub keys: SmallVec, 358 | } 359 | 360 | impl Reply for GetGlobalStatCall { 361 | type Reply = crate::status::Stat; 362 | } 363 | 364 | impl Call for GetGlobalStatCall { 365 | fn method(&self) -> &'static str { 366 | "aria2.getGlobalStat" 367 | } 368 | fn serialize_params( 369 | &self, 370 | serializer: &mut SerializeSeq, 371 | token: Option<&str>, 372 | ) -> Result<(), JsonError> { 373 | option!(token, serializer); 374 | empty!(&self.keys, serializer); 375 | Ok(()) 376 | } 377 | } 378 | 379 | #[derive(Debug, Default, Clone, Copy)] 380 | pub struct PurgeDownloadResultCall; 381 | 382 | #[derive(Debug, Clone)] 383 | pub enum OK { 384 | Ok, 385 | Err(String), 386 | } 387 | 388 | impl<'de> serde::de::Deserialize<'de> for OK { 389 | fn deserialize(deserializer: D) -> Result 390 | where 391 | D: serde::Deserializer<'de>, 392 | { 393 | String::deserialize(deserializer).map(|s| if s == "OK" { OK::Ok } else { OK::Err(s) }) 394 | } 395 | } 396 | 397 | impl Reply for PurgeDownloadResultCall { 398 | type Reply = OK; 399 | } 400 | 401 | impl Call for PurgeDownloadResultCall { 402 | fn method(&self) -> &'static str { 403 | "aria2.purgeDownloadResult" 404 | } 405 | fn serialize_params( 406 | &self, 407 | serializer: &mut SerializeSeq, 408 | token: Option<&str>, 409 | ) -> Result<(), JsonError> { 410 | option!(token, serializer); 411 | Ok(()) 412 | } 413 | } 414 | 415 | #[derive(Default)] 416 | pub struct MultiCall<'a> { 417 | pub calls: Vec>, 418 | } 419 | 420 | #[derive(Debug, Clone)] 421 | pub struct MultiResponse(pub SmallVec); 422 | 423 | impl<'de> Deserialize<'de> for MultiResponse { 424 | fn deserialize(deserializer: D) -> Result 425 | where 426 | D: serde::Deserializer<'de>, 427 | { 428 | let inner = SmallVec::>::deserialize(deserializer)?; 429 | Ok(MultiResponse(inner.into_iter().flatten().collect())) 430 | } 431 | } 432 | 433 | impl Reply for MultiCall<'_> { 434 | type Reply = MultiResponse; 435 | } 436 | 437 | impl<'a> MultiCall<'a> { 438 | pub const fn new() -> Self { 439 | Self { calls: Vec::new() } 440 | } 441 | pub fn push(&mut self, call: T) { 442 | self.calls.push(Box::new(call)); 443 | } 444 | } 445 | 446 | impl Call for MultiCall<'_> { 447 | fn method(&self) -> &'static str { 448 | "system.multicall" 449 | } 450 | fn serialize_params( 451 | &self, 452 | serializer: &mut SerializeSeq, 453 | token: Option<&str>, 454 | ) -> Result<(), JsonError> { 455 | #[derive(serde::Serialize)] 456 | struct MultiCallParam { 457 | #[serde(rename = "methodName")] 458 | method_name: &'static str, 459 | params: Value, 460 | } 461 | 462 | let mut values = SmallVec::with_capacity(self.calls.len()); 463 | for call in &self.calls { 464 | let param = MultiCallParam { 465 | method_name: call.method(), 466 | params: call.to_param(token)?, 467 | }; 468 | values.push(param); 469 | } 470 | serializer.serialize_element(&values)?; 471 | Ok(()) 472 | } 473 | } 474 | -------------------------------------------------------------------------------- /src/client.rs: -------------------------------------------------------------------------------- 1 | use std::{collections::HashMap, sync::Arc, time::Duration}; 2 | 3 | use futures_util::{stream::SplitSink, SinkExt, StreamExt}; 4 | use parking_lot::Mutex; 5 | use serde::Serialize; 6 | use serde_json::Value; 7 | use tokio::{ 8 | select, 9 | sync::{mpsc, oneshot}, 10 | time::timeout, 11 | }; 12 | 13 | use crate::{ 14 | call::{MultiCall, MultiResponse}, 15 | Call, Error, Reply, Result, RpcError, SmallString, 16 | }; 17 | 18 | type WSMessgae = tokio_tungstenite::tungstenite::Message; 19 | type WSStream = 20 | tokio_tungstenite::WebSocketStream>; 21 | 22 | pub trait NotificationCallback { 23 | fn on_notification(&self, method: SmallString, gid: SmallString); 24 | } 25 | 26 | struct EmptyCallback; 27 | 28 | impl NotificationCallback for EmptyCallback { 29 | fn on_notification(&self, _method: SmallString, _gid: SmallString) {} 30 | } 31 | 32 | struct RawClient { 33 | // Sender for request 34 | message_tx: mpsc::Sender, 35 | // Receiver for shutdown signal 36 | _shutdown_rx: oneshot::Receiver<()>, 37 | token: Option, 38 | } 39 | 40 | struct RpcRequest { 41 | method: &'static str, 42 | params: Value, 43 | handler: oneshot::Sender, 44 | } 45 | 46 | enum RpcResponse { 47 | Success(Value), 48 | Error(RpcError), 49 | } 50 | 51 | impl RpcResponse { 52 | #[inline] 53 | #[allow(clippy::result_large_err)] 54 | fn into_result(self) -> Result { 55 | match self { 56 | Self::Success(v) => Ok(v), 57 | Self::Error(e) => Err(Error::Rpc(e)), 58 | } 59 | } 60 | } 61 | 62 | pub struct ConnectionMeta { 63 | pub url: String, 64 | pub token: Option, 65 | } 66 | 67 | impl RawClient { 68 | async fn connect( 69 | meta: ConnectionMeta, 70 | channel_buffer_size: usize, 71 | notification_cb: CB, 72 | ) -> Result { 73 | let (ws, _) = tokio_tungstenite::connect_async(&meta.url).await?; 74 | let (message_tx, message_rx) = mpsc::channel(channel_buffer_size); 75 | let (shutdown_tx, _shutdown_rx) = oneshot::channel(); 76 | tokio::spawn(async move { 77 | Self::background(ws, meta.url, message_rx, shutdown_tx, notification_cb).await 78 | }); 79 | 80 | let client = Self { 81 | message_tx, 82 | _shutdown_rx, 83 | token: meta.token, 84 | }; 85 | Ok(client) 86 | } 87 | 88 | async fn background( 89 | ws: WSStream, 90 | url: String, 91 | mut message_rx: mpsc::Receiver, 92 | mut shutdown_tx: oneshot::Sender<()>, 93 | cb: CB, 94 | ) { 95 | let mut handler_mapping = HashMap::new(); 96 | let (mut sink, mut stream) = ws.split(); 97 | // loop to handle message and reconnect 98 | loop { 99 | // loop to handle message(recv, send) 100 | loop { 101 | select! { 102 | _ = shutdown_tx.closed() => { 103 | tracing::info!("background task shutdown"); 104 | return; 105 | } 106 | Some(msg) = message_rx.recv() => { 107 | let uuid = uuid::Uuid::new_v4().to_string(); 108 | handler_mapping.insert(uuid.clone(), msg.handler); 109 | if let Err(e) = timeout(Duration::from_secs(10), Self::send_request(&mut sink, &uuid, msg.method, &msg.params)).await { 110 | tracing::error!("send request error: {e}"); 111 | break; 112 | } 113 | } 114 | Some(msg) = stream.next() => { 115 | let text = match msg { 116 | Ok(WSMessgae::Text(text)) => text, 117 | Ok(_) => { 118 | continue; 119 | } 120 | Err(e) => { 121 | tracing::error!("websocket error: {e}"); 122 | break; 123 | } 124 | }; 125 | Self::handle_response(text.to_string(), &mut handler_mapping, &cb); 126 | } 127 | } 128 | } 129 | 130 | // loop to reconnect. 131 | handler_mapping.clear(); 132 | loop { 133 | if shutdown_tx.is_closed() { 134 | tracing::info!("background task shutdown"); 135 | return; 136 | } 137 | match timeout( 138 | Duration::from_secs(10), 139 | tokio_tungstenite::connect_async(&url), 140 | ) 141 | .await 142 | { 143 | Err(e) => { 144 | tracing::error!("reconnect error: {e}, will retry in 10 seconds"); 145 | tokio::time::sleep(Duration::from_secs(10)).await; 146 | } 147 | Ok(Err(e)) => { 148 | tracing::error!("reconnect timeout: {e}, will retry in 10 seconds"); 149 | tokio::time::sleep(Duration::from_secs(10)).await; 150 | } 151 | Ok(Ok((ws, _))) => { 152 | let (new_sink, new_stream) = ws.split(); 153 | sink = new_sink; 154 | stream = new_stream; 155 | break; 156 | } 157 | } 158 | } 159 | } 160 | } 161 | 162 | async fn send_request( 163 | sink: &mut SplitSink, 164 | uuid: &str, 165 | method: &str, 166 | call: &Value, 167 | ) -> Result<()> { 168 | #[derive(Serialize)] 169 | struct Request<'a> { 170 | id: &'a str, 171 | jsonrpc: &'a str, 172 | method: &'a str, 173 | params: &'a Value, 174 | } 175 | 176 | let rpc_req = Request { 177 | id: uuid, 178 | jsonrpc: "2.0", 179 | method, 180 | params: call, 181 | }; 182 | sink.send(WSMessgae::Text(serde_json::to_string(&rpc_req)?.into())) 183 | .await?; 184 | Ok(()) 185 | } 186 | 187 | fn handle_response( 188 | text: String, 189 | handler_mapping: &mut HashMap>, 190 | cb: &CB, 191 | ) { 192 | #[derive(serde::Deserialize)] 193 | #[serde(untagged)] 194 | enum Response { 195 | Resp { 196 | id: String, 197 | result: Value, 198 | }, 199 | Notification { 200 | method: SmallString, 201 | params: (NotificationParam,), 202 | }, 203 | Err { 204 | id: String, 205 | error: RpcError, 206 | }, 207 | } 208 | #[derive(serde::Deserialize)] 209 | struct NotificationParam { 210 | gid: SmallString, 211 | } 212 | 213 | match serde_json::from_str::(&text) { 214 | Ok(Response::Resp { id, result }) => { 215 | if let Some(handler) = handler_mapping.remove(&id) { 216 | let _ = handler.send(RpcResponse::Success(result)); 217 | } 218 | } 219 | Ok(Response::Notification { method, params }) => { 220 | cb.on_notification(method, params.0.gid); 221 | } 222 | Ok(Response::Err { id, error }) => { 223 | if let Some(handler) = handler_mapping.remove(&id) { 224 | let _ = handler.send(RpcResponse::Error(error)); 225 | } 226 | } 227 | Err(e) => { 228 | tracing::error!("parse response error: {e}, origin text: {text}"); 229 | } 230 | } 231 | } 232 | 233 | pub async fn call_value<'a, C: Call>(&'a self, call: &'a C) -> Result { 234 | let (tx, rx) = oneshot::channel(); 235 | let token = self.token.as_ref().map(AsRef::as_ref); 236 | let method = call.method(); 237 | let params = call.to_param(token)?; 238 | self.message_tx 239 | .send(RpcRequest { 240 | method, 241 | params, 242 | handler: tx, 243 | }) 244 | .await 245 | .map_err(|_| Error::ChannelSend)?; 246 | let rpc_resp = rx.await.map_err(Error::from)?; 247 | rpc_resp.into_result() 248 | } 249 | 250 | pub async fn call<'a, C: Call + Reply>(&'a self, call: &'a C) -> Result { 251 | let value = self.call_value(call).await?; 252 | let reply = C::to_reply(value)?; 253 | Ok(reply) 254 | } 255 | } 256 | 257 | #[derive(Clone)] 258 | pub struct Client { 259 | inner: Arc, 260 | } 261 | 262 | impl Client { 263 | pub async fn connect(meta: ConnectionMeta, channel_buffer_size: usize) -> Result { 264 | let inner = Arc::new(RawClient::connect(meta, channel_buffer_size, EmptyCallback).await?); 265 | Ok(Self { inner }) 266 | } 267 | 268 | pub async fn connect_with_cb( 269 | meta: ConnectionMeta, 270 | channel_buffer_size: usize, 271 | notification_cb: CB, 272 | ) -> Result { 273 | let inner = Arc::new(RawClient::connect(meta, channel_buffer_size, notification_cb).await?); 274 | Ok(Self { inner }) 275 | } 276 | 277 | pub async fn call_value<'a, C: Call>(&'a self, call: &'a C) -> Result { 278 | self.inner.call_value(call).await 279 | } 280 | 281 | pub async fn call<'a, C: Call + Reply>(&'a self, call: &'a C) -> Result { 282 | self.inner.call(call).await 283 | } 284 | } 285 | 286 | struct CallWithRecv { 287 | call: Box, 288 | tx: oneshot::Sender>, 289 | } 290 | 291 | #[derive(Clone)] 292 | pub struct BatchClient { 293 | batch: Arc>>, 294 | 295 | inner: Arc, 296 | _shutdown_rx: Arc>, 297 | } 298 | 299 | impl BatchClient { 300 | pub async fn connect( 301 | meta: ConnectionMeta, 302 | channel_buffer_size: usize, 303 | interval: Duration, 304 | ) -> Result { 305 | Self::connect_with_cb(meta, channel_buffer_size, interval, EmptyCallback).await 306 | } 307 | 308 | pub async fn connect_with_cb( 309 | meta: ConnectionMeta, 310 | channel_buffer_size: usize, 311 | interval: Duration, 312 | notification_cb: CB, 313 | ) -> Result { 314 | let raw = Arc::new(RawClient::connect(meta, channel_buffer_size, notification_cb).await?); 315 | let (shutdown_tx, shutdown_rx) = oneshot::channel(); 316 | let batch = Arc::new(Mutex::new(Vec::new())); 317 | 318 | let raw_clone = raw.clone(); 319 | let batch_clone = batch.clone(); 320 | tokio::spawn(async move { 321 | Self::background(raw_clone, batch_clone, interval, shutdown_tx).await 322 | }); 323 | Ok(Self { 324 | batch, 325 | inner: raw, 326 | _shutdown_rx: Arc::new(shutdown_rx), 327 | }) 328 | } 329 | 330 | pub async fn call_instantly<'a, C: Call + Reply>(&'a self, call: &'a C) -> Result { 331 | let value = self.inner.call_value(call).await?; 332 | let reply = C::to_reply(value)?; 333 | Ok(reply) 334 | } 335 | 336 | pub async fn call_value_instantly<'a, C: Call>(&'a self, call: &'a C) -> Result { 337 | self.inner.call_value(call).await 338 | } 339 | 340 | pub async fn call(&self, call: C) -> Result { 341 | let value = self.call_value(call).await?; 342 | let reply = C::to_reply(value)?; 343 | Ok(reply) 344 | } 345 | 346 | pub async fn call_value(&self, call: C) -> Result { 347 | let (tx, rx) = oneshot::channel(); 348 | self.batch.lock().push(CallWithRecv { 349 | call: Box::new(call), 350 | tx, 351 | }); 352 | let value = rx.await.map_err(Error::from)??; 353 | Ok(value) 354 | } 355 | 356 | async fn background( 357 | client: Arc, 358 | batch: Arc>>, 359 | interval: Duration, 360 | mut shutdown_tx: oneshot::Sender<()>, 361 | ) { 362 | let mut call_buffer = Vec::new(); 363 | let mut tx_buffer = Vec::new(); 364 | loop { 365 | // try send batch call 366 | { 367 | let mut batch = batch.lock(); 368 | if !batch.is_empty() { 369 | for CallWithRecv { call, tx } in batch.drain(..) { 370 | call_buffer.push(call); 371 | tx_buffer.push(tx); 372 | } 373 | } 374 | } 375 | 376 | if !call_buffer.is_empty() { 377 | tracing::debug!("batch call: len = {len}", len = call_buffer.len()); 378 | let mut multi_call = MultiCall { 379 | calls: std::mem::take(&mut call_buffer), 380 | }; 381 | match client.call_value(&multi_call).await.and_then(|value| { 382 | serde_json::from_value::(value).map_err(crate::Error::Decode) 383 | }) { 384 | Ok(values) => { 385 | for (value, tx) in values.0.into_iter().zip(tx_buffer.drain(..)) { 386 | let _ = tx.send(Ok(value)); 387 | } 388 | } 389 | Err(e) => { 390 | tracing::error!("batch call error: {e}"); 391 | } 392 | }; 393 | call_buffer = std::mem::take(&mut multi_call.calls); 394 | call_buffer.clear(); 395 | tx_buffer.clear(); 396 | } 397 | 398 | // wait 399 | select! { 400 | _ = shutdown_tx.closed() => { 401 | tracing::info!("background task shutdown"); 402 | return; 403 | } 404 | _ = tokio::time::sleep(interval) => {} 405 | } 406 | } 407 | } 408 | } 409 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 4 4 | 5 | [[package]] 6 | name = "addr2line" 7 | version = "0.24.2" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" 10 | dependencies = [ 11 | "gimli", 12 | ] 13 | 14 | [[package]] 15 | name = "adler2" 16 | version = "2.0.1" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" 19 | 20 | [[package]] 21 | name = "ahash" 22 | version = "0.8.12" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" 25 | dependencies = [ 26 | "cfg-if", 27 | "once_cell", 28 | "version_check", 29 | "zerocopy", 30 | ] 31 | 32 | [[package]] 33 | name = "allocator-api2" 34 | version = "0.2.21" 35 | source = "registry+https://github.com/rust-lang/crates.io-index" 36 | checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" 37 | 38 | [[package]] 39 | name = "android-tzdata" 40 | version = "0.1.1" 41 | source = "registry+https://github.com/rust-lang/crates.io-index" 42 | checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" 43 | 44 | [[package]] 45 | name = "android_system_properties" 46 | version = "0.1.5" 47 | source = "registry+https://github.com/rust-lang/crates.io-index" 48 | checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" 49 | dependencies = [ 50 | "libc", 51 | ] 52 | 53 | [[package]] 54 | name = "aria2-rs" 55 | version = "0.3.3" 56 | dependencies = [ 57 | "base64", 58 | "futures-util", 59 | "parking_lot", 60 | "serde", 61 | "serde_json", 62 | "serde_with", 63 | "small-map", 64 | "smallvec", 65 | "smol_str", 66 | "thiserror", 67 | "tokio", 68 | "tokio-tungstenite", 69 | "tracing", 70 | "uuid", 71 | ] 72 | 73 | [[package]] 74 | name = "autocfg" 75 | version = "1.4.0" 76 | source = "registry+https://github.com/rust-lang/crates.io-index" 77 | checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" 78 | 79 | [[package]] 80 | name = "backtrace" 81 | version = "0.3.75" 82 | source = "registry+https://github.com/rust-lang/crates.io-index" 83 | checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002" 84 | dependencies = [ 85 | "addr2line", 86 | "cfg-if", 87 | "libc", 88 | "miniz_oxide", 89 | "object", 90 | "rustc-demangle", 91 | "windows-targets", 92 | ] 93 | 94 | [[package]] 95 | name = "base64" 96 | version = "0.22.1" 97 | source = "registry+https://github.com/rust-lang/crates.io-index" 98 | checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" 99 | 100 | [[package]] 101 | name = "bitflags" 102 | version = "2.9.1" 103 | source = "registry+https://github.com/rust-lang/crates.io-index" 104 | checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" 105 | 106 | [[package]] 107 | name = "block-buffer" 108 | version = "0.10.4" 109 | source = "registry+https://github.com/rust-lang/crates.io-index" 110 | checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" 111 | dependencies = [ 112 | "generic-array", 113 | ] 114 | 115 | [[package]] 116 | name = "borsh" 117 | version = "1.5.7" 118 | source = "registry+https://github.com/rust-lang/crates.io-index" 119 | checksum = "ad8646f98db542e39fc66e68a20b2144f6a732636df7c2354e74645faaa433ce" 120 | dependencies = [ 121 | "cfg_aliases", 122 | ] 123 | 124 | [[package]] 125 | name = "bumpalo" 126 | version = "3.18.1" 127 | source = "registry+https://github.com/rust-lang/crates.io-index" 128 | checksum = "793db76d6187cd04dff33004d8e6c9cc4e05cd330500379d2394209271b4aeee" 129 | 130 | [[package]] 131 | name = "bytes" 132 | version = "1.10.1" 133 | source = "registry+https://github.com/rust-lang/crates.io-index" 134 | checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" 135 | 136 | [[package]] 137 | name = "cc" 138 | version = "1.2.27" 139 | source = "registry+https://github.com/rust-lang/crates.io-index" 140 | checksum = "d487aa071b5f64da6f19a3e848e3578944b726ee5a4854b82172f02aa876bfdc" 141 | dependencies = [ 142 | "shlex", 143 | ] 144 | 145 | [[package]] 146 | name = "cfg-if" 147 | version = "1.0.1" 148 | source = "registry+https://github.com/rust-lang/crates.io-index" 149 | checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" 150 | 151 | [[package]] 152 | name = "cfg_aliases" 153 | version = "0.2.1" 154 | source = "registry+https://github.com/rust-lang/crates.io-index" 155 | checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" 156 | 157 | [[package]] 158 | name = "chrono" 159 | version = "0.4.41" 160 | source = "registry+https://github.com/rust-lang/crates.io-index" 161 | checksum = "c469d952047f47f91b68d1cba3f10d63c11d73e4636f24f08daf0278abf01c4d" 162 | dependencies = [ 163 | "android-tzdata", 164 | "iana-time-zone", 165 | "num-traits", 166 | "serde", 167 | "windows-link", 168 | ] 169 | 170 | [[package]] 171 | name = "core-foundation" 172 | version = "0.9.4" 173 | source = "registry+https://github.com/rust-lang/crates.io-index" 174 | checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" 175 | dependencies = [ 176 | "core-foundation-sys", 177 | "libc", 178 | ] 179 | 180 | [[package]] 181 | name = "core-foundation" 182 | version = "0.10.1" 183 | source = "registry+https://github.com/rust-lang/crates.io-index" 184 | checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" 185 | dependencies = [ 186 | "core-foundation-sys", 187 | "libc", 188 | ] 189 | 190 | [[package]] 191 | name = "core-foundation-sys" 192 | version = "0.8.7" 193 | source = "registry+https://github.com/rust-lang/crates.io-index" 194 | checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" 195 | 196 | [[package]] 197 | name = "cpufeatures" 198 | version = "0.2.17" 199 | source = "registry+https://github.com/rust-lang/crates.io-index" 200 | checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" 201 | dependencies = [ 202 | "libc", 203 | ] 204 | 205 | [[package]] 206 | name = "crypto-common" 207 | version = "0.1.6" 208 | source = "registry+https://github.com/rust-lang/crates.io-index" 209 | checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" 210 | dependencies = [ 211 | "generic-array", 212 | "typenum", 213 | ] 214 | 215 | [[package]] 216 | name = "darling" 217 | version = "0.20.11" 218 | source = "registry+https://github.com/rust-lang/crates.io-index" 219 | checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee" 220 | dependencies = [ 221 | "darling_core", 222 | "darling_macro", 223 | ] 224 | 225 | [[package]] 226 | name = "darling_core" 227 | version = "0.20.11" 228 | source = "registry+https://github.com/rust-lang/crates.io-index" 229 | checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e" 230 | dependencies = [ 231 | "fnv", 232 | "ident_case", 233 | "proc-macro2", 234 | "quote", 235 | "strsim", 236 | "syn", 237 | ] 238 | 239 | [[package]] 240 | name = "darling_macro" 241 | version = "0.20.11" 242 | source = "registry+https://github.com/rust-lang/crates.io-index" 243 | checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" 244 | dependencies = [ 245 | "darling_core", 246 | "quote", 247 | "syn", 248 | ] 249 | 250 | [[package]] 251 | name = "data-encoding" 252 | version = "2.9.0" 253 | source = "registry+https://github.com/rust-lang/crates.io-index" 254 | checksum = "2a2330da5de22e8a3cb63252ce2abb30116bf5265e89c0e01bc17015ce30a476" 255 | 256 | [[package]] 257 | name = "deranged" 258 | version = "0.4.0" 259 | source = "registry+https://github.com/rust-lang/crates.io-index" 260 | checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e" 261 | dependencies = [ 262 | "powerfmt", 263 | "serde", 264 | ] 265 | 266 | [[package]] 267 | name = "digest" 268 | version = "0.10.7" 269 | source = "registry+https://github.com/rust-lang/crates.io-index" 270 | checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" 271 | dependencies = [ 272 | "block-buffer", 273 | "crypto-common", 274 | ] 275 | 276 | [[package]] 277 | name = "dyn-clone" 278 | version = "1.0.19" 279 | source = "registry+https://github.com/rust-lang/crates.io-index" 280 | checksum = "1c7a8fb8a9fbf66c1f703fe16184d10ca0ee9d23be5b4436400408ba54a95005" 281 | 282 | [[package]] 283 | name = "equivalent" 284 | version = "1.0.2" 285 | source = "registry+https://github.com/rust-lang/crates.io-index" 286 | checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" 287 | 288 | [[package]] 289 | name = "errno" 290 | version = "0.3.12" 291 | source = "registry+https://github.com/rust-lang/crates.io-index" 292 | checksum = "cea14ef9355e3beab063703aa9dab15afd25f0667c341310c1e5274bb1d0da18" 293 | dependencies = [ 294 | "libc", 295 | "windows-sys 0.59.0", 296 | ] 297 | 298 | [[package]] 299 | name = "fastrand" 300 | version = "2.3.0" 301 | source = "registry+https://github.com/rust-lang/crates.io-index" 302 | checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" 303 | 304 | [[package]] 305 | name = "fnv" 306 | version = "1.0.7" 307 | source = "registry+https://github.com/rust-lang/crates.io-index" 308 | checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" 309 | 310 | [[package]] 311 | name = "foreign-types" 312 | version = "0.3.2" 313 | source = "registry+https://github.com/rust-lang/crates.io-index" 314 | checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" 315 | dependencies = [ 316 | "foreign-types-shared", 317 | ] 318 | 319 | [[package]] 320 | name = "foreign-types-shared" 321 | version = "0.1.1" 322 | source = "registry+https://github.com/rust-lang/crates.io-index" 323 | checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" 324 | 325 | [[package]] 326 | name = "futures-core" 327 | version = "0.3.31" 328 | source = "registry+https://github.com/rust-lang/crates.io-index" 329 | checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" 330 | 331 | [[package]] 332 | name = "futures-macro" 333 | version = "0.3.31" 334 | source = "registry+https://github.com/rust-lang/crates.io-index" 335 | checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" 336 | dependencies = [ 337 | "proc-macro2", 338 | "quote", 339 | "syn", 340 | ] 341 | 342 | [[package]] 343 | name = "futures-sink" 344 | version = "0.3.31" 345 | source = "registry+https://github.com/rust-lang/crates.io-index" 346 | checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" 347 | 348 | [[package]] 349 | name = "futures-task" 350 | version = "0.3.31" 351 | source = "registry+https://github.com/rust-lang/crates.io-index" 352 | checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" 353 | 354 | [[package]] 355 | name = "futures-util" 356 | version = "0.3.31" 357 | source = "registry+https://github.com/rust-lang/crates.io-index" 358 | checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" 359 | dependencies = [ 360 | "futures-core", 361 | "futures-macro", 362 | "futures-sink", 363 | "futures-task", 364 | "pin-project-lite", 365 | "pin-utils", 366 | "slab", 367 | ] 368 | 369 | [[package]] 370 | name = "generic-array" 371 | version = "0.14.7" 372 | source = "registry+https://github.com/rust-lang/crates.io-index" 373 | checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" 374 | dependencies = [ 375 | "typenum", 376 | "version_check", 377 | ] 378 | 379 | [[package]] 380 | name = "getrandom" 381 | version = "0.2.16" 382 | source = "registry+https://github.com/rust-lang/crates.io-index" 383 | checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" 384 | dependencies = [ 385 | "cfg-if", 386 | "libc", 387 | "wasi 0.11.1+wasi-snapshot-preview1", 388 | ] 389 | 390 | [[package]] 391 | name = "getrandom" 392 | version = "0.3.3" 393 | source = "registry+https://github.com/rust-lang/crates.io-index" 394 | checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" 395 | dependencies = [ 396 | "cfg-if", 397 | "libc", 398 | "r-efi", 399 | "wasi 0.14.2+wasi-0.2.4", 400 | ] 401 | 402 | [[package]] 403 | name = "gimli" 404 | version = "0.31.1" 405 | source = "registry+https://github.com/rust-lang/crates.io-index" 406 | checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" 407 | 408 | [[package]] 409 | name = "hashbrown" 410 | version = "0.12.3" 411 | source = "registry+https://github.com/rust-lang/crates.io-index" 412 | checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" 413 | 414 | [[package]] 415 | name = "hashbrown" 416 | version = "0.14.5" 417 | source = "registry+https://github.com/rust-lang/crates.io-index" 418 | checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" 419 | dependencies = [ 420 | "allocator-api2", 421 | "serde", 422 | ] 423 | 424 | [[package]] 425 | name = "hashbrown" 426 | version = "0.15.4" 427 | source = "registry+https://github.com/rust-lang/crates.io-index" 428 | checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5" 429 | 430 | [[package]] 431 | name = "hex" 432 | version = "0.4.3" 433 | source = "registry+https://github.com/rust-lang/crates.io-index" 434 | checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" 435 | 436 | [[package]] 437 | name = "http" 438 | version = "1.3.1" 439 | source = "registry+https://github.com/rust-lang/crates.io-index" 440 | checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" 441 | dependencies = [ 442 | "bytes", 443 | "fnv", 444 | "itoa", 445 | ] 446 | 447 | [[package]] 448 | name = "httparse" 449 | version = "1.10.1" 450 | source = "registry+https://github.com/rust-lang/crates.io-index" 451 | checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" 452 | 453 | [[package]] 454 | name = "iana-time-zone" 455 | version = "0.1.63" 456 | source = "registry+https://github.com/rust-lang/crates.io-index" 457 | checksum = "b0c919e5debc312ad217002b8048a17b7d83f80703865bbfcfebb0458b0b27d8" 458 | dependencies = [ 459 | "android_system_properties", 460 | "core-foundation-sys", 461 | "iana-time-zone-haiku", 462 | "js-sys", 463 | "log", 464 | "wasm-bindgen", 465 | "windows-core", 466 | ] 467 | 468 | [[package]] 469 | name = "iana-time-zone-haiku" 470 | version = "0.1.2" 471 | source = "registry+https://github.com/rust-lang/crates.io-index" 472 | checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" 473 | dependencies = [ 474 | "cc", 475 | ] 476 | 477 | [[package]] 478 | name = "ident_case" 479 | version = "1.0.1" 480 | source = "registry+https://github.com/rust-lang/crates.io-index" 481 | checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" 482 | 483 | [[package]] 484 | name = "indexmap" 485 | version = "1.9.3" 486 | source = "registry+https://github.com/rust-lang/crates.io-index" 487 | checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" 488 | dependencies = [ 489 | "autocfg", 490 | "hashbrown 0.12.3", 491 | "serde", 492 | ] 493 | 494 | [[package]] 495 | name = "indexmap" 496 | version = "2.9.0" 497 | source = "registry+https://github.com/rust-lang/crates.io-index" 498 | checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" 499 | dependencies = [ 500 | "equivalent", 501 | "hashbrown 0.15.4", 502 | "serde", 503 | ] 504 | 505 | [[package]] 506 | name = "itoa" 507 | version = "1.0.15" 508 | source = "registry+https://github.com/rust-lang/crates.io-index" 509 | checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" 510 | 511 | [[package]] 512 | name = "js-sys" 513 | version = "0.3.77" 514 | source = "registry+https://github.com/rust-lang/crates.io-index" 515 | checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" 516 | dependencies = [ 517 | "once_cell", 518 | "wasm-bindgen", 519 | ] 520 | 521 | [[package]] 522 | name = "libc" 523 | version = "0.2.173" 524 | source = "registry+https://github.com/rust-lang/crates.io-index" 525 | checksum = "d8cfeafaffdbc32176b64fb251369d52ea9f0a8fbc6f8759edffef7b525d64bb" 526 | 527 | [[package]] 528 | name = "linux-raw-sys" 529 | version = "0.9.4" 530 | source = "registry+https://github.com/rust-lang/crates.io-index" 531 | checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" 532 | 533 | [[package]] 534 | name = "lock_api" 535 | version = "0.4.13" 536 | source = "registry+https://github.com/rust-lang/crates.io-index" 537 | checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765" 538 | dependencies = [ 539 | "autocfg", 540 | "scopeguard", 541 | ] 542 | 543 | [[package]] 544 | name = "log" 545 | version = "0.4.27" 546 | source = "registry+https://github.com/rust-lang/crates.io-index" 547 | checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" 548 | 549 | [[package]] 550 | name = "memchr" 551 | version = "2.7.5" 552 | source = "registry+https://github.com/rust-lang/crates.io-index" 553 | checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" 554 | 555 | [[package]] 556 | name = "miniz_oxide" 557 | version = "0.8.9" 558 | source = "registry+https://github.com/rust-lang/crates.io-index" 559 | checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" 560 | dependencies = [ 561 | "adler2", 562 | ] 563 | 564 | [[package]] 565 | name = "mio" 566 | version = "1.0.4" 567 | source = "registry+https://github.com/rust-lang/crates.io-index" 568 | checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c" 569 | dependencies = [ 570 | "libc", 571 | "wasi 0.11.1+wasi-snapshot-preview1", 572 | "windows-sys 0.59.0", 573 | ] 574 | 575 | [[package]] 576 | name = "native-tls" 577 | version = "0.2.14" 578 | source = "registry+https://github.com/rust-lang/crates.io-index" 579 | checksum = "87de3442987e9dbec73158d5c715e7ad9072fda936bb03d19d7fa10e00520f0e" 580 | dependencies = [ 581 | "libc", 582 | "log", 583 | "openssl", 584 | "openssl-probe", 585 | "openssl-sys", 586 | "schannel", 587 | "security-framework 2.11.1", 588 | "security-framework-sys", 589 | "tempfile", 590 | ] 591 | 592 | [[package]] 593 | name = "num-conv" 594 | version = "0.1.0" 595 | source = "registry+https://github.com/rust-lang/crates.io-index" 596 | checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" 597 | 598 | [[package]] 599 | name = "num-traits" 600 | version = "0.2.19" 601 | source = "registry+https://github.com/rust-lang/crates.io-index" 602 | checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" 603 | dependencies = [ 604 | "autocfg", 605 | ] 606 | 607 | [[package]] 608 | name = "object" 609 | version = "0.36.7" 610 | source = "registry+https://github.com/rust-lang/crates.io-index" 611 | checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" 612 | dependencies = [ 613 | "memchr", 614 | ] 615 | 616 | [[package]] 617 | name = "once_cell" 618 | version = "1.21.3" 619 | source = "registry+https://github.com/rust-lang/crates.io-index" 620 | checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" 621 | 622 | [[package]] 623 | name = "openssl" 624 | version = "0.10.73" 625 | source = "registry+https://github.com/rust-lang/crates.io-index" 626 | checksum = "8505734d46c8ab1e19a1dce3aef597ad87dcb4c37e7188231769bd6bd51cebf8" 627 | dependencies = [ 628 | "bitflags", 629 | "cfg-if", 630 | "foreign-types", 631 | "libc", 632 | "once_cell", 633 | "openssl-macros", 634 | "openssl-sys", 635 | ] 636 | 637 | [[package]] 638 | name = "openssl-macros" 639 | version = "0.1.1" 640 | source = "registry+https://github.com/rust-lang/crates.io-index" 641 | checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" 642 | dependencies = [ 643 | "proc-macro2", 644 | "quote", 645 | "syn", 646 | ] 647 | 648 | [[package]] 649 | name = "openssl-probe" 650 | version = "0.1.6" 651 | source = "registry+https://github.com/rust-lang/crates.io-index" 652 | checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" 653 | 654 | [[package]] 655 | name = "openssl-src" 656 | version = "300.5.0+3.5.0" 657 | source = "registry+https://github.com/rust-lang/crates.io-index" 658 | checksum = "e8ce546f549326b0e6052b649198487d91320875da901e7bd11a06d1ee3f9c2f" 659 | dependencies = [ 660 | "cc", 661 | ] 662 | 663 | [[package]] 664 | name = "openssl-sys" 665 | version = "0.9.109" 666 | source = "registry+https://github.com/rust-lang/crates.io-index" 667 | checksum = "90096e2e47630d78b7d1c20952dc621f957103f8bc2c8359ec81290d75238571" 668 | dependencies = [ 669 | "cc", 670 | "libc", 671 | "openssl-src", 672 | "pkg-config", 673 | "vcpkg", 674 | ] 675 | 676 | [[package]] 677 | name = "parking_lot" 678 | version = "0.12.4" 679 | source = "registry+https://github.com/rust-lang/crates.io-index" 680 | checksum = "70d58bf43669b5795d1576d0641cfb6fbb2057bf629506267a92807158584a13" 681 | dependencies = [ 682 | "lock_api", 683 | "parking_lot_core", 684 | ] 685 | 686 | [[package]] 687 | name = "parking_lot_core" 688 | version = "0.9.11" 689 | source = "registry+https://github.com/rust-lang/crates.io-index" 690 | checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5" 691 | dependencies = [ 692 | "cfg-if", 693 | "libc", 694 | "redox_syscall", 695 | "smallvec", 696 | "windows-targets", 697 | ] 698 | 699 | [[package]] 700 | name = "pin-project-lite" 701 | version = "0.2.16" 702 | source = "registry+https://github.com/rust-lang/crates.io-index" 703 | checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" 704 | 705 | [[package]] 706 | name = "pin-utils" 707 | version = "0.1.0" 708 | source = "registry+https://github.com/rust-lang/crates.io-index" 709 | checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" 710 | 711 | [[package]] 712 | name = "pkg-config" 713 | version = "0.3.32" 714 | source = "registry+https://github.com/rust-lang/crates.io-index" 715 | checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" 716 | 717 | [[package]] 718 | name = "powerfmt" 719 | version = "0.2.0" 720 | source = "registry+https://github.com/rust-lang/crates.io-index" 721 | checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" 722 | 723 | [[package]] 724 | name = "ppv-lite86" 725 | version = "0.2.21" 726 | source = "registry+https://github.com/rust-lang/crates.io-index" 727 | checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" 728 | dependencies = [ 729 | "zerocopy", 730 | ] 731 | 732 | [[package]] 733 | name = "proc-macro2" 734 | version = "1.0.95" 735 | source = "registry+https://github.com/rust-lang/crates.io-index" 736 | checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" 737 | dependencies = [ 738 | "unicode-ident", 739 | ] 740 | 741 | [[package]] 742 | name = "quote" 743 | version = "1.0.40" 744 | source = "registry+https://github.com/rust-lang/crates.io-index" 745 | checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" 746 | dependencies = [ 747 | "proc-macro2", 748 | ] 749 | 750 | [[package]] 751 | name = "r-efi" 752 | version = "5.2.0" 753 | source = "registry+https://github.com/rust-lang/crates.io-index" 754 | checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" 755 | 756 | [[package]] 757 | name = "rand" 758 | version = "0.9.1" 759 | source = "registry+https://github.com/rust-lang/crates.io-index" 760 | checksum = "9fbfd9d094a40bf3ae768db9361049ace4c0e04a4fd6b359518bd7b73a73dd97" 761 | dependencies = [ 762 | "rand_chacha", 763 | "rand_core", 764 | ] 765 | 766 | [[package]] 767 | name = "rand_chacha" 768 | version = "0.9.0" 769 | source = "registry+https://github.com/rust-lang/crates.io-index" 770 | checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" 771 | dependencies = [ 772 | "ppv-lite86", 773 | "rand_core", 774 | ] 775 | 776 | [[package]] 777 | name = "rand_core" 778 | version = "0.9.3" 779 | source = "registry+https://github.com/rust-lang/crates.io-index" 780 | checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" 781 | dependencies = [ 782 | "getrandom 0.3.3", 783 | ] 784 | 785 | [[package]] 786 | name = "redox_syscall" 787 | version = "0.5.13" 788 | source = "registry+https://github.com/rust-lang/crates.io-index" 789 | checksum = "0d04b7d0ee6b4a0207a0a7adb104d23ecb0b47d6beae7152d0fa34b692b29fd6" 790 | dependencies = [ 791 | "bitflags", 792 | ] 793 | 794 | [[package]] 795 | name = "ref-cast" 796 | version = "1.0.24" 797 | source = "registry+https://github.com/rust-lang/crates.io-index" 798 | checksum = "4a0ae411dbe946a674d89546582cea4ba2bb8defac896622d6496f14c23ba5cf" 799 | dependencies = [ 800 | "ref-cast-impl", 801 | ] 802 | 803 | [[package]] 804 | name = "ref-cast-impl" 805 | version = "1.0.24" 806 | source = "registry+https://github.com/rust-lang/crates.io-index" 807 | checksum = "1165225c21bff1f3bbce98f5a1f889949bc902d3575308cc7b0de30b4f6d27c7" 808 | dependencies = [ 809 | "proc-macro2", 810 | "quote", 811 | "syn", 812 | ] 813 | 814 | [[package]] 815 | name = "ring" 816 | version = "0.17.14" 817 | source = "registry+https://github.com/rust-lang/crates.io-index" 818 | checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" 819 | dependencies = [ 820 | "cc", 821 | "cfg-if", 822 | "getrandom 0.2.16", 823 | "libc", 824 | "untrusted", 825 | "windows-sys 0.52.0", 826 | ] 827 | 828 | [[package]] 829 | name = "rustc-demangle" 830 | version = "0.1.25" 831 | source = "registry+https://github.com/rust-lang/crates.io-index" 832 | checksum = "989e6739f80c4ad5b13e0fd7fe89531180375b18520cc8c82080e4dc4035b84f" 833 | 834 | [[package]] 835 | name = "rustc-hash" 836 | version = "1.1.0" 837 | source = "registry+https://github.com/rust-lang/crates.io-index" 838 | checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" 839 | 840 | [[package]] 841 | name = "rustix" 842 | version = "1.0.7" 843 | source = "registry+https://github.com/rust-lang/crates.io-index" 844 | checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266" 845 | dependencies = [ 846 | "bitflags", 847 | "errno", 848 | "libc", 849 | "linux-raw-sys", 850 | "windows-sys 0.59.0", 851 | ] 852 | 853 | [[package]] 854 | name = "rustls" 855 | version = "0.23.27" 856 | source = "registry+https://github.com/rust-lang/crates.io-index" 857 | checksum = "730944ca083c1c233a75c09f199e973ca499344a2b7ba9e755c457e86fb4a321" 858 | dependencies = [ 859 | "once_cell", 860 | "rustls-pki-types", 861 | "rustls-webpki", 862 | "subtle", 863 | "zeroize", 864 | ] 865 | 866 | [[package]] 867 | name = "rustls-native-certs" 868 | version = "0.8.1" 869 | source = "registry+https://github.com/rust-lang/crates.io-index" 870 | checksum = "7fcff2dd52b58a8d98a70243663a0d234c4e2b79235637849d15913394a247d3" 871 | dependencies = [ 872 | "openssl-probe", 873 | "rustls-pki-types", 874 | "schannel", 875 | "security-framework 3.2.0", 876 | ] 877 | 878 | [[package]] 879 | name = "rustls-pki-types" 880 | version = "1.12.0" 881 | source = "registry+https://github.com/rust-lang/crates.io-index" 882 | checksum = "229a4a4c221013e7e1f1a043678c5cc39fe5171437c88fb47151a21e6f5b5c79" 883 | dependencies = [ 884 | "zeroize", 885 | ] 886 | 887 | [[package]] 888 | name = "rustls-webpki" 889 | version = "0.103.3" 890 | source = "registry+https://github.com/rust-lang/crates.io-index" 891 | checksum = "e4a72fe2bcf7a6ac6fd7d0b9e5cb68aeb7d4c0a0271730218b3e92d43b4eb435" 892 | dependencies = [ 893 | "ring", 894 | "rustls-pki-types", 895 | "untrusted", 896 | ] 897 | 898 | [[package]] 899 | name = "rustversion" 900 | version = "1.0.21" 901 | source = "registry+https://github.com/rust-lang/crates.io-index" 902 | checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d" 903 | 904 | [[package]] 905 | name = "ryu" 906 | version = "1.0.20" 907 | source = "registry+https://github.com/rust-lang/crates.io-index" 908 | checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" 909 | 910 | [[package]] 911 | name = "schannel" 912 | version = "0.1.27" 913 | source = "registry+https://github.com/rust-lang/crates.io-index" 914 | checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d" 915 | dependencies = [ 916 | "windows-sys 0.59.0", 917 | ] 918 | 919 | [[package]] 920 | name = "schemars" 921 | version = "0.9.0" 922 | source = "registry+https://github.com/rust-lang/crates.io-index" 923 | checksum = "4cd191f9397d57d581cddd31014772520aa448f65ef991055d7f61582c65165f" 924 | dependencies = [ 925 | "dyn-clone", 926 | "ref-cast", 927 | "serde", 928 | "serde_json", 929 | ] 930 | 931 | [[package]] 932 | name = "scopeguard" 933 | version = "1.2.0" 934 | source = "registry+https://github.com/rust-lang/crates.io-index" 935 | checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" 936 | 937 | [[package]] 938 | name = "security-framework" 939 | version = "2.11.1" 940 | source = "registry+https://github.com/rust-lang/crates.io-index" 941 | checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" 942 | dependencies = [ 943 | "bitflags", 944 | "core-foundation 0.9.4", 945 | "core-foundation-sys", 946 | "libc", 947 | "security-framework-sys", 948 | ] 949 | 950 | [[package]] 951 | name = "security-framework" 952 | version = "3.2.0" 953 | source = "registry+https://github.com/rust-lang/crates.io-index" 954 | checksum = "271720403f46ca04f7ba6f55d438f8bd878d6b8ca0a1046e8228c4145bcbb316" 955 | dependencies = [ 956 | "bitflags", 957 | "core-foundation 0.10.1", 958 | "core-foundation-sys", 959 | "libc", 960 | "security-framework-sys", 961 | ] 962 | 963 | [[package]] 964 | name = "security-framework-sys" 965 | version = "2.14.0" 966 | source = "registry+https://github.com/rust-lang/crates.io-index" 967 | checksum = "49db231d56a190491cb4aeda9527f1ad45345af50b0851622a7adb8c03b01c32" 968 | dependencies = [ 969 | "core-foundation-sys", 970 | "libc", 971 | ] 972 | 973 | [[package]] 974 | name = "serde" 975 | version = "1.0.219" 976 | source = "registry+https://github.com/rust-lang/crates.io-index" 977 | checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" 978 | dependencies = [ 979 | "serde_derive", 980 | ] 981 | 982 | [[package]] 983 | name = "serde_derive" 984 | version = "1.0.219" 985 | source = "registry+https://github.com/rust-lang/crates.io-index" 986 | checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" 987 | dependencies = [ 988 | "proc-macro2", 989 | "quote", 990 | "syn", 991 | ] 992 | 993 | [[package]] 994 | name = "serde_json" 995 | version = "1.0.140" 996 | source = "registry+https://github.com/rust-lang/crates.io-index" 997 | checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" 998 | dependencies = [ 999 | "itoa", 1000 | "memchr", 1001 | "ryu", 1002 | "serde", 1003 | ] 1004 | 1005 | [[package]] 1006 | name = "serde_with" 1007 | version = "3.13.0" 1008 | source = "registry+https://github.com/rust-lang/crates.io-index" 1009 | checksum = "bf65a400f8f66fb7b0552869ad70157166676db75ed8181f8104ea91cf9d0b42" 1010 | dependencies = [ 1011 | "base64", 1012 | "chrono", 1013 | "hex", 1014 | "indexmap 1.9.3", 1015 | "indexmap 2.9.0", 1016 | "schemars", 1017 | "serde", 1018 | "serde_derive", 1019 | "serde_json", 1020 | "serde_with_macros", 1021 | "time", 1022 | ] 1023 | 1024 | [[package]] 1025 | name = "serde_with_macros" 1026 | version = "3.13.0" 1027 | source = "registry+https://github.com/rust-lang/crates.io-index" 1028 | checksum = "81679d9ed988d5e9a5e6531dc3f2c28efbd639cbd1dfb628df08edea6004da77" 1029 | dependencies = [ 1030 | "darling", 1031 | "proc-macro2", 1032 | "quote", 1033 | "syn", 1034 | ] 1035 | 1036 | [[package]] 1037 | name = "sha1" 1038 | version = "0.10.6" 1039 | source = "registry+https://github.com/rust-lang/crates.io-index" 1040 | checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" 1041 | dependencies = [ 1042 | "cfg-if", 1043 | "cpufeatures", 1044 | "digest", 1045 | ] 1046 | 1047 | [[package]] 1048 | name = "shlex" 1049 | version = "1.3.0" 1050 | source = "registry+https://github.com/rust-lang/crates.io-index" 1051 | checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" 1052 | 1053 | [[package]] 1054 | name = "slab" 1055 | version = "0.4.9" 1056 | source = "registry+https://github.com/rust-lang/crates.io-index" 1057 | checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" 1058 | dependencies = [ 1059 | "autocfg", 1060 | ] 1061 | 1062 | [[package]] 1063 | name = "small-map" 1064 | version = "0.1.3" 1065 | source = "registry+https://github.com/rust-lang/crates.io-index" 1066 | checksum = "f5aafd06ff06be157677c1dc018e22f1c9cd642e3f55e2dbb5544edc68c6bf67" 1067 | dependencies = [ 1068 | "ahash", 1069 | "hashbrown 0.14.5", 1070 | "rustc-hash", 1071 | "serde", 1072 | ] 1073 | 1074 | [[package]] 1075 | name = "smallvec" 1076 | version = "1.15.1" 1077 | source = "registry+https://github.com/rust-lang/crates.io-index" 1078 | checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" 1079 | dependencies = [ 1080 | "serde", 1081 | ] 1082 | 1083 | [[package]] 1084 | name = "smol_str" 1085 | version = "0.3.2" 1086 | source = "registry+https://github.com/rust-lang/crates.io-index" 1087 | checksum = "9676b89cd56310a87b93dec47b11af744f34d5fc9f367b829474eec0a891350d" 1088 | dependencies = [ 1089 | "borsh", 1090 | "serde", 1091 | ] 1092 | 1093 | [[package]] 1094 | name = "socket2" 1095 | version = "0.5.10" 1096 | source = "registry+https://github.com/rust-lang/crates.io-index" 1097 | checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678" 1098 | dependencies = [ 1099 | "libc", 1100 | "windows-sys 0.52.0", 1101 | ] 1102 | 1103 | [[package]] 1104 | name = "strsim" 1105 | version = "0.11.1" 1106 | source = "registry+https://github.com/rust-lang/crates.io-index" 1107 | checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" 1108 | 1109 | [[package]] 1110 | name = "subtle" 1111 | version = "2.6.1" 1112 | source = "registry+https://github.com/rust-lang/crates.io-index" 1113 | checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" 1114 | 1115 | [[package]] 1116 | name = "syn" 1117 | version = "2.0.103" 1118 | source = "registry+https://github.com/rust-lang/crates.io-index" 1119 | checksum = "e4307e30089d6fd6aff212f2da3a1f9e32f3223b1f010fb09b7c95f90f3ca1e8" 1120 | dependencies = [ 1121 | "proc-macro2", 1122 | "quote", 1123 | "unicode-ident", 1124 | ] 1125 | 1126 | [[package]] 1127 | name = "tempfile" 1128 | version = "3.20.0" 1129 | source = "registry+https://github.com/rust-lang/crates.io-index" 1130 | checksum = "e8a64e3985349f2441a1a9ef0b853f869006c3855f2cda6862a94d26ebb9d6a1" 1131 | dependencies = [ 1132 | "fastrand", 1133 | "getrandom 0.3.3", 1134 | "once_cell", 1135 | "rustix", 1136 | "windows-sys 0.59.0", 1137 | ] 1138 | 1139 | [[package]] 1140 | name = "thiserror" 1141 | version = "2.0.12" 1142 | source = "registry+https://github.com/rust-lang/crates.io-index" 1143 | checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" 1144 | dependencies = [ 1145 | "thiserror-impl", 1146 | ] 1147 | 1148 | [[package]] 1149 | name = "thiserror-impl" 1150 | version = "2.0.12" 1151 | source = "registry+https://github.com/rust-lang/crates.io-index" 1152 | checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" 1153 | dependencies = [ 1154 | "proc-macro2", 1155 | "quote", 1156 | "syn", 1157 | ] 1158 | 1159 | [[package]] 1160 | name = "time" 1161 | version = "0.3.41" 1162 | source = "registry+https://github.com/rust-lang/crates.io-index" 1163 | checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40" 1164 | dependencies = [ 1165 | "deranged", 1166 | "itoa", 1167 | "num-conv", 1168 | "powerfmt", 1169 | "serde", 1170 | "time-core", 1171 | "time-macros", 1172 | ] 1173 | 1174 | [[package]] 1175 | name = "time-core" 1176 | version = "0.1.4" 1177 | source = "registry+https://github.com/rust-lang/crates.io-index" 1178 | checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c" 1179 | 1180 | [[package]] 1181 | name = "time-macros" 1182 | version = "0.2.22" 1183 | source = "registry+https://github.com/rust-lang/crates.io-index" 1184 | checksum = "3526739392ec93fd8b359c8e98514cb3e8e021beb4e5f597b00a0221f8ed8a49" 1185 | dependencies = [ 1186 | "num-conv", 1187 | "time-core", 1188 | ] 1189 | 1190 | [[package]] 1191 | name = "tokio" 1192 | version = "1.45.1" 1193 | source = "registry+https://github.com/rust-lang/crates.io-index" 1194 | checksum = "75ef51a33ef1da925cea3e4eb122833cb377c61439ca401b770f54902b806779" 1195 | dependencies = [ 1196 | "backtrace", 1197 | "bytes", 1198 | "libc", 1199 | "mio", 1200 | "pin-project-lite", 1201 | "socket2", 1202 | "tokio-macros", 1203 | "windows-sys 0.52.0", 1204 | ] 1205 | 1206 | [[package]] 1207 | name = "tokio-macros" 1208 | version = "2.5.0" 1209 | source = "registry+https://github.com/rust-lang/crates.io-index" 1210 | checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" 1211 | dependencies = [ 1212 | "proc-macro2", 1213 | "quote", 1214 | "syn", 1215 | ] 1216 | 1217 | [[package]] 1218 | name = "tokio-native-tls" 1219 | version = "0.3.1" 1220 | source = "registry+https://github.com/rust-lang/crates.io-index" 1221 | checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" 1222 | dependencies = [ 1223 | "native-tls", 1224 | "tokio", 1225 | ] 1226 | 1227 | [[package]] 1228 | name = "tokio-rustls" 1229 | version = "0.26.2" 1230 | source = "registry+https://github.com/rust-lang/crates.io-index" 1231 | checksum = "8e727b36a1a0e8b74c376ac2211e40c2c8af09fb4013c60d910495810f008e9b" 1232 | dependencies = [ 1233 | "rustls", 1234 | "tokio", 1235 | ] 1236 | 1237 | [[package]] 1238 | name = "tokio-tungstenite" 1239 | version = "0.27.0" 1240 | source = "registry+https://github.com/rust-lang/crates.io-index" 1241 | checksum = "489a59b6730eda1b0171fcfda8b121f4bee2b35cba8645ca35c5f7ba3eb736c1" 1242 | dependencies = [ 1243 | "futures-util", 1244 | "log", 1245 | "native-tls", 1246 | "rustls", 1247 | "rustls-native-certs", 1248 | "rustls-pki-types", 1249 | "tokio", 1250 | "tokio-native-tls", 1251 | "tokio-rustls", 1252 | "tungstenite", 1253 | "webpki-roots 0.26.11", 1254 | ] 1255 | 1256 | [[package]] 1257 | name = "tracing" 1258 | version = "0.1.41" 1259 | source = "registry+https://github.com/rust-lang/crates.io-index" 1260 | checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" 1261 | dependencies = [ 1262 | "pin-project-lite", 1263 | "tracing-attributes", 1264 | "tracing-core", 1265 | ] 1266 | 1267 | [[package]] 1268 | name = "tracing-attributes" 1269 | version = "0.1.29" 1270 | source = "registry+https://github.com/rust-lang/crates.io-index" 1271 | checksum = "1b1ffbcf9c6f6b99d386e7444eb608ba646ae452a36b39737deb9663b610f662" 1272 | dependencies = [ 1273 | "proc-macro2", 1274 | "quote", 1275 | "syn", 1276 | ] 1277 | 1278 | [[package]] 1279 | name = "tracing-core" 1280 | version = "0.1.34" 1281 | source = "registry+https://github.com/rust-lang/crates.io-index" 1282 | checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" 1283 | dependencies = [ 1284 | "once_cell", 1285 | ] 1286 | 1287 | [[package]] 1288 | name = "tungstenite" 1289 | version = "0.27.0" 1290 | source = "registry+https://github.com/rust-lang/crates.io-index" 1291 | checksum = "eadc29d668c91fcc564941132e17b28a7ceb2f3ebf0b9dae3e03fd7a6748eb0d" 1292 | dependencies = [ 1293 | "bytes", 1294 | "data-encoding", 1295 | "http", 1296 | "httparse", 1297 | "log", 1298 | "native-tls", 1299 | "rand", 1300 | "rustls", 1301 | "rustls-pki-types", 1302 | "sha1", 1303 | "thiserror", 1304 | "utf-8", 1305 | ] 1306 | 1307 | [[package]] 1308 | name = "typenum" 1309 | version = "1.18.0" 1310 | source = "registry+https://github.com/rust-lang/crates.io-index" 1311 | checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" 1312 | 1313 | [[package]] 1314 | name = "unicode-ident" 1315 | version = "1.0.18" 1316 | source = "registry+https://github.com/rust-lang/crates.io-index" 1317 | checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" 1318 | 1319 | [[package]] 1320 | name = "untrusted" 1321 | version = "0.9.0" 1322 | source = "registry+https://github.com/rust-lang/crates.io-index" 1323 | checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" 1324 | 1325 | [[package]] 1326 | name = "utf-8" 1327 | version = "0.7.6" 1328 | source = "registry+https://github.com/rust-lang/crates.io-index" 1329 | checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" 1330 | 1331 | [[package]] 1332 | name = "uuid" 1333 | version = "1.17.0" 1334 | source = "registry+https://github.com/rust-lang/crates.io-index" 1335 | checksum = "3cf4199d1e5d15ddd86a694e4d0dffa9c323ce759fea589f00fef9d81cc1931d" 1336 | dependencies = [ 1337 | "getrandom 0.3.3", 1338 | "js-sys", 1339 | "rand", 1340 | "wasm-bindgen", 1341 | ] 1342 | 1343 | [[package]] 1344 | name = "vcpkg" 1345 | version = "0.2.15" 1346 | source = "registry+https://github.com/rust-lang/crates.io-index" 1347 | checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" 1348 | 1349 | [[package]] 1350 | name = "version_check" 1351 | version = "0.9.5" 1352 | source = "registry+https://github.com/rust-lang/crates.io-index" 1353 | checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" 1354 | 1355 | [[package]] 1356 | name = "wasi" 1357 | version = "0.11.1+wasi-snapshot-preview1" 1358 | source = "registry+https://github.com/rust-lang/crates.io-index" 1359 | checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" 1360 | 1361 | [[package]] 1362 | name = "wasi" 1363 | version = "0.14.2+wasi-0.2.4" 1364 | source = "registry+https://github.com/rust-lang/crates.io-index" 1365 | checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" 1366 | dependencies = [ 1367 | "wit-bindgen-rt", 1368 | ] 1369 | 1370 | [[package]] 1371 | name = "wasm-bindgen" 1372 | version = "0.2.100" 1373 | source = "registry+https://github.com/rust-lang/crates.io-index" 1374 | checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" 1375 | dependencies = [ 1376 | "cfg-if", 1377 | "once_cell", 1378 | "rustversion", 1379 | "wasm-bindgen-macro", 1380 | ] 1381 | 1382 | [[package]] 1383 | name = "wasm-bindgen-backend" 1384 | version = "0.2.100" 1385 | source = "registry+https://github.com/rust-lang/crates.io-index" 1386 | checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" 1387 | dependencies = [ 1388 | "bumpalo", 1389 | "log", 1390 | "proc-macro2", 1391 | "quote", 1392 | "syn", 1393 | "wasm-bindgen-shared", 1394 | ] 1395 | 1396 | [[package]] 1397 | name = "wasm-bindgen-macro" 1398 | version = "0.2.100" 1399 | source = "registry+https://github.com/rust-lang/crates.io-index" 1400 | checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" 1401 | dependencies = [ 1402 | "quote", 1403 | "wasm-bindgen-macro-support", 1404 | ] 1405 | 1406 | [[package]] 1407 | name = "wasm-bindgen-macro-support" 1408 | version = "0.2.100" 1409 | source = "registry+https://github.com/rust-lang/crates.io-index" 1410 | checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" 1411 | dependencies = [ 1412 | "proc-macro2", 1413 | "quote", 1414 | "syn", 1415 | "wasm-bindgen-backend", 1416 | "wasm-bindgen-shared", 1417 | ] 1418 | 1419 | [[package]] 1420 | name = "wasm-bindgen-shared" 1421 | version = "0.2.100" 1422 | source = "registry+https://github.com/rust-lang/crates.io-index" 1423 | checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" 1424 | dependencies = [ 1425 | "unicode-ident", 1426 | ] 1427 | 1428 | [[package]] 1429 | name = "webpki-roots" 1430 | version = "0.26.11" 1431 | source = "registry+https://github.com/rust-lang/crates.io-index" 1432 | checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9" 1433 | dependencies = [ 1434 | "webpki-roots 1.0.0", 1435 | ] 1436 | 1437 | [[package]] 1438 | name = "webpki-roots" 1439 | version = "1.0.0" 1440 | source = "registry+https://github.com/rust-lang/crates.io-index" 1441 | checksum = "2853738d1cc4f2da3a225c18ec6c3721abb31961096e9dbf5ab35fa88b19cfdb" 1442 | dependencies = [ 1443 | "rustls-pki-types", 1444 | ] 1445 | 1446 | [[package]] 1447 | name = "windows-core" 1448 | version = "0.61.2" 1449 | source = "registry+https://github.com/rust-lang/crates.io-index" 1450 | checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" 1451 | dependencies = [ 1452 | "windows-implement", 1453 | "windows-interface", 1454 | "windows-link", 1455 | "windows-result", 1456 | "windows-strings", 1457 | ] 1458 | 1459 | [[package]] 1460 | name = "windows-implement" 1461 | version = "0.60.0" 1462 | source = "registry+https://github.com/rust-lang/crates.io-index" 1463 | checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" 1464 | dependencies = [ 1465 | "proc-macro2", 1466 | "quote", 1467 | "syn", 1468 | ] 1469 | 1470 | [[package]] 1471 | name = "windows-interface" 1472 | version = "0.59.1" 1473 | source = "registry+https://github.com/rust-lang/crates.io-index" 1474 | checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" 1475 | dependencies = [ 1476 | "proc-macro2", 1477 | "quote", 1478 | "syn", 1479 | ] 1480 | 1481 | [[package]] 1482 | name = "windows-link" 1483 | version = "0.1.3" 1484 | source = "registry+https://github.com/rust-lang/crates.io-index" 1485 | checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" 1486 | 1487 | [[package]] 1488 | name = "windows-result" 1489 | version = "0.3.4" 1490 | source = "registry+https://github.com/rust-lang/crates.io-index" 1491 | checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" 1492 | dependencies = [ 1493 | "windows-link", 1494 | ] 1495 | 1496 | [[package]] 1497 | name = "windows-strings" 1498 | version = "0.4.2" 1499 | source = "registry+https://github.com/rust-lang/crates.io-index" 1500 | checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" 1501 | dependencies = [ 1502 | "windows-link", 1503 | ] 1504 | 1505 | [[package]] 1506 | name = "windows-sys" 1507 | version = "0.52.0" 1508 | source = "registry+https://github.com/rust-lang/crates.io-index" 1509 | checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" 1510 | dependencies = [ 1511 | "windows-targets", 1512 | ] 1513 | 1514 | [[package]] 1515 | name = "windows-sys" 1516 | version = "0.59.0" 1517 | source = "registry+https://github.com/rust-lang/crates.io-index" 1518 | checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" 1519 | dependencies = [ 1520 | "windows-targets", 1521 | ] 1522 | 1523 | [[package]] 1524 | name = "windows-targets" 1525 | version = "0.52.6" 1526 | source = "registry+https://github.com/rust-lang/crates.io-index" 1527 | checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" 1528 | dependencies = [ 1529 | "windows_aarch64_gnullvm", 1530 | "windows_aarch64_msvc", 1531 | "windows_i686_gnu", 1532 | "windows_i686_gnullvm", 1533 | "windows_i686_msvc", 1534 | "windows_x86_64_gnu", 1535 | "windows_x86_64_gnullvm", 1536 | "windows_x86_64_msvc", 1537 | ] 1538 | 1539 | [[package]] 1540 | name = "windows_aarch64_gnullvm" 1541 | version = "0.52.6" 1542 | source = "registry+https://github.com/rust-lang/crates.io-index" 1543 | checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" 1544 | 1545 | [[package]] 1546 | name = "windows_aarch64_msvc" 1547 | version = "0.52.6" 1548 | source = "registry+https://github.com/rust-lang/crates.io-index" 1549 | checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" 1550 | 1551 | [[package]] 1552 | name = "windows_i686_gnu" 1553 | version = "0.52.6" 1554 | source = "registry+https://github.com/rust-lang/crates.io-index" 1555 | checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" 1556 | 1557 | [[package]] 1558 | name = "windows_i686_gnullvm" 1559 | version = "0.52.6" 1560 | source = "registry+https://github.com/rust-lang/crates.io-index" 1561 | checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" 1562 | 1563 | [[package]] 1564 | name = "windows_i686_msvc" 1565 | version = "0.52.6" 1566 | source = "registry+https://github.com/rust-lang/crates.io-index" 1567 | checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" 1568 | 1569 | [[package]] 1570 | name = "windows_x86_64_gnu" 1571 | version = "0.52.6" 1572 | source = "registry+https://github.com/rust-lang/crates.io-index" 1573 | checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" 1574 | 1575 | [[package]] 1576 | name = "windows_x86_64_gnullvm" 1577 | version = "0.52.6" 1578 | source = "registry+https://github.com/rust-lang/crates.io-index" 1579 | checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" 1580 | 1581 | [[package]] 1582 | name = "windows_x86_64_msvc" 1583 | version = "0.52.6" 1584 | source = "registry+https://github.com/rust-lang/crates.io-index" 1585 | checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" 1586 | 1587 | [[package]] 1588 | name = "wit-bindgen-rt" 1589 | version = "0.39.0" 1590 | source = "registry+https://github.com/rust-lang/crates.io-index" 1591 | checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" 1592 | dependencies = [ 1593 | "bitflags", 1594 | ] 1595 | 1596 | [[package]] 1597 | name = "zerocopy" 1598 | version = "0.8.25" 1599 | source = "registry+https://github.com/rust-lang/crates.io-index" 1600 | checksum = "a1702d9583232ddb9174e01bb7c15a2ab8fb1bc6f227aa1233858c351a3ba0cb" 1601 | dependencies = [ 1602 | "zerocopy-derive", 1603 | ] 1604 | 1605 | [[package]] 1606 | name = "zerocopy-derive" 1607 | version = "0.8.25" 1608 | source = "registry+https://github.com/rust-lang/crates.io-index" 1609 | checksum = "28a6e20d751156648aa063f3800b706ee209a32c0b4d9f24be3d980b01be55ef" 1610 | dependencies = [ 1611 | "proc-macro2", 1612 | "quote", 1613 | "syn", 1614 | ] 1615 | 1616 | [[package]] 1617 | name = "zeroize" 1618 | version = "1.8.1" 1619 | source = "registry+https://github.com/rust-lang/crates.io-index" 1620 | checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" 1621 | --------------------------------------------------------------------------------