Refactor, use tokio-rustls, split streams

This commit is contained in:
famfo 2022-01-16 15:31:35 +01:00
parent d955bef06e
commit d9d94daaca
17 changed files with 1303 additions and 1572 deletions

View file

@ -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"]

View file

@ -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"

View file

@ -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);
}

View file

@ -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"

View file

@ -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);
}

View file

@ -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"

View file

@ -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);
}

View file

@ -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);
}

80
src/client/config.rs Normal file
View file

@ -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<String>,
pub mode: Option<String>,
pub channels: Vec<String>,
}
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<String>,
mode: Option<String>,
channels: Vec<String>,
) -> 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<P: AsRef<Path>>(file: P) -> Result<Self, tokio::io::Error> {
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)))
}
}

499
src/client/mod.rs Normal file
View file

@ -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(())
}
}

View file

@ -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 <nickname> <reserved> <distribution> <type> <reserved> <info>
// SQUIT <server> <comment>
//
/// 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<String>,
/// Server to foreward request to
Option<String>,
),
/// 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<String>,
),
/// 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()),
}
}
}

1
src/commands/mod.rs Normal file
View file

@ -0,0 +1 @@
pub mod parser;

82
src/commands/parser.rs Normal file
View file

@ -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<String>, Option<String>),
MODE(String, Option<String>),
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()),
}
}
}

View file

@ -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.<br>
//! 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<TcpStream>,
#[cfg(feature = "tls")]
buf_reader: BufReader<tokio_native_tls::TlsStream<tokio::net::TcpStream>>,
}
/// Config for the IRC client<br>
/// 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<String>,
host: String,
mode: Option<String>,
nickname: Option<String>,
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<Self, Error> {
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<Option<String>, 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<Option<commands::Command>, 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<String>;
if let Some(mode) = mode {
mode_config = Some(mode.to_string());
} else {
mode_config = None;
}
let nickname_config: Option<String>;
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<String>,
host: String,
mode: Option<String>,
nickname: Option<String>,
port: u16,
username: String,
) -> Self {
tracing::info!("New async-circe client config");
let mode_config: Option<String>;
if let Some(mode) = mode {
mode_config = Some(mode);
} else {
mode_config = None;
}
let nickname_config: Option<String>;
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<P: AsRef<Path>>(path: P) -> Result<Self, Error> {
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;

2
src/stream/mod.rs Normal file
View file

@ -0,0 +1,2 @@
pub mod read;
pub mod write;

45
src/stream/read.rs Normal file
View file

@ -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<ReadHalf<TlsStream<TcpStream>>>,
#[cfg(not(feature = "tls"))]
pub recv: BufReader<ReadHalf<TcpStream>>,
}
impl Read {
#[must_use]
#[cfg(feature = "tls")]
pub fn new(recv: BufReader<ReadHalf<TlsStream<TcpStream>>>) -> Self {
Self { recv }
}
#[must_use]
#[cfg(not(feature = "tls"))]
pub fn new(recv: BufReader<ReadHalf<TcpStream>>) -> Self {
Self { recv }
}
/// # Errors
/// Returns errors from the ``TcpStream``.
pub async fn read(&mut self) -> Result<Option<Command>, 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))
}
}

39
src/stream/write.rs Normal file
View file

@ -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<RwLock<WriteHalf<TlsStream<TcpStream>>>>,
#[cfg(not(feature = "tls"))]
pub tx: Arc<RwLock<WriteHalf<TcpStream>>>,
}
impl Write {
#[cfg(not(feature = "tls"))]
pub async fn new(tx: WriteHalf<TcpStream>) -> Self {
Self {
tx: Arc::new(RwLock::new(tx)),
}
}
#[cfg(feature = "tls")]
pub async fn new(tx: WriteHalf<TlsStream<TcpStream>>) -> 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(())
}
}