irc_bot/core/
irc_msgs.rs

1use super::ServerId;
2
3#[derive(Clone, Copy, Debug, Eq, PartialEq)]
4pub struct MsgDest<'a> {
5    pub server_id: ServerId,
6    pub target: &'a str,
7}
8
9#[derive(Clone, Copy, Debug, Eq, PartialEq)]
10pub struct MsgPrefix<'a> {
11    pub nick: Option<&'a str>,
12    pub user: Option<&'a str>,
13    pub host: Option<&'a str>,
14}
15
16#[derive(Debug)]
17pub struct MsgMetadata<'a> {
18    pub dest: MsgDest<'a>,
19    pub prefix: MsgPrefix<'a>,
20}
21
22#[derive(Debug)]
23pub struct OwningMsgPrefix {
24    backing: String,
25}
26
27#[cfg(feature = "pircolate")]
28fn prefix_from_pircolate<'a>(
29    pirc_pfx: Option<(&'a str, Option<&'a str>, Option<&'a str>)>,
30) -> MsgPrefix<'a> {
31    match pirc_pfx {
32        Some((nick, user, host)) => MsgPrefix {
33            nick: Some(nick),
34            user: user,
35            host: host,
36        },
37        None => MsgPrefix {
38            nick: None,
39            user: None,
40            host: None,
41        },
42    }
43}
44
45pub(super) fn is_msg_to_nick(target: &str, msg: &str, nick: &str) -> bool {
46    target == nick || msg == nick || (msg.starts_with(nick) && (msg.find(|c: char| {
47        [':', ','].contains(&c)
48    }) == Some(nick.len())))
49}
50
51pub(super) fn parse_msg_to_nick<'msg>(
52    text: &'msg str,
53    target: &str,
54    nick: &str,
55) -> Option<&'msg str> {
56    if is_msg_to_nick(target, text, nick) {
57        Some(
58            text.trim_left_matches(nick)
59                .trim_left_matches(|c: char| [':', ','].contains(&c))
60                .trim(),
61        )
62    } else {
63        None
64    }
65}
66
67pub(super) fn parse_prefix(prefix: &str) -> MsgPrefix {
68    let mut iter = prefix.rsplitn(2, '@');
69    let host = iter.next();
70    let mut iter = iter.next().unwrap_or("").splitn(2, '!');
71    let nick = iter.next();
72    let user = iter.next();
73    MsgPrefix {
74        nick: nick,
75        user: user,
76        host: host,
77    }
78}
79
80impl<'a> MsgPrefix<'a> {
81    /// Returns an upper bound on the length of the message prefix, accurate to within a few bytes.
82    pub fn len(&self) -> usize {
83        fn component_len(component: Option<&str>) -> usize {
84            component.map(|s| s.len()).unwrap_or(0)
85        }
86
87        component_len(self.nick) + component_len(self.user) + component_len(self.host) + 2
88    }
89
90    /// Converts the `MsgPrefix` into an `OwningMsgPrefix`.
91    ///
92    /// This can't be a `ToOwned` implementation because that would conflict with `MsgPrefix`'s
93    /// `Clone` implementation.
94    pub fn to_owning(&self) -> OwningMsgPrefix {
95        OwningMsgPrefix::from_string(format!(
96            "{}!{}@{}",
97            self.nick.unwrap_or(""),
98            self.user.unwrap_or(""),
99            self.host.unwrap_or("")
100        ))
101    }
102}
103
104impl OwningMsgPrefix {
105    pub fn from_string(prefix: String) -> Self {
106        OwningMsgPrefix { backing: prefix }
107    }
108
109    pub fn parse<'a>(&'a self) -> MsgPrefix<'a> {
110        parse_prefix(&self.backing)
111    }
112
113    /// Returns the exact length of the message prefix.
114    pub fn len(&self) -> usize {
115        self.backing.len()
116    }
117
118    /// Write each non-`None` field of the given message prefix over the corresponding field in
119    /// `self`.
120    pub(super) fn update_from(&mut self, new: &MsgPrefix) {
121        fn updated<'old, 'new>(old: Option<&'old str>, new: Option<&'new str>) -> &'old str
122        where
123            'new: 'old,
124        {
125            match (old, new) {
126                (_, Some(s)) => s,
127                (Some(s), None) => s,
128                (None, None) => "",
129            }
130        }
131
132        self.backing = {
133            let old = self.parse();
134            format!(
135                "{}!{}@{}",
136                updated(old.nick, new.nick),
137                updated(old.user, new.user),
138                updated(old.host, new.host)
139            )
140        }
141    }
142}