aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorMax Audron <audron@cocaine.farm>2020-05-02 18:32:28 +0200
committerMax Audron <audron@cocaine.farm>2020-05-02 18:32:28 +0200
commitb69cdca85d2a2e7563b086426ab0165b45ea5eb4 (patch)
tree944744fcae385e5f82985f368386cd4e96e3555e /src
parentrewrite of datetime parsing to use the time crate (diff)
add parsing of signalproxy messages
Diffstat (limited to 'src')
-rw-r--r--src/bin/quassel-client.rs8
-rw-r--r--src/client/mod.rs17
-rw-r--r--src/message/mod.rs2
-rw-r--r--src/message/signalproxy.rs309
-rw-r--r--src/primitive/datetime.rs171
-rw-r--r--src/primitive/variant.rs13
-rw-r--r--src/tests/mod.rs1
-rw-r--r--src/util.rs5
8 files changed, 454 insertions, 72 deletions
diff --git a/src/bin/quassel-client.rs b/src/bin/quassel-client.rs
index 9d25088..233d3ae 100644
--- a/src/bin/quassel-client.rs
+++ b/src/bin/quassel-client.rs
@@ -16,10 +16,18 @@ async fn main() -> Result<(), Error> {
// true,
// ).await.unwrap();
+ let username = std::env::args().nth(1).expect("no username given");
+ let password = std::env::args().nth(2).expect("no password given");
+
+
let mut client = client::Client::<tokio_tls::TlsStream<tokio::net::TcpStream>>::connect_tls(
"cocaine.farm",
4242,
true,
+ client::User {
+ name: username,
+ password: password,
+ }
).await.unwrap();
client.run().await;
diff --git a/src/client/mod.rs b/src/client/mod.rs
index b43115b..9f0d66c 100644
--- a/src/client/mod.rs
+++ b/src/client/mod.rs
@@ -30,6 +30,12 @@ pub struct Client<T: AsyncRead + AsyncWrite + Unpin> {
pub tls: bool,
pub compression: bool,
pub state: ClientState,
+ pub user: User,
+}
+
+pub struct User {
+ pub name: String,
+ pub password: String,
}
pub enum ClientState {
@@ -69,7 +75,7 @@ impl <T: AsyncRead + AsyncWrite + Unpin> Client<T> {
};
}
- pub async fn connect(address: &'static str, port: u64, compression: bool) -> Result<Client<TcpStream>, Error> {
+ pub async fn connect(address: &'static str, port: u64, compression: bool, user: User) -> Result<Client<TcpStream>, Error> {
let mut stream = TcpStream::connect(format!("{}:{}", address, port)).await?;
info!(target: "init", "Establishing Connection");
@@ -90,10 +96,11 @@ impl <T: AsyncRead + AsyncWrite + Unpin> Client<T> {
tls: false,
compression,
state: ClientState::Handshake,
+ user,
});
}
- pub async fn connect_tls(address: &'static str, port: u64, compression: bool) -> Result<Client<TlsStream<TcpStream>>, Error> {
+ pub async fn connect_tls(address: &'static str, port: u64, compression: bool, user: User) -> Result<Client<TlsStream<TcpStream>>, Error> {
let mut stream: TcpStream = TcpStream::connect(format!("{}:{}", address, port)).await?;
info!(target: "init", "Establishing Connection");
@@ -118,6 +125,7 @@ impl <T: AsyncRead + AsyncWrite + Unpin> Client<T> {
tls: true,
compression,
state: ClientState::Handshake,
+ user,
});
}
@@ -137,7 +145,7 @@ pub async fn handle_login_message<T: AsyncRead + AsyncWrite + Unpin>(client: &mu
"ClientInitAck" => {
info!(target: "init", "Initialization successfull");
info!(target: "login", "Starting Login");
- let login = ClientLogin {user: "audron".to_string(), password: "audron".to_string()};
+ let login = ClientLogin {user: client.user.name.clone(), password: client.user.password.clone()};
client.stream.send(login.serialize()?).await?;
},
"ClientInitReject" => {
@@ -162,12 +170,13 @@ pub async fn handle_login_message<T: AsyncRead + AsyncWrite + Unpin>(client: &mu
}
pub async fn handle_message<T: AsyncRead + AsyncWrite + Unpin>(client: &mut Client<T>, buf: &[u8]) -> Result<(), Error> {
+ use crate::message::Message;
use crate::primitive::VariantList;
use crate::Deserialize;
use crate::Serialize;
trace!(target: "message", "Received bytes: {:x?}", buf);
- let (_, res) = VariantList::parse(buf)?;
+ let (_, res) = Message::parse(buf)?;
debug!(target: "init", "Received Messsage: {:#?}", res);
return Ok(());
diff --git a/src/message/mod.rs b/src/message/mod.rs
index 16f2088..96cc910 100644
--- a/src/message/mod.rs
+++ b/src/message/mod.rs
@@ -1,3 +1,5 @@
mod handshake;
+mod signalproxy;
pub use handshake::*;
+pub use signalproxy::*;
diff --git a/src/message/signalproxy.rs b/src/message/signalproxy.rs
new file mode 100644
index 0000000..22a498a
--- /dev/null
+++ b/src/message/signalproxy.rs
@@ -0,0 +1,309 @@
+use crate::primitive::{DateTime, Variant, VariantList};
+use crate::{Deserialize, Serialize};
+
+#[derive(Clone, Debug, std::cmp::PartialEq)]
+pub enum Message {
+ /// Bidirectional
+ SyncMessage(SyncMessage),
+ /// Bidirectional
+ RpcCall(RpcCall),
+ InitRequest(InitRequest),
+ InitData(InitData),
+ /// Bidirectional
+ HeartBeat(HeartBeat),
+ /// Bidirectional
+ HeartBeatReply(HeartBeatReply),
+}
+
+impl Serialize for Message {
+ fn serialize(&self) -> Result<Vec<std::primitive::u8>, failure::Error> {
+ match &self {
+ Message::SyncMessage(value) => value.serialize(),
+ Message::RpcCall(value) => value.serialize(),
+ Message::InitRequest(value) => value.serialize(),
+ Message::InitData(value) => value.serialize(),
+ Message::HeartBeat(value) => value.serialize(),
+ Message::HeartBeatReply(value) => value.serialize(),
+ }
+ }
+}
+
+impl Deserialize for Message {
+ fn parse(b: &[std::primitive::u8]) -> Result<(std::primitive::usize, Self), failure::Error> {
+ let (_, message_type) = i32::parse(&b[9..13])?;
+
+ match MessageType::from(message_type) {
+ MessageType::SyncMessage => {
+ let (size, res) = SyncMessage::parse(&b)?;
+
+ Ok((size, Message::SyncMessage(res)))
+ }
+ MessageType::RpcCall => {
+ let (size, res) = RpcCall::parse(&b)?;
+
+ Ok((size, Message::RpcCall(res)))
+ }
+ MessageType::InitRequest => {
+ let (size, res) = InitRequest::parse(&b)?;
+
+ Ok((size, Message::InitRequest(res)))
+ }
+ MessageType::InitData => {
+ let (size, res) = InitData::parse(&b)?;
+
+ Ok((size, Message::InitData(res)))
+ }
+ MessageType::HeartBeat => {
+ let (size, res) = HeartBeat::parse(&b)?;
+
+ Ok((size, Message::HeartBeat(res)))
+ }
+ MessageType::HeartBeatReply => {
+ let (size, res) = HeartBeatReply::parse(&b)?;
+
+ Ok((size, Message::HeartBeatReply(res)))
+ }
+ }
+ }
+}
+
+/// Type of an SignalProxy Message
+/// The first element in the VariantList that is received
+#[repr(i32)]
+#[derive(Copy, Clone, Debug, std::cmp::PartialEq)]
+pub enum MessageType {
+ /// Bidirectional
+ SyncMessage = 0x00000001,
+ /// Bidirectional
+ RpcCall = 0x00000002,
+ InitRequest = 0x00000003,
+ InitData = 0x00000004,
+ /// Bidirectional
+ HeartBeat = 0x00000005,
+ /// Bidirectional
+ HeartBeatReply = 0x00000006,
+}
+
+impl From<i32> for MessageType {
+ fn from(val: i32) -> Self {
+ match val {
+ 0x00000001 => MessageType::SyncMessage,
+ 0x00000002 => MessageType::RpcCall,
+ 0x00000003 => MessageType::InitRequest,
+ 0x00000004 => MessageType::InitData,
+ 0x00000005 => MessageType::HeartBeat,
+ 0x00000006 => MessageType::HeartBeatReply,
+ _ => unimplemented!(),
+ }
+ }
+}
+
+#[derive(Clone, Debug, std::cmp::PartialEq)]
+pub struct SyncMessage {
+ class_name: String,
+ object_name: String,
+ slot_name: String,
+ params: VariantList,
+}
+
+impl Serialize for SyncMessage {
+ fn serialize(&self) -> Result<Vec<std::primitive::u8>, failure::Error> {
+ let mut res = VariantList::new();
+
+ res.push(Variant::i32(MessageType::SyncMessage as i32));
+ res.push(Variant::StringUTF8(self.class_name.clone()));
+ res.push(Variant::StringUTF8(self.object_name.clone()));
+ res.push(Variant::StringUTF8(self.slot_name.clone()));
+
+ res.append(&mut self.params.clone());
+
+ res.serialize()
+ }
+}
+
+impl Deserialize for SyncMessage {
+ fn parse(b: &[std::primitive::u8]) -> Result<(std::primitive::usize, Self), failure::Error> {
+ let (size, mut res) = VariantList::parse(&b)?;
+
+ res.remove(0);
+
+ Ok((
+ size,
+ Self {
+ class_name: match_variant!(res.remove(0), Variant::StringUTF8),
+ object_name: match_variant!(res.remove(0), Variant::StringUTF8),
+ slot_name: match_variant!(res.remove(0), Variant::StringUTF8),
+ params: res,
+ },
+ ))
+ }
+}
+
+#[derive(Clone, Debug, std::cmp::PartialEq)]
+pub struct RpcCall {
+ slot_name: String,
+ params: VariantList,
+}
+
+impl Serialize for RpcCall {
+ fn serialize(&self) -> Result<Vec<std::primitive::u8>, failure::Error> {
+ let mut res = VariantList::new();
+
+ res.push(Variant::i32(MessageType::RpcCall as i32));
+ res.push(Variant::StringUTF8(self.slot_name.clone()));
+
+ res.append(&mut self.params.clone());
+
+ res.serialize()
+ }
+}
+
+impl Deserialize for RpcCall {
+ fn parse(b: &[std::primitive::u8]) -> Result<(std::primitive::usize, Self), failure::Error> {
+ let (size, mut res) = VariantList::parse(&b)?;
+
+ res.remove(0);
+
+ Ok((
+ size,
+ Self {
+ slot_name: match_variant!(res.remove(0), Variant::StringUTF8),
+ params: res,
+ },
+ ))
+ }
+}
+
+#[derive(Clone, Debug, std::cmp::PartialEq)]
+pub struct InitRequest {
+ class_name: String,
+ object_name: String,
+}
+
+impl Serialize for InitRequest {
+ fn serialize(&self) -> Result<Vec<std::primitive::u8>, failure::Error> {
+ let mut res = VariantList::new();
+
+ res.push(Variant::i32(MessageType::InitRequest as i32));
+ res.push(Variant::StringUTF8(self.class_name.clone()));
+ res.push(Variant::StringUTF8(self.object_name.clone()));
+
+ res.serialize()
+ }
+}
+
+impl Deserialize for InitRequest {
+ fn parse(b: &[std::primitive::u8]) -> Result<(std::primitive::usize, Self), failure::Error> {
+ let (size, mut res) = VariantList::parse(&b)?;
+
+ res.remove(0);
+
+ Ok((
+ size,
+ Self {
+ class_name: match_variant!(res.remove(0), Variant::StringUTF8),
+ object_name: match_variant!(res.remove(0), Variant::StringUTF8),
+ },
+ ))
+ }
+}
+
+#[derive(Clone, Debug, std::cmp::PartialEq)]
+pub struct InitData {
+ class_name: String,
+ object_name: String,
+ init_data: VariantList,
+}
+
+impl Serialize for InitData {
+ fn serialize(&self) -> Result<Vec<std::primitive::u8>, failure::Error> {
+ let mut res = VariantList::new();
+
+ res.push(Variant::i32(MessageType::InitData as i32));
+ res.push(Variant::StringUTF8(self.class_name.clone()));
+ res.push(Variant::StringUTF8(self.object_name.clone()));
+
+ res.append(&mut self.init_data.clone());
+
+ res.serialize()
+ }
+}
+
+impl Deserialize for InitData {
+ fn parse(b: &[std::primitive::u8]) -> Result<(std::primitive::usize, Self), failure::Error> {
+ let (size, mut res) = VariantList::parse(&b)?;
+
+ res.remove(0);
+
+ Ok((
+ size,
+ Self {
+ class_name: match_variant!(res.remove(0), Variant::StringUTF8),
+ object_name: match_variant!(res.remove(0), Variant::StringUTF8),
+ init_data: res,
+ },
+ ))
+ }
+}
+
+#[derive(Clone, Debug, std::cmp::PartialEq)]
+pub struct HeartBeat {
+ timestamp: DateTime,
+}
+
+impl Serialize for HeartBeat {
+ fn serialize(&self) -> Result<Vec<std::primitive::u8>, failure::Error> {
+ let mut res = VariantList::new();
+
+ res.push(Variant::i32(MessageType::HeartBeat as i32));
+ res.push(Variant::DateTime(self.timestamp.clone()));
+
+ res.serialize()
+ }
+}
+
+impl Deserialize for HeartBeat {
+ fn parse(b: &[std::primitive::u8]) -> Result<(std::primitive::usize, Self), failure::Error> {
+ let (size, mut res) = VariantList::parse(&b)?;
+
+ res.remove(0);
+
+ Ok((
+ size,
+ Self {
+ timestamp: match_variant!(res.remove(0), Variant::DateTime),
+ },
+ ))
+ }
+}
+
+#[derive(Clone, Debug, std::cmp::PartialEq)]
+pub struct HeartBeatReply {
+ timestamp: DateTime,
+}
+
+impl Serialize for HeartBeatReply {
+ fn serialize(&self) -> Result<Vec<std::primitive::u8>, failure::Error> {
+ let mut res = VariantList::new();
+
+ res.push(Variant::i32(MessageType::HeartBeatReply as i32));
+ res.push(Variant::DateTime(self.timestamp.clone()));
+
+ res.serialize()
+ }
+}
+
+impl Deserialize for HeartBeatReply {
+ fn parse(b: &[std::primitive::u8]) -> Result<(std::primitive::usize, Self), failure::Error> {
+ let (size, mut res) = VariantList::parse(&b)?;
+
+ res.remove(0);
+
+ Ok((
+ size,
+ Self {
+ timestamp: match_variant!(res.remove(0), Variant::DateTime),
+ },
+ ))
+ }
+}
diff --git a/src/primitive/datetime.rs b/src/primitive/datetime.rs
index e6946b9..cbcdd51 100644
--- a/src/primitive/datetime.rs
+++ b/src/primitive/datetime.rs
@@ -1,107 +1,160 @@
use crate::Deserialize;
use crate::Serialize;
-/// The DateTime struct represents a DateTime as received in IRC
-///
-/// DateTime is, like all other struct based types, serialized sequentially.
-#[derive(Clone, Debug, std::cmp::PartialEq)]
-pub struct DateTime {
- /// Day in Julian calendar, unknown if signed or unsigned
- julian_day: i32,
- /// Milliseconds since start of day
- millis_of_day: i32,
- /// Timezone of DateTime, 0x00 is local, 0x01 is UTC
- zone: u8,
+use time::{OffsetDateTime, PrimitiveDateTime, UtcOffset};
+
+// The DateTime struct represents a DateTime as received in IRC
+//
+// DateTime is, like all other struct based types, serialized sequentially.
+// #[derive(Clone, Debug, std::cmp::PartialEq)]
+// pub struct DateTime {
+// /// Day in Julian calendar, unknown if signed or unsigned
+// julian_day: i32,
+// /// Milliseconds since start of day
+// millis_of_day: i32,
+// /// Timezone of DateTime, 0x00 is local, 0x01 is UTC
+// zone: u8,
+// }
+
+pub type DateTime = OffsetDateTime;
+pub use time::{Date, Time};
+
+/// TimeSpec specifies whether the time is a local time, daylightsaving local time or a form of UTC Offset
+#[repr(i8)]
+#[derive(Copy, Clone, Debug, std::cmp::PartialEq)]
+pub enum TimeSpec {
+ LocalUnknown = -0x01,
+ LocalStandard = 0x00,
+ LocalDST = 0x01,
+ UTC = 0x02,
+ OffsetFromUTC = 0x03,
}
-impl Serialize for DateTime {
- fn serialize(&self) -> Result<Vec<std::primitive::u8>, failure::Error> {
+impl From<i8> for TimeSpec {
+ fn from(val: i8) -> Self {
+ match val {
+ -0x01 => TimeSpec::LocalUnknown,
+ 0x00 => TimeSpec::LocalStandard,
+ 0x01 => TimeSpec::LocalDST,
+ 0x02 => TimeSpec::UTC,
+ 0x03 => TimeSpec::OffsetFromUTC,
+ _ => unimplemented!(),
+ }
+ }
+}
+
+impl Serialize for OffsetDateTime {
+ fn serialize(&self) -> Result<Vec<u8>, failure::Error> {
let mut values: Vec<u8> = Vec::new();
- values.append(&mut i32::serialize(&self.julian_day)?);
- values.append(&mut i32::serialize(&self.millis_of_day)?);
- values.append(&mut u8::serialize(&(self.zone))?);
+ values.extend(i32::serialize(&(self.date().julian_day() as i32))?);
+
+ let time: i32 = {
+ let hour: i32 = self.time().hour() as i32;
+ let minute: i32 = self.time().minute() as i32;
+ let second: i32 = self.time().second() as i32;
+ let milli: i32 = self.time().millisecond() as i32;
+
+ milli + (second * 1000) + (minute * 60000) + (hour * 60 * 60000)
+ };
+
+ values.extend(i32::serialize(&time)?);
+ values.extend(u8::serialize(&(TimeSpec::OffsetFromUTC as u8))?);
+ values.extend(i32::serialize(&self.offset().as_seconds())?);
Ok(values)
}
}
-impl Deserialize for DateTime {
- fn parse(b: &[std::primitive::u8]) -> Result<(std::primitive::usize, Self), failure::Error>
- where
- Self: Sized,
- {
+impl Deserialize for OffsetDateTime {
+ fn parse(b: &[u8]) -> Result<(usize, Self), failure::Error> {
let (_, julian_day) = i32::parse(&b[0..4])?;
let (_, millis_of_day) = i32::parse(&b[4..8])?;
let (_, zone) = u8::parse(&b[8..9])?;
- return Ok((
- 9,
- DateTime {
- julian_day,
- millis_of_day,
- zone,
- },
- ));
- }
-}
+ let mut pos = 9;
+
+ let zone = TimeSpec::from(zone as i8);
+
+ let offset: UtcOffset;
+ match zone {
+ TimeSpec::LocalUnknown | TimeSpec::LocalStandard | TimeSpec::LocalDST => {
+ offset = UtcOffset::current_local_offset()
+ }
+ TimeSpec::UTC => offset = UtcOffset::UTC,
+ TimeSpec::OffsetFromUTC => {
+ let (_, tmp_offset) = i32::parse(&b[9..13])?;
+ pos += 4;
+ offset = UtcOffset::seconds(tmp_offset)
+ }
+ }
-/// The Date struct represents a Date as received in IRC
-///
-/// Date is, like all other struct based types, serialized sequentially.
-#[derive(Clone, Debug, std::cmp::PartialEq)]
-pub struct Date {
- /// Day in Julian calendar, unknown if signed or unsigned
- julian_day: i32,
+ let date = Date::from_julian_day(julian_day as i64);
+
+ let hour = millis_of_day / 60 / 60000;
+ let minute = (millis_of_day - (hour * 60 * 60000)) / 60000;
+ let seconds = (millis_of_day - (hour * 60 * 60000) - (minute * 60000)) / 1000;
+ let millis = millis_of_day - (hour * 60 * 60000) - (minute * 60000) - (seconds * 1000);
+
+ let time =
+ Time::try_from_hms_milli(hour as u8, minute as u8, seconds as u8, millis as u16)?;
+ let primitivedatetime = PrimitiveDateTime::new(date, time);
+ let datetime = primitivedatetime.assume_offset(offset);
+
+ Ok((pos, datetime))
+ }
}
impl Serialize for Date {
fn serialize(&self) -> Result<Vec<std::primitive::u8>, failure::Error> {
let mut values: Vec<u8> = Vec::new();
- values.append(&mut i32::serialize(&self.julian_day)?);
+ values.extend(i32::serialize(&(self.julian_day() as i32))?);
Ok(values)
}
}
impl Deserialize for Date {
- fn parse(b: &[std::primitive::u8]) -> Result<(std::primitive::usize, Self), failure::Error>
- where
- Self: Sized,
- {
+ fn parse(b: &[std::primitive::u8]) -> Result<(std::primitive::usize, Self), failure::Error> {
let (_, julian_day) = i32::parse(&b[0..4])?;
+ let date = Date::from_julian_day(julian_day as i64);
- return Ok((9, Date { julian_day }));
+ Ok((4, date))
}
}
-/// The Time struct represents a Time as received in IRC
-///
-/// Time is, like all other struct based types, serialized sequentially.
-#[derive(Clone, Debug, std::cmp::PartialEq)]
-pub struct Time {
- /// Milliseconds since start of day
- millis_of_day: i32,
-}
-
impl Serialize for Time {
fn serialize(&self) -> Result<Vec<std::primitive::u8>, failure::Error> {
let mut values: Vec<u8> = Vec::new();
- values.append(&mut i32::serialize(&self.millis_of_day)?);
+ let time: i32 = {
+ let hour: i32 = self.hour() as i32;
+ let minute: i32 = self.minute() as i32;
+ let second: i32 = self.second() as i32;
+ let milli: i32 = self.millisecond() as i32;
+
+ milli + (second * 1000) + (minute * 60000) + (hour * 60 * 60000)
+ };
+
+ values.extend(i32::serialize(&time)?);
Ok(values)
}
}
impl Deserialize for Time {
- fn parse(b: &[std::primitive::u8]) -> Result<(std::primitive::usize, Self), failure::Error>
- where
- Self: Sized,
- {
+ fn parse(b: &[std::primitive::u8]) -> Result<(std::primitive::usize, Self), failure::Error> {
let (_, millis_of_day) = i32::parse(&b[0..4])?;
- return Ok((4, Time { millis_of_day }));
+ let hour = millis_of_day / 60 / 60000;
+ let minute = (millis_of_day - (hour * 60 * 60000)) / 60000;
+ let seconds = (millis_of_day - (hour * 60 * 60000) - (minute * 60000)) / 1000;
+ let millis = millis_of_day - (hour * 60 * 60000) - (minute * 60000) - (seconds * 1000);
+
+ let time =
+ Time::try_from_hms_milli(hour as u8, minute as u8, seconds as u8, millis as u16)?;
+
+ Ok((4, time))
}
}
diff --git a/src/primitive/variant.rs b/src/primitive/variant.rs
index 71ddc4a..9242d90 100644
--- a/src/primitive/variant.rs
+++ b/src/primitive/variant.rs
@@ -12,9 +12,7 @@ use crate::{Serialize, SerializeUTF8};
extern crate bytes;
-use crate::primitive::{
- BufferInfo, Date, DateTime, Message, Time, VariantList, VariantMap,
-};
+use crate::primitive::{BufferInfo, Date, DateTime, Message, Time, VariantList, VariantMap};
/// Variant represents the possible types we can receive
///
@@ -204,17 +202,18 @@ impl Deserialize for Variant {
}
primitive::QDATETIME => {
trace!(target: "primitive::Variant", "Parsing Variant: Date");
- let (vlen, value) = Date::parse(&b[len..])?;
- return Ok((len + vlen, Variant::Date(value.clone())));
+ // let (vlen, value) = DateTime::parse(&b[len..])?;
+ let (vlen, value): (usize, DateTime) = Deserialize::parse(&b[len..])?;
+ return Ok((len + vlen, Variant::DateTime(value.clone())));
}
primitive::QDATE => {
trace!(target: "primitive::Variant", "Parsing Variant: Date");
- let (vlen, value) = Date::parse(&b[len..])?;
+ let (vlen, value): (usize, Date) = Deserialize::parse(&b[len..])?;
return Ok((len + vlen, Variant::Date(value.clone())));
}
primitive::QTIME => {
trace!(target: "primitive::Variant", "Parsing Variant: Time");
- let (vlen, value) = Time::parse(&b[len..])?;
+ let (vlen, value): (usize, Time) = Deserialize::parse(&b[len..])?;
return Ok((len + vlen, Variant::Time(value.clone())));
}
primitive::BOOL => {
diff --git a/src/tests/mod.rs b/src/tests/mod.rs
index 16fd124..1e99bfe 100644
--- a/src/tests/mod.rs
+++ b/src/tests/mod.rs
@@ -1,4 +1,5 @@
pub mod base_types;
+pub mod datetime;
#[allow(unused_imports)]
#[allow(unused_macros)]
diff --git a/src/util.rs b/src/util.rs
index 192e270..eeeda4e 100644
--- a/src/util.rs
+++ b/src/util.rs
@@ -3,9 +3,10 @@
/// # Example
///
/// ```
+/// use libquassel::match_variant;
/// use libquassel::primitive::{VariantMap, Variant};
///
-/// let var = Variant::String("test string");
+/// let var = Variant::String("test string".to_string());
/// let result = match_variant!(var, Variant::String);
/// ```
#[macro_export]
@@ -13,7 +14,7 @@ macro_rules! match_variant {
( $values:expr, $x:path ) => {
match &$values {
$x(x) => Ok(x.clone()),
- _ => Err(""),
+ err => Err(err),
}
.unwrap();
};