mirror of
https://github.com/vinezombie/vinezombie.git
synced 2026-01-25 07:26:18 +00:00
Include ChannelSpec in Client
Oftentimes the channel spec isn't going to change frequently, so it becomes fairly redundant as a parameter on Client::add. This alters the Client creation functions to include the channel spec, as well as adding a type parameter to Client for it. Also removes new_client as it's basically pointless.
This commit is contained in:
@@ -3,8 +3,8 @@ use vinezombie::{
|
||||
self,
|
||||
auth::Clear,
|
||||
channel::SyncChannels,
|
||||
new_client,
|
||||
register::{register_as_bot, Options},
|
||||
Client,
|
||||
},
|
||||
ircmsg::ClientMsg,
|
||||
string::{Line, Word},
|
||||
@@ -33,13 +33,13 @@ fn main() -> std::io::Result<()> {
|
||||
// `Client` bundles the connection and serves as a host for Handlers
|
||||
// that process IRC messages. It also rate-limits outgoing messages to avoid
|
||||
// disconnections for flooding, and can adjust the message queue based in incoming messages.
|
||||
let mut client = new_client(sock);
|
||||
// For convenience, we use SyncChannels as a way to build an appropriate channel type for
|
||||
// the registration handler.
|
||||
let mut client = Client::new(sock, SyncChannels);
|
||||
// We're not ready to go just yet.
|
||||
// The initial connection registration handshake needs to happen.
|
||||
// Handlers return values through channels, one channel per handler.
|
||||
// For convenience, we use SyncChannels as a way to build an appropriate channel type for
|
||||
// the registration handler.
|
||||
let (_id, reg_result) = client.add(&SyncChannels, ®ister_as_bot(), &options)?;
|
||||
let (_id, reg_result) = client.add(®ister_as_bot(), &options)?;
|
||||
// Let's actually run the handler now!
|
||||
// Normally `run` returns the ids of handlers that have yielded values and finished,
|
||||
// but we're only running one handler that always yields one value on completion,
|
||||
|
||||
@@ -3,8 +3,8 @@ use vinezombie::{
|
||||
self,
|
||||
auth::Clear,
|
||||
channel::TokioChannels,
|
||||
new_client,
|
||||
register::{register_as_bot, Options},
|
||||
Client,
|
||||
},
|
||||
ircmsg::ClientMsg,
|
||||
string::{Line, Word},
|
||||
@@ -20,12 +20,12 @@ async fn main() -> std::io::Result<()> {
|
||||
// First difference! We use a different function here to connect asynchronously.
|
||||
// Many of the synchronous functions have `_tokio` variants for Tokio-flavored async.
|
||||
let sock = address.connect_tokio(|| client::tls::TlsConfigOptions::default().build()).await?;
|
||||
let mut client = new_client(sock);
|
||||
// We also use TokioChannels instead of SyncChannels, which changes the flavor
|
||||
// of channel used by our handler later on.
|
||||
let mut client = Client::new(sock, TokioChannels);
|
||||
// We still use the same handler for connection registration,
|
||||
// but instead we run it using a run_tokio function.
|
||||
// We also use TokioChannels instead of SyncChannels, which changes the flavor
|
||||
// of channel used by our handler.
|
||||
let (_id, reg_result) = client.add(&TokioChannels, ®ister_as_bot(), &options)?;
|
||||
let (_id, reg_result) = client.add(®ister_as_bot(), &options)?;
|
||||
client.run_tokio().await?;
|
||||
let reg = reg_result.await.unwrap()?;
|
||||
let network_name = reg.isupport.get_parsed(vinezombie::names::isupport::NETWORK).transpose()?;
|
||||
|
||||
@@ -5,8 +5,8 @@ use vinezombie::{
|
||||
channel::TokioChannels,
|
||||
conn::ServerAddr,
|
||||
handlers::{AutoPong, YieldParsed},
|
||||
new_client,
|
||||
register::{register_as_bot, Options},
|
||||
Client,
|
||||
},
|
||||
ircmsg::ClientMsg,
|
||||
names::cmd::{JOIN, PRIVMSG},
|
||||
@@ -27,15 +27,15 @@ async fn main() -> std::io::Result<()> {
|
||||
options.realname = Some(Line::from_str("Vinezombie Example: msglog"));
|
||||
let address = ServerAddr::from_host_str("irc.libera.chat");
|
||||
let sock = address.connect_tokio(|| client::tls::TlsConfigOptions::default().build()).await?;
|
||||
let mut client = new_client(sock);
|
||||
let (_id, reg_result) = client.add(&TokioChannels, ®ister_as_bot(), &options)?;
|
||||
let mut client = Client::new(sock, TokioChannels);
|
||||
let (_id, reg_result) = client.add(®ister_as_bot(), &options)?;
|
||||
client.run_tokio().await?;
|
||||
// The only piece of reg info we care about for this example is our nick.
|
||||
let nick = reg_result.await.unwrap()?.nick;
|
||||
tracing::info!("nick: {}", nick);
|
||||
// Let's add a handler to auto-reply to PING messages for us. Most IRC networks need this.
|
||||
// Do this before anything else.
|
||||
let _ = client.add(&TokioChannels, (), AutoPong);
|
||||
let _ = client.add((), AutoPong);
|
||||
// Let's join all the channels provided as command-line arguments.
|
||||
// This is a VERY crude way of joining multiple channels, but works for illustration
|
||||
// of how to construct messages that are less-trivial than a no-argument QUIT message.
|
||||
@@ -58,7 +58,7 @@ async fn main() -> std::io::Result<()> {
|
||||
}
|
||||
// We now need to receive PRIVMSGs and send them somewhere for further processing.
|
||||
// `YieldParsed` exists exactly for this purpose.
|
||||
let (_, mut msgs) = client.add(&TokioChannels, (), YieldParsed::just(PRIVMSG)).unwrap();
|
||||
let (_, mut msgs) = client.add((), YieldParsed::just(PRIVMSG)).unwrap();
|
||||
// Since we are async, let's do the actual printing in another task, because we can.
|
||||
tokio::spawn(async move {
|
||||
while let Some(msg) = msgs.recv().await {
|
||||
|
||||
@@ -3,12 +3,12 @@ use vinezombie::{
|
||||
client::{
|
||||
self,
|
||||
auth::Clear,
|
||||
channel::{SyncChannels, TokioChannels},
|
||||
channel::SyncChannels,
|
||||
conn::{ServerAddr, Stream},
|
||||
handlers::{AutoPong, YieldParsed},
|
||||
new_client,
|
||||
register::{register_as_bot, Options},
|
||||
tls::TlsConfig,
|
||||
Client,
|
||||
},
|
||||
names::cmd::PRIVMSG,
|
||||
string::Line,
|
||||
@@ -44,19 +44,19 @@ fn main() -> std::io::Result<()> {
|
||||
options.realname = Some(Line::from_str("Vinezombie Example: reconnect"));
|
||||
let address = ServerAddr::from_host_str("irc.libera.chat");
|
||||
let mut tls_config: Option<TlsConfig> = None;
|
||||
let mut client = new_client(make_sock(&mut tls_config, &address)?);
|
||||
let mut client = Client::new(make_sock(&mut tls_config, &address)?, SyncChannels);
|
||||
loop {
|
||||
let (_, reg_result) = client.add(&SyncChannels, ®ister_as_bot(), &options)?;
|
||||
let (_, reg_result) = client.add(®ister_as_bot(), &options)?;
|
||||
client.run()?;
|
||||
let nick = reg_result.0.recv_now().unwrap()?.nick;
|
||||
let _ = client.add(&TokioChannels, (), AutoPong);
|
||||
let _ = client.add((), AutoPong);
|
||||
// For the purposes of this example, let's quit and reconnect
|
||||
// if literally anyone sends us a message containing the letter "q".
|
||||
// In previous examples, we've been ignoring that the client's `run` methods
|
||||
// actually disclose which handlers produced values and/or finished.
|
||||
// This time we're actually going to use that information,
|
||||
// starting by saving the id of the message handler.
|
||||
let (id, msgs) = client.add(&SyncChannels, (), YieldParsed::just(PRIVMSG)).unwrap();
|
||||
let (id, msgs) = client.add((), YieldParsed::just(PRIVMSG)).unwrap();
|
||||
tracing::info!("bot {nick} ready for 'q'~");
|
||||
loop {
|
||||
let Ok(result) = client.run() else {
|
||||
|
||||
@@ -18,9 +18,11 @@ use self::{channel::ChannelSpec, queue::Queue};
|
||||
|
||||
/// A client connection.
|
||||
#[derive(Default)]
|
||||
pub struct Client<C> {
|
||||
pub struct Client<C, S> {
|
||||
/// The connection to the IRC server.
|
||||
conn: C,
|
||||
/// The [`ChannelSpec`] for creating now channels.
|
||||
spec: S,
|
||||
/// A message queue for rate-limiting.
|
||||
queue: Box<Queue>,
|
||||
/// A buffer that is used internally for inbound message I/O.
|
||||
@@ -33,22 +35,16 @@ pub struct Client<C> {
|
||||
timeout: Box<conn::TimeLimits>,
|
||||
}
|
||||
|
||||
/// Creates a new [`Client`] out of a connection with sensible default types.
|
||||
///
|
||||
/// Note that connection registration will still likely need to happen after this.
|
||||
pub fn new_client<C>(conn: C) -> Client<C> {
|
||||
Client::new(conn)
|
||||
}
|
||||
|
||||
impl<C> Client<C> {
|
||||
impl<C, S: ChannelSpec> Client<C, S> {
|
||||
/// Creates a new `Client` from the provided connection.
|
||||
pub fn new(conn: C) -> Self {
|
||||
Self::new_with_queue(conn, Queue::new())
|
||||
pub fn new(conn: C, spec: S) -> Self {
|
||||
Self::new_with_queue(conn, spec, Queue::new())
|
||||
}
|
||||
/// Creates a new `Client` from the provided connection and [`Queue`].
|
||||
pub fn new_with_queue(conn: C, queue: Queue) -> Self {
|
||||
pub fn new_with_queue(conn: C, spec: S, queue: Queue) -> Self {
|
||||
Client {
|
||||
conn,
|
||||
spec,
|
||||
queue: Box::new(queue),
|
||||
buf_i: Vec::new(),
|
||||
buf_o: Vec::new(),
|
||||
@@ -56,6 +52,20 @@ impl<C> Client<C> {
|
||||
timeout: Box::default(),
|
||||
}
|
||||
}
|
||||
/// Adds a handler. Creates a new channel using the internal [`ChannelSpec`].
|
||||
///
|
||||
/// Returns the handler id and the receiver half of the channel.
|
||||
pub fn add<T, M: MakeHandler<T>>(
|
||||
&mut self,
|
||||
make_handler: M,
|
||||
value: T,
|
||||
) -> Result<(usize, M::Receiver<S>), M::Error> {
|
||||
let (send, recv) = M::make_channel(&self.spec);
|
||||
Ok((self.add_with_sender(send, make_handler, value)?, recv))
|
||||
}
|
||||
}
|
||||
|
||||
impl<C, S> Client<C, S> {
|
||||
/// Extracts the connection from `self`, allowing it to be used elsewhere.
|
||||
pub fn take_conn(self) -> C {
|
||||
self.conn
|
||||
@@ -65,10 +75,16 @@ impl<C> Client<C> {
|
||||
/// This connection does not change any of [`Client`]s state aside from
|
||||
/// requiring an update of the connection's IO timeouts.
|
||||
/// Additionally use [`reset`][Client::reset] if you want to reset the state.
|
||||
pub fn with_conn<C2>(self, conn: C2) -> Client<C2> {
|
||||
let Self { queue, buf_i, buf_o, handlers, mut timeout, .. } = self;
|
||||
pub fn with_conn<C2>(self, conn: C2) -> Client<C2, S> {
|
||||
let Self { spec, queue, buf_i, buf_o, handlers, mut timeout, .. } = self;
|
||||
timeout.require_update();
|
||||
Client { conn, queue, buf_i, buf_o, timeout, handlers }
|
||||
Client { conn, spec, queue, buf_i, buf_o, timeout, handlers }
|
||||
}
|
||||
/// Uses the provided [`ChannelSpec`] for `self`.
|
||||
/// This changes the type of channels returned by [`add`][Client::add].
|
||||
pub fn with_spec<S2: ChannelSpec>(self, spec: S2) -> Client<C, S2> {
|
||||
let Self { conn, queue, buf_i, buf_o, handlers, timeout, .. } = self;
|
||||
Client { conn, spec, queue, buf_i, buf_o, timeout, handlers }
|
||||
}
|
||||
/// Returns a shared reference to the internal [`Queue`].
|
||||
pub fn queue(&self) -> &Queue {
|
||||
@@ -100,12 +116,12 @@ impl<C> Client<C> {
|
||||
/// Adds a handler. Creates a new channel using the provided [`ChannelSpec`].
|
||||
///
|
||||
/// Returns the handler id and the receiver half of the channel.
|
||||
pub fn add<T, M: MakeHandler<T>, S: ChannelSpec>(
|
||||
pub fn add_with_spec<T, M: MakeHandler<T>, S2: ChannelSpec>(
|
||||
&mut self,
|
||||
chanspec: &S,
|
||||
chanspec: &S2,
|
||||
make_handler: M,
|
||||
value: T,
|
||||
) -> Result<(usize, M::Receiver<S>), M::Error> {
|
||||
) -> Result<(usize, M::Receiver<S2>), M::Error> {
|
||||
let (send, recv) = M::make_channel(chanspec);
|
||||
Ok((self.add_with_sender(send, make_handler, value)?, recv))
|
||||
}
|
||||
@@ -147,11 +163,5 @@ impl<C> Client<C> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<C: Default> From<Queue> for Client<C> {
|
||||
fn from(queue: Queue) -> Self {
|
||||
Self::new_with_queue(C::default(), queue)
|
||||
}
|
||||
}
|
||||
|
||||
// Implementations of other Client methods can be found in `conn`,
|
||||
// specifically the submodules depending on I/O flavor.
|
||||
|
||||
@@ -281,7 +281,7 @@ impl<T: ReadTimeout + WriteTimeout + Read + Write> Connection for BufReader<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<C: Connection> crate::client::Client<C> {
|
||||
impl<C: Connection, S> crate::client::Client<C, S> {
|
||||
/// Runs handlers off of the connection until any of them yield or finish.
|
||||
///
|
||||
/// Returns the IDs of the handlers that yielded or finished, respectively.
|
||||
|
||||
@@ -153,7 +153,7 @@ impl<T: tokio::io::AsyncRead + tokio::io::AsyncWrite + Unpin> ConnectionTokio fo
|
||||
}
|
||||
}
|
||||
|
||||
impl<C: ConnectionTokio> crate::client::Client<C> {
|
||||
impl<C: ConnectionTokio, S> crate::client::Client<C, S> {
|
||||
/// Runs handlers off of the connection until any of them yield or finish.
|
||||
///
|
||||
/// Returns the IDs of the handlers that yielded or finished, respectively.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use std::{io::Cursor, time::Duration};
|
||||
|
||||
use crate::{
|
||||
client::{auth::Clear, channel::SyncChannels, conn::Bidir, new_client},
|
||||
client::{auth::Clear, channel::SyncChannels, conn::Bidir, Client},
|
||||
string::{Key, Nick},
|
||||
};
|
||||
|
||||
@@ -13,9 +13,9 @@ fn static_register(msg: &[u8]) -> Result<Registration, HandlerError> {
|
||||
options.nicks = vec![Nick::from_str("Me")];
|
||||
let reg = register_as_bot(); // Somewhat more deterministic.
|
||||
let io = Bidir::<Cursor<Vec<u8>>, _>(Cursor::new(msg.to_vec()), std::io::sink());
|
||||
let mut client = new_client(io);
|
||||
let mut client = Client::new(io, SyncChannels);
|
||||
client.queue_mut().set_rate_limit(Duration::ZERO, 1);
|
||||
let (_, reg) = client.add(&SyncChannels, ®, &options).unwrap();
|
||||
let (_, reg) = client.add(®, &options).unwrap();
|
||||
client.run().unwrap();
|
||||
reg.0.recv_now().unwrap()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user