use std::cmp::Ordering;
use std::convert::TryFrom;
use std::fmt::{Display, Formatter, Result as FmtResult};
use std::ops::{Deref, Range};
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
pub struct UserString {
mask: String,
nick: Range<usize>,
username: Range<usize>,
address: Range<usize>,
host: Range<usize>,
domain: Range<usize>,
}
impl Ord for UserString {
fn cmp(&self, other: &Self) -> Ordering {
self.get_nick()
.cmp(&other.get_nick())
.then_with(|| self.get_username().cmp(&other.get_username()))
.then_with(|| self.get_address().cmp(other.get_address()))
}
}
impl PartialOrd for UserString {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl UserString {
pub fn new(mask: impl Into<String>) -> Option<Self> {
let mask = mask.into();
let user_offset = mask.find('!')?;
let ip_offset = mask.find('@')?;
let len = mask.len();
if user_offset > ip_offset
|| user_offset == 0
|| ip_offset == user_offset + 1
|| ip_offset == len - 1
{
return None;
}
let (host, domain) = {
let address = &mask[(ip_offset + 1)..len];
let begin = ip_offset + 1;
if address.chars().all(|c| c.is_ascii_digit()) {
let offset = address.rfind('.')?;
((begin + offset)..len, begin..(begin + offset))
} else {
let first_dot = address.rfind('.')?;
let offset = address[..first_dot].rfind('.')?;
(begin..(begin + offset), (begin + offset)..len)
}
};
Some(Self {
mask,
nick: 0..user_offset,
username: (user_offset + 1)..ip_offset,
address: (ip_offset + 1)..len,
host,
domain,
})
}
pub fn from_parts(nick: &str, username: &str, address: &str) -> Option<Self> {
let mask = format!("{}!{}@{}", nick, username, address);
let user_offset = nick.len();
let addr_offset = username.len() + user_offset + 1;
let begin = addr_offset + 1;
let len = mask.len();
let (host, domain) = if address.chars().all(|c| c.is_ascii_digit()) {
let offset = address.rfind('.')?;
((begin + offset + 1)..len, begin..(begin + offset))
} else {
let first_dot = address.rfind('.')?;
let offset = address[..first_dot].rfind('.')?;
(begin..(begin + offset), (begin + offset)..len)
};
Some(Self {
mask,
nick: 0..user_offset,
username: (user_offset + 1)..addr_offset,
address: (addr_offset + 1)..len,
host,
domain,
})
}
pub fn as_str(&self) -> &str {
&self.mask
}
pub fn into_string(self) -> String {
self.mask
}
pub fn get_nick(&self) -> IrcIdentRef {
IrcIdentRef(&self.mask[self.nick.clone()])
}
pub fn get_username(&self) -> IrcIdentRef {
IrcIdentRef(&self.mask[self.username.clone()])
}
pub fn get_address(&self) -> &str {
&self.mask[self.address.clone()]
}
pub fn get_host(&self) -> &str {
&self.mask[self.host.clone()]
}
pub fn get_domain(&self) -> &str {
&self.mask[self.domain.clone()]
}
}
impl Deref for UserString {
type Target = str;
fn deref(&self) -> &str {
self.as_str()
}
}
impl TryFrom<String> for UserString {
type Error = ();
fn try_from(string: String) -> Result<Self, Self::Error> {
Self::new(string).ok_or(())
}
}
impl Into<String> for UserString {
fn into(self) -> String {
self.into_string()
}
}
impl Display for UserString {
fn fmt(&self, f: &mut Formatter) -> FmtResult {
write!(f, "{}", &self.mask)
}
}
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
pub struct UserMask {
mask: String,
nick: Range<usize>,
username: Range<usize>,
address: Range<usize>,
host: Range<usize>,
domain: Range<usize>,
}
impl Ord for UserMask {
fn cmp(&self, other: &Self) -> Ordering {
self.get_nick()
.cmp(&other.get_nick())
.then_with(|| self.get_username().cmp(&other.get_username()))
.then_with(|| self.get_host().cmp(&other.get_host()))
.then_with(|| self.get_domain().cmp(&other.get_domain()))
}
}
impl PartialOrd for UserMask {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl UserMask {
pub fn new(mask: impl Into<String>) -> Option<Self> {
let mask = mask.into();
let user_offset = mask.find('!')?;
let ip_offset = mask.find('@')?;
let len = mask.len();
if user_offset > ip_offset
|| user_offset == 0
|| ip_offset == user_offset + 1
|| ip_offset == len - 1
{
return None;
}
let (host, domain) = {
let address = &mask[(ip_offset + 1)..len];
let begin = ip_offset + 1;
if address.chars().all(|c| c.is_ascii_digit()) {
let offset = address.rfind('.')?;
((begin + offset)..len, begin..(begin + offset))
} else {
let first_dot = address.rfind('.')?;
let offset = address[..first_dot].rfind('.')?;
(begin..(begin + offset), (begin + offset)..len)
}
};
Some(Self {
mask,
nick: 0..user_offset,
username: (user_offset + 1)..ip_offset,
address: (ip_offset + 1)..len,
host,
domain,
})
}
pub fn as_str(&self) -> &str {
&self.mask
}
pub fn into_string(self) -> String {
self.mask
}
fn get_or_wildcard(&self, range: Range<usize>) -> Option<&str> {
let string = &self.mask[range];
if string == "*" {
None
} else {
Some(string)
}
}
pub fn get_nick(&self) -> Option<IrcIdentRef> {
self.get_or_wildcard(self.nick.clone()).map(IrcIdentRef)
}
pub fn get_username(&self) -> Option<IrcIdentRef> {
self.get_or_wildcard(self.username.clone()).map(IrcIdentRef)
}
pub fn get_host(&self) -> Option<&str> {
self.get_or_wildcard(self.host.clone())
}
pub fn get_domain(&self) -> Option<&str> {
self.get_or_wildcard(self.domain.clone())
}
}
impl TryFrom<String> for UserMask {
type Error = ();
fn try_from(string: String) -> Result<Self, Self::Error> {
Self::new(string).ok_or(())
}
}
impl Into<String> for UserMask {
fn into(self) -> String {
self.into_string()
}
}
impl Deref for UserMask {
type Target = str;
fn deref(&self) -> &Self::Target {
self.as_str()
}
}
impl Display for UserMask {
fn fmt(&self, f: &mut Formatter) -> FmtResult {
write!(f, "{}", &self.mask)
}
}
impl TryFrom<UserMask> for UserString {
type Error = ();
fn try_from(mask: UserMask) -> Result<Self, Self::Error> {
if mask.get_nick() == None
|| mask.get_username() == None
|| mask.get_host() == None
|| mask.get_domain() == None
{
Err(())
} else {
let UserMask {
mask,
nick,
username,
address,
host,
domain,
} = mask;
Ok(Self {
mask,
nick,
username,
address,
host,
domain,
})
}
}
}
impl From<UserString> for UserMask {
fn from(mask: UserString) -> Self {
let UserString {
mask,
nick,
username,
address,
host,
domain,
} = mask;
Self {
mask,
nick,
username,
address,
host,
domain,
}
}
}
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
pub struct IrcIdentRef<'a>(pub &'a str);
impl<'a> Ord for IrcIdentRef<'a> {
fn cmp(&self, other: &Self) -> Ordering {
crate::name_cmp(self.0, other.0)
}
}
impl<'a> PartialOrd for IrcIdentRef<'a> {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl<'a> Deref for IrcIdentRef<'a> {
type Target = str;
fn deref(&self) -> &Self::Target {
self.0
}
}
impl<'a> Into<&'a str> for IrcIdentRef<'a> {
fn into(self) -> &'a str {
self.0
}
}
impl<'a> From<&'a str> for IrcIdentRef<'a> {
fn from(string: &'a str) -> Self {
Self(string)
}
}
impl<'a> Display for IrcIdentRef<'a> {
fn fmt(&self, f: &mut Formatter) -> FmtResult {
write!(f, "{}", self.0)
}
}
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub struct IrcIdent(pub String);
impl IrcIdent {
pub fn as_ref(&self) -> IrcIdentRef {
IrcIdentRef(&self.0)
}
}
impl Ord for IrcIdent {
fn cmp(&self, other: &Self) -> Ordering {
crate::name_cmp(&self.0, &other.0)
}
}
impl PartialOrd for IrcIdent {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Deref for IrcIdent {
type Target = String;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl Into<String> for IrcIdent {
fn into(self) -> String {
self.0
}
}
impl From<String> for IrcIdent {
fn from(string: String) -> Self {
Self(string)
}
}
impl Display for IrcIdent {
fn fmt(&self, f: &mut Formatter) -> FmtResult {
write!(f, "{}", &self.0)
}
}