From d9d94daaca6c8586652ac0d29418e87ccb8a7cce Mon Sep 17 00:00:00 2001 From: famfo Date: Sun, 16 Jan 2022 15:31:35 +0100 Subject: [PATCH] Refactor, use tokio-rustls, split streams --- Cargo.toml | 43 +- examples/basic-config/Cargo.lock | 353 +++++++------ examples/basic-config/src/main.rs | 18 +- examples/config-from-toml/Cargo.lock | 353 +++++++------ examples/config-from-toml/src/main.rs | 11 +- examples/debugging/Cargo.lock | 347 ++++++------- examples/debugging/src/main.rs | 18 +- examples/non-tls-irc/src/main.rs | 20 +- src/client/config.rs | 80 +++ src/client/mod.rs | 499 +++++++++++++++++++ src/commands.rs | 272 ---------- src/commands/mod.rs | 1 + src/commands/parser.rs | 82 +++ src/lib.rs | 692 +------------------------- src/stream/mod.rs | 2 + src/stream/read.rs | 45 ++ src/stream/write.rs | 39 ++ 17 files changed, 1303 insertions(+), 1572 deletions(-) create mode 100644 src/client/config.rs create mode 100644 src/client/mod.rs delete mode 100644 src/commands.rs create mode 100644 src/commands/mod.rs create mode 100644 src/commands/parser.rs create mode 100644 src/stream/mod.rs create mode 100644 src/stream/read.rs create mode 100644 src/stream/write.rs diff --git a/Cargo.toml b/Cargo.toml index 73bde42..cc7c60c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,40 +8,23 @@ license = "Unlicense" name = "async-circe" readme = "README.md" repository = "https://git.karx.xyz/circe/async-circe.git" -version = "0.2.3" +version = "0.3.0" -[dependencies.toml] -version = "0.5" -optional = true +[dependencies] +parking_lot = "0.11" +tokio = {version = "1.15", features = ["io-util", "net", "rt", "fs"]} +tracing = "0.1" -[dependencies.serde] -version = "1.0" -optional = true +# TLS +tokio-rustls = {version = "0.23", optional = true} +webpki-roots = {version = "0.22", optional = true} -[dependencies.serde_derive] -version = "1.0" -optional = true - -[dependencies.tokio] -version = "1.15" -features = ["io-util", "net"] - -[dependencies.tokio-native-tls] -version= "0.3" -optional = true - -[dependencies.native-tls] -version= "0.2" -optional = true - -[dependencies.tracing] -version = "0.1" +# Config from toml +toml = {version = "0.5", optional = true} +serde = {version = "1.0", optional = true} +serde_derive = {version = "1.0", optional = true} [features] default = ["tls"] -tls = ["tokio-native-tls", "native-tls"] +tls = ["tokio-rustls", "webpki-roots"] toml_config = ["toml", "serde", "serde_derive"] - -[package.metadata.docs.rs] -all-features = true -rustdoc-args = ["--cfg", "docsrs"] diff --git a/examples/basic-config/Cargo.lock b/examples/basic-config/Cargo.lock index e09890a..b778f0d 100644 --- a/examples/basic-config/Cargo.lock +++ b/examples/basic-config/Cargo.lock @@ -4,20 +4,15 @@ version = 3 [[package]] name = "async-circe" -version = "0.2.2" +version = "0.3.0" dependencies = [ - "native-tls", + "parking_lot", "tokio", - "tokio-native-tls", + "tokio-rustls", "tracing", + "webpki-roots", ] -[[package]] -name = "autocfg" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" - [[package]] name = "basic-config" version = "0.1.0" @@ -32,6 +27,12 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bumpalo" +version = "3.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4a45a46ab1f2412e53d3a0ade76ffad2025804294569aae387231a0cd6e0899" + [[package]] name = "bytes" version = "1.1.0" @@ -51,45 +52,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] -name = "core-foundation" -version = "0.9.2" +name = "instant" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6888e10551bb93e424d8df1d07f1a8b4fceb0001a3a4b048bfc47554946f47b3" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "core-foundation-sys" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" - -[[package]] -name = "foreign-types" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" -dependencies = [ - "foreign-types-shared", -] - -[[package]] -name = "foreign-types-shared" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" - -[[package]] -name = "getrandom" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" dependencies = [ "cfg-if", - "libc", - "wasi", +] + +[[package]] +name = "js-sys" +version = "0.3.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cc9ffccd38c451a86bf13657df244e9c3f37493cce8e5e21e940963777acc84" +dependencies = [ + "wasm-bindgen", ] [[package]] @@ -104,6 +81,15 @@ version = "0.2.107" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fbe5e23404da5b4f555ef85ebed98fb4083e55a00c317800bc2a50ede9f3d219" +[[package]] +name = "lock_api" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712a4d093c9976e24e7dbca41db895dabcbac38eb5f4045393d17a95bdfb1109" +dependencies = [ + "scopeguard", +] + [[package]] name = "log" version = "0.4.14" @@ -141,24 +127,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "native-tls" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48ba9f7719b5a0f42f338907614285fb5fd70e53858141f69898a1fb7203b24d" -dependencies = [ - "lazy_static", - "libc", - "log", - "openssl", - "openssl-probe", - "openssl-sys", - "schannel", - "security-framework", - "security-framework-sys", - "tempfile", -] - [[package]] name = "ntapi" version = "0.3.6" @@ -175,36 +143,28 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "692fcb63b64b1758029e0a96ee63e049ce8c5948587f2f7208df04625e5f6b56" [[package]] -name = "openssl" -version = "0.10.38" +name = "parking_lot" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c7ae222234c30df141154f159066c5093ff73b63204dcda7121eb082fc56a95" +checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" dependencies = [ - "bitflags", - "cfg-if", - "foreign-types", - "libc", - "once_cell", - "openssl-sys", + "instant", + "lock_api", + "parking_lot_core", ] [[package]] -name = "openssl-probe" -version = "0.1.4" +name = "parking_lot_core" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28988d872ab76095a6e6ac88d99b54fd267702734fd7ffe610ca27f533ddb95a" - -[[package]] -name = "openssl-sys" -version = "0.9.72" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e46109c383602735fa0a2e48dd2b7c892b048e1bf69e5c3b1d804b7d9c203cb" +checksum = "d76e8e1493bcac0d2766c42737f34458f1c8c50c0d23bcb24ea953affb273216" dependencies = [ - "autocfg", - "cc", + "cfg-if", + "instant", "libc", - "pkg-config", - "vcpkg", + "redox_syscall", + "smallvec", + "winapi", ] [[package]] @@ -213,18 +173,6 @@ version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8d31d11c69a6b52a174b42bdc0c30e5e11670f90788b2c471c31c1d17d449443" -[[package]] -name = "pkg-config" -version = "0.3.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58893f751c9b0412871a09abd62ecd2a00298c6c83befa223ef98c52aef40cbe" - -[[package]] -name = "ppv-lite86" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed0cfbc8191465bed66e1718596ee0b0b35d5ee1f41c5df2189d0fe8bde535ba" - [[package]] name = "proc-macro2" version = "1.0.32" @@ -243,46 +191,6 @@ dependencies = [ "proc-macro2", ] -[[package]] -name = "rand" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8" -dependencies = [ - "libc", - "rand_chacha", - "rand_core", - "rand_hc", -] - -[[package]] -name = "rand_chacha" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", - "rand_core", -] - -[[package]] -name = "rand_core" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" -dependencies = [ - "getrandom", -] - -[[package]] -name = "rand_hc" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d51e9f596de227fda2ea6c84607f5558e196eeaf43c986b724ba4fb8fdf497e7" -dependencies = [ - "rand_core", -] - [[package]] name = "redox_syscall" version = "0.2.10" @@ -293,46 +201,59 @@ dependencies = [ ] [[package]] -name = "remove_dir_all" -version = "0.5.3" +name = "ring" +version = "0.16.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" +checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" dependencies = [ + "cc", + "libc", + "once_cell", + "spin", + "untrusted", + "web-sys", "winapi", ] [[package]] -name = "schannel" -version = "0.1.19" +name = "rustls" +version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f05ba609c234e60bee0d547fe94a4c7e9da733d1c962cf6e59efa4cd9c8bc75" +checksum = "d37e5e2290f3e040b594b1a9e04377c2c671f1a1cfd9bfdef82106ac1c113f84" dependencies = [ - "lazy_static", - "winapi", + "log", + "ring", + "sct", + "webpki", ] [[package]] -name = "security-framework" -version = "2.4.2" +name = "scopeguard" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "525bc1abfda2e1998d152c45cf13e696f76d0a4972310b22fac1658b05df7c87" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "sct" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4" dependencies = [ - "bitflags", - "core-foundation", - "core-foundation-sys", - "libc", - "security-framework-sys", + "ring", + "untrusted", ] [[package]] -name = "security-framework-sys" -version = "2.4.2" +name = "smallvec" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9dd14d83160b528b7bfd66439110573efcfbe281b17fc2ca9f39f550d619c7e" -dependencies = [ - "core-foundation-sys", - "libc", -] +checksum = "f2dd574626839106c320a323308629dcb1acfc96e32a8cba364ddc61ac23ee83" + +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" [[package]] name = "syn" @@ -345,20 +266,6 @@ dependencies = [ "unicode-xid", ] -[[package]] -name = "tempfile" -version = "3.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dac1c663cfc93810f88aed9b8941d48cabf856a1b111c29a40439018d870eb22" -dependencies = [ - "cfg-if", - "libc", - "rand", - "redox_syscall", - "remove_dir_all", - "winapi", -] - [[package]] name = "tokio" version = "1.15.0" @@ -386,13 +293,14 @@ dependencies = [ ] [[package]] -name = "tokio-native-tls" -version = "0.3.0" +name = "tokio-rustls" +version = "0.23.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7d995660bd2b7f8c1568414c1126076c13fbb725c40112dc0120b78eb9b717b" +checksum = "a27d5f2b839802bd8267fa19b0530f5a08b9c08cd417976be2a65d130fe1c11b" dependencies = [ - "native-tls", + "rustls", "tokio", + "webpki", ] [[package]] @@ -434,16 +342,93 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" [[package]] -name = "vcpkg" -version = "0.2.15" +name = "untrusted" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" +checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" [[package]] -name = "wasi" -version = "0.10.2+wasi-snapshot-preview1" +name = "wasm-bindgen" +version = "0.2.78" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" +checksum = "632f73e236b219150ea279196e54e610f5dbafa5d61786303d4da54f84e47fce" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a317bf8f9fba2476b4b2c85ef4c4af8ff39c3c7f0cdfeed4f82c34a880aa837b" +dependencies = [ + "bumpalo", + "lazy_static", + "log", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d56146e7c495528bf6587663bea13a8eb588d39b36b679d83972e1a2dbbdacf9" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7803e0eea25835f8abdc585cd3021b3deb11543c6fe226dcd30b228857c5c5ab" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0237232789cf037d5480773fe568aac745bfe2afbc11a863e97901780a6b47cc" + +[[package]] +name = "web-sys" +version = "0.3.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38eb105f1c59d9eaa6b5cdc92b859d85b926e82cb2e0945cd0c9259faa6fe9fb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webpki" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f095d78192e208183081cc07bc5515ef55216397af48b873e5edcd72637fa1bd" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "webpki-roots" +version = "0.22.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "552ceb903e957524388c4d3475725ff2c8b7960922063af6ce53c9a43da07449" +dependencies = [ + "webpki", +] [[package]] name = "winapi" diff --git a/examples/basic-config/src/main.rs b/examples/basic-config/src/main.rs index dd621f1..37a7531 100644 --- a/examples/basic-config/src/main.rs +++ b/examples/basic-config/src/main.rs @@ -1,20 +1,22 @@ -use async_circe::{commands::Command, Client, Config}; +use async_circe::client::{Client, Config}; +use async_circe::commands::parser::Command; #[tokio::main(flavor = "current_thread")] async fn main() -> Result<(), tokio::io::Error> { let config = Config::new( - &["#main", "#no-normies"], "karx.xyz", - Some("+B"), - Some("async-circe"), 6697, "circe", - ); - let mut client = Client::new(config).await.unwrap(); - client.identify().await.unwrap(); + Some("async-circe"), + Some("+B"), + &["#main", "#no-normies"], + ) + .await; + let (client, mut recv) = Client::new(config).await; + client.identify(&mut recv).await.unwrap(); loop { - if let Some(command) = client.read().await? { + if let Some(command) = recv.read().await? { if let Command::PRIVMSG(nick, channel, message) = command { println!("{} in {}: {}", nick, channel, message); } diff --git a/examples/config-from-toml/Cargo.lock b/examples/config-from-toml/Cargo.lock index 84b9786..858007f 100644 --- a/examples/config-from-toml/Cargo.lock +++ b/examples/config-from-toml/Cargo.lock @@ -4,29 +4,30 @@ version = 3 [[package]] name = "async-circe" -version = "0.2.2" +version = "0.3.0" dependencies = [ - "native-tls", + "parking_lot", "serde", "serde_derive", "tokio", - "tokio-native-tls", + "tokio-rustls", "toml", "tracing", + "webpki-roots", ] -[[package]] -name = "autocfg" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" - [[package]] name = "bitflags" version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bumpalo" +version = "3.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4a45a46ab1f2412e53d3a0ade76ffad2025804294569aae387231a0cd6e0899" + [[package]] name = "bytes" version = "1.1.0" @@ -54,45 +55,21 @@ dependencies = [ ] [[package]] -name = "core-foundation" -version = "0.9.2" +name = "instant" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6888e10551bb93e424d8df1d07f1a8b4fceb0001a3a4b048bfc47554946f47b3" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "core-foundation-sys" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" - -[[package]] -name = "foreign-types" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" -dependencies = [ - "foreign-types-shared", -] - -[[package]] -name = "foreign-types-shared" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" - -[[package]] -name = "getrandom" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" dependencies = [ "cfg-if", - "libc", - "wasi", +] + +[[package]] +name = "js-sys" +version = "0.3.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cc9ffccd38c451a86bf13657df244e9c3f37493cce8e5e21e940963777acc84" +dependencies = [ + "wasm-bindgen", ] [[package]] @@ -107,6 +84,15 @@ version = "0.2.107" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fbe5e23404da5b4f555ef85ebed98fb4083e55a00c317800bc2a50ede9f3d219" +[[package]] +name = "lock_api" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712a4d093c9976e24e7dbca41db895dabcbac38eb5f4045393d17a95bdfb1109" +dependencies = [ + "scopeguard", +] + [[package]] name = "log" version = "0.4.14" @@ -144,24 +130,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "native-tls" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48ba9f7719b5a0f42f338907614285fb5fd70e53858141f69898a1fb7203b24d" -dependencies = [ - "lazy_static", - "libc", - "log", - "openssl", - "openssl-probe", - "openssl-sys", - "schannel", - "security-framework", - "security-framework-sys", - "tempfile", -] - [[package]] name = "ntapi" version = "0.3.6" @@ -178,36 +146,28 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "692fcb63b64b1758029e0a96ee63e049ce8c5948587f2f7208df04625e5f6b56" [[package]] -name = "openssl" -version = "0.10.38" +name = "parking_lot" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c7ae222234c30df141154f159066c5093ff73b63204dcda7121eb082fc56a95" +checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" dependencies = [ - "bitflags", - "cfg-if", - "foreign-types", - "libc", - "once_cell", - "openssl-sys", + "instant", + "lock_api", + "parking_lot_core", ] [[package]] -name = "openssl-probe" -version = "0.1.4" +name = "parking_lot_core" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28988d872ab76095a6e6ac88d99b54fd267702734fd7ffe610ca27f533ddb95a" - -[[package]] -name = "openssl-sys" -version = "0.9.70" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6517987b3f8226b5da3661dad65ff7f300cc59fb5ea8333ca191fc65fde3edf" +checksum = "d76e8e1493bcac0d2766c42737f34458f1c8c50c0d23bcb24ea953affb273216" dependencies = [ - "autocfg", - "cc", + "cfg-if", + "instant", "libc", - "pkg-config", - "vcpkg", + "redox_syscall", + "smallvec", + "winapi", ] [[package]] @@ -216,18 +176,6 @@ version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8d31d11c69a6b52a174b42bdc0c30e5e11670f90788b2c471c31c1d17d449443" -[[package]] -name = "pkg-config" -version = "0.3.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12295df4f294471248581bc09bef3c38a5e46f1e36d6a37353621a0c6c357e1f" - -[[package]] -name = "ppv-lite86" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed0cfbc8191465bed66e1718596ee0b0b35d5ee1f41c5df2189d0fe8bde535ba" - [[package]] name = "proc-macro2" version = "1.0.32" @@ -246,46 +194,6 @@ dependencies = [ "proc-macro2", ] -[[package]] -name = "rand" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8" -dependencies = [ - "libc", - "rand_chacha", - "rand_core", - "rand_hc", -] - -[[package]] -name = "rand_chacha" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", - "rand_core", -] - -[[package]] -name = "rand_core" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" -dependencies = [ - "getrandom", -] - -[[package]] -name = "rand_hc" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d51e9f596de227fda2ea6c84607f5558e196eeaf43c986b724ba4fb8fdf497e7" -dependencies = [ - "rand_core", -] - [[package]] name = "redox_syscall" version = "0.2.10" @@ -296,45 +204,46 @@ dependencies = [ ] [[package]] -name = "remove_dir_all" -version = "0.5.3" +name = "ring" +version = "0.16.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" +checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" dependencies = [ + "cc", + "libc", + "once_cell", + "spin", + "untrusted", + "web-sys", "winapi", ] [[package]] -name = "schannel" -version = "0.1.19" +name = "rustls" +version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f05ba609c234e60bee0d547fe94a4c7e9da733d1c962cf6e59efa4cd9c8bc75" +checksum = "d37e5e2290f3e040b594b1a9e04377c2c671f1a1cfd9bfdef82106ac1c113f84" dependencies = [ - "lazy_static", - "winapi", + "log", + "ring", + "sct", + "webpki", ] [[package]] -name = "security-framework" -version = "2.4.2" +name = "scopeguard" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "525bc1abfda2e1998d152c45cf13e696f76d0a4972310b22fac1658b05df7c87" -dependencies = [ - "bitflags", - "core-foundation", - "core-foundation-sys", - "libc", - "security-framework-sys", -] +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" [[package]] -name = "security-framework-sys" -version = "2.4.2" +name = "sct" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9dd14d83160b528b7bfd66439110573efcfbe281b17fc2ca9f39f550d619c7e" +checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4" dependencies = [ - "core-foundation-sys", - "libc", + "ring", + "untrusted", ] [[package]] @@ -354,6 +263,18 @@ dependencies = [ "syn", ] +[[package]] +name = "smallvec" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2dd574626839106c320a323308629dcb1acfc96e32a8cba364ddc61ac23ee83" + +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + [[package]] name = "syn" version = "1.0.81" @@ -365,20 +286,6 @@ dependencies = [ "unicode-xid", ] -[[package]] -name = "tempfile" -version = "3.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dac1c663cfc93810f88aed9b8941d48cabf856a1b111c29a40439018d870eb22" -dependencies = [ - "cfg-if", - "libc", - "rand", - "redox_syscall", - "remove_dir_all", - "winapi", -] - [[package]] name = "tokio" version = "1.15.0" @@ -406,13 +313,14 @@ dependencies = [ ] [[package]] -name = "tokio-native-tls" -version = "0.3.0" +name = "tokio-rustls" +version = "0.23.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7d995660bd2b7f8c1568414c1126076c13fbb725c40112dc0120b78eb9b717b" +checksum = "a27d5f2b839802bd8267fa19b0530f5a08b9c08cd417976be2a65d130fe1c11b" dependencies = [ - "native-tls", + "rustls", "tokio", + "webpki", ] [[package]] @@ -463,16 +371,93 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" [[package]] -name = "vcpkg" -version = "0.2.15" +name = "untrusted" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" +checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" [[package]] -name = "wasi" -version = "0.10.2+wasi-snapshot-preview1" +name = "wasm-bindgen" +version = "0.2.78" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" +checksum = "632f73e236b219150ea279196e54e610f5dbafa5d61786303d4da54f84e47fce" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a317bf8f9fba2476b4b2c85ef4c4af8ff39c3c7f0cdfeed4f82c34a880aa837b" +dependencies = [ + "bumpalo", + "lazy_static", + "log", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d56146e7c495528bf6587663bea13a8eb588d39b36b679d83972e1a2dbbdacf9" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7803e0eea25835f8abdc585cd3021b3deb11543c6fe226dcd30b228857c5c5ab" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0237232789cf037d5480773fe568aac745bfe2afbc11a863e97901780a6b47cc" + +[[package]] +name = "web-sys" +version = "0.3.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38eb105f1c59d9eaa6b5cdc92b859d85b926e82cb2e0945cd0c9259faa6fe9fb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webpki" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f095d78192e208183081cc07bc5515ef55216397af48b873e5edcd72637fa1bd" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "webpki-roots" +version = "0.22.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "552ceb903e957524388c4d3475725ff2c8b7960922063af6ce53c9a43da07449" +dependencies = [ + "webpki", +] [[package]] name = "winapi" diff --git a/examples/config-from-toml/src/main.rs b/examples/config-from-toml/src/main.rs index 4488002..a82b16b 100644 --- a/examples/config-from-toml/src/main.rs +++ b/examples/config-from-toml/src/main.rs @@ -1,13 +1,14 @@ -use async_circe::{commands::Command, Client, Config}; +use async_circe::client::{Client, Config}; +use async_circe::commands::parser::Command; #[tokio::main(flavor = "current_thread")] async fn main() -> Result<(), std::io::Error> { - let config = Config::from_toml("Config.toml")?; - let mut client = Client::new(config).await.unwrap(); - client.identify().await.unwrap(); + let config = Config::new_toml("Config.toml").await?; + let (client, mut recv) = Client::new(config).await; + client.identify(&mut recv).await.unwrap(); loop { - if let Some(command) = client.read().await? { + if let Some(command) = recv.read().await? { if let Command::PRIVMSG(nick, channel, message) = command { println!("{} in {}: {}", nick, channel, message); } diff --git a/examples/debugging/Cargo.lock b/examples/debugging/Cargo.lock index 1f80661..bbadd2c 100644 --- a/examples/debugging/Cargo.lock +++ b/examples/debugging/Cargo.lock @@ -13,20 +13,15 @@ dependencies = [ [[package]] name = "async-circe" -version = "0.2.3" +version = "0.3.0" dependencies = [ - "native-tls", + "parking_lot", "tokio", - "tokio-native-tls", + "tokio-rustls", "tracing", + "webpki-roots", ] -[[package]] -name = "autocfg" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" - [[package]] name = "basic-config" version = "0.1.0" @@ -42,6 +37,12 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bumpalo" +version = "3.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4a45a46ab1f2412e53d3a0ade76ffad2025804294569aae387231a0cd6e0899" + [[package]] name = "bytes" version = "1.1.0" @@ -61,45 +62,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] -name = "core-foundation" -version = "0.9.2" +name = "instant" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6888e10551bb93e424d8df1d07f1a8b4fceb0001a3a4b048bfc47554946f47b3" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "core-foundation-sys" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" - -[[package]] -name = "foreign-types" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" -dependencies = [ - "foreign-types-shared", -] - -[[package]] -name = "foreign-types-shared" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" - -[[package]] -name = "getrandom" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" dependencies = [ "cfg-if", - "libc", - "wasi", +] + +[[package]] +name = "js-sys" +version = "0.3.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cc9ffccd38c451a86bf13657df244e9c3f37493cce8e5e21e940963777acc84" +dependencies = [ + "wasm-bindgen", ] [[package]] @@ -114,6 +91,15 @@ version = "0.2.112" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b03d17f364a3a042d5e5d46b053bbbf82c92c9430c592dd4c064dc6ee997125" +[[package]] +name = "lock_api" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712a4d093c9976e24e7dbca41db895dabcbac38eb5f4045393d17a95bdfb1109" +dependencies = [ + "scopeguard", +] + [[package]] name = "log" version = "0.4.14" @@ -160,24 +146,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "native-tls" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48ba9f7719b5a0f42f338907614285fb5fd70e53858141f69898a1fb7203b24d" -dependencies = [ - "lazy_static", - "libc", - "log", - "openssl", - "openssl-probe", - "openssl-sys", - "schannel", - "security-framework", - "security-framework-sys", - "tempfile", -] - [[package]] name = "ntapi" version = "0.3.6" @@ -194,36 +162,28 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da32515d9f6e6e489d7bc9d84c71b060db7247dc035bbe44eac88cf87486d8d5" [[package]] -name = "openssl" -version = "0.10.38" +name = "parking_lot" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c7ae222234c30df141154f159066c5093ff73b63204dcda7121eb082fc56a95" +checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" dependencies = [ - "bitflags", - "cfg-if", - "foreign-types", - "libc", - "once_cell", - "openssl-sys", + "instant", + "lock_api", + "parking_lot_core", ] [[package]] -name = "openssl-probe" -version = "0.1.4" +name = "parking_lot_core" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28988d872ab76095a6e6ac88d99b54fd267702734fd7ffe610ca27f533ddb95a" - -[[package]] -name = "openssl-sys" -version = "0.9.72" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e46109c383602735fa0a2e48dd2b7c892b048e1bf69e5c3b1d804b7d9c203cb" +checksum = "d76e8e1493bcac0d2766c42737f34458f1c8c50c0d23bcb24ea953affb273216" dependencies = [ - "autocfg", - "cc", + "cfg-if", + "instant", "libc", - "pkg-config", - "vcpkg", + "redox_syscall", + "smallvec", + "winapi", ] [[package]] @@ -232,18 +192,6 @@ version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8d31d11c69a6b52a174b42bdc0c30e5e11670f90788b2c471c31c1d17d449443" -[[package]] -name = "pkg-config" -version = "0.3.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58893f751c9b0412871a09abd62ecd2a00298c6c83befa223ef98c52aef40cbe" - -[[package]] -name = "ppv-lite86" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" - [[package]] name = "proc-macro2" version = "1.0.36" @@ -262,46 +210,6 @@ dependencies = [ "proc-macro2", ] -[[package]] -name = "rand" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8" -dependencies = [ - "libc", - "rand_chacha", - "rand_core", - "rand_hc", -] - -[[package]] -name = "rand_chacha" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", - "rand_core", -] - -[[package]] -name = "rand_core" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" -dependencies = [ - "getrandom", -] - -[[package]] -name = "rand_hc" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d51e9f596de227fda2ea6c84607f5558e196eeaf43c986b724ba4fb8fdf497e7" -dependencies = [ - "rand_core", -] - [[package]] name = "redox_syscall" version = "0.2.10" @@ -336,45 +244,46 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" [[package]] -name = "remove_dir_all" -version = "0.5.3" +name = "ring" +version = "0.16.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" +checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" dependencies = [ + "cc", + "libc", + "once_cell", + "spin", + "untrusted", + "web-sys", "winapi", ] [[package]] -name = "schannel" -version = "0.1.19" +name = "rustls" +version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f05ba609c234e60bee0d547fe94a4c7e9da733d1c962cf6e59efa4cd9c8bc75" +checksum = "d37e5e2290f3e040b594b1a9e04377c2c671f1a1cfd9bfdef82106ac1c113f84" dependencies = [ - "lazy_static", - "winapi", + "log", + "ring", + "sct", + "webpki", ] [[package]] -name = "security-framework" -version = "2.4.2" +name = "scopeguard" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "525bc1abfda2e1998d152c45cf13e696f76d0a4972310b22fac1658b05df7c87" -dependencies = [ - "bitflags", - "core-foundation", - "core-foundation-sys", - "libc", - "security-framework-sys", -] +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" [[package]] -name = "security-framework-sys" -version = "2.4.2" +name = "sct" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9dd14d83160b528b7bfd66439110573efcfbe281b17fc2ca9f39f550d619c7e" +checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4" dependencies = [ - "core-foundation-sys", - "libc", + "ring", + "untrusted", ] [[package]] @@ -392,6 +301,12 @@ version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ecab6c735a6bb4139c0caafd0cc3635748bbb3acf4550e8138122099251f309" +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + [[package]] name = "syn" version = "1.0.84" @@ -403,20 +318,6 @@ dependencies = [ "unicode-xid", ] -[[package]] -name = "tempfile" -version = "3.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dac1c663cfc93810f88aed9b8941d48cabf856a1b111c29a40439018d870eb22" -dependencies = [ - "cfg-if", - "libc", - "rand", - "redox_syscall", - "remove_dir_all", - "winapi", -] - [[package]] name = "thread_local" version = "1.1.3" @@ -453,13 +354,14 @@ dependencies = [ ] [[package]] -name = "tokio-native-tls" -version = "0.3.0" +name = "tokio-rustls" +version = "0.23.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7d995660bd2b7f8c1568414c1126076c13fbb725c40112dc0120b78eb9b717b" +checksum = "a27d5f2b839802bd8267fa19b0530f5a08b9c08cd417976be2a65d130fe1c11b" dependencies = [ - "native-tls", + "rustls", "tokio", + "webpki", ] [[package]] @@ -530,16 +432,93 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" [[package]] -name = "vcpkg" -version = "0.2.15" +name = "untrusted" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" +checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" [[package]] -name = "wasi" -version = "0.10.2+wasi-snapshot-preview1" +name = "wasm-bindgen" +version = "0.2.78" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" +checksum = "632f73e236b219150ea279196e54e610f5dbafa5d61786303d4da54f84e47fce" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a317bf8f9fba2476b4b2c85ef4c4af8ff39c3c7f0cdfeed4f82c34a880aa837b" +dependencies = [ + "bumpalo", + "lazy_static", + "log", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d56146e7c495528bf6587663bea13a8eb588d39b36b679d83972e1a2dbbdacf9" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7803e0eea25835f8abdc585cd3021b3deb11543c6fe226dcd30b228857c5c5ab" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0237232789cf037d5480773fe568aac745bfe2afbc11a863e97901780a6b47cc" + +[[package]] +name = "web-sys" +version = "0.3.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38eb105f1c59d9eaa6b5cdc92b859d85b926e82cb2e0945cd0c9259faa6fe9fb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webpki" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f095d78192e208183081cc07bc5515ef55216397af48b873e5edcd72637fa1bd" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "webpki-roots" +version = "0.22.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "552ceb903e957524388c4d3475725ff2c8b7960922063af6ce53c9a43da07449" +dependencies = [ + "webpki", +] [[package]] name = "winapi" diff --git a/examples/debugging/src/main.rs b/examples/debugging/src/main.rs index 45b9cbc..6f783d8 100644 --- a/examples/debugging/src/main.rs +++ b/examples/debugging/src/main.rs @@ -1,4 +1,5 @@ -use async_circe::{commands::Command, Client, Config}; +use async_circe::client::{Client, Config}; +use async_circe::commands::parser::Command; #[tokio::main(flavor = "current_thread")] async fn main() -> Result<(), tokio::io::Error> { @@ -9,18 +10,19 @@ async fn main() -> Result<(), tokio::io::Error> { .init(); let config = Config::new( - &["#main", "#no-normies"], "karx.xyz", - Some("+B"), - Some("async-circe"), 6697, "circe", - ); - let mut client = Client::new(config).await.unwrap(); - client.identify().await.unwrap(); + Some("async-circe"), + Some("+B"), + &["#main", "#no-normies"], + ) + .await; + let (client, mut recv) = Client::new(config).await; + client.identify(&mut recv).await.unwrap(); loop { - if let Some(command) = client.read().await? { + if let Some(command) = recv.read().await? { if let Command::PRIVMSG(nick, channel, message) = command { println!("{} in {}: {}", nick, channel, message); } diff --git a/examples/non-tls-irc/src/main.rs b/examples/non-tls-irc/src/main.rs index 442750e..55a2ba4 100644 --- a/examples/non-tls-irc/src/main.rs +++ b/examples/non-tls-irc/src/main.rs @@ -1,20 +1,22 @@ -use async_circe::{commands::Command, Client, Config}; +use async_circe::client::{Client, Config}; +use async_circe::commands::parser::Command; #[tokio::main(flavor = "current_thread")] async fn main() -> Result<(), tokio::io::Error> { let config = Config::new( - &["#main", "#no-normies"], "192.168.178.100", - Some("+B"), - Some("async-circe"), 6667, - "async-circe", - ); - let mut client = Client::new(config).await.unwrap(); - client.identify().await.unwrap(); + "circe", + Some("async-circe"), + Some("+B"), + &["#main", "#no-normies"], + ) + .await; + let (client, mut recv) = Client::new(config).await; + client.identify(&mut recv).await.unwrap(); loop { - if let Some(command) = client.read().await? { + if let Some(command) = recv.read().await? { if let Command::PRIVMSG(nick, channel, message) = command { println!("{} in {}: {}", nick, channel, message); } diff --git a/src/client/config.rs b/src/client/config.rs new file mode 100644 index 0000000..9070bed --- /dev/null +++ b/src/client/config.rs @@ -0,0 +1,80 @@ +use std::string::ToString; + +#[cfg(feature = "toml_config")] +use serde_derive::Deserialize; +#[cfg(feature = "toml_config")] +use std::path::Path; + +#[derive(Clone, Default)] +#[cfg_attr(feature = "toml_config", derive(Deserialize))] +pub struct Config { + pub host: String, + pub port: u16, + pub username: String, + pub nickname: Option, + pub mode: Option, + pub channels: Vec, +} + +impl Config { + /// Creates new config for a circe client + pub async fn new( + host: &'static str, + port: u16, + username: &'static str, + nickname: Option<&'static str>, + mode: Option<&'static str>, + channels: &[&'static str], + ) -> Self { + let nickname = nickname.map(ToString::to_string); + let mode = mode.map(ToString::to_string); + let channels = { + let mut channel_vec = Vec::new(); + for channel in channels { + channel_vec.push((*channel).to_string()); + } + channel_vec + }; + + Config { + host: host.into(), + port, + username: username.into(), + nickname, + mode, + channels, + } + } + + /// Creates new config for a circe client + pub async fn new_runtime( + host: String, + port: u16, + username: String, + nickname: Option, + mode: Option, + channels: Vec, + ) -> Self { + Config { + host, + port, + username, + nickname, + mode, + channels, + } + } + + /// Creates new config for a circe client from a toml file + #[cfg(feature = "toml_config")] + pub async fn new_toml>(file: P) -> Result { + use tokio::fs::File; + use tokio::io::{AsyncReadExt, Error, ErrorKind}; + + let mut file = File::open(&file).await?; + let mut config = String::new(); + file.read_to_string(&mut config).await?; + toml::from_str(&config) + .map_err(|e| Error::new(ErrorKind::Other, format!("Invalid TOML: {}", e))) + } +} diff --git a/src/client/mod.rs b/src/client/mod.rs new file mode 100644 index 0000000..d32fc8e --- /dev/null +++ b/src/client/mod.rs @@ -0,0 +1,499 @@ +pub mod config; + +pub use crate::client::config::Config; +use crate::commands::parser::{CapMode, Command}; +use crate::stream::read::Read; +use crate::stream::write::Write; + +use tokio::io::{split, BufReader, Error}; +use tokio::net::TcpStream; + +#[cfg(feature = "tls")] +use std::sync::Arc; +#[cfg(feature = "tls")] +use tokio_rustls::rustls::{self, OwnedTrustAnchor}; +#[cfg(feature = "tls")] +use tokio_rustls::TlsConnector; + +#[derive(Clone)] +pub struct Client { + config: Config, + tx: Write, +} + +impl Client { + /// Creates a new circe client + pub async fn new(config: config::Config) -> (Self, Read) { + let stream = TcpStream::connect(format!("{}:{}", config.host, config.port)) + .await + .expect("Failed to connect to the server"); + + #[cfg(feature = "tls")] + { + let mut root_cert_store = rustls::RootCertStore::empty(); + root_cert_store.add_server_trust_anchors(webpki_roots::TLS_SERVER_ROOTS.0.iter().map( + |ta| { + OwnedTrustAnchor::from_subject_spki_name_constraints( + ta.subject, + ta.spki, + ta.name_constraints, + ) + }, + )); + + let tls_config = rustls::ClientConfig::builder() + .with_safe_defaults() + .with_root_certificates(root_cert_store) + .with_no_client_auth(); + + let domain = + rustls::ServerName::try_from(config.host.as_str()).expect("Invalid DNS name"); + let connector = TlsConnector::from(Arc::new(tls_config)); + let tls_stream = connector + .connect(domain, stream) + .await + .expect("Failed to connect to the server"); + + let (recv, tx) = split(tls_stream); + let recv = Read::new(BufReader::new(recv)); + let tx = Write::new(tx).await; + + tracing::info!("New client created"); + (Self { config, tx }, recv) + } + + #[cfg(not(feature = "tls"))] + { + let (recv, tx) = split(stream); + let recv = Read::new(BufReader::new(recv)); + let tx = Write::new(tx).await; + + tracing::info!("New client created"); + (Self { config, tx }, recv) + } + } + + /// Identifys the client on the server + /// # Errors + /// Returns errors from the ``TcpStream``. + pub async fn identify(self, recv: &mut Read) -> Result<(), Error> { + let config = self.config.clone(); + self.clone().cap_mode(CapMode::LS).await?; + self.clone().cap_mode(CapMode::END).await?; + self.clone() + .user( + self.config.username.clone(), + "*", + "*", + self.config.username.clone(), + ) + .await?; + + if let Some(nick) = self.config.nickname.clone() { + self.clone().nick(&nick).await?; + } else { + self.clone().nick(&self.config.username.clone()).await?; + } + + loop { + if let Some(command) = recv.read().await? { + match command { + Command::PING(code) => { + self.clone().pong(&code).await?; + } + Command::OTHER(line) => { + if line.contains("001") { + break; + } + } + _ => {} + } + } + } + + let user = { + if let Some(nick) = config.nickname { + nick + } else { + config.username + } + }; + + if let Some(mode) = config.mode { + self.clone().mode(&user, Some(&mode)).await?; + } + + for channel in &config.channels { + self.clone().join(channel).await?; + } + + Ok(()) + } + + // TODO: rewrite + + /// Request information about the admin of a given server. + /// # Example + /// ```run_fut + /// # use async_circe::*; + /// # let config = Default::default(); + /// # let mut client = Client::new(config).await?; + /// # client.identify().await?; + /// client.admin("libera.chat").await?; + /// # Ok(()) + /// ``` + /// # Errors + /// Returns IO errors from the ``TcpStream``. + pub async fn admin(self, target: &str) -> Result<(), Error> { + self.tx + .clone() + .write(format!("ADMIN {}\r\n", target)) + .await?; + + Ok(()) + } + + /// Set the status of the client. + /// # Example + /// ```run_fut + /// # let config = Default::default(); + /// # let mut client = Client::new(config).await?; + /// # client.identify().await?; + /// client.away("afk").await?; + /// # Ok(()) + /// ``` + /// # Errors + /// Returns IO errors from the ``TcpStream``. + pub async fn away(self, message: &str) -> Result<(), Error> { + self.tx + .clone() + .write(format!("AWAY {}\r\n", message)) + .await?; + Ok(()) + } + + #[doc(hidden)] + pub async fn cap_mode(self, mode: CapMode) -> Result<(), Error> { + let cap_mode = match mode { + CapMode::LS => "CAP LS 302\r\n", + CapMode::END => "CAP END\r\n", + }; + self.tx.clone().write(cap_mode.to_string()).await?; + Ok(()) + } + + /// Invite someone to a channel. + /// # Example + /// ```run_fut + /// # let config = Default::default(); + /// # let mut client = Client::new(config).await?; + /// # client.identify().await?; + /// client.invite("liblemonirc", "#async-circe").await?; + /// # Ok(()) + /// ``` + /// # Errors + /// Returns IO errors from the ``TcpStream``. + pub async fn invite(self, username: &str, channel: &str) -> Result<(), Error> { + self.tx + .clone() + .write(format!("INVITE {} {}\r\n", username, channel)) + .await?; + Ok(()) + } + + /// Join a channel. + /// # Example + /// ```run_fut + /// # let config = Default::default(); + /// # let mut client = Client::new(config).await?; + /// # client.identify().await?; + /// client.join("#chaos").await?; + /// # Ok(()) + /// ``` + /// # Errors + /// Returns IO errors from the ``TcpStream``. + pub async fn join(self, channel: &str) -> Result<(), Error> { + self.tx + .clone() + .write(format!("JOIN {}\r\n", channel)) + .await?; + Ok(()) + } + + /// List available channels on an IRC, or users in a channel. + /// # Example + /// ```run_fut + /// # let config = Default::default(); + /// # let mut client = Client::new(config).await?; + /// # client.identify().await?; + /// client.list(None, None).await?; + /// # Ok(()) + /// ``` + /// # Errors + /// Returns IO errors from the ``TcpStream``. + pub async fn list(self, channel: Option<&str>, server: Option<&str>) -> Result<(), Error> { + let mut formatted = "LIST".to_string(); + if let Some(channel) = channel { + formatted.push_str(format!(" {}", channel).as_str()); + } + if let Some(server) = server { + formatted.push_str(format!(" {}", server).as_str()); + } + formatted.push_str("\r\n"); + self.tx.clone().write(formatted).await?; + Ok(()) + } + + /// Set the mode for a user. + /// # Example + /// ```run_fut + /// # let config = Default::default(); + /// # let mut client = Client::new(config).await?; + /// # client.identify().await?; + /// client.mode("test", Some("+B")).await?; + /// # Ok(()) + /// ``` + /// # Errors + /// Returns IO errors from the ``TcpStream``. + pub async fn mode(self, target: &str, mode: Option<&str>) -> Result<(), Error> { + if let Some(mode) = mode { + self.tx + .clone() + .write(format!("MODE {} {}\r\n", target, mode,)) + .await?; + } else { + self.tx + .clone() + .write(format!("MODE {}\r\n", target)) + .await?; + } + Ok(()) + } + + /// Get all the people online in channels. + /// # Example + /// ```run_fut + /// # let config = Default::default(); + /// # let mut client = Client::new(config).await?; + /// # client.identify().await?; + /// client.names("#chaos,#async-circe", None).await?; + /// # Ok(()) + /// ``` + /// # Errors + /// Returns IO errors from the ``TcpStream``. + pub async fn names(self, channel: &str, server: Option<&str>) -> Result<(), Error> { + if let Some(server) = server { + self.tx + .clone() + .write(format!("NAMES {} {}\r\n", channel, server)) + .await?; + } else { + self.tx + .clone() + .write(format!("NAMES {}\r\n", channel)) + .await?; + } + Ok(()) + } + + /// Change your nickname on a server. + /// # Example + /// ```run_fut + /// # let config = Default::default(); + /// # let mut client = Client::new(config).await?; + /// # client.identify().await?; + /// client.nick("Not async-circe").await?; + /// # Ok(()) + /// ``` + /// # Errors + /// Returns IO errors from the ``TcpStream``. + pub async fn nick(self, nickname: &str) -> Result<(), Error> { + self.tx + .clone() + .write(format!("NICK {}\r\n", nickname)) + .await?; + Ok(()) + } + + /// Authentificate as an operator on a server. + /// # Example + /// ```run_fut + /// # let config = Default::default(); + /// # let mut client = Client::new(config).await?; + /// # client.identify().await?; + /// client.oper("username", "password").await?; + /// # Ok(()) + /// ``` + /// # Errors + /// Returns IO errors from the ``TcpStream``. + pub async fn oper(self, username: &str, password: &str) -> Result<(), Error> { + self.tx + .clone() + .write(format!("OPER {} {}\r\n", username, password)) + .await?; + Ok(()) + } + + /// Leave a channel. + /// # Example + /// ```run_fut + /// # let config = Default::default(); + /// # let mut client = Client::new(config).await?; + /// # client.identify().await?; + /// client.part("#chaos").await?; + /// # Ok(()) + /// ``` + /// # Errors + /// Returns IO errors from the ``TcpStream``. + pub async fn part(self, channel: &str) -> Result<(), Error> { + self.tx + .clone() + .write(format!("PART {}\r\n", channel)) + .await?; + Ok(()) + } + + #[doc(hidden)] + pub async fn pass(self, password: &str) -> Result<(), Error> { + self.tx + .clone() + .write(format!("PASS {}\r\n", password)) + .await?; + Ok(()) + } + + /// Tests the presence of a connection to a server. + /// # Example + /// ```run_fut + /// # let config = Default::default(); + /// # let mut client = Client::new(config).await?; + /// # client.identify().await?; + /// client.ping("libera.chat", None).await?; + /// # Ok(()) + /// ``` + /// # Errors + /// Returns IO errors from the ``TcpStream``. + pub async fn ping(self, server1: &str, server2: Option<&str>) -> Result<(), Error> { + if let Some(server2) = server2 { + self.tx + .clone() + .write(format!("PING {} {}\r\n", server1, server2)) + .await?; + } else { + self.tx + .clone() + .write(format!("PING {}\r\n", server1)) + .await?; + } + // TODO look if we actually get a PONG back + Ok(()) + } + + #[doc(hidden)] + pub async fn pong(self, ping: &str) -> Result<(), Error> { + self.tx.clone().write(format!("PONG {}\r\n", ping)).await?; + Ok(()) + } + + /// Send a message to a channel. + /// # Example + /// ```run_fut + /// # let config = Default::default(); + /// # let mut client = Client::new(config).await?; + /// # client.identify().await?; + /// client.privmsg("#chaos", "Hello").await?; + /// # Ok(()) + /// ``` + /// # Errors + /// Returns IO errors from the ``TcpStream``. + pub async fn privmsg(self, channel: &str, message: &str) -> Result<(), Error> { + self.tx + .clone() + .write(format!("PRIVMSG {} :{}\r\n", channel, message)) + .await?; + Ok(()) + } + + /// Leave the IRC server you are connected to. + /// # Example + /// ```run_fut + /// # use async_circe::*; + /// # let config = Default::default(); + /// # let mut client = Client::new(config).await?; + /// # client.identify().await?; + /// client.quit(None).await?; + /// # Ok(()) + /// ``` + /// # Errors + /// Returns IO errors from the ``TcpStream``. + pub async fn quit(self, recv: Read, message: Option<&str>) -> Result<(), Error> { + if let Some(message) = message { + self.tx + .clone() + .write(format!("QUIT :{}\r\n", message)) + .await?; + } else { + self.tx + .clone() + .write(format!( + "QUIT :async-circe {} (https://crates.io/crates/async-circe)\r\n", + env!("CARGO_PKG_VERSION") + )) + .await?; + } + + Ok(()) + } + + /// Get the topic of a channel. + /// # Example + /// ```run_fut + /// # use async_circe::*; + /// # let config = Default::default(); + /// # let mut client = Client::new(config).await?; + /// # client.identify().await?; + /// client.topic("#chaos", None).await?; + /// # Ok(()) + /// ``` + /// Set the topic of a channel. + /// # Example + /// ```run_fut + /// # let config = Default::default(); + /// # let mut client = Client::new(config).await?; + /// # client.identify().await?; + /// client.topic("#chaos", Some("CHAOS")).await?; + /// # Ok(()) + /// ``` + /// # Errors + /// Returns IO errors from the ``TcpStream``. + pub async fn topic(self, channel: &str, topic: Option<&str>) -> Result<(), Error> { + if let Some(topic) = topic { + self.tx + .clone() + .write(format!("TOPIC {} :{}\r\n", channel, topic)) + .await?; + } else { + self.tx + .clone() + .write(format!("TOPIC {}\r\n", channel)) + .await?; + } + + Ok(()) + } + + #[doc(hidden)] + pub async fn user( + self, + username: String, + s1: &str, + s2: &str, + realname: String, + ) -> Result<(), Error> { + self.tx + .clone() + .write(format!("USER {} {} {} :{}\r\n", username, s1, s2, realname)) + .await?; + Ok(()) + } +} diff --git a/src/commands.rs b/src/commands.rs deleted file mode 100644 index e9d08f9..0000000 --- a/src/commands.rs +++ /dev/null @@ -1,272 +0,0 @@ -//! IRC commands -//! - commands that can be recived from a server -//! - commands that can be send to the server - -#[doc(hidden)] -#[derive(Debug)] -pub enum CapMode { - LS, - END, -} - -/// Commands that can be send or recived from an IRC server. -#[derive(Debug)] -pub enum Command { - // TODO: - // SERVICE - // SQUIT - // - /// Request information about the admin of a given server. - /// ```run_fut - /// # let config = Default::default(); - /// # let mut client = Client::new(config).await?; - /// # client.identify().await?; - /// client.admin("libera.chat").await?; - /// # Ok(()) - /// ``` - /// # Errors - /// Returns IO errors from the TcpStream. - ADMIN( - /// Target - String, - ), - /// Set the status of the client. - /// ```run_fut - /// # let config = Default::default(); - /// # let mut client = Client::new(config).await?; - /// # client.identify().await?; - /// client.away("afk").await?; - /// # Ok(()) - /// ``` - AWAY( - /// Message - String, - ), - #[doc(hidden)] - CAP(CapMode), - /// Invite someone to a channel. - /// ```run_fut - /// # let config = Default::default(); - /// # let mut client = Client::new(config).await?; - /// # client.identify().await?; - /// client.invite("liblemonirc", "#async-circe").await?; - /// # Ok(()) - /// ``` - INVITE( - /// User - String, - /// Channel - String, - ), - /// Join a channel. - /// ```run_fut - /// # let config = Default::default(); - /// # let mut client = Client::new(config).await?; - /// # client.identify().await?; - /// client.join("#chaos").await?; - /// # Ok(()) - /// ``` - JOIN( - /// Channel - String, - ), - /// List available channels on an IRC, or users in a channel. - /// ```run_fut - /// # let config = Default::default(); - /// # let mut client = Client::new(config).await?; - /// # client.identify().await?; - /// client.list(None, None).await?; - /// # Ok(()) - /// ``` - LIST( - /// Channel - Option, - /// Server to foreward request to - Option, - ), - /// Set the mode for a user. - /// ```run_fut - /// # let config = Default::default(); - /// # let mut client = Client::new(config).await?; - /// # client.identify().await?; - /// client.mode("test", Some("+B")).await?; - /// # Ok(()) - /// ``` - MODE( - /// Channel - String, - /// Mode - Option, - ), - /// Get all the people online in channels. - /// ```run_fut - /// # let config = Default::default(); - /// # let mut client = Client::new(config).await?; - /// # client.identify().await?; - /// client.names("#chaos,#async-circe", None).await?; - /// # Ok(()) - /// ``` - NAMES( - /// Channel - String, - /// User - String, - ), - /// Change your nickname on a server. - /// ```run_fut - /// # let config = Default::default(); - /// # let mut client = Client::new(config).await?; - /// # client.identify().await?; - /// client.nick("Not async-circe").await?; - /// # Ok(()) - /// ``` - NICK( - /// Nickname - String, - ), - /// Authentificate as an operator on a server. - /// ```run_fut - /// # let config = Default::default(); - /// # let mut client = Client::new(config).await?; - /// # client.identify().await?; - /// client.oper("username", "password").await?; - /// # Ok(()) - /// ``` - OPER( - /// Username - String, - /// Password - String, - ), - /// Everything that is not a command - OTHER(String), - /// Leave a channel. - /// ```run_fut - /// # let config = Default::default(); - /// # let mut client = Client::new(config).await?; - /// # client.identify().await?; - /// client.part("#chaos").await?; - /// # Ok(()) - /// ``` - PART( - /// Target - String, - ), - #[doc(hidden)] - PASS(String), - /// Tests the presence of a connection to a server. - /// ```run_fut - /// # let config = Default::default(); - /// # let mut client = Client::new(config).await?; - /// # client.identify().await?; - /// client.ping("libera.chat", None).await?; - /// # Ok(()) - /// ``` - PING(String), - #[doc(hidden)] - PONG(String, String), - /// Message send in a channel - PRIVMSG( - /// Source Nickname - String, - /// Channel - String, - /// Message - String, - ), - /// Leave the IRC server you are connected to. - /// ```run_fut - /// # let config = Default::default(); - /// # let mut client = Client::new(config).await?; - /// # client.identify().await?; - /// client.quit(None).await?; - /// # Ok(()) - /// ``` - QUIT( - /// Leave message - String, - ), - /// Get the topic of a channel. - /// # Example - /// ```run_fut - /// # let config = Default::default(); - /// # let mut client = Client::new(config).await?; - /// # client.identify().await?; - /// client.topic("#chaos", None).await?; - /// # Ok(()) - /// ``` - /// Set the topic of a channel. - /// # Example - /// ```run_fut - /// # let config = Default::default(); - /// # let mut client = Client::new(config).await?; - /// # client.identify().await?; - /// client.topic("#chaos", Some("main channel")).await?; - /// # Ok(()) - /// ``` - /// # Errors - /// Returns IO errors from the TcpStream. - TOPIC( - /// Channel - String, - /// Topic - String, - ), - /// User the set the topic in a channel at a time - TOPIC_BY(String, String), - #[doc(hidden)] - USER(String, String, String, String), -} - -impl Command { - /// Creates a Command from a `&str`. Currently `[PRIVMSG]` `[TOPIC]` `[NAMES]` and `[PONG]` are supported. - /// - /// # Panics - /// This function will panic if the ``IRCd`` sends malformed messages. Please contact the - /// maintainer of your ``IRCd`` if this happens. - pub async fn command_from_str(s: String) -> Self { - let new = s.trim(); - tracing::trace!("{}", new); - let parts: Vec<&str> = new.split_whitespace().collect(); - - if parts.get(0) == Some(&"PING") { - return Self::PING(parts[1].to_string()); - } - - match parts.get(1) { - Some(&"PRIVMSG") => { - let nick_realname = parts[0]; - let nick: String; - - let index = nick_realname.chars().position(|c| c == '!'); - if let Some(index) = index { - if index > 0 { - nick = (nick_realname[1..index]).to_string(); - } else { - nick = String::new(); - } - } else { - nick = String::new(); - } - - let msg = parts[3..].join(" "); - - Self::PRIVMSG(nick, parts[2].to_string(), (msg[1..]).to_string()) - } - Some(&"331") | Some(&"332") => { - let topic = parts[4..].join(" "); - Self::TOPIC(parts[3].to_string(), (topic[1..]).to_string()) - } - Some(&"333") => Self::TOPIC_BY(parts[4].to_string(), parts[5].to_string()), - Some(&"353") => { - let user = parts[5..].join(" "); - Self::NAMES(parts[4].to_string(), (user[1..]).to_string()) - } - Some(&"PONG") => { - let server = parts[3]; - Self::PONG(parts[2].to_string(), (server[1..]).to_string()) - } - _ => Self::OTHER(new.to_string()), - } - } -} diff --git a/src/commands/mod.rs b/src/commands/mod.rs new file mode 100644 index 0000000..67c567f --- /dev/null +++ b/src/commands/mod.rs @@ -0,0 +1 @@ +pub mod parser; diff --git a/src/commands/parser.rs b/src/commands/parser.rs new file mode 100644 index 0000000..cb1fe48 --- /dev/null +++ b/src/commands/parser.rs @@ -0,0 +1,82 @@ +#[doc(hidden)] +#[derive(Debug)] +pub enum CapMode { + LS, + END, +} + +#[derive(Debug)] +pub enum Command { + ADMIN(String), + AWAY(String), + #[doc(hidden)] + CAP(CapMode), + INVITE(String, String), + JOIN(String), + LIST(Option, Option), + MODE(String, Option), + NAMES(String, String), + NICK(String), + OPER(String, String), + OTHER(String), + PART(String), + #[doc(hidden)] + PASS(String), + PING(String), + #[doc(hidden)] + PONG(String, String), + PRIVMSG(String, String, String), + QUIT(String), + TOPIC(String, String), + TOPIC_BY(String, String), + #[doc(hidden)] + USER(String, String, String, String), +} + +impl Command { + pub async fn command_from_str(s: String) -> Self { + let new = s.trim(); + tracing::trace!("{}", new); + + let parts: Vec<&str> = new.split_whitespace().collect(); + if parts.get(0) == Some(&"PING") { + return Self::PING(parts[1].to_string()); + } + + match parts.get(1) { + Some(&"PRIVMSG") => { + let nick_realname = parts[0]; + let nick: String; + + let index = nick_realname.chars().position(|c| c == '!'); + if let Some(index) = index { + if index > 0 { + nick = (nick_realname[1..index]).to_string(); + } else { + nick = String::new(); + } + } else { + nick = String::new(); + } + + let msg = parts[3..].join(" "); + + Self::PRIVMSG(nick, parts[2].to_string(), (msg[1..]).to_string()) + } + Some(&("331" | "332")) => { + let topic = parts[4..].join(" "); + Self::TOPIC(parts[3].to_string(), (topic[1..]).to_string()) + } + Some(&"333") => Self::TOPIC_BY(parts[4].to_string(), parts[5].to_string()), + Some(&"353") => { + let user = parts[5..].join(" "); + Self::NAMES(parts[4].to_string(), (user[1..]).to_string()) + } + Some(&"PONG") => { + let server = parts[3]; + Self::PONG(parts[2].to_string(), (server[1..]).to_string()) + } + _ => Self::OTHER(new.to_string()), + } + } +} diff --git a/src/lib.rs b/src/lib.rs index 42718aa..f330182 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,690 +1,6 @@ -//! A simple IRC crate written in rust -//! ```run_fut -//! use async_circe::{commands::Command, Client, Config}; -//! -//! #[tokio::main(flavor = "current_thread")] -//! async fn main() -> Result<(), tokio::io::Error> { -//! let config = Config::default(); -//! let mut client = Client::new(config).await.unwrap(); -//! client.identify().await.unwrap(); -//! -//! loop { -//! if let Some(command) = client.read().await? { -//! if let Command::PRIVMSG(nick, channel, message) = command { -//! println!("{} in {}: {}", nick, channel, message); -//! } -//! } -//! } -//! } -//! ``` -//! -//! The crate requires `tokio` with the `macros` and `rt` feature enabled to function.
-//! For more examples (connecting to IRCs that dont use SSL, getting the config from -//! a toml file, debugging or just in general an overview how a project with async-circe -//! is structured) see the [examples](https://git.karx.xyz/circe/async-circe/src/branch/master/examples) folder on our git. - -#![warn(missing_docs)] // We want everything documented -#![allow(clippy::needless_return, non_camel_case_types)] // Wants to remove a return statement, but when it's removed the code doesn't compile -#![feature(doc_cfg)] - -use tokio::io::BufReader; -use tokio::io::Error; -use tokio::io::{AsyncBufReadExt, AsyncWriteExt}; -use tokio::net::TcpStream; - -#[cfg(feature = "tls")] -use native_tls::TlsConnector; - -#[cfg(feature = "toml_config")] -use serde_derive::Deserialize; -#[cfg(feature = "toml_config")] -use std::fs::File; -#[cfg(feature = "toml_config")] -use std::io::Read; -#[cfg(any(doc, feature = "toml_config"))] -use std::path::Path; -#[cfg(feature = "toml_config")] -use tokio::io::ErrorKind; +#![allow(non_camel_case_types)] +#![feature(destructuring_assignment)] +pub mod client; pub mod commands; - -/// An IRC client -pub struct Client { - config: Config, - #[cfg(not(feature = "tls"))] - buf_reader: BufReader, - #[cfg(feature = "tls")] - buf_reader: BufReader>, -} - -/// Config for the IRC client
-/// For more information about what arguments the IRC commands take, see [`commands::Command`]. -#[derive(Clone, Debug, Default)] -#[cfg_attr(feature = "toml_config", derive(Deserialize))] -pub struct Config { - channels: Vec, - host: String, - mode: Option, - nickname: Option, - port: u16, - username: String, -} - -impl Client { - /// Creates a new client with a given [`Config`]. - /// # Example - /// ```run_fut - /// # let config = Default::default(); - /// let mut client = Client::new(config).await?; - /// # Ok(()) - /// ``` - /// # Errors - /// Returns error if the client could not connect to the host. - /// # Panics - /// Panics if the client can't connect to the given host. - pub async fn new(config: Config) -> Result { - tracing::debug!("Creating TcpStream"); - let stream = TcpStream::connect(format!("{}:{}", config.host, config.port)) - .await - .unwrap(); - tracing::debug!("New TcpStream created"); - - #[cfg(feature = "tls")] - { - let connector = TlsConnector::builder().build().unwrap(); - let async_connector = tokio_native_tls::TlsConnector::from(connector); - - let sslstream = async_connector - .connect(config.host.as_str(), stream) - .await - .unwrap(); - - let buf_reader = BufReader::new(sslstream); - - return Ok(Self { config, buf_reader }); - }; - - #[cfg(not(feature = "tls"))] - { - let buf_reader = BufReader::new(stream); - return Ok(Self { config, buf_reader }); - }; - } - - /// Identify user and joins the in the [`Config`] specified channels. - /// # Example - /// ```run_fut - /// # let config = Default::default(); - /// # let mut client = Client::new(config).await?; - /// client.identify().await?; - /// # Ok(()) - /// ``` - /// # Errors - /// Returns error if the client could not write to the stream. - pub async fn identify(&mut self) -> Result<(), Error> { - self.cap_mode(commands::CapMode::LS).await?; - self.cap_mode(commands::CapMode::END).await?; - - self.user( - self.config.username.clone(), - "*", - "*", - self.config.username.clone(), - ) - .await?; - - if let Some(nick) = self.config.nickname.clone() { - self.nick(&nick).await?; - } else { - self.nick(&self.config.username.clone()).await?; - } - - loop { - if let Some(command) = self.read().await? { - match command { - commands::Command::PING(code) => { - self.pong(&code).await?; - } - commands::Command::OTHER(line) => { - tracing::trace!("{}", line); - if line.contains("001") { - break; - } - } - _ => {} - } - } - } - - let config = self.config.clone(); - let user = { - if let Some(nick) = config.nickname { - nick - } else { - config.username - } - }; - - if let Some(mode) = config.mode { - self.mode(&user, Some(&mode)).await?; - } - - for channel in &config.channels { - self.join(channel).await?; - } - - tracing::info!("async-circe has identified"); - Ok(()) - } - - async fn read_string(&mut self) -> Result, tokio::io::Error> { - let mut buffer = String::new(); - - let num_bytes = match self.buf_reader.read_line(&mut buffer).await { - Ok(b) => b, - Err(e) => { - tracing::error!("{}", e); - return Err(e); - } - }; - - if num_bytes == 0 { - return Ok(None); - } - - Ok(Some(buffer)) - } - - /// Read data coming from the IRC as a [`commands::Command`]. - /// # Example - /// ```run_fut - /// # use async_circe::*; - /// # let config = Default::default(); - /// # let mut client = Client::new(config).await?; - /// # client.identify().await?; - /// loop { - /// if let Some(ref command) = client.read().await? { - /// if let commands::Command::PRIVMSG(nick, channel, message) = command { - /// println!("{} in {}: {}", nick, channel, message); - /// } - /// } - /// # break; - /// } - /// # Ok(()) - /// ``` - /// # Errors - /// Returns IO errors from the TcpStream. - pub async fn read(&mut self) -> Result, tokio::io::Error> { - if let Some(string) = self.read_string().await? { - let command = commands::Command::command_from_str(string).await; - - if let commands::Command::PING(command) = command { - if let Err(_e) = self.pong(&command).await { - return Ok(None); - } - return Ok(Some(commands::Command::PING("".to_string()))); - } - return Ok(Some(command)); - } - - Ok(None) - } - - async fn write(&mut self, data: String) -> Result<(), Error> { - tracing::trace!("{:?}", data); - self.buf_reader.write_all(data.as_bytes()).await?; - - Ok(()) - } - - /// Request information about the admin of a given server. - /// # Example - /// ```run_fut - /// # use async_circe::*; - /// # let config = Default::default(); - /// # let mut client = Client::new(config).await?; - /// # client.identify().await?; - /// client.admin("libera.chat").await?; - /// # Ok(()) - /// ``` - /// # Errors - /// Returns IO errors from the TcpStream. - pub async fn admin(&mut self, target: &str) -> Result<(), Error> { - self.write(format!("ADMIN {}\r\n", target)).await?; - Ok(()) - } - - /// Set the status of the client. - /// # Example - /// ```run_fut - /// # let config = Default::default(); - /// # let mut client = Client::new(config).await?; - /// # client.identify().await?; - /// client.away("afk").await?; - /// # Ok(()) - /// ``` - /// # Errors - /// Returns IO errors from the TcpStream. - pub async fn away(&mut self, message: &str) -> Result<(), Error> { - self.write(format!("AWAY {}\r\n", message)).await?; - Ok(()) - } - - #[doc(hidden)] - pub async fn cap_mode(&mut self, mode: commands::CapMode) -> Result<(), Error> { - let cap_mode = match mode { - commands::CapMode::LS => "CAP LS 302\r\n", - commands::CapMode::END => "CAP END\r\n", - }; - self.write(cap_mode.to_string()).await?; - Ok(()) - } - - /// Invite someone to a channel. - /// # Example - /// ```run_fut - /// # let config = Default::default(); - /// # let mut client = Client::new(config).await?; - /// # client.identify().await?; - /// client.invite("liblemonirc", "#async-circe").await?; - /// # Ok(()) - /// ``` - /// # Errors - /// Returns IO errors from the TcpStream. - pub async fn invite(&mut self, username: &str, channel: &str) -> Result<(), Error> { - self.write(format!("INVITE {} {}\r\n", username, channel)) - .await?; - Ok(()) - } - - /// Join a channel. - /// # Example - /// ```run_fut - /// # let config = Default::default(); - /// # let mut client = Client::new(config).await?; - /// # client.identify().await?; - /// client.join("#chaos").await?; - /// # Ok(()) - /// ``` - /// # Errors - /// Returns IO errors from the TcpStream. - pub async fn join(&mut self, channel: &str) -> Result<(), Error> { - self.write(format!("JOIN {}\r\n", channel)).await?; - Ok(()) - } - - /// List available channels on an IRC, or users in a channel. - /// # Example - /// ```run_fut - /// # let config = Default::default(); - /// # let mut client = Client::new(config).await?; - /// # client.identify().await?; - /// client.list(None, None).await?; - /// # Ok(()) - /// ``` - /// # Errors - /// Returns IO errors from the TcpStream. - pub async fn list(&mut self, channel: Option<&str>, server: Option<&str>) -> Result<(), Error> { - let mut formatted = "LIST".to_string(); - if let Some(channel) = channel { - formatted.push_str(format!(" {}", channel).as_str()); - } - if let Some(server) = server { - formatted.push_str(format!(" {}", server).as_str()); - } - formatted.push_str("\r\n"); - self.write(formatted).await?; - Ok(()) - } - - /// Set the mode for a user. - /// # Example - /// ```run_fut - /// # let config = Default::default(); - /// # let mut client = Client::new(config).await?; - /// # client.identify().await?; - /// client.mode("test", Some("+B")).await?; - /// # Ok(()) - /// ``` - /// # Errors - /// Returns IO errors from the TcpStream. - pub async fn mode(&mut self, target: &str, mode: Option<&str>) -> Result<(), Error> { - if let Some(mode) = mode { - self.write(format!("MODE {} {}\r\n", target, mode,)).await?; - } else { - self.write(format!("MODE {}\r\n", target)).await?; - } - Ok(()) - } - - /// Get all the people online in channels. - /// # Example - /// ```run_fut - /// # let config = Default::default(); - /// # let mut client = Client::new(config).await?; - /// # client.identify().await?; - /// client.names("#chaos,#async-circe", None).await?; - /// # Ok(()) - /// ``` - /// # Errors - /// Returns IO errors from the TcpStream. - pub async fn names(&mut self, channel: &str, server: Option<&str>) -> Result<(), Error> { - if let Some(server) = server { - self.write(format!("NAMES {} {}\r\n", channel, server)) - .await?; - } else { - self.write(format!("NAMES {}\r\n", channel)).await?; - } - Ok(()) - } - - /// Change your nickname on a server. - /// # Example - /// ```run_fut - /// # let config = Default::default(); - /// # let mut client = Client::new(config).await?; - /// # client.identify().await?; - /// client.nick("Not async-circe").await?; - /// # Ok(()) - /// ``` - /// # Errors - /// Returns IO errors from the TcpStream. - pub async fn nick(&mut self, nickname: &str) -> Result<(), Error> { - self.write(format!("NICK {}\r\n", nickname)).await?; - Ok(()) - } - - /// Authentificate as an operator on a server. - /// # Example - /// ```run_fut - /// # let config = Default::default(); - /// # let mut client = Client::new(config).await?; - /// # client.identify().await?; - /// client.oper("username", "password").await?; - /// # Ok(()) - /// ``` - /// # Errors - /// Returns IO errors from the TcpStream. - pub async fn oper(&mut self, username: &str, password: &str) -> Result<(), Error> { - self.write(format!("OPER {} {}\r\n", username, password)) - .await?; - Ok(()) - } - - /// Leave a channel. - /// # Example - /// ```run_fut - /// # let config = Default::default(); - /// # let mut client = Client::new(config).await?; - /// # client.identify().await?; - /// client.part("#chaos").await?; - /// # Ok(()) - /// ``` - /// # Errors - /// Returns IO errors from the TcpStream. - pub async fn part(&mut self, channel: &str) -> Result<(), Error> { - self.write(format!("PART {}\r\n", channel)).await?; - Ok(()) - } - - #[doc(hidden)] - pub async fn pass(&mut self, password: &str) -> Result<(), Error> { - self.write(format!("PASS {}\r\n", password)).await?; - Ok(()) - } - - /// Tests the presence of a connection to a server. - /// # Example - /// ```run_fut - /// # let config = Default::default(); - /// # let mut client = Client::new(config).await?; - /// # client.identify().await?; - /// client.ping("libera.chat", None).await?; - /// # Ok(()) - /// ``` - /// # Errors - /// Returns IO errors from the TcpStream. - pub async fn ping(&mut self, server1: &str, server2: Option<&str>) -> Result<(), Error> { - if let Some(server2) = server2 { - self.write(format!("PING {} {}\r\n", server1, server2)) - .await?; - } else { - self.write(format!("PING {}\r\n", server1)).await?; - } - // TODO look if we actually get a PONG back - Ok(()) - } - - #[doc(hidden)] - pub async fn pong(&mut self, ping: &str) -> Result<(), Error> { - self.write(format!("PONG {}\r\n", ping)).await?; - Ok(()) - } - - /// Send a message to a channel. - /// # Example - /// ```run_fut - /// # let config = Default::default(); - /// # let mut client = Client::new(config).await?; - /// # client.identify().await?; - /// client.privmsg("#chaos", "Hello").await?; - /// # Ok(()) - /// ``` - /// # Errors - /// Returns IO errors from the TcpStream. - pub async fn privmsg(&mut self, channel: &str, message: &str) -> Result<(), Error> { - self.write(format!("PRIVMSG {} :{}\r\n", channel, message)) - .await?; - Ok(()) - } - - /// Leave the IRC server you are connected to. - /// # Example - /// ```run_fut - /// # use async_circe::*; - /// # let config = Default::default(); - /// # let mut client = Client::new(config).await?; - /// # client.identify().await?; - /// client.quit(None).await?; - /// # Ok(()) - /// ``` - /// # Errors - /// Returns IO errors from the TcpStream. - pub async fn quit(&mut self, message: Option<&str>) -> Result<(), Error> { - if let Some(message) = message { - self.write(format!("QUIT :{}\r\n", message)).await?; - } else { - self.write(format!( - "QUIT :async-circe {} (https://crates.io/crates/async-circe)\r\n", - env!("CARGO_PKG_VERSION") - )) - .await?; - } - tracing::info!("async-circe shutting down"); - self.buf_reader.shutdown().await?; - Ok(()) - } - - /// Get the topic of a channel. - /// # Example - /// ```run_fut - /// # use async_circe::*; - /// # let config = Default::default(); - /// # let mut client = Client::new(config).await?; - /// # client.identify().await?; - /// client.topic("#chaos", None).await?; - /// # Ok(()) - /// ``` - /// Set the topic of a channel. - /// # Example - /// ```run_fut - /// # let config = Default::default(); - /// # let mut client = Client::new(config).await?; - /// # client.identify().await?; - /// client.topic("#chaos", Some("CHAOS")).await?; - /// # Ok(()) - /// ``` - /// # Errors - /// Returns IO errors from the TcpStream. - pub async fn topic(&mut self, channel: &str, topic: Option<&str>) -> Result<(), Error> { - if let Some(topic) = topic { - self.write(format!("TOPIC {} :{}\r\n", channel, topic)) - .await?; - } else { - self.write(format!("TOPIC {}\r\n", channel)).await?; - } - - Ok(()) - } - - #[doc(hidden)] - pub async fn user( - &mut self, - username: String, - s1: &str, - s2: &str, - realname: String, - ) -> Result<(), Error> { - self.write(format!("USER {} {} {} :{}\r\n", username, s1, s2, realname)) - .await?; - Ok(()) - } -} - -impl Config { - /// Create a new config for the client - /// # Example - /// ```rust - /// # use async_circe::Config; - /// let config = Config::new( - /// &["#chaos", "#async-circe"], - /// "karx.xyz", - /// Some("+B"), - /// Some("async-circe"), - /// 6697, - /// "circe", - /// ); - /// ``` - #[must_use] - pub fn new( - channels: &[&'static str], - host: &str, - mode: Option<&'static str>, - nickname: Option<&'static str>, - port: u16, - username: &str, - ) -> Self { - tracing::info!("New async-circe client config"); - // Conversion from &'static str to String - let channels_config = channels - .iter() - .map(|channel| (*channel).to_string()) - .collect(); - - let mode_config: Option; - if let Some(mode) = mode { - mode_config = Some(mode.to_string()); - } else { - mode_config = None; - } - - let nickname_config: Option; - if let Some(nickname) = nickname { - nickname_config = Some(nickname.to_string()); - } else { - nickname_config = Some(username.to_string()); - } - - let config = Self { - channels: channels_config, - host: host.into(), - mode: mode_config, - nickname: nickname_config, - port, - username: username.into(), - }; - - tracing::debug!("async-circe config: {:?}", config); - config - } - - /// Allows for configuring async-circe at runtime. - /// # Example - /// ```run_fut - /// # use async_circe::Config; - /// let config = Config::runtime_config( - /// vec!["#chaos".to_string(), "#async-circe".to_string()], - /// "karx.xyz".to_string(), - /// Some("+B".to_string()), - /// Some("async-circe".to_string()), - /// 6697, - /// "circe".to_string(), - /// ); - /// ``` - #[must_use] - pub fn runtime_config( - channels: Vec, - host: String, - mode: Option, - nickname: Option, - port: u16, - username: String, - ) -> Self { - tracing::info!("New async-circe client config"); - let mode_config: Option; - if let Some(mode) = mode { - mode_config = Some(mode); - } else { - mode_config = None; - } - - let nickname_config: Option; - if let Some(nickname) = nickname { - nickname_config = Some(nickname); - } else { - nickname_config = Some(username.to_string()); - } - - let config = Self { - channels, - host, - mode: mode_config, - nickname: nickname_config, - port, - username, - }; - - tracing::debug!("async-circe config: {:?}", config); - config - } - - /// Create a config from a toml file - /// # Example - /// ```toml - /// #Config.toml - /// channels = ["#chaos", "#async-circe"] - /// host = "karx.xyz" - /// mode = "+B" - /// nickname = "async-circe" - /// port = 6667 - /// username = "circe" - /// ``` - /// ```run_fut - /// let config = Config::from_toml("Config.toml")?; - /// ``` - /// # Errors - /// Returns an Error if the file cannot be opened or if the TOML is invalid - #[cfg(any(doc, feature = "toml_config"))] - #[doc(cfg(feature = "toml_config"))] - pub fn from_toml>(path: P) -> Result { - tracing::info!("New async-circe client config"); - let mut file = File::open(&path)?; - let mut data = String::new(); - file.read_to_string(&mut data)?; - - let config = toml::from_str(&data) - .map_err(|e| Error::new(ErrorKind::Other, format!("Invalid TOML: {}", e))); - tracing::debug!("async-circe config: {:?}", config); - config - } -} +pub mod stream; diff --git a/src/stream/mod.rs b/src/stream/mod.rs new file mode 100644 index 0000000..6295529 --- /dev/null +++ b/src/stream/mod.rs @@ -0,0 +1,2 @@ +pub mod read; +pub mod write; diff --git a/src/stream/read.rs b/src/stream/read.rs new file mode 100644 index 0000000..74d5cb5 --- /dev/null +++ b/src/stream/read.rs @@ -0,0 +1,45 @@ +use crate::commands::parser::Command; + +use tokio::io::{AsyncBufReadExt, BufReader, Error, ReadHalf}; +use tokio::net::TcpStream; + +#[cfg(feature = "tls")] +use tokio_rustls::client::TlsStream; + +pub struct Read { + #[cfg(feature = "tls")] + pub recv: BufReader>>, + #[cfg(not(feature = "tls"))] + pub recv: BufReader>, +} + +impl Read { + #[must_use] + #[cfg(feature = "tls")] + pub fn new(recv: BufReader>>) -> Self { + Self { recv } + } + + #[must_use] + #[cfg(not(feature = "tls"))] + pub fn new(recv: BufReader>) -> Self { + Self { recv } + } + + /// # Errors + /// Returns errors from the ``TcpStream``. + pub async fn read(&mut self) -> Result, Error> { + let mut buf = String::new(); + let bytes = match self.recv.read_line(&mut buf).await { + Ok(b) => b, + Err(e) => return Err(e), + }; + + if bytes == 0 { + return Ok(None); + } + + let command = Command::command_from_str(buf).await; + Ok(Some(command)) + } +} diff --git a/src/stream/write.rs b/src/stream/write.rs new file mode 100644 index 0000000..d4d4053 --- /dev/null +++ b/src/stream/write.rs @@ -0,0 +1,39 @@ +use parking_lot::RwLock; +use std::sync::Arc; +use tokio::io::{AsyncWriteExt, Error, WriteHalf}; +use tokio::net::TcpStream; + +#[cfg(feature = "tls")] +use tokio_rustls::client::TlsStream; + +#[derive(Clone)] +pub struct Write { + #[cfg(feature = "tls")] + pub tx: Arc>>>, + #[cfg(not(feature = "tls"))] + pub tx: Arc>>, +} + +impl Write { + #[cfg(not(feature = "tls"))] + pub async fn new(tx: WriteHalf) -> Self { + Self { + tx: Arc::new(RwLock::new(tx)), + } + } + + #[cfg(feature = "tls")] + pub async fn new(tx: WriteHalf>) -> Self { + Self { + tx: Arc::new(RwLock::new(tx)), + } + } + + /// # Errors + /// Returns errors from the ``TcpStream``. + pub async fn write(self, buf: String) -> Result<(), Error> { + tracing::trace!("{:?}", buf); + self.tx.write().write_all(buf.as_bytes()).await?; + Ok(()) + } +}