Skip to main content

sl_types/
chat.rs

1//! Types related to SL chat
2
3#[cfg(feature = "chumsky")]
4use chumsky::{IterParser as _, Parser, text::digits};
5
6/// represents a Second Life chat channel
7#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
8#[expect(
9    clippy::module_name_repetitions,
10    reason = "this type is used outside of this module"
11)]
12pub struct ChatChannel(pub i32);
13
14/// parse a chat channel
15///
16/// # Errors
17///
18/// returns an error if the string could not be parsed
19#[cfg(feature = "chumsky")]
20#[must_use]
21#[expect(
22    clippy::module_name_repetitions,
23    reason = "this parser is used outside of this module"
24)]
25pub fn chat_channel_parser<'src>()
26-> impl Parser<'src, &'src str, ChatChannel, chumsky::extra::Err<chumsky::error::Rich<'src, char>>>
27{
28    digits(10).collect::<String>().try_map(|d, span| {
29        let d: i32 = d
30            .parse()
31            .map_err(|e| chumsky::error::Rich::custom(span, format!("{e:?}")))?;
32        Ok(ChatChannel(d))
33    })
34}
35
36impl std::fmt::Display for ChatChannel {
37    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
38        write!(f, "{}", self.0)
39    }
40}
41
42impl std::str::FromStr for ChatChannel {
43    type Err = <i32 as std::str::FromStr>::Err;
44
45    fn from_str(s: &str) -> Result<Self, Self::Err> {
46        <i32 as std::str::FromStr>::from_str(s).map(ChatChannel)
47    }
48}
49
50/// the public chat channel on Second Life
51pub const PUBLIC_CHANNEL: ChatChannel = ChatChannel(0);
52
53/// the combat log event chat channel on Second Life
54pub const COMBAT_CHANNEL: ChatChannel = ChatChannel(0x7FFF_FFFE);
55
56/// the script debug chat channel on Second Life
57pub const DEBUG_CHANNEL: ChatChannel = ChatChannel(0x7FFF_FFFF);
58
59/// represents a Second Life chat volume
60#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, strum::EnumIs)]
61#[expect(
62    clippy::module_name_repetitions,
63    reason = "this type is used outside of this module"
64)]
65pub enum ChatVolume {
66    /// whisper (10m)
67    Whisper,
68    /// say (20m, default, a.k.a. chat range)
69    Say,
70    /// shout (100m)
71    Shout,
72    /// region say (the whole region)
73    RegionSay,
74}
75
76impl ChatVolume {
77    /// identify the chat volume of a message and strip it off the message
78    #[must_use]
79    pub fn volume_and_message(s: String) -> (Self, String) {
80        if let Some(whisper_message) = s.strip_prefix("whispers: ") {
81            (Self::Whisper, whisper_message.to_string())
82        } else if let Some(shout_message) = s.strip_prefix("shouts: ") {
83            (Self::Shout, shout_message.to_string())
84        } else {
85            (Self::Say, s)
86        }
87    }
88}