weechat_relay_rs/
messages.rs

1pub use crate::basic_types::{Compression, PasswordHashAlgo, Pointer};
2use std::collections::HashMap;
3
4/// A message received from WeeChat.
5#[derive(Debug, PartialEq, Eq)]
6pub struct Message {
7    pub id: Identifier,
8    pub objects: Vec<Object>,
9}
10
11impl Message {
12    /// Constructor for a new `Message`. You are unlikely to need to call this directly, typically a
13    /// message is created via parser.
14    pub fn new(id: Identifier, objects: Vec<Object>) -> Self {
15        Self { id, objects }
16    }
17}
18
19/// Any type that can be an object in a message.
20pub trait MessageType {
21    fn to_object(self) -> Object;
22    fn to_object_ref(&self) -> ObjectRef;
23    fn to_warray(vec: Vec<Self>) -> WArray
24    where
25        Self: Sized;
26}
27
28/// A WeeChat
29/// [Char](https://weechat.org/files/doc/devel/weechat_relay_protocol.en.html#object_char). Note
30/// that WeeChat Char types are signed.
31pub type WChar = i8;
32/// A WeeChat
33/// [Integer](https://weechat.org/files/doc/devel/weechat_relay_protocol.en.html#object_integer).
34pub type WInteger = i32;
35/// A WeeChat [Long
36/// integer](https://weechat.org/files/doc/devel/weechat_relay_protocol.en.html#object_long_integer).
37pub type WLongInteger = i64;
38/// A WeeChat
39/// [String](https://weechat.org/files/doc/devel/weechat_relay_protocol.en.html#object_string).
40///
41/// This type would be identical to the [`WBuffer`] type, except that it is intended to be a
42/// human-readable string.  It is not just a Rust String because there is no way, at the protocol
43/// level, to know what the encoding of the string is (it is the job of the application to know
44/// this). The `None` variant represents a `NULL` string.
45#[derive(Debug, PartialEq, Eq, Hash, Clone)]
46pub struct WString {
47    bytes: Option<Vec<u8>>,
48}
49
50impl WString {
51    pub fn new(bytes: Option<Vec<u8>>) -> Self {
52        Self { bytes }
53    }
54
55    pub fn from_ref(bytes: &[u8]) -> Self {
56        Self::new(Some(bytes.to_vec()))
57    }
58
59    /// Get the bytes representing this string.
60    pub fn bytes(&self) -> &Option<Vec<u8>> {
61        &self.bytes
62    }
63}
64
65/// A WeeChat
66/// [Buffer](https://weechat.org/files/doc/devel/weechat_relay_protocol.en.html#object_buffer). The
67/// `None` variant represents a `NULL` buffer.
68pub type WBuffer = Option<Vec<u8>>;
69/// A WeeChat [Time](https://weechat.org/files/doc/devel/weechat_relay_protocol.en.html#object_time)
70/// (i.e., number of seconds).
71pub type WTime = u64;
72
73/** A WeeChat
74[Hashtable](https://weechat.org/files/doc/devel/weechat_relay_protocol.en.html#object_hashtable).
75
76Why is this not just a [`HashMap`]?
77
78While we do provide a [function to create a true `HashMap`](to_hashmap) from this object, we do not
79use one by default because the spec is under-specified as to how valid such a transformation
80is. Namely, the spec does not detail whether it is valid for a hashtable to contain multiple
81instances of the same key, or whether ordering is/can be significant. While the answer is almost
82certainly "keys cannot be duplicated and ordering is not significant", as is the case in Rust's
83`HashMap` (so you probably almost always want to convert this immediately), it's possible that
84custom extensions could violate these assumptions without violating the spec, so we opt to defer to
85the safer interpretation.
86*/
87#[derive(Debug, PartialEq, Eq, Hash, Clone)]
88pub struct WHashtable {
89    pub keys: WArray,
90    pub vals: WArray,
91}
92
93/// A generic WeeChat
94/// [Hdata](https://weechat.org/files/doc/devel/weechat_relay_protocol.en.html#object_hdata).
95#[derive(Debug, PartialEq, Eq, Hash, Clone)]
96pub struct GenericHdata {
97    pub hpath: WString, // FIXME: this should prolly be some kind of "path" type
98    pub ppaths: Vec<Vec<Pointer>>,
99    // in order to preserve type information better, we represent sets "column-wise"
100    // (i.e., the n'th HdataValues represents the n'th values of each set),
101    // as opposed to the "row-wise" order they are sent in (set1, set2, etc.)
102    pub set_values: Vec<HdataValues>,
103}
104
105impl GenericHdata {
106    /// Returns a Vec of the object sets in the Hdata.
107    ///
108    /// This renders some of the type consistency inaccessible to the compiler, so only use this in
109    /// more general cases, where you're not actually doing anything with the particular Hdata.
110    pub fn sets(&self) -> Vec<HashMap<&Vec<u8>, ObjectRef>> {
111        let mut ret = vec![HashMap::new(); self.set_values[0].values.len()];
112        for hdata_values in self.set_values.iter() {
113            let values = hdata_values.values.to_ref_vec();
114            for (i, v) in values.into_iter().enumerate() {
115                ret[i].insert(&hdata_values.key, v);
116            }
117        }
118        ret
119    }
120}
121
122#[derive(Debug, PartialEq, Eq, Hash, Clone)]
123pub struct HdataValues {
124    pub key: Vec<u8>,
125    pub values: WArray,
126}
127
128/// A WeeChat
129/// [Info](https://weechat.org/files/doc/devel/weechat_relay_protocol.en.html#object_info).
130#[derive(Debug, PartialEq, Eq, Hash, Clone)]
131pub struct WInfo {
132    pub name: WString,
133    pub value: WString,
134}
135
136/// A WeeChat
137/// [Infolist](https://weechat.org/files/doc/devel/weechat_relay_protocol.en.html#object_infolist).
138#[derive(Debug, PartialEq, Eq, Hash, Clone)]
139pub struct WInfolist {
140    pub name: WString,
141    pub items: Vec<InfolistItem>,
142}
143
144/// An
145/// [Infolist](https://weechat.org/files/doc/devel/weechat_relay_protocol.en.html#object_infolist)
146/// item---an element of an Infolist.
147#[derive(Debug, PartialEq, Eq, Hash, Clone)]
148pub struct InfolistItem {
149    pub variables: Vec<InfolistVariable>,
150}
151
152/// An
153/// [Infolist](https://weechat.org/files/doc/devel/weechat_relay_protocol.en.html#object_infolist)
154/// variable---an element of an Infolist item.
155#[derive(Debug, PartialEq, Eq, Hash, Clone)]
156pub struct InfolistVariable {
157    pub name: WString,
158    pub value: Object,
159}
160
161impl MessageType for WChar {
162    fn to_object(self) -> Object {
163        Object::Chr(self)
164    }
165    fn to_object_ref(&self) -> ObjectRef {
166        ObjectRef::Chr(self)
167    }
168    fn to_warray(vec: Vec<Self>) -> WArray {
169        WArray::Chr(vec)
170    }
171}
172impl MessageType for WInteger {
173    fn to_object(self) -> Object {
174        Object::Int(self)
175    }
176    fn to_object_ref(&self) -> ObjectRef {
177        ObjectRef::Int(self)
178    }
179    fn to_warray(vec: Vec<Self>) -> WArray {
180        WArray::Int(vec)
181    }
182}
183impl MessageType for WLongInteger {
184    fn to_object(self) -> Object {
185        Object::Lon(self)
186    }
187    fn to_object_ref(&self) -> ObjectRef {
188        ObjectRef::Lon(self)
189    }
190    fn to_warray(vec: Vec<Self>) -> WArray {
191        WArray::Lon(vec)
192    }
193}
194impl MessageType for WString {
195    fn to_object(self) -> Object {
196        Object::Str(self)
197    }
198    fn to_object_ref(&self) -> ObjectRef {
199        ObjectRef::Str(self)
200    }
201    fn to_warray(vec: Vec<Self>) -> WArray {
202        WArray::Str(vec)
203    }
204}
205impl MessageType for WBuffer {
206    fn to_object(self) -> Object {
207        Object::Buf(self)
208    }
209    fn to_object_ref(&self) -> ObjectRef {
210        ObjectRef::Buf(self)
211    }
212    fn to_warray(vec: Vec<Self>) -> WArray {
213        WArray::Buf(vec)
214    }
215}
216impl MessageType for Pointer {
217    fn to_object(self) -> Object {
218        Object::Ptr(self)
219    }
220    fn to_object_ref(&self) -> ObjectRef {
221        ObjectRef::Ptr(self)
222    }
223    fn to_warray(vec: Vec<Self>) -> WArray {
224        WArray::Ptr(vec)
225    }
226}
227impl MessageType for WTime {
228    fn to_object(self) -> Object {
229        Object::Tim(self)
230    }
231    fn to_object_ref(&self) -> ObjectRef {
232        ObjectRef::Tim(self)
233    }
234    fn to_warray(vec: Vec<Self>) -> WArray {
235        WArray::Tim(vec)
236    }
237}
238impl MessageType for WHashtable {
239    fn to_object(self) -> Object {
240        Object::Htb(self)
241    }
242    fn to_object_ref(&self) -> ObjectRef {
243        ObjectRef::Htb(self)
244    }
245    fn to_warray(vec: Vec<Self>) -> WArray {
246        WArray::Htb(vec)
247    }
248}
249impl MessageType for GenericHdata {
250    fn to_object(self) -> Object {
251        Object::Hda(self)
252    }
253    fn to_object_ref(&self) -> ObjectRef {
254        ObjectRef::Hda(self)
255    }
256    fn to_warray(vec: Vec<Self>) -> WArray {
257        WArray::Hda(vec)
258    }
259}
260impl MessageType for WInfo {
261    fn to_object(self) -> Object {
262        Object::Inf(self)
263    }
264    fn to_object_ref(&self) -> ObjectRef {
265        ObjectRef::Inf(self)
266    }
267    fn to_warray(vec: Vec<Self>) -> WArray {
268        WArray::Inf(vec)
269    }
270}
271impl MessageType for WInfolist {
272    fn to_object(self) -> Object {
273        Object::Inl(self)
274    }
275    fn to_object_ref(&self) -> ObjectRef {
276        ObjectRef::Inl(self)
277    }
278    fn to_warray(vec: Vec<Self>) -> WArray {
279        WArray::Inl(vec)
280    }
281}
282impl MessageType for WArray {
283    fn to_object(self) -> Object {
284        Object::Arr(self)
285    }
286    fn to_object_ref(&self) -> ObjectRef {
287        ObjectRef::Arr(self)
288    }
289    fn to_warray(vec: Vec<Self>) -> WArray {
290        WArray::Arr(vec)
291    }
292}
293
294/// A WeeChat
295/// [identifier](https://weechat.org/files/doc/devel/weechat_relay_protocol.en.html#message_identifier).
296#[derive(Debug, PartialEq, Eq)]
297pub enum Identifier {
298    Client(Vec<u8>),
299    Event(Event),
300}
301
302/// A WeeChat
303/// [event](https://weechat.org/files/doc/devel/weechat_relay_protocol.en.html#message_identifier)---i.e.,
304/// a reserved identifier.
305#[derive(Debug, PartialEq, Eq)]
306pub enum Event {
307    BufferOpened,
308    BufferTypeChanged,
309    BufferMoved,
310    BufferMerged,
311    BufferUnmerged,
312    BufferHidden,
313    BufferUnhidden,
314    BufferRenamed,
315    BufferTitleChanged,
316    BufferLocalvarAdded,
317    BufferLocalvarChanged,
318    BufferLocalvarRemoved,
319    BufferClosing,
320    BufferCleared,
321    BufferLineAdded,
322    Nicklist,
323    NicklistDiff,
324    Pong,
325    Upgrade,
326    UpgradeEnded,
327}
328
329/// The possible object types (but not the objects themselves).
330#[derive(Clone, Copy)]
331pub enum ObjectType {
332    Chr,
333    Int,
334    Lon,
335    Str,
336    Buf,
337    Ptr,
338    Tim,
339    Htb,
340    Hda,
341    Inf,
342    Inl,
343    Arr,
344}
345
346impl ObjectType {
347    pub fn new_warray(&self, capacity: usize) -> WArray {
348        match self {
349            Self::Chr => WArray::Chr(Vec::with_capacity(capacity)),
350            Self::Int => WArray::Int(Vec::with_capacity(capacity)),
351            Self::Lon => WArray::Lon(Vec::with_capacity(capacity)),
352            Self::Str => WArray::Str(Vec::with_capacity(capacity)),
353            Self::Buf => WArray::Buf(Vec::with_capacity(capacity)),
354            Self::Ptr => WArray::Ptr(Vec::with_capacity(capacity)),
355            Self::Tim => WArray::Tim(Vec::with_capacity(capacity)),
356            Self::Htb => WArray::Htb(Vec::with_capacity(capacity)),
357            Self::Hda => WArray::Hda(Vec::with_capacity(capacity)),
358            Self::Inf => WArray::Inf(Vec::with_capacity(capacity)),
359            Self::Inl => WArray::Inl(Vec::with_capacity(capacity)),
360            Self::Arr => WArray::Arr(Vec::with_capacity(capacity)),
361        }
362    }
363}
364
365/// One of the valid WeeChat
366/// [Objects](https://weechat.org/files/doc/devel/weechat_relay_protocol.en.html#objects).
367#[derive(Debug, PartialEq, Eq, Hash, Clone)]
368pub enum Object {
369    Chr(WChar),
370    Int(WInteger),
371    Lon(WLongInteger),
372    Str(WString),
373    Buf(WBuffer),
374    Ptr(Pointer),
375    Tim(WTime),
376    Htb(WHashtable),
377    Hda(GenericHdata),
378    Inf(WInfo),
379    Inl(WInfolist),
380    Arr(WArray),
381    // ^ WArrays look a lot like this enum, but are Vecs over the possible types,
382    // to better preserve the type information than a Vec<Object> would be able
383}
384
385impl Object {
386    fn vec_to_object_vec<T: MessageType>(v: Vec<T>) -> Vec<Self> {
387        v.into_iter().map(|o: T| o.to_object()).collect()
388    }
389
390    pub fn object_ref(&self) -> ObjectRef {
391        match self {
392            Self::Chr(v) => ObjectRef::Chr(v),
393            Self::Int(v) => ObjectRef::Int(v),
394            Self::Lon(v) => ObjectRef::Lon(v),
395            Self::Str(v) => ObjectRef::Str(v),
396            Self::Buf(v) => ObjectRef::Buf(v),
397            Self::Ptr(v) => ObjectRef::Ptr(v),
398            Self::Tim(v) => ObjectRef::Tim(v),
399            Self::Htb(v) => ObjectRef::Htb(v),
400            Self::Hda(v) => ObjectRef::Hda(v),
401            Self::Inf(v) => ObjectRef::Inf(v),
402            Self::Inl(v) => ObjectRef::Inl(v),
403            Self::Arr(v) => ObjectRef::Arr(v),
404        }
405    }
406}
407
408#[derive(Debug, PartialEq, Eq, Hash, Clone)]
409pub enum ObjectRef<'a> {
410    Chr(&'a WChar),
411    Int(&'a WInteger),
412    Lon(&'a WLongInteger),
413    Str(&'a WString),
414    Buf(&'a WBuffer),
415    Ptr(&'a Pointer),
416    Tim(&'a WTime),
417    Htb(&'a WHashtable),
418    Hda(&'a GenericHdata),
419    Inf(&'a WInfo),
420    Inl(&'a WInfolist),
421    Arr(&'a WArray),
422}
423
424impl<'a> ObjectRef<'a> {
425    fn vec_to_object_ref_vec<T: MessageType + 'a>(v: &'a [T]) -> Vec<Self> {
426        v.iter().map(|o: &T| o.to_object_ref()).collect()
427    }
428}
429
430/// A WeeChat
431/// [Array](https://weechat.org/files/doc/devel/weechat_relay_protocol.en.html#object_array)---a vec
432/// of a single WeeChat type.
433#[derive(Debug, PartialEq, Eq, Hash, Clone)]
434pub enum WArray {
435    Chr(Vec<WChar>),
436    Int(Vec<WInteger>),
437    Lon(Vec<WLongInteger>),
438    Str(Vec<WString>),
439    Buf(Vec<WBuffer>),
440    Ptr(Vec<Pointer>),
441    Tim(Vec<WTime>),
442    Htb(Vec<WHashtable>),
443    Hda(Vec<GenericHdata>),
444    Inf(Vec<WInfo>),
445    Inl(Vec<WInfolist>),
446    Arr(Vec<WArray>),
447}
448
449/// Apply an expression to any variant of a [`WArray`].
450///
451/// Each variant of a `WArray` holds a different type, so there is no way to generically apply an
452/// identical expression to all variants.  However, sometimes we don't care about the type, and just
453/// want to apply an expression with the same syntax and analogous semantics regardless (e.g., we
454/// want to know the length).  This macro does just that.  This obviously means the expression must
455/// resolve to valid code when called on any of the particular types.  The expression is applied as
456/// a function call.
457///
458/// E.g., `len` is defined as
459/// `
460/// pub fn len(&self) -> usize {
461///     apply_to_warray!(self, Vec::len)
462/// }
463/// `
464#[macro_export]
465macro_rules! apply_to_warray {
466    ( $warray:expr, $function:expr ) => {
467        match $warray {
468            WArray::Chr(v) => $function(v),
469            WArray::Int(v) => $function(v),
470            WArray::Lon(v) => $function(v),
471            WArray::Str(v) => $function(v),
472            WArray::Buf(v) => $function(v),
473            WArray::Ptr(v) => $function(v),
474            WArray::Tim(v) => $function(v),
475            WArray::Htb(v) => $function(v),
476            WArray::Hda(v) => $function(v),
477            WArray::Inf(v) => $function(v),
478            WArray::Inl(v) => $function(v),
479            WArray::Arr(v) => $function(v),
480        }
481    };
482}
483
484/// Apply an expression to any variant of a [`WArray`], with auxiliary data.
485///
486/// This is the same as [`apply_to_warray`], but with the second argument given as the second
487/// argument to the function call.
488#[macro_export]
489macro_rules! apply_to_warray_with {
490    ( $warray:expr, $data:expr, $function:expr ) => {
491        match $warray {
492            WArray::Chr(v) => $function(v, $data),
493            WArray::Int(v) => $function(v, $data),
494            WArray::Lon(v) => $function(v, $data),
495            WArray::Str(v) => $function(v, $data),
496            WArray::Buf(v) => $function(v, $data),
497            WArray::Ptr(v) => $function(v, $data),
498            WArray::Tim(v) => $function(v, $data),
499            WArray::Htb(v) => $function(v, $data),
500            WArray::Hda(v) => $function(v, $data),
501            WArray::Inf(v) => $function(v, $data),
502            WArray::Inl(v) => $function(v, $data),
503            WArray::Arr(v) => $function(v, $data),
504        }
505    };
506}
507
508impl WArray {
509    pub fn len(&self) -> usize {
510        apply_to_warray!(self, Vec::len)
511    }
512
513    pub fn is_empty(&self) -> bool {
514        apply_to_warray!(self, Vec::is_empty)
515    }
516
517    pub fn to_vec(self) -> Vec<Object> {
518        apply_to_warray!(self, Object::vec_to_object_vec)
519    }
520
521    pub fn to_ref_vec(&self) -> Vec<ObjectRef> {
522        apply_to_warray!(self, ObjectRef::vec_to_object_ref_vec)
523    }
524}
525
526/// For converting a [`WHashtable`] into a [`HashMap`]
527pub fn to_hashmap<K, V>(keys: Vec<K>, vals: Vec<V>) -> HashMap<K, V>
528where
529    K: Eq + std::hash::Hash,
530{
531    keys.into_iter().zip(vals).collect::<HashMap<K, V>>()
532}