use crate::state::buffer::{Buffer, NickEntry};
pub const DEFAULT_PREFIX_ORDER: &str = "~&@%+";
pub fn sort_buffers<'a, F>(buffers: &[&'a Buffer], label_fn: F) -> Vec<&'a Buffer>
where
F: Fn(&str) -> String,
{
let mut keyed: Vec<(String, String, &'a Buffer)> = buffers
.iter()
.map(|&b| {
(
label_fn(&b.connection_id).to_lowercase(),
b.name.to_lowercase(),
b,
)
})
.collect();
keyed.sort_by(|(la, na, a), (lb, nb, b)| {
la.cmp(lb)
.then_with(|| a.buffer_type.sort_group().cmp(&b.buffer_type.sort_group()))
.then_with(|| na.cmp(nb))
});
keyed.into_iter().map(|(_, _, b)| b).collect()
}
pub fn sort_nicks<'a>(nicks: &[&'a NickEntry], prefix_order: &str) -> Vec<&'a NickEntry> {
let mut keyed: Vec<(String, &'a NickEntry)> =
nicks.iter().map(|&n| (n.nick.to_lowercase(), n)).collect();
keyed.sort_by(|(la, a), (lb, b)| {
let rank_a = prefix_rank(&a.prefix, prefix_order);
let rank_b = prefix_rank(&b.prefix, prefix_order);
rank_a.cmp(&rank_b).then_with(|| la.cmp(lb))
});
keyed.into_iter().map(|(_, n)| n).collect()
}
fn prefix_rank(prefix: &str, prefix_order: &str) -> usize {
if prefix.is_empty() {
return prefix_order.len();
}
let Some(ch) = prefix.chars().next() else {
return prefix_order.len();
};
prefix_order.find(ch).unwrap_or(prefix_order.len())
}
#[cfg(test)]
mod tests {
use super::*;
use crate::state::buffer::*;
use chrono::Utc;
use std::collections::HashMap;
fn make_buffer(conn_id: &str, btype: BufferType, name: &str) -> Buffer {
Buffer {
id: make_buffer_id(conn_id, name),
connection_id: conn_id.to_string(),
buffer_type: btype,
name: name.to_string(),
messages: Vec::new(),
activity: ActivityLevel::None,
unread_count: 0,
last_read: Utc::now(),
topic: None,
topic_set_by: None,
users: HashMap::new(),
modes: None,
mode_params: None,
list_modes: HashMap::new(),
last_speakers: Vec::new(),
}
}
fn make_nick(nick: &str, prefix: &str) -> NickEntry {
NickEntry {
nick: nick.to_string(),
prefix: prefix.to_string(),
modes: String::new(),
away: false,
account: None,
ident: None,
host: None,
}
}
#[test]
fn sort_buffers_by_type_then_name() {
let chan_b = make_buffer("libera", BufferType::Channel, "#beta");
let chan_a = make_buffer("libera", BufferType::Channel, "#alpha");
let server = make_buffer("libera", BufferType::Server, "libera");
let query = make_buffer("libera", BufferType::Query, "someone");
let input: Vec<&Buffer> = vec![&chan_b, &query, &server, &chan_a];
let result = sort_buffers(&input, ToString::to_string);
assert_eq!(result[0].name, "libera"); assert_eq!(result[1].name, "#alpha"); assert_eq!(result[2].name, "#beta");
assert_eq!(result[3].name, "someone"); }
#[test]
fn sort_buffers_by_connection_label() {
let srv_a = make_buffer("net2", BufferType::Server, "IRCnet");
let chan_a = make_buffer("net2", BufferType::Channel, "#rust");
let srv_b = make_buffer("net1", BufferType::Server, "IRCnet2");
let chan_b = make_buffer("net1", BufferType::Channel, "#linux");
let input: Vec<&Buffer> = vec![&srv_b, &chan_b, &srv_a, &chan_a];
let labels = std::collections::HashMap::from([("net1", "IRCnet2"), ("net2", "IRCnet")]);
let result = sort_buffers(&input, |id| labels.get(id).unwrap_or(&id).to_string());
assert_eq!(result[0].connection_id, "net2");
assert_eq!(result[0].name, "IRCnet");
assert_eq!(result[1].connection_id, "net2");
assert_eq!(result[1].name, "#rust");
assert_eq!(result[2].connection_id, "net1");
assert_eq!(result[2].name, "IRCnet2");
assert_eq!(result[3].connection_id, "net1");
assert_eq!(result[3].name, "#linux");
}
#[test]
fn sort_nicks_ops_before_voice_before_normal() {
let op = make_nick("alice", "@");
let voice = make_nick("bob", "+");
let normal = make_nick("charlie", "");
let input: Vec<&NickEntry> = vec![&normal, &voice, &op];
let result = sort_nicks(&input, DEFAULT_PREFIX_ORDER);
assert_eq!(result[0].nick, "alice"); assert_eq!(result[1].nick, "bob"); assert_eq!(result[2].nick, "charlie"); }
#[test]
fn sort_nicks_multi_prefix_uses_highest() {
let op_voice = make_nick("alice", "@+");
let voice = make_nick("bob", "+");
let normal = make_nick("charlie", "");
let input: Vec<&NickEntry> = vec![&normal, &voice, &op_voice];
let result = sort_nicks(&input, DEFAULT_PREFIX_ORDER);
assert_eq!(result[0].nick, "alice"); assert_eq!(result[1].nick, "bob"); assert_eq!(result[2].nick, "charlie"); }
#[test]
fn sort_nicks_alphabetical_same_prefix() {
let a = make_nick("Zara", "@");
let b = make_nick("alice", "@");
let c = make_nick("Bob", "@");
let input: Vec<&NickEntry> = vec![&a, &b, &c];
let result = sort_nicks(&input, DEFAULT_PREFIX_ORDER);
assert_eq!(result[0].nick, "alice");
assert_eq!(result[1].nick, "Bob");
assert_eq!(result[2].nick, "Zara");
}
}