weechat_relay_rs/
basic_types.rs

1use std::fmt::{Display, Formatter};
2
3/// A
4/// [Pointer](https://weechat.org/files/doc/devel/weechat_relay_protocol.en.html#object_pointer)
5/// as provided by the WeeChat relay.
6///
7/// Note that while these pointers are probably parsed into an actual machine pointer type on the
8/// relay side, the protocol treats them as strings with particular constraints. This means that
9/// there are possible cases where a relay and a client may have a different interpretation of
10/// things like whether two pointers are equal (e.g., leading zeros), or a pointer is valid at all
11/// (e.g., Pointers longer than the relay machine's pointer width). Because the representation is
12/// only specified at the protocol level, we strongly advise to only use pointers as opaque handles
13/// provided by the relay.
14// pointers are always represented as ASCII hex strings in the protocol
15// e.g., command string "0xf00" == 3"f00" binary message
16#[derive(Debug, PartialEq, Eq, Hash, Clone)]
17pub struct Pointer(Vec<u8>);
18
19/// Errors when attempting to construct a pointer.
20#[derive(Debug)]
21pub enum PointerError {
22    /// The pointer string contains non-ASCII characters.
23    NonAscii,
24    /// The pointer string is valid UTF-8, but contains non-hexadecimal characters.
25    NonHex,
26    /// The pointer string is too long to fit in a pointer (as of version 3.7 of the WeeChat Relay
27    /// protocol, 255 bytes).
28    TooLong,
29}
30
31impl Pointer {
32    pub fn new(ascii_bytes: Vec<u8>) -> Result<Pointer, PointerError> {
33        let ascii_bytes = if ascii_bytes.starts_with(b"0x") {
34            ascii_bytes[2..].to_vec()
35        } else {
36            ascii_bytes
37        };
38        if ascii_bytes.len() > 255 {
39            return Err(PointerError::TooLong);
40        }
41        let pointer_str = std::str::from_utf8(&ascii_bytes).or(Err(PointerError::NonAscii))?;
42        if pointer_str.chars().all(|c| char::is_ascii_hexdigit(&c)) {
43            Ok(Pointer(ascii_bytes))
44        } else {
45            Err(PointerError::NonHex)
46        }
47    }
48
49    // NULL is explicitly defined as having "a length of 1 with value 0",
50    // with the demonstration showing that 0 to be ASCII 0, i.e. 0x30
51    /// Construct a `NULL` pointer.
52    ///
53    /// The `NULL` pointer is always an invalid identifier, using it in commands will yield empty
54    /// responses.
55    pub fn null() -> Pointer {
56        Pointer(vec![b'0'])
57    }
58
59    /// Whether this pointer is a `NULL` pointer.
60    ///
61    /// The `NULL` pointer is always an invalid identifier, using it in commands will yield empty
62    /// responses.
63    pub fn is_null(&self) -> bool {
64        self.0.len() == 1 && self.0[0] == b'0'
65    }
66}
67
68impl Display for Pointer {
69    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
70        let pointer_str = std::str::from_utf8(&self.0).expect("Invalid pointer: non-UTF-8");
71        write!(f, "0x{}", pointer_str)
72    }
73}
74
75/// A compression algorithm, or lack thereof.
76#[derive(Debug, PartialEq, Eq)]
77pub enum Compression {
78    Off,
79    Zlib,
80    Zstd,
81}
82
83impl Compression {
84    pub fn to_str(&self) -> &'static str {
85        match self {
86            Compression::Off => "off",
87            Compression::Zlib => "zlib",
88            Compression::Zstd => "zstd",
89        }
90    }
91
92    /// Parse the given `&str` into a Compression setting.
93    ///
94    /// The given string must be one of the strings listed in the Weechat Relay
95    /// [handshake docs](https://weechat.org/files/doc/devel/weechat_relay_protocol.en.html#command_handshake),
96    /// else this will return `None`.
97    pub fn parse(compression: &str) -> Option<Self> {
98        match compression {
99            "off" => Some(Compression::Off),
100            "zlib" => Some(Compression::Zlib),
101            "zstd" => Some(Compression::Zstd),
102            _ => None,
103        }
104    }
105}
106
107/// A password hash algorithm.
108#[derive(Debug, PartialEq, Eq)]
109pub enum PasswordHashAlgo {
110    Plain,
111    Sha256,
112    Sha512,
113    Pbkdf2Sha256,
114    Pbkdf2Sha512,
115}
116
117impl PasswordHashAlgo {
118    pub fn to_str(&self) -> &'static str {
119        match self {
120            PasswordHashAlgo::Plain => "plain",
121            PasswordHashAlgo::Sha256 => "sha256",
122            PasswordHashAlgo::Sha512 => "sha512",
123            PasswordHashAlgo::Pbkdf2Sha256 => "pbkdf2+sha256",
124            PasswordHashAlgo::Pbkdf2Sha512 => "pbkdf2+sha512",
125        }
126    }
127
128    /// Parse the given `&str` into a PasswordHashAlgo.
129    ///
130    /// The given string must be one of the strings listed in the Weechat Relay
131    /// [handshake docs](https://weechat.org/files/doc/devel/weechat_relay_protocol.en.html#command_handshake),
132    /// else this will return `None`.
133    pub fn parse(algo: &str) -> Option<PasswordHashAlgo> {
134        match algo {
135            "plain" => Some(PasswordHashAlgo::Plain),
136            "sha256" => Some(PasswordHashAlgo::Sha256),
137            "sha512" => Some(PasswordHashAlgo::Sha512),
138            "pbkdf2+sha256" => Some(PasswordHashAlgo::Pbkdf2Sha256),
139            "pbkdf2+sha512" => Some(PasswordHashAlgo::Pbkdf2Sha512),
140            _ => None,
141        }
142    }
143}