socketioxide_core/
lib.rs

1#![warn(
2    clippy::all,
3    clippy::todo,
4    clippy::empty_enum,
5    clippy::mem_forget,
6    clippy::unused_self,
7    clippy::filter_map_next,
8    clippy::needless_continue,
9    clippy::needless_borrow,
10    clippy::match_wildcard_for_single_variants,
11    clippy::if_let_mutex,
12    clippy::await_holding_lock,
13    clippy::match_on_vec_items,
14    clippy::imprecise_flops,
15    clippy::suboptimal_flops,
16    clippy::lossy_float_literal,
17    clippy::rest_pat_in_fully_bound_structs,
18    clippy::fn_params_excessive_bools,
19    clippy::exit,
20    clippy::inefficient_to_string,
21    clippy::linkedlist,
22    clippy::macro_use_imports,
23    clippy::option_option,
24    clippy::verbose_file_reads,
25    clippy::unnested_or_patterns,
26    rust_2018_idioms,
27    future_incompatible,
28    nonstandard_style,
29    missing_docs
30)]
31
32//! This crate is the core of the socketioxide crate.
33//! It contains basic types and interfaces for the socketioxide crate and the parser sub-crates.
34
35pub mod adapter;
36pub mod errors;
37pub mod packet;
38pub mod parser;
39
40use std::{collections::VecDeque, ops::Deref};
41
42use bytes::Bytes;
43pub use engineioxide::{sid::Sid, Str};
44use serde::{Deserialize, Serialize};
45
46/// Represents a unique identifier for a server.
47#[derive(Clone, Serialize, Deserialize, Debug, Copy, PartialEq, Eq, Default)]
48pub struct Uid(Sid);
49impl Deref for Uid {
50    type Target = Sid;
51    fn deref(&self) -> &Self::Target {
52        &self.0
53    }
54}
55impl std::fmt::Display for Uid {
56    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
57        self.0.fmt(f)
58    }
59}
60impl Uid {
61    /// A zeroed server id.
62    pub const ZERO: Self = Self(Sid::ZERO);
63    /// Create a new unique identifier.
64    pub fn new() -> Self {
65        Self(Sid::new())
66    }
67}
68
69/// Represents a value that can be sent over the engine.io wire as an engine.io packet
70/// or the data that can be outputed by a binary parser (e.g. [`MsgPackParser`](../socketioxide_parser_msgpack/index.html))
71/// or a string parser (e.g. [`CommonParser`](../socketioxide_parser_common/index.html))).
72///
73/// If you want to deserialize this value to a specific type. You should manually call the `Data` extractor.
74#[derive(Debug, Clone, PartialEq)]
75pub enum Value {
76    /// A string payload that will be sent as a string engine.io packet.
77    /// It can also contain adjacent binary payloads.
78    Str(Str, Option<VecDeque<bytes::Bytes>>),
79    /// A binary payload that will be sent as a binary engine.io packet
80    Bytes(bytes::Bytes),
81}
82
83/// Custom implementation to serialize enum variant as u8.
84impl Serialize for Value {
85    fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
86        let raw = match self {
87            Value::Str(data, bins) => (0u8, data.as_bytes(), bins),
88            Value::Bytes(data) => (1u8, data.as_ref(), &None),
89        };
90        raw.serialize(serializer)
91    }
92}
93impl<'de> Deserialize<'de> for Value {
94    fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
95        let (idx, data, bins): (u8, Vec<u8>, Option<VecDeque<Bytes>>) =
96            Deserialize::deserialize(deserializer)?;
97        let res = match idx {
98            0 => Value::Str(
99                Str::from(String::from_utf8(data).map_err(serde::de::Error::custom)?),
100                bins,
101            ),
102            1 => Value::Bytes(Bytes::from(data)),
103            i => Err(serde::de::Error::custom(format!(
104                "invalid value type: {}",
105                i
106            )))?,
107        };
108        Ok(res)
109    }
110}
111
112#[cfg(fuzzing)]
113#[doc(hidden)]
114impl arbitrary::Arbitrary<'_> for Value {
115    fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
116        let res = match u.arbitrary::<bool>()? {
117            true => Value::Bytes(u.arbitrary::<Vec<u8>>()?.into()),
118            false => Value::Str(
119                u.arbitrary::<String>()?.into(),
120                Some(
121                    u.arbitrary_iter::<Vec<u8>>()?
122                        .filter_map(|b| b.ok().map(bytes::Bytes::from))
123                        .collect(),
124                ),
125            ),
126        };
127        Ok(res)
128    }
129}
130
131impl Value {
132    /// Convert the value to a str slice if it can or return None
133    pub fn as_str(&self) -> Option<&Str> {
134        match self {
135            Value::Str(data, _) => Some(data),
136            Value::Bytes(_) => None,
137        }
138    }
139    /// Convert the value to a [`bytes::Bytes`] instance if it can or return None
140    pub fn as_bytes(&self) -> Option<&bytes::Bytes> {
141        match self {
142            Value::Str(_, _) => None,
143            Value::Bytes(data) => Some(data),
144        }
145    }
146    /// Get the length of the value
147    pub fn len(&self) -> usize {
148        match self {
149            Value::Str(data, _) => data.len(),
150            Value::Bytes(data) => data.len(),
151        }
152    }
153    /// Check if the value is empty
154    pub fn is_empty(&self) -> bool {
155        self.len() == 0
156    }
157}
158
159#[cfg(test)]
160mod tests {
161    use super::{Str, Value};
162    use bytes::Bytes;
163    use std::collections::VecDeque;
164
165    fn assert_serde_value(value: Value) {
166        let serialized = serde_json::to_string(&value).unwrap();
167        let deserialized: Value = serde_json::from_str(&serialized).unwrap();
168
169        assert_eq!(value, deserialized);
170    }
171
172    #[test]
173    fn value_serde_str_with_bins() {
174        let mut bins = VecDeque::new();
175        bins.push_back(Bytes::from_static(&[1, 2, 3, 4]));
176        bins.push_back(Bytes::from_static(&[5, 6, 7, 8]));
177
178        let value = Value::Str(Str::from("value".to_string()), Some(bins));
179        assert_serde_value(value);
180    }
181
182    #[test]
183    fn value_serde_bytes() {
184        let value = Value::Bytes(Bytes::from_static(&[1, 2, 3, 4]));
185        assert_serde_value(value);
186    }
187
188    #[test]
189    fn value_serde_str_without_bins() {
190        let value = Value::Str(Str::from("value_no_bins".to_string()), None);
191        assert_serde_value(value);
192    }
193
194    #[test]
195    fn value_serde_invalid_type() {
196        let invalid_data = "[2, [1,2,3,4], null]";
197        let result: Result<Value, _> = serde_json::from_str(invalid_data);
198        assert!(result.is_err());
199    }
200}