Merge pull request #220 from udoprog/develop
Bump to Tokio 0.3.0 and clean up a few dependencies
This commit is contained in:
commit
d77696b389
7 changed files with 358 additions and 358 deletions
17
Cargo.toml
17
Cargo.toml
|
@ -36,24 +36,20 @@ yaml = ["yaml_config"]
|
||||||
|
|
||||||
proxy = ["tokio-socks"]
|
proxy = ["tokio-socks"]
|
||||||
|
|
||||||
tls-native = ["native-tls", "tokio-tls"]
|
tls-native = ["native-tls", "tokio-native-tls"]
|
||||||
tls-rust = ["tokio-rustls", "webpki-roots"]
|
tls-rust = ["tokio-rustls", "webpki-roots"]
|
||||||
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
bufstream = "0.1.0"
|
|
||||||
bytes = "0.5.0"
|
|
||||||
chrono = "0.4.0"
|
chrono = "0.4.0"
|
||||||
encoding = "0.2.0"
|
encoding = "0.2.0"
|
||||||
futures-channel = "0.3.0"
|
futures-util = { version = "0.3.0", default-features = false, features = ["sink"] }
|
||||||
futures-util = { version = "0.3.0", features = ["sink"] }
|
|
||||||
irc-proto = { version = "0.14.0", path = "irc-proto" }
|
irc-proto = { version = "0.14.0", path = "irc-proto" }
|
||||||
log = "0.4.0"
|
log = "0.4.0"
|
||||||
parking_lot = "0.11.0"
|
parking_lot = "0.11.0"
|
||||||
pin-utils = "0.1.0-alpha.4"
|
|
||||||
thiserror = "1.0.0"
|
thiserror = "1.0.0"
|
||||||
tokio = { version = "0.2.0", features = ["macros", "net", "stream", "time"] }
|
tokio = { version = "0.3.0", features = ["net", "stream", "time", "sync"] }
|
||||||
tokio-util = { version = "0.3.0", features = ["codec"] }
|
tokio-util = { version = "0.4.0", features = ["codec"] }
|
||||||
|
|
||||||
# Feature - Config
|
# Feature - Config
|
||||||
serde = { version = "1.0.0", optional = true }
|
serde = { version = "1.0.0", optional = true }
|
||||||
|
@ -67,8 +63,8 @@ tokio-socks = { version = "0.3.0", optional = true }
|
||||||
|
|
||||||
# Feature - TLS
|
# Feature - TLS
|
||||||
native-tls = { version = "0.2.0", optional = true }
|
native-tls = { version = "0.2.0", optional = true }
|
||||||
tokio-rustls = { version = "0.14.0", optional = true }
|
tokio-rustls = { version = "0.20.0", optional = true }
|
||||||
tokio-tls = { version = "0.3.0", optional = true }
|
tokio-native-tls = { version = "0.2.0", optional = true }
|
||||||
webpki-roots = { version = "0.20.0", optional = true }
|
webpki-roots = { version = "0.20.0", optional = true }
|
||||||
|
|
||||||
|
|
||||||
|
@ -78,6 +74,7 @@ args = "2.0.0"
|
||||||
env_logger = "0.7.0"
|
env_logger = "0.7.0"
|
||||||
futures = "0.3.0"
|
futures = "0.3.0"
|
||||||
getopts = "0.2.0"
|
getopts = "0.2.0"
|
||||||
|
tokio = { version = "0.3.0", features = ["rt", "rt-multi-thread", "macros", "net", "stream", "time"] }
|
||||||
|
|
||||||
|
|
||||||
[[example]]
|
[[example]]
|
||||||
|
|
|
@ -21,5 +21,5 @@ encoding = "0.2.0"
|
||||||
thiserror = "1.0.0"
|
thiserror = "1.0.0"
|
||||||
|
|
||||||
bytes = { version = "0.5.0", optional = true }
|
bytes = { version = "0.5.0", optional = true }
|
||||||
tokio = { version = "0.2.0", optional = true }
|
tokio = { version = "0.3.0", optional = true }
|
||||||
tokio-util = { version = "0.3.0", features = ["codec"], optional = true }
|
tokio-util = { version = "0.4.0", features = ["codec"], optional = true }
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
//! A module providing IRC connections for use by `IrcServer`s.
|
//! A module providing IRC connections for use by `IrcServer`s.
|
||||||
use futures_channel::mpsc::UnboundedSender;
|
|
||||||
use futures_util::{sink::Sink, stream::Stream};
|
use futures_util::{sink::Sink, stream::Stream};
|
||||||
use std::{
|
use std::{
|
||||||
fmt,
|
fmt,
|
||||||
|
@ -7,7 +6,8 @@ use std::{
|
||||||
task::{Context, Poll},
|
task::{Context, Poll},
|
||||||
};
|
};
|
||||||
use tokio::net::TcpStream;
|
use tokio::net::TcpStream;
|
||||||
use tokio_util::codec::Decoder;
|
use tokio::sync::mpsc::UnboundedSender;
|
||||||
|
use tokio_util::codec::Framed;
|
||||||
|
|
||||||
#[cfg(feature = "proxy")]
|
#[cfg(feature = "proxy")]
|
||||||
use tokio_socks::tcp::Socks5Stream;
|
use tokio_socks::tcp::Socks5Stream;
|
||||||
|
@ -22,7 +22,7 @@ use std::{fs::File, io::Read};
|
||||||
use native_tls::{Certificate, Identity, TlsConnector};
|
use native_tls::{Certificate, Identity, TlsConnector};
|
||||||
|
|
||||||
#[cfg(feature = "tls-native")]
|
#[cfg(feature = "tls-native")]
|
||||||
use tokio_tls::{self, TlsStream};
|
use tokio_native_tls::{self, TlsStream};
|
||||||
|
|
||||||
#[cfg(feature = "tls-rust")]
|
#[cfg(feature = "tls-rust")]
|
||||||
use std::{
|
use std::{
|
||||||
|
@ -150,7 +150,7 @@ impl Connection {
|
||||||
tx: UnboundedSender<Message>,
|
tx: UnboundedSender<Message>,
|
||||||
) -> error::Result<Transport<TcpStream>> {
|
) -> error::Result<Transport<TcpStream>> {
|
||||||
let stream = Self::new_stream(config).await?;
|
let stream = Self::new_stream(config).await?;
|
||||||
let framed = IrcCodec::new(config.encoding())?.framed(stream);
|
let framed = Framed::new(stream, IrcCodec::new(config.encoding())?);
|
||||||
|
|
||||||
Ok(Transport::new(&config, framed, tx))
|
Ok(Transport::new(&config, framed, tx))
|
||||||
}
|
}
|
||||||
|
@ -184,12 +184,12 @@ impl Connection {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let connector: tokio_tls::TlsConnector = builder.build()?.into();
|
let connector: tokio_native_tls::TlsConnector = builder.build()?.into();
|
||||||
let domain = config.server()?;
|
let domain = config.server()?;
|
||||||
|
|
||||||
let stream = Self::new_stream(config).await?;
|
let stream = Self::new_stream(config).await?;
|
||||||
let stream = connector.connect(domain, stream).await?;
|
let stream = connector.connect(domain, stream).await?;
|
||||||
let framed = IrcCodec::new(config.encoding())?.framed(stream);
|
let framed = Framed::new(stream, IrcCodec::new(config.encoding())?);
|
||||||
|
|
||||||
Ok(Transport::new(&config, framed, tx))
|
Ok(Transport::new(&config, framed, tx))
|
||||||
}
|
}
|
||||||
|
@ -236,7 +236,7 @@ impl Connection {
|
||||||
|
|
||||||
let stream = Self::new_stream(config).await?;
|
let stream = Self::new_stream(config).await?;
|
||||||
let stream = connector.connect(domain, stream).await?;
|
let stream = connector.connect(domain, stream).await?;
|
||||||
let framed = IrcCodec::new(config.encoding())?.framed(stream);
|
let framed = Framed::new(stream, IrcCodec::new(config.encoding())?);
|
||||||
|
|
||||||
Ok(Transport::new(&config, framed, tx))
|
Ok(Transport::new(&config, framed, tx))
|
||||||
}
|
}
|
||||||
|
@ -262,7 +262,7 @@ impl Connection {
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let stream = MockStream::new(&initial);
|
let stream = MockStream::new(&initial);
|
||||||
let framed = IrcCodec::new(config.encoding())?.framed(stream);
|
let framed = Framed::new(stream, IrcCodec::new(config.encoding())?);
|
||||||
|
|
||||||
Ok(Transport::new(&config, framed, tx))
|
Ok(Transport::new(&config, framed, tx))
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@ use std::{
|
||||||
pin::Pin,
|
pin::Pin,
|
||||||
task::{Context, Poll},
|
task::{Context, Poll},
|
||||||
};
|
};
|
||||||
use tokio::io::{AsyncRead, AsyncWrite};
|
use tokio::io::{AsyncRead, AsyncWrite, ReadBuf};
|
||||||
|
|
||||||
/// A fake stream for testing network applications backed by buffers.
|
/// A fake stream for testing network applications backed by buffers.
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
|
@ -41,9 +41,11 @@ impl AsyncRead for MockStream {
|
||||||
fn poll_read(
|
fn poll_read(
|
||||||
mut self: Pin<&mut Self>,
|
mut self: Pin<&mut Self>,
|
||||||
_: &mut Context<'_>,
|
_: &mut Context<'_>,
|
||||||
buf: &mut [u8],
|
buf: &mut ReadBuf<'_>,
|
||||||
) -> Poll<io::Result<usize>> {
|
) -> Poll<io::Result<()>> {
|
||||||
Poll::Ready(self.as_mut().received.read(buf))
|
let n = self.as_mut().received.read(buf.initialize_unfilled())?;
|
||||||
|
buf.advance(n);
|
||||||
|
Poll::Ready(Ok(()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -49,7 +49,6 @@
|
||||||
|
|
||||||
#[cfg(feature = "ctcp")]
|
#[cfg(feature = "ctcp")]
|
||||||
use chrono::prelude::*;
|
use chrono::prelude::*;
|
||||||
use futures_channel::mpsc::{self, UnboundedReceiver, UnboundedSender};
|
|
||||||
use futures_util::{
|
use futures_util::{
|
||||||
future::{FusedFuture, Future},
|
future::{FusedFuture, Future},
|
||||||
ready,
|
ready,
|
||||||
|
@ -68,6 +67,7 @@ use std::{
|
||||||
sync::Arc,
|
sync::Arc,
|
||||||
task::{Context, Poll},
|
task::{Context, Poll},
|
||||||
};
|
};
|
||||||
|
use tokio::sync::mpsc::{self, UnboundedReceiver, UnboundedSender};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
client::{
|
client::{
|
||||||
|
@ -95,326 +95,338 @@ pub mod transport;
|
||||||
|
|
||||||
macro_rules! pub_state_base {
|
macro_rules! pub_state_base {
|
||||||
() => {
|
() => {
|
||||||
/// Changes the modes for the specified target.
|
/// Changes the modes for the specified target.
|
||||||
pub fn send_mode<S, T>(&self, target: S, modes: &[Mode<T>]) -> error::Result<()>
|
pub fn send_mode<S, T>(&self, target: S, modes: &[Mode<T>]) -> error::Result<()>
|
||||||
where
|
where
|
||||||
S: fmt::Display,
|
S: fmt::Display,
|
||||||
T: ModeType,
|
T: ModeType,
|
||||||
{
|
{
|
||||||
self.send(T::mode(&target.to_string(), modes))
|
self.send(T::mode(&target.to_string(), modes))
|
||||||
}
|
|
||||||
|
|
||||||
/// Joins the specified channel or chanlist.
|
|
||||||
pub fn send_join<S>(&self, chanlist: S) -> error::Result<()>
|
|
||||||
where
|
|
||||||
S: fmt::Display,
|
|
||||||
{
|
|
||||||
self.send(JOIN(chanlist.to_string(), None, None))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Joins the specified channel or chanlist using the specified key or keylist.
|
|
||||||
pub fn send_join_with_keys<S1, S2>(&self, chanlist: &str, keylist: &str) -> error::Result<()>
|
|
||||||
where
|
|
||||||
S1: fmt::Display,
|
|
||||||
S2: fmt::Display,
|
|
||||||
{
|
|
||||||
self.send(JOIN(chanlist.to_string(), Some(keylist.to_string()), None))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sends a notice to the specified target.
|
|
||||||
pub fn send_notice<S1, S2>(&self, target: S1, message: S2) -> error::Result<()>
|
|
||||||
where
|
|
||||||
S1: fmt::Display,
|
|
||||||
S2: fmt::Display,
|
|
||||||
{
|
|
||||||
let message = message.to_string();
|
|
||||||
for line in message.split("\r\n") {
|
|
||||||
self.send(NOTICE(target.to_string(), line.to_string()))?
|
|
||||||
}
|
}
|
||||||
Ok(())
|
|
||||||
}
|
/// Joins the specified channel or chanlist.
|
||||||
}
|
pub fn send_join<S>(&self, chanlist: S) -> error::Result<()>
|
||||||
|
where
|
||||||
|
S: fmt::Display,
|
||||||
|
{
|
||||||
|
self.send(JOIN(chanlist.to_string(), None, None))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Joins the specified channel or chanlist using the specified key or keylist.
|
||||||
|
pub fn send_join_with_keys<S1, S2>(
|
||||||
|
&self,
|
||||||
|
chanlist: &str,
|
||||||
|
keylist: &str,
|
||||||
|
) -> error::Result<()>
|
||||||
|
where
|
||||||
|
S1: fmt::Display,
|
||||||
|
S2: fmt::Display,
|
||||||
|
{
|
||||||
|
self.send(JOIN(chanlist.to_string(), Some(keylist.to_string()), None))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sends a notice to the specified target.
|
||||||
|
pub fn send_notice<S1, S2>(&self, target: S1, message: S2) -> error::Result<()>
|
||||||
|
where
|
||||||
|
S1: fmt::Display,
|
||||||
|
S2: fmt::Display,
|
||||||
|
{
|
||||||
|
let message = message.to_string();
|
||||||
|
for line in message.split("\r\n") {
|
||||||
|
self.send(NOTICE(target.to_string(), line.to_string()))?
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! pub_sender_base {
|
macro_rules! pub_sender_base {
|
||||||
() => {
|
() => {
|
||||||
/// Sends a request for a list of server capabilities for a specific IRCv3 version.
|
/// Sends a request for a list of server capabilities for a specific IRCv3 version.
|
||||||
pub fn send_cap_ls(&self, version: NegotiationVersion) -> error::Result<()> {
|
pub fn send_cap_ls(&self, version: NegotiationVersion) -> error::Result<()> {
|
||||||
self.send(Command::CAP(
|
self.send(Command::CAP(
|
||||||
None,
|
None,
|
||||||
LS,
|
LS,
|
||||||
match version {
|
match version {
|
||||||
NegotiationVersion::V301 => None,
|
NegotiationVersion::V301 => None,
|
||||||
NegotiationVersion::V302 => Some("302".to_owned()),
|
NegotiationVersion::V302 => Some("302".to_owned()),
|
||||||
},
|
},
|
||||||
None,
|
None,
|
||||||
))
|
))
|
||||||
}
|
|
||||||
|
|
||||||
/// Sends an IRCv3 capabilities request for the specified extensions.
|
|
||||||
pub fn send_cap_req(&self, extensions: &[Capability]) -> error::Result<()> {
|
|
||||||
let append = |mut s: String, c| {
|
|
||||||
s.push_str(c);
|
|
||||||
s.push(' ');
|
|
||||||
s
|
|
||||||
};
|
|
||||||
let mut exts = extensions
|
|
||||||
.iter()
|
|
||||||
.map(|c| c.as_ref())
|
|
||||||
.fold(String::new(), append);
|
|
||||||
let len = exts.len() - 1;
|
|
||||||
exts.truncate(len);
|
|
||||||
self.send(CAP(None, REQ, None, Some(exts)))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sends a SASL AUTHENTICATE message with the specified data.
|
|
||||||
pub fn send_sasl<S: fmt::Display>(&self, data: S) -> error::Result<()> {
|
|
||||||
self.send(AUTHENTICATE(data.to_string()))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sends a SASL AUTHENTICATE request to use the PLAIN mechanism.
|
|
||||||
pub fn send_sasl_plain(&self) -> error::Result<()> {
|
|
||||||
self.send_sasl("PLAIN")
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sends a SASL AUTHENTICATE request to use the EXTERNAL mechanism.
|
|
||||||
pub fn send_sasl_external(&self) -> error::Result<()> {
|
|
||||||
self.send_sasl("EXTERNAL")
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sends a SASL AUTHENTICATE request to abort authentication.
|
|
||||||
pub fn send_sasl_abort(&self) -> error::Result<()> {
|
|
||||||
self.send_sasl("*")
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sends a PONG with the specified message.
|
|
||||||
pub fn send_pong<S>(&self, msg: S) -> error::Result<()>
|
|
||||||
where
|
|
||||||
S: fmt::Display,
|
|
||||||
{
|
|
||||||
self.send(PONG(msg.to_string(), None))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Parts the specified channel or chanlist.
|
|
||||||
pub fn send_part<S>(&self, chanlist: S) -> error::Result<()>
|
|
||||||
where
|
|
||||||
S: fmt::Display,
|
|
||||||
{
|
|
||||||
self.send(PART(chanlist.to_string(), None))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Attempts to oper up using the specified username and password.
|
|
||||||
pub fn send_oper<S1, S2>(&self, username: S1, password: S2) -> error::Result<()>
|
|
||||||
where
|
|
||||||
S1: fmt::Display,
|
|
||||||
S2: fmt::Display,
|
|
||||||
{
|
|
||||||
self.send(OPER(username.to_string(), password.to_string()))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sends a message to the specified target. If the message contains IRC newlines (`\r\n`), it
|
|
||||||
/// will automatically be split and sent as multiple separate `PRIVMSG`s to the specified
|
|
||||||
/// target. If you absolutely must avoid this behavior, you can do
|
|
||||||
/// `client.send(PRIVMSG(target, message))` directly.
|
|
||||||
pub fn send_privmsg<S1, S2>(&self, target: S1, message: S2) -> error::Result<()>
|
|
||||||
where
|
|
||||||
S1: fmt::Display,
|
|
||||||
S2: fmt::Display,
|
|
||||||
{
|
|
||||||
let message = message.to_string();
|
|
||||||
for line in message.split("\r\n") {
|
|
||||||
self.send(PRIVMSG(target.to_string(), line.to_string()))?
|
|
||||||
}
|
}
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets the topic of a channel or requests the current one.
|
/// Sends an IRCv3 capabilities request for the specified extensions.
|
||||||
/// If `topic` is an empty string, it won't be included in the message.
|
pub fn send_cap_req(&self, extensions: &[Capability]) -> error::Result<()> {
|
||||||
pub fn send_topic<S1, S2>(&self, channel: S1, topic: S2) -> error::Result<()>
|
let append = |mut s: String, c| {
|
||||||
where
|
s.push_str(c);
|
||||||
S1: fmt::Display,
|
s.push(' ');
|
||||||
S2: fmt::Display,
|
s
|
||||||
{
|
};
|
||||||
let topic = topic.to_string();
|
let mut exts = extensions
|
||||||
self.send(TOPIC(
|
.iter()
|
||||||
channel.to_string(),
|
.map(|c| c.as_ref())
|
||||||
if topic.is_empty() { None } else { Some(topic) },
|
.fold(String::new(), append);
|
||||||
))
|
let len = exts.len() - 1;
|
||||||
}
|
exts.truncate(len);
|
||||||
|
self.send(CAP(None, REQ, None, Some(exts)))
|
||||||
/// Kills the target with the provided message.
|
|
||||||
pub fn send_kill<S1, S2>(&self, target: S1, message: S2) -> error::Result<()>
|
|
||||||
where
|
|
||||||
S1: fmt::Display,
|
|
||||||
S2: fmt::Display,
|
|
||||||
{
|
|
||||||
self.send(KILL(target.to_string(), message.to_string()))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Kicks the listed nicknames from the listed channels with a comment.
|
|
||||||
/// If `message` is an empty string, it won't be included in the message.
|
|
||||||
pub fn send_kick<S1, S2, S3>(
|
|
||||||
&self,
|
|
||||||
chanlist: S1,
|
|
||||||
nicklist: S2,
|
|
||||||
message: S3,
|
|
||||||
) -> error::Result<()>
|
|
||||||
where
|
|
||||||
S1: fmt::Display,
|
|
||||||
S2: fmt::Display,
|
|
||||||
S3: fmt::Display,
|
|
||||||
{
|
|
||||||
let message = message.to_string();
|
|
||||||
self.send(KICK(
|
|
||||||
chanlist.to_string(),
|
|
||||||
nicklist.to_string(),
|
|
||||||
if message.is_empty() {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(message)
|
|
||||||
},
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Changes the mode of the target by force.
|
|
||||||
/// If `modeparams` is an empty string, it won't be included in the message.
|
|
||||||
pub fn send_samode<S1, S2, S3>(&self, target: S1, mode: S2, modeparams: S3) -> error::Result<()>
|
|
||||||
where
|
|
||||||
S1: fmt::Display,
|
|
||||||
S2: fmt::Display,
|
|
||||||
S3: fmt::Display,
|
|
||||||
{
|
|
||||||
let modeparams = modeparams.to_string();
|
|
||||||
self.send(SAMODE(
|
|
||||||
target.to_string(),
|
|
||||||
mode.to_string(),
|
|
||||||
if modeparams.is_empty() {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(modeparams)
|
|
||||||
},
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Forces a user to change from the old nickname to the new nickname.
|
|
||||||
pub fn send_sanick<S1, S2>(&self, old_nick: S1, new_nick: S2) -> error::Result<()>
|
|
||||||
where
|
|
||||||
S1: fmt::Display,
|
|
||||||
S2: fmt::Display,
|
|
||||||
{
|
|
||||||
self.send(SANICK(old_nick.to_string(), new_nick.to_string()))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Invites a user to the specified channel.
|
|
||||||
pub fn send_invite<S1, S2>(&self, nick: S1, chan: S2) -> error::Result<()>
|
|
||||||
where
|
|
||||||
S1: fmt::Display,
|
|
||||||
S2: fmt::Display,
|
|
||||||
{
|
|
||||||
self.send(INVITE(nick.to_string(), chan.to_string()))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Quits the server entirely with a message.
|
|
||||||
/// This defaults to `Powered by Rust.` if none is specified.
|
|
||||||
pub fn send_quit<S>(&self, msg: S) -> error::Result<()>
|
|
||||||
where
|
|
||||||
S: fmt::Display,
|
|
||||||
{
|
|
||||||
let msg = msg.to_string();
|
|
||||||
self.send(QUIT(Some(if msg.is_empty() {
|
|
||||||
"Powered by Rust.".to_string()
|
|
||||||
} else {
|
|
||||||
msg
|
|
||||||
})))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sends a CTCP-escaped message to the specified target.
|
|
||||||
/// This requires the CTCP feature to be enabled.
|
|
||||||
#[cfg(feature = "ctcp")]
|
|
||||||
pub fn send_ctcp<S1, S2>(&self, target: S1, msg: S2) -> error::Result<()>
|
|
||||||
where
|
|
||||||
S1: fmt::Display,
|
|
||||||
S2: fmt::Display,
|
|
||||||
{
|
|
||||||
let msg = msg.to_string();
|
|
||||||
for line in msg.split("\r\n") {
|
|
||||||
self.send(PRIVMSG(target.to_string(), format!("\u{001}{}\u{001}", line)))?
|
|
||||||
}
|
}
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sends an action command to the specified target.
|
/// Sends a SASL AUTHENTICATE message with the specified data.
|
||||||
/// This requires the CTCP feature to be enabled.
|
pub fn send_sasl<S: fmt::Display>(&self, data: S) -> error::Result<()> {
|
||||||
#[cfg(feature = "ctcp")]
|
self.send(AUTHENTICATE(data.to_string()))
|
||||||
pub fn send_action<S1, S2>(&self, target: S1, msg: S2) -> error::Result<()>
|
}
|
||||||
where
|
|
||||||
S1: fmt::Display,
|
|
||||||
S2: fmt::Display,
|
|
||||||
{
|
|
||||||
self.send_ctcp(target, &format!("ACTION {}", msg.to_string())[..])
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sends a finger request to the specified target.
|
/// Sends a SASL AUTHENTICATE request to use the PLAIN mechanism.
|
||||||
/// This requires the CTCP feature to be enabled.
|
pub fn send_sasl_plain(&self) -> error::Result<()> {
|
||||||
#[cfg(feature = "ctcp")]
|
self.send_sasl("PLAIN")
|
||||||
pub fn send_finger<S: fmt::Display>(&self, target: S) -> error::Result<()>
|
}
|
||||||
where
|
|
||||||
S: fmt::Display,
|
|
||||||
{
|
|
||||||
self.send_ctcp(target, "FINGER")
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sends a version request to the specified target.
|
/// Sends a SASL AUTHENTICATE request to use the EXTERNAL mechanism.
|
||||||
/// This requires the CTCP feature to be enabled.
|
pub fn send_sasl_external(&self) -> error::Result<()> {
|
||||||
#[cfg(feature = "ctcp")]
|
self.send_sasl("EXTERNAL")
|
||||||
pub fn send_version<S>(&self, target: S) -> error::Result<()>
|
}
|
||||||
where
|
|
||||||
S: fmt::Display,
|
|
||||||
{
|
|
||||||
self.send_ctcp(target, "VERSION")
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sends a source request to the specified target.
|
/// Sends a SASL AUTHENTICATE request to abort authentication.
|
||||||
/// This requires the CTCP feature to be enabled.
|
pub fn send_sasl_abort(&self) -> error::Result<()> {
|
||||||
#[cfg(feature = "ctcp")]
|
self.send_sasl("*")
|
||||||
pub fn send_source<S>(&self, target: S) -> error::Result<()>
|
}
|
||||||
where
|
|
||||||
S: fmt::Display,
|
|
||||||
{
|
|
||||||
self.send_ctcp(target, "SOURCE")
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sends a user info request to the specified target.
|
/// Sends a PONG with the specified message.
|
||||||
/// This requires the CTCP feature to be enabled.
|
pub fn send_pong<S>(&self, msg: S) -> error::Result<()>
|
||||||
#[cfg(feature = "ctcp")]
|
where
|
||||||
pub fn send_user_info<S>(&self, target: S) -> error::Result<()>
|
S: fmt::Display,
|
||||||
where
|
{
|
||||||
S: fmt::Display,
|
self.send(PONG(msg.to_string(), None))
|
||||||
{
|
}
|
||||||
self.send_ctcp(target, "USERINFO")
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sends a finger request to the specified target.
|
/// Parts the specified channel or chanlist.
|
||||||
/// This requires the CTCP feature to be enabled.
|
pub fn send_part<S>(&self, chanlist: S) -> error::Result<()>
|
||||||
#[cfg(feature = "ctcp")]
|
where
|
||||||
pub fn send_ctcp_ping<S>(&self, target: S) -> error::Result<()>
|
S: fmt::Display,
|
||||||
where
|
{
|
||||||
S: fmt::Display,
|
self.send(PART(chanlist.to_string(), None))
|
||||||
{
|
}
|
||||||
let time = Local::now();
|
|
||||||
self.send_ctcp(target, &format!("PING {}", time.timestamp())[..])
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sends a time request to the specified target.
|
/// Attempts to oper up using the specified username and password.
|
||||||
/// This requires the CTCP feature to be enabled.
|
pub fn send_oper<S1, S2>(&self, username: S1, password: S2) -> error::Result<()>
|
||||||
#[cfg(feature = "ctcp")]
|
where
|
||||||
pub fn send_time<S>(&self, target: S) -> error::Result<()>
|
S1: fmt::Display,
|
||||||
where
|
S2: fmt::Display,
|
||||||
S: fmt::Display,
|
{
|
||||||
{
|
self.send(OPER(username.to_string(), password.to_string()))
|
||||||
self.send_ctcp(target, "TIME")
|
}
|
||||||
}
|
|
||||||
}
|
/// Sends a message to the specified target. If the message contains IRC newlines (`\r\n`), it
|
||||||
|
/// will automatically be split and sent as multiple separate `PRIVMSG`s to the specified
|
||||||
|
/// target. If you absolutely must avoid this behavior, you can do
|
||||||
|
/// `client.send(PRIVMSG(target, message))` directly.
|
||||||
|
pub fn send_privmsg<S1, S2>(&self, target: S1, message: S2) -> error::Result<()>
|
||||||
|
where
|
||||||
|
S1: fmt::Display,
|
||||||
|
S2: fmt::Display,
|
||||||
|
{
|
||||||
|
let message = message.to_string();
|
||||||
|
for line in message.split("\r\n") {
|
||||||
|
self.send(PRIVMSG(target.to_string(), line.to_string()))?
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the topic of a channel or requests the current one.
|
||||||
|
/// If `topic` is an empty string, it won't be included in the message.
|
||||||
|
pub fn send_topic<S1, S2>(&self, channel: S1, topic: S2) -> error::Result<()>
|
||||||
|
where
|
||||||
|
S1: fmt::Display,
|
||||||
|
S2: fmt::Display,
|
||||||
|
{
|
||||||
|
let topic = topic.to_string();
|
||||||
|
self.send(TOPIC(
|
||||||
|
channel.to_string(),
|
||||||
|
if topic.is_empty() { None } else { Some(topic) },
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Kills the target with the provided message.
|
||||||
|
pub fn send_kill<S1, S2>(&self, target: S1, message: S2) -> error::Result<()>
|
||||||
|
where
|
||||||
|
S1: fmt::Display,
|
||||||
|
S2: fmt::Display,
|
||||||
|
{
|
||||||
|
self.send(KILL(target.to_string(), message.to_string()))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Kicks the listed nicknames from the listed channels with a comment.
|
||||||
|
/// If `message` is an empty string, it won't be included in the message.
|
||||||
|
pub fn send_kick<S1, S2, S3>(
|
||||||
|
&self,
|
||||||
|
chanlist: S1,
|
||||||
|
nicklist: S2,
|
||||||
|
message: S3,
|
||||||
|
) -> error::Result<()>
|
||||||
|
where
|
||||||
|
S1: fmt::Display,
|
||||||
|
S2: fmt::Display,
|
||||||
|
S3: fmt::Display,
|
||||||
|
{
|
||||||
|
let message = message.to_string();
|
||||||
|
self.send(KICK(
|
||||||
|
chanlist.to_string(),
|
||||||
|
nicklist.to_string(),
|
||||||
|
if message.is_empty() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(message)
|
||||||
|
},
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Changes the mode of the target by force.
|
||||||
|
/// If `modeparams` is an empty string, it won't be included in the message.
|
||||||
|
pub fn send_samode<S1, S2, S3>(
|
||||||
|
&self,
|
||||||
|
target: S1,
|
||||||
|
mode: S2,
|
||||||
|
modeparams: S3,
|
||||||
|
) -> error::Result<()>
|
||||||
|
where
|
||||||
|
S1: fmt::Display,
|
||||||
|
S2: fmt::Display,
|
||||||
|
S3: fmt::Display,
|
||||||
|
{
|
||||||
|
let modeparams = modeparams.to_string();
|
||||||
|
self.send(SAMODE(
|
||||||
|
target.to_string(),
|
||||||
|
mode.to_string(),
|
||||||
|
if modeparams.is_empty() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(modeparams)
|
||||||
|
},
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Forces a user to change from the old nickname to the new nickname.
|
||||||
|
pub fn send_sanick<S1, S2>(&self, old_nick: S1, new_nick: S2) -> error::Result<()>
|
||||||
|
where
|
||||||
|
S1: fmt::Display,
|
||||||
|
S2: fmt::Display,
|
||||||
|
{
|
||||||
|
self.send(SANICK(old_nick.to_string(), new_nick.to_string()))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Invites a user to the specified channel.
|
||||||
|
pub fn send_invite<S1, S2>(&self, nick: S1, chan: S2) -> error::Result<()>
|
||||||
|
where
|
||||||
|
S1: fmt::Display,
|
||||||
|
S2: fmt::Display,
|
||||||
|
{
|
||||||
|
self.send(INVITE(nick.to_string(), chan.to_string()))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Quits the server entirely with a message.
|
||||||
|
/// This defaults to `Powered by Rust.` if none is specified.
|
||||||
|
pub fn send_quit<S>(&self, msg: S) -> error::Result<()>
|
||||||
|
where
|
||||||
|
S: fmt::Display,
|
||||||
|
{
|
||||||
|
let msg = msg.to_string();
|
||||||
|
self.send(QUIT(Some(if msg.is_empty() {
|
||||||
|
"Powered by Rust.".to_string()
|
||||||
|
} else {
|
||||||
|
msg
|
||||||
|
})))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sends a CTCP-escaped message to the specified target.
|
||||||
|
/// This requires the CTCP feature to be enabled.
|
||||||
|
#[cfg(feature = "ctcp")]
|
||||||
|
pub fn send_ctcp<S1, S2>(&self, target: S1, msg: S2) -> error::Result<()>
|
||||||
|
where
|
||||||
|
S1: fmt::Display,
|
||||||
|
S2: fmt::Display,
|
||||||
|
{
|
||||||
|
let msg = msg.to_string();
|
||||||
|
for line in msg.split("\r\n") {
|
||||||
|
self.send(PRIVMSG(
|
||||||
|
target.to_string(),
|
||||||
|
format!("\u{001}{}\u{001}", line),
|
||||||
|
))?
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sends an action command to the specified target.
|
||||||
|
/// This requires the CTCP feature to be enabled.
|
||||||
|
#[cfg(feature = "ctcp")]
|
||||||
|
pub fn send_action<S1, S2>(&self, target: S1, msg: S2) -> error::Result<()>
|
||||||
|
where
|
||||||
|
S1: fmt::Display,
|
||||||
|
S2: fmt::Display,
|
||||||
|
{
|
||||||
|
self.send_ctcp(target, &format!("ACTION {}", msg.to_string())[..])
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sends a finger request to the specified target.
|
||||||
|
/// This requires the CTCP feature to be enabled.
|
||||||
|
#[cfg(feature = "ctcp")]
|
||||||
|
pub fn send_finger<S: fmt::Display>(&self, target: S) -> error::Result<()>
|
||||||
|
where
|
||||||
|
S: fmt::Display,
|
||||||
|
{
|
||||||
|
self.send_ctcp(target, "FINGER")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sends a version request to the specified target.
|
||||||
|
/// This requires the CTCP feature to be enabled.
|
||||||
|
#[cfg(feature = "ctcp")]
|
||||||
|
pub fn send_version<S>(&self, target: S) -> error::Result<()>
|
||||||
|
where
|
||||||
|
S: fmt::Display,
|
||||||
|
{
|
||||||
|
self.send_ctcp(target, "VERSION")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sends a source request to the specified target.
|
||||||
|
/// This requires the CTCP feature to be enabled.
|
||||||
|
#[cfg(feature = "ctcp")]
|
||||||
|
pub fn send_source<S>(&self, target: S) -> error::Result<()>
|
||||||
|
where
|
||||||
|
S: fmt::Display,
|
||||||
|
{
|
||||||
|
self.send_ctcp(target, "SOURCE")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sends a user info request to the specified target.
|
||||||
|
/// This requires the CTCP feature to be enabled.
|
||||||
|
#[cfg(feature = "ctcp")]
|
||||||
|
pub fn send_user_info<S>(&self, target: S) -> error::Result<()>
|
||||||
|
where
|
||||||
|
S: fmt::Display,
|
||||||
|
{
|
||||||
|
self.send_ctcp(target, "USERINFO")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sends a finger request to the specified target.
|
||||||
|
/// This requires the CTCP feature to be enabled.
|
||||||
|
#[cfg(feature = "ctcp")]
|
||||||
|
pub fn send_ctcp_ping<S>(&self, target: S) -> error::Result<()>
|
||||||
|
where
|
||||||
|
S: fmt::Display,
|
||||||
|
{
|
||||||
|
let time = Local::now();
|
||||||
|
self.send_ctcp(target, &format!("PING {}", time.timestamp())[..])
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sends a time request to the specified target.
|
||||||
|
/// This requires the CTCP feature to be enabled.
|
||||||
|
#[cfg(feature = "ctcp")]
|
||||||
|
pub fn send_time<S>(&self, target: S) -> error::Result<()>
|
||||||
|
where
|
||||||
|
S: fmt::Display,
|
||||||
|
{
|
||||||
|
self.send_ctcp(target, "TIME")
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A stream of `Messages` received from an IRC server via an `Client`.
|
/// A stream of `Messages` received from an IRC server via an `Client`.
|
||||||
|
@ -813,7 +825,7 @@ pub struct Sender {
|
||||||
impl Sender {
|
impl Sender {
|
||||||
/// Send a single message to the unbounded queue.
|
/// Send a single message to the unbounded queue.
|
||||||
pub fn send<M: Into<Message>>(&self, msg: M) -> error::Result<()> {
|
pub fn send<M: Into<Message>>(&self, msg: M) -> error::Result<()> {
|
||||||
Ok(self.tx_outgoing.unbounded_send(msg.into())?)
|
Ok(self.tx_outgoing.send(msg.into())?)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub_state_base!();
|
pub_state_base!();
|
||||||
|
@ -920,7 +932,7 @@ impl Client {
|
||||||
/// single, shared event loop. It can also be used to take more control over execution and error
|
/// single, shared event loop. It can also be used to take more control over execution and error
|
||||||
/// handling. Connection will not occur until the event loop is run.
|
/// handling. Connection will not occur until the event loop is run.
|
||||||
pub async fn from_config(config: Config) -> error::Result<Client> {
|
pub async fn from_config(config: Config) -> error::Result<Client> {
|
||||||
let (tx_outgoing, rx_outgoing) = mpsc::unbounded();
|
let (tx_outgoing, rx_outgoing) = mpsc::unbounded_channel();
|
||||||
let conn = Connection::new(&config, tx_outgoing.clone()).await?;
|
let conn = Connection::new(&config, tx_outgoing.clone()).await?;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
|
@ -9,18 +9,18 @@ use std::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use chrono::prelude::*;
|
use chrono::prelude::*;
|
||||||
use futures_channel::mpsc::UnboundedSender;
|
|
||||||
use futures_util::{future::Future, ready, sink::Sink, stream::Stream};
|
use futures_util::{future::Future, ready, sink::Sink, stream::Stream};
|
||||||
|
use tokio::sync::mpsc::UnboundedSender;
|
||||||
use tokio::{
|
use tokio::{
|
||||||
io::{AsyncRead, AsyncWrite},
|
io::{AsyncRead, AsyncWrite},
|
||||||
time::{self, Delay, Interval},
|
time::{self, Interval, Sleep},
|
||||||
};
|
};
|
||||||
use tokio_util::codec::Framed;
|
use tokio_util::codec::Framed;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
client::data::Config,
|
client::data::Config,
|
||||||
error,
|
error,
|
||||||
proto::{Command, Response, IrcCodec, Message},
|
proto::{Command, IrcCodec, Message, Response},
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Pinger-based futures helper.
|
/// Pinger-based futures helper.
|
||||||
|
@ -31,7 +31,7 @@ struct Pinger {
|
||||||
/// The amount of time to wait before timing out from no ping response.
|
/// The amount of time to wait before timing out from no ping response.
|
||||||
ping_timeout: Duration,
|
ping_timeout: Duration,
|
||||||
/// The instant that the last ping was sent to the server.
|
/// The instant that the last ping was sent to the server.
|
||||||
ping_deadline: Option<Delay>,
|
ping_deadline: Option<Sleep>,
|
||||||
/// The interval at which to send pings.
|
/// The interval at which to send pings.
|
||||||
ping_interval: Interval,
|
ping_interval: Interval,
|
||||||
}
|
}
|
||||||
|
@ -77,8 +77,7 @@ impl Pinger {
|
||||||
|
|
||||||
/// Send a pong.
|
/// Send a pong.
|
||||||
fn send_pong(&mut self, data: &str) -> error::Result<()> {
|
fn send_pong(&mut self, data: &str) -> error::Result<()> {
|
||||||
self.tx
|
self.tx.send(Command::PONG(data.to_owned(), None).into())?;
|
||||||
.unbounded_send(Command::PONG(data.to_owned(), None).into())?;
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -89,8 +88,7 @@ impl Pinger {
|
||||||
// Creates new ping data using the local timestamp.
|
// Creates new ping data using the local timestamp.
|
||||||
let data = format!("{}", Local::now().timestamp());
|
let data = format!("{}", Local::now().timestamp());
|
||||||
|
|
||||||
self.tx
|
self.tx.send(Command::PING(data.clone(), None).into())?;
|
||||||
.unbounded_send(Command::PING(data.clone(), None).into())?;
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -98,7 +96,7 @@ impl Pinger {
|
||||||
/// Set the ping deadline.
|
/// Set the ping deadline.
|
||||||
fn set_deadline(&mut self) {
|
fn set_deadline(&mut self) {
|
||||||
if self.ping_deadline.is_none() {
|
if self.ping_deadline.is_none() {
|
||||||
let ping_deadline = time::delay_for(self.ping_timeout);
|
let ping_deadline = time::sleep(self.ping_timeout);
|
||||||
self.ping_deadline = Some(ping_deadline);
|
self.ping_deadline = Some(ping_deadline);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
25
src/error.rs
25
src/error.rs
|
@ -3,11 +3,8 @@
|
||||||
use std::io::Error as IoError;
|
use std::io::Error as IoError;
|
||||||
use std::sync::mpsc::RecvError;
|
use std::sync::mpsc::RecvError;
|
||||||
|
|
||||||
use futures_channel::{
|
|
||||||
mpsc::{SendError, TrySendError},
|
|
||||||
oneshot::Canceled,
|
|
||||||
};
|
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
use tokio::sync::mpsc::error::{SendError, TrySendError};
|
||||||
|
|
||||||
#[cfg(feature = "tls-rust")]
|
#[cfg(feature = "tls-rust")]
|
||||||
use tokio_rustls::webpki::InvalidDNSNameError;
|
use tokio_rustls::webpki::InvalidDNSNameError;
|
||||||
|
@ -45,11 +42,11 @@ pub enum Error {
|
||||||
|
|
||||||
/// An internal asynchronous channel closed.
|
/// An internal asynchronous channel closed.
|
||||||
#[error("an async channel closed")]
|
#[error("an async channel closed")]
|
||||||
AsyncChannelClosed(#[source] SendError),
|
AsyncChannelClosed,
|
||||||
|
|
||||||
/// An internal oneshot channel closed.
|
/// An internal oneshot channel closed.
|
||||||
#[error("a oneshot channel closed")]
|
#[error("a oneshot channel closed")]
|
||||||
OneShotCanceled(#[source] Canceled),
|
OneShotCanceled,
|
||||||
|
|
||||||
/// Error for invalid configurations.
|
/// Error for invalid configurations.
|
||||||
#[error("invalid config: {}", path)]
|
#[error("invalid config: {}", path)]
|
||||||
|
@ -205,20 +202,14 @@ impl From<RecvError> for Error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<SendError> for Error {
|
impl<T> From<SendError<T>> for Error {
|
||||||
fn from(e: SendError) -> Error {
|
fn from(_: SendError<T>) -> Error {
|
||||||
Error::AsyncChannelClosed(e)
|
Error::AsyncChannelClosed
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> From<TrySendError<T>> for Error {
|
impl<T> From<TrySendError<T>> for Error {
|
||||||
fn from(e: TrySendError<T>) -> Error {
|
fn from(_: TrySendError<T>) -> Error {
|
||||||
Error::AsyncChannelClosed(e.into_send_error())
|
Error::AsyncChannelClosed
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Canceled> for Error {
|
|
||||||
fn from(e: Canceled) -> Error {
|
|
||||||
Error::OneShotCanceled(e)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue