Restructure Register's nick and cap options

Moved capability options into Register.
This was originally kept seperate due to being especially
application-specific (where most of Register's other options aren't),
but ultimately this way is tidier, and applications can always
change caps before calling handler.

nicks is now a struct containing nickgen (as "gen").
This should make things a bit tidier.

Added an option to skip attempting to use the first nick.
Useful for having the first nick exclusively as a fallback,
or just jumping directly to generated nicks.
This commit is contained in:
TheDaemoness
2023-07-12 16:04:12 -07:00
parent 3166bea99d
commit 3feae669cd
5 changed files with 77 additions and 34 deletions

View File

@@ -1,4 +1,3 @@
use std::collections::BTreeSet;
use vinezombie::{
client::{self, auth::Clear},
ircmsg::ClientMsg,
@@ -28,14 +27,10 @@ fn main() -> std::io::Result<()> {
let mut sock = address.connect(tls_config)?;
// The initial connection registration handshake needs to happen,
// so let's build a handler for that.
// The provided set is a set of capabilities to request.
// We don't need anything, so this set is empty.
// If we need SASL, that's added automatically.
// `BotDefaults` provides default values for anything we didn't specify
// in `options` above.
// Passing the `queue` populates it with the initial message burst.
let mut handler =
options.handler(BTreeSet::new(), &client::register::BotDefaults, &mut queue)?;
let mut handler = options.handler(&client::register::BotDefaults, &mut queue)?;
// Let's do connection registration!
let reg = vinezombie::client::run_handler(&mut sock, &mut queue, &mut handler)?;
// Connection registration is done!

View File

@@ -1,4 +1,3 @@
use std::collections::BTreeSet;
use vinezombie::{
client::{self, auth::Clear},
ircmsg::ClientMsg,
@@ -23,8 +22,7 @@ async fn main() -> std::io::Result<()> {
// but instead we run it using a run_handler_tokio function.
// This function is actually more general than run_handler,
// but we're not going to make use of its functionality in this example.
let mut handler =
options.handler(BTreeSet::new(), &client::register::BotDefaults, &mut queue)?;
let mut handler = options.handler(&client::register::BotDefaults, &mut queue)?;
let reg = vinezombie::client::run_handler_tokio(&mut sock, &mut queue, &mut handler).await?;
tracing::info!("{} connected to Libera!", reg.nick);
// As with the earlier example, let's just quit here.

View File

@@ -3,6 +3,20 @@
use crate::string::{Nick, NickBuilder};
use std::{borrow::Cow, error::Error};
/// Standard nickname options.
#[derive(Clone, PartialEq, Eq, Hash, Debug, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Nicks<N> {
/// The list of nicknames to use.
pub nicks: Vec<Nick<'static>>,
/// Whether to skip attempting to use the first nickname,
/// using it only for fallbacks.
#[cfg_attr(feature = "serde", serde(default))]
pub skip_first: bool,
/// The [`NickTransformer`] for generating new nicknames from the first one.
pub gen: std::sync::Arc<N>,
}
/// Error indicating that a nickname generator cannot generate any more nicknames.
#[derive(Clone, Copy, Default, Debug)]
pub struct EndOfNicks;

View File

@@ -5,12 +5,16 @@ mod handler;
pub use {fallbacks::*, handler::*};
use super::{auth::Sasl, nick::NickTransformer, ClientMsgSink};
use super::{
auth::Sasl,
nick::{NickTransformer, Nicks},
ClientMsgSink,
};
use crate::{
client::auth::Secret,
error::InvalidByte,
ircmsg::ClientMsg,
string::{Key, Line, Nick, User},
string::{Key, Line, User},
};
use std::{
collections::{BTreeMap, BTreeSet, VecDeque},
@@ -21,15 +25,16 @@ use std::{
///
/// These are used to create the messages sent during the initial connection registration phase,
/// such as USER and NICK.
#[derive(Clone, Debug, Default)]
#[derive(Clone, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(default))]
pub struct Register<P, S, N> {
/// The set of capabilities to request.
pub caps: BTreeSet<Key<'static>>,
/// The server password.
pub pass: Option<P>,
/// The list of nicknames to use.
pub nicks: Vec<Nick<'static>>,
/// A fallback nick transformer, for when all of the nicks in the list are unavailable.
pub nickgen: Arc<N>,
/// Options for nickname use and generation.
pub nicks: Nicks<N>,
/// The username, historically one's local account name.
pub username: Option<User<'static>>,
/// The realname, also sometimes known as the gecos.
@@ -42,12 +47,26 @@ pub struct Register<P, S, N> {
pub allow_sasl_fail: bool,
}
impl<P, S, N: Default> Default for Register<P, S, N> {
fn default() -> Self {
Register {
caps: BTreeSet::new(),
pass: None,
nicks: Nicks::default(),
username: None,
realname: None,
sasl: Vec::new(),
allow_sasl_fail: false,
}
}
}
/// Creates a new blank [`Register`] the provided choice of [`Secret`] implementation.
pub fn new<S: Secret>() -> Register<S, crate::client::auth::AnySasl<S>, ()> {
Register {
caps: BTreeSet::new(),
pass: None,
nicks: Vec::new(),
nickgen: Arc::new(()),
nicks: Nicks::default(),
username: None,
realname: None,
sasl: Vec::new(),
@@ -64,9 +83,9 @@ impl<P, S, N> Register<P, S, N> {
let pass = pass.try_into().map_err(|e| e.into())?.secret();
let secret = P2::new(pass.into())?;
Ok(Register {
caps: self.caps,
pass: Some(secret),
nicks: self.nicks,
nickgen: self.nickgen,
username: self.username,
realname: self.realname,
sasl: self.sasl,
@@ -76,9 +95,13 @@ impl<P, S, N> Register<P, S, N> {
/// Uses the provided [`NickTransformer`] for fallback nicks.
pub fn with_nickgen<N2: NickTransformer>(self, ng: N2) -> Register<P, S, N2> {
Register {
caps: self.caps,
pass: self.pass,
nicks: self.nicks,
nickgen: Arc::new(ng),
nicks: Nicks {
nicks: self.nicks.nicks,
skip_first: self.nicks.skip_first,
gen: Arc::new(ng),
},
username: self.username,
realname: self.realname,
sasl: self.sasl,
@@ -129,7 +152,7 @@ impl<P: Secret, S, N: NickTransformer> Register<P, S, N> {
sink.send(msg)?;
// NICK message.
msg = ClientMsg::new_cmd(NICK);
let (nick, fallbacks) = FallbackNicks::new(self, defaults);
let (nick, fallbacks) = FallbackNicks::new(&self.nicks, defaults);
msg.args.add(nick);
sink.send(msg)?;
Ok(fallbacks)
@@ -143,11 +166,11 @@ impl<P: Secret, S: Sasl, N: NickTransformer> Register<P, S, N> {
/// Errors only if `send_fn` errors.
pub fn handler<N2: NickTransformer>(
&self,
mut caps: BTreeSet<Key<'static>>,
defaults: &'static impl Defaults<NickGen = N2>,
sink: impl ClientMsgSink<'static>,
) -> std::io::Result<Handler<N, N2>> {
let nicks = self.register_msgs(defaults, sink)?;
let mut caps = self.caps.clone();
let (auths, needs_auth) = if !self.sasl.is_empty() {
caps.insert(Key::from_str("sasl"));
let mut auths = Vec::with_capacity(self.sasl.len());

View File

@@ -1,11 +1,10 @@
use super::Register;
use crate::{
client::nick::{NickSuffix, NickTransformer, SuffixRandom},
client::nick::{NickSuffix, NickTransformer, Nicks, SuffixRandom},
string::{Line, Nick, User},
};
use std::{collections::VecDeque, sync::Arc};
/// Source of fallback nicks from a [`Register`] and [`Defaults`].
/// Source of fallback nicks from a [`Register`][super::Register] and [`Defaults`].
#[derive(Clone, Debug)]
pub struct FallbackNicks<N1: NickTransformer, N2: NickTransformer + 'static> {
state: FallbackNicksState<N1::State, N2::State>,
@@ -13,20 +12,34 @@ pub struct FallbackNicks<N1: NickTransformer, N2: NickTransformer + 'static> {
n2: &'static N2,
}
#[allow(clippy::type_complexity)]
fn nicks_init<N1: NickTransformer, N2: NickTransformer + 'static>(
reg: &Nicks<N1>,
) -> Option<(Nick<'static>, FallbackNicksState<N1::State, N2::State>)> {
let (first, rest) = reg.nicks.split_first()?;
let nicks = if reg.skip_first { rest } else { &reg.nicks };
if let Some((nick, rest)) = nicks.split_first() {
let rest: VecDeque<Nick<'static>> = rest.to_vec().into();
Some((nick.clone(), FallbackNicksState::Select(first.clone(), rest)))
} else if let Some((nick, state)) = reg.gen.init(first) {
let state = state.map(FallbackNicksState::Gen1).unwrap_or(FallbackNicksState::Done);
Some((nick, state))
} else {
None
}
}
impl<N1: NickTransformer, N2: NickTransformer> FallbackNicks<N1, N2> {
/// Generate the first nickname and a `FallbackNicks` for more.
pub fn new<P, S>(
reg: &Register<P, S, N1>,
pub fn new(
reg: &Nicks<N1>,
reg_def: &'static impl Defaults<NickGen = N2>,
) -> (Nick<'static>, Self) {
let (nick, state) = if let Some((nick, rest)) = reg.nicks.split_first() {
let rest: VecDeque<Nick<'static>> = rest.to_vec().into();
(nick.clone(), FallbackNicksState::Select(nick.clone(), rest))
} else {
let (nick, state) = nicks_init::<N1, N2>(reg).unwrap_or_else(|| {
let (nick, state) = reg_def.nick();
(nick, state.map(FallbackNicksState::Gen2).unwrap_or(FallbackNicksState::Done))
};
(nick, FallbackNicks { state, n1: reg.nickgen.clone(), n2: reg_def.nick_gen() })
});
(nick, Self { state, n1: reg.gen.clone(), n2: reg_def.nick_gen() })
}
/// Returns `true` if the next nickname yielded by `self` is a user-specified one.
pub fn has_user_nicks(&self) -> bool {