From 326354d3b4d5e7cefad361206f544a06193e8b72 Mon Sep 17 00:00:00 2001 From: Michael Pfaff Date: Thu, 3 Feb 2022 09:52:53 -0500 Subject: [PATCH] Overhaul (part 1) --- Cargo.toml | 9 +- examples/discord_presence.rs | 22 ++- examples/discord_presence_subscribe.rs | 23 ++- src/client.rs | 75 +++++---- src/connection/base.rs | 11 +- src/connection/manager.rs | 220 ++++++++++++++----------- src/connection/mod.rs | 2 +- src/connection/unix.rs | 36 ++-- src/connection/windows.rs | 6 +- src/error.rs | 48 +++--- src/lib.rs | 14 +- src/models/commands.rs | 5 +- src/models/events.rs | 7 +- src/models/message.rs | 22 +-- src/models/mod.rs | 30 ++-- src/models/payload.rs | 24 ++- src/models/rich_presence.rs | 69 ++++---- src/models/shared.rs | 2 +- src/utils.rs | 1 - 19 files changed, 329 insertions(+), 297 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index d6837e2..2e184f8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,17 +1,12 @@ [package] -authors = ["Patrick Auernig "] +authors = ["Patrick Auernig ", "Michael Pfaff "] name = "discord-rpc-client" description = "A Rust client for Discord RPC." keywords = ["discord", "rpc", "ipc"] license = "MIT" readme = "README.md" repository = "https://gitlab.com/valeth/discord-rpc-client.rs.git" -version = "0.3.0" - -[badges] -travis-ci = { repository = "valeth/discord-rpc-client.rs" } -appveyor = { repository = "valeth/discord-rpc-client.rs", service = "gitlab" } -maintenance = { status = "experimental" } +version = "0.4.0" [dependencies] serde = "^1.0" diff --git a/examples/discord_presence.rs b/examples/discord_presence.rs index 04bb2eb..4d11c26 100644 --- a/examples/discord_presence.rs +++ b/examples/discord_presence.rs @@ -1,16 +1,16 @@ -extern crate simplelog; extern crate discord_rpc_client; +extern crate simplelog; -use std::io; +use discord_rpc_client::{models::Activity, Client as DiscordRPC}; use simplelog::*; -use discord_rpc_client::Client as DiscordRPC; +use std::io; -fn main() { +fn main() -> discord_rpc_client::Result<()> { TermLogger::init(LevelFilter::Debug, Config::default()).unwrap(); let mut drpc = DiscordRPC::new(425407036495495169); - drpc.start(); + drpc.connect()?; loop { let mut buf = String::new(); @@ -23,16 +23,14 @@ fn main() { println!("Failed to clear presence: {}", why); } } else { - if let Err(why) = drpc.set_activity(|a| a - .state(buf) - .assets(|ass| ass - .large_image("ferris_wat") + if let Err(why) = drpc.set_activity(Activity::new().state(buf).assets(|ass| { + ass.large_image("ferris_wat") .large_text("wat.") .small_image("rusting") - .small_text("rusting..."))) - { + .small_text("rusting...") + })) { println!("Failed to set presence: {}", why); } } - }; + } } diff --git a/examples/discord_presence_subscribe.rs b/examples/discord_presence_subscribe.rs index b120033..b3742b8 100644 --- a/examples/discord_presence_subscribe.rs +++ b/examples/discord_presence_subscribe.rs @@ -1,26 +1,21 @@ -extern crate simplelog; extern crate discord_rpc_client; +extern crate simplelog; -use std::{thread, time}; +use discord_rpc_client::{models::Event, Client as DiscordRPC}; use simplelog::*; -use discord_rpc_client::{ - Client as DiscordRPC, - models::Event, -}; +use std::{thread, time}; -fn main() { +fn main() -> discord_rpc_client::Result<()> { TermLogger::init(LevelFilter::Debug, Config::default()).unwrap(); let mut drpc = DiscordRPC::new(425407036495495169); - drpc.start(); + drpc.connect()?; - drpc.subscribe(Event::ActivityJoin, |j| j - .secret("123456")) + drpc.subscribe(Event::ActivityJoin, |j| j.secret("123456")) .expect("Failed to subscribe to event"); - drpc.subscribe(Event::ActivitySpectate, |s| s - .secret("123456")) + drpc.subscribe(Event::ActivitySpectate, |s| s.secret("123456")) .expect("Failed to subscribe to event"); drpc.subscribe(Event::ActivityJoinRequest, |s| s) @@ -29,5 +24,7 @@ fn main() { drpc.unsubscribe(Event::ActivityJoinRequest, |j| j) .expect("Failed to unsubscribe from event"); - loop { thread::sleep(time::Duration::from_millis(500)); } + loop { + thread::sleep(time::Duration::from_millis(500)); + } } diff --git a/src/client.rs b/src/client.rs index acda5a1..72e45d7 100644 --- a/src/client.rs +++ b/src/client.rs @@ -1,27 +1,22 @@ -use serde::{Serialize, de::DeserializeOwned}; +use serde::{de::DeserializeOwned, Serialize}; #[allow(unused)] use serde_json::Value; use connection::Manager as ConnectionManager; -use models::{ - OpCode, - Command, - Event, - payload::Payload, - message::Message, - commands::{SubscriptionArgs, Subscription}, -}; +use error::{Error, Result}; #[cfg(feature = "rich_presence")] use models::rich_presence::{ - SetActivityArgs, - Activity, - SendActivityJoinInviteArgs, - CloseActivityRequestArgs, + Activity, CloseActivityRequestArgs, SendActivityJoinInviteArgs, SetActivityArgs, +}; +use models::{ + commands::{Subscription, SubscriptionArgs}, + message::Message, + payload::Payload, + Command, Event, OpCode, }; -use error::{Result, Error}; - -#[derive(Clone)] +#[derive(Clone, Debug)] +#[repr(transparent)] pub struct Client { connection_manager: ConnectionManager, } @@ -32,30 +27,40 @@ impl Client { Self { connection_manager } } - pub fn start(&mut self) { - self.connection_manager.start(); + pub fn client_id(&self) -> u64 { + self.connection_manager.client_id() + } + + pub fn connect(&mut self) -> Result<()> { + self.connection_manager.connect() + } + + pub fn disconnect(&mut self) { + self.connection_manager.disconnect() } fn execute(&mut self, cmd: Command, args: A, evt: Option) -> Result> - where A: Serialize + Send + Sync, - E: Serialize + DeserializeOwned + Send + Sync + where + A: Serialize + Send + Sync, + E: Serialize + DeserializeOwned + Send + Sync, { - let message = Message::new(OpCode::Frame, Payload::with_nonce(cmd, Some(args), None, evt)); + let message = Message::new( + OpCode::Frame, + Payload::with_nonce(cmd, Some(args), None, evt), + ); self.connection_manager.send(message)?; let Message { payload, .. } = self.connection_manager.recv()?; let response: Payload = serde_json::from_str(&payload)?; match response.evt { Some(Event::Error) => Err(Error::SubscriptionFailed), - _ => Ok(response) + _ => Ok(response), } } #[cfg(feature = "rich_presence")] - pub fn set_activity(&mut self, f: F) -> Result> - where F: FnOnce(Activity) -> Activity - { - self.execute(Command::SetActivity, SetActivityArgs::new(f), None) + pub fn set_activity(&mut self, activity: Activity) -> Result> { + self.execute(Command::SetActivity, SetActivityArgs::new(activity), None) } #[cfg(feature = "rich_presence")] @@ -68,22 +73,32 @@ impl Client { // they are not documented. #[cfg(feature = "rich_presence")] pub fn send_activity_join_invite(&mut self, user_id: u64) -> Result> { - self.execute(Command::SendActivityJoinInvite, SendActivityJoinInviteArgs::new(user_id), None) + self.execute( + Command::SendActivityJoinInvite, + SendActivityJoinInviteArgs::new(user_id), + None, + ) } #[cfg(feature = "rich_presence")] pub fn close_activity_request(&mut self, user_id: u64) -> Result> { - self.execute(Command::CloseActivityRequest, CloseActivityRequestArgs::new(user_id), None) + self.execute( + Command::CloseActivityRequest, + CloseActivityRequestArgs::new(user_id), + None, + ) } pub fn subscribe(&mut self, evt: Event, f: F) -> Result> - where F: FnOnce(SubscriptionArgs) -> SubscriptionArgs + where + F: FnOnce(SubscriptionArgs) -> SubscriptionArgs, { self.execute(Command::Subscribe, f(SubscriptionArgs::new()), Some(evt)) } pub fn unsubscribe(&mut self, evt: Event, f: F) -> Result> - where F: FnOnce(SubscriptionArgs) -> SubscriptionArgs + where + F: FnOnce(SubscriptionArgs) -> SubscriptionArgs, { self.execute(Command::Unsubscribe, f(SubscriptionArgs::new()), Some(evt)) } diff --git a/src/connection/base.rs b/src/connection/base.rs index bc862ba..c09dfd5 100644 --- a/src/connection/base.rs +++ b/src/connection/base.rs @@ -1,17 +1,15 @@ use std::{ - io::{Write, Read, ErrorKind}, + io::{ErrorKind, Read, Write}, marker::Sized, path::PathBuf, - thread, - time, + thread, time, }; use bytes::BytesMut; -use utils; -use models::message::{Message, OpCode}; use error::{Error, Result}; - +use models::message::{Message, OpCode}; +use utils; /// Wait for a non-blocking connection until it's complete. macro_rules! try_until_done { @@ -28,7 +26,6 @@ macro_rules! try_until_done { } } - pub trait Connection: Sized { type Socket: Write + Read; diff --git a/src/connection/manager.rs b/src/connection/manager.rs index bd0013f..445161e 100644 --- a/src/connection/manager.rs +++ b/src/connection/manager.rs @@ -1,138 +1,166 @@ -use std::{ - thread, - sync::{ - Arc, - }, - time, - io::ErrorKind -}; -use crossbeam_channel::{unbounded, Receiver, Sender}; -use parking_lot::Mutex; +use crossbeam_channel::{unbounded, Receiver, Sender, TryRecvError}; +use parking_lot::{RwLock, RwLockUpgradableReadGuard}; +use std::{io::ErrorKind, sync::Arc, thread, time}; -use super::{ - Connection, - SocketConnection, -}; +use super::{Connection, SocketConnection}; +use error::{Error, Result}; use models::Message; -use error::{Result, Error}; - type Tx = Sender; type Rx = Receiver; -#[derive(Clone)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +enum ConnectionState { + Disconnected, + Connecting, + Connected, + Disconnecting, +} + +#[derive(Clone, Debug)] pub struct Manager { - connection: Arc>>, + state: Arc>, client_id: u64, outbound: (Rx, Tx), inbound: (Rx, Tx), - handshake_completed: bool, } impl Manager { pub fn new(client_id: u64) -> Self { - let connection = Arc::new(None); let (sender_o, receiver_o) = unbounded(); let (sender_i, receiver_i) = unbounded(); Self { - connection, + state: Arc::new(RwLock::new(ConnectionState::Disconnected)), client_id, - handshake_completed: false, inbound: (receiver_i, sender_i), outbound: (receiver_o, sender_o), } } - pub fn start(&mut self) { - let manager_inner = self.clone(); - thread::spawn(move || { - send_and_receive_loop(manager_inner); - }); + pub fn client_id(&self) -> u64 { + self.client_id } pub fn send(&self, message: Message) -> Result<()> { - self.outbound.1.send(message).unwrap(); + self.outbound.1.send(message)?; Ok(()) } + // TODO: timeout pub fn recv(&self) -> Result { - let message = self.inbound.0.recv().unwrap(); - Ok(message) - } - - fn connect(&mut self) -> Result<()> { - if self.connection.is_some() { - return Ok(()); - } - - debug!("Connecting"); - - let mut new_connection = SocketConnection::connect()?; - - debug!("Performing handshake"); - new_connection.handshake(self.client_id)?; - debug!("Handshake completed"); - - self.connection = Arc::new(Some(Mutex::new(new_connection))); - - debug!("Connected"); - - Ok(()) - } - - fn disconnect(&mut self) { - self.handshake_completed = false; - self.connection = Arc::new(None); - } - -} - - -fn send_and_receive_loop(mut manager: Manager) { - debug!("Starting sender loop"); - - let mut inbound = manager.inbound.1.clone(); - let outbound = manager.outbound.0.clone(); - - loop { - let connection = manager.connection.clone(); - - match *connection { - Some(ref conn) => { - let mut connection = conn.lock(); - match send_and_receive(&mut *connection, &mut inbound, &outbound) { - Err(Error::IoError(ref err)) if err.kind() == ErrorKind::WouldBlock => (), - Err(Error::IoError(_)) | Err(Error::ConnectionClosed) => manager.disconnect(), - Err(why) => error!("error: {}", why), - _ => (), - } - - thread::sleep(time::Duration::from_millis(500)); - }, - None => { - match manager.connect() { - Err(err) => { - match err { - Error::IoError(ref err) if err.kind() == ErrorKind::ConnectionRefused => (), - why => error!("Failed to connect: {:?}", why), - } - thread::sleep(time::Duration::from_secs(10)); - }, - _ => manager.handshake_completed = true, - } + while *self.state.read() == ConnectionState::Connected { + match self.inbound.0.try_recv() { + Ok(message) => return Ok(message), + Err(TryRecvError::Empty) => {} + Err(TryRecvError::Disconnected) => break } } + + Err(Error::ConnectionClosed) + } + + pub fn connect(&mut self) -> Result<()> { + // check with a read lock first + let state = self.state.upgradable_read(); + match *state { + ConnectionState::Disconnected => { + // no need to double-check after this because the read lock is upgraded instead of replace with a write lock (no possibility of mutation before the upgrade). + let mut state = RwLockUpgradableReadGuard::upgrade(state); + *state = ConnectionState::Connecting; + + // we are in the connecting state and no longer need a lock to prevent the creation of many threads. + drop(state); + + debug!("Connecting"); + + let mut new_connection = SocketConnection::connect()?; + + debug!("Performing handshake"); + new_connection.handshake(self.client_id)?; + debug!("Handshake completed"); + + let mut state = self.state.write(); + match *state { + ConnectionState::Disconnected => panic!("Illegal state: {:?}", *state), + ConnectionState::Connecting => { + *state = ConnectionState::Connected; + + drop(state); + + debug!("Connected"); + + let state_arc = self.state.clone(); + let inbound = self.inbound.1.clone(); + let outbound = self.outbound.0.clone(); + thread::spawn(move || { + send_and_receive_loop(state_arc, inbound, outbound, new_connection); + }); + + Ok(()) + } + ConnectionState::Connected => panic!("Illegal state: {:?}", *state), + ConnectionState::Disconnecting => { + *state = ConnectionState::Disconnected; + + Err(Error::ConnectionClosed) + } + } + } + ConnectionState::Connecting => Ok(()), + ConnectionState::Connected => Ok(()), + ConnectionState::Disconnecting => Err(Error::Busy), + } + } + + pub fn disconnect(&mut self) { + let state = self.state.upgradable_read(); + if *state != ConnectionState::Disconnected { + let mut state = RwLockUpgradableReadGuard::upgrade(state); + *state = ConnectionState::Disconnecting; + } } } -fn send_and_receive(connection: &mut SocketConnection, inbound: &mut Tx, outbound: &Rx) -> Result<()> { - while let Ok(msg) = outbound.try_recv() { - connection.send(msg).expect("Failed to send outgoing data"); +fn send_and_receive_loop( + state: Arc>, + mut inbound: Sender, + outbound: Receiver, + mut conn: SocketConnection, +) { + debug!("Starting sender loop"); + + loop { + match send_and_receive(&mut conn, &mut inbound, &outbound) { + Err(Error::IoError(ref err)) if err.kind() == ErrorKind::WouldBlock => {} + Err(Error::IoError(_)) | Err(Error::ConnectionClosed) => { + let mut state = state.write(); + *state = ConnectionState::Disconnected; + break; + } + Err(e) => error!("send_and_receive error: {}", e), + _ => {} + } + + thread::sleep(time::Duration::from_millis(500)); + } +} + +fn send_and_receive( + connection: &mut SocketConnection, + inbound: &mut Tx, + outbound: &Rx, +) -> Result<()> { + loop { + match outbound.try_recv() { + Ok(msg) => connection.send(msg)?, + Err(TryRecvError::Empty) => break, + Err(TryRecvError::Disconnected) => return Err(Error::ConnectionClosed), + } } let msg = connection.recv()?; - inbound.send(msg).expect("Failed to send received data"); + inbound.send(msg)?; Ok(()) } diff --git a/src/connection/mod.rs b/src/connection/mod.rs index dbdad2f..31a66dc 100644 --- a/src/connection/mod.rs +++ b/src/connection/mod.rs @@ -5,7 +5,7 @@ mod unix; #[cfg(windows)] mod windows; -pub use self::base::Connection as Connection; +pub use self::base::Connection; pub use self::manager::Manager; #[cfg(unix)] pub use self::unix::UnixConnection as SocketConnection; diff --git a/src/connection/unix.rs b/src/connection/unix.rs index a8fd5fb..b45daa6 100644 --- a/src/connection/unix.rs +++ b/src/connection/unix.rs @@ -1,15 +1,9 @@ -use std::{ - time, - path::PathBuf, - env, - os::unix::net::UnixStream, - net::Shutdown, -}; +use std::{env, net::Shutdown, os::unix::net::UnixStream, path::PathBuf, time}; use super::base::Connection; use error::Result; - +#[derive(Debug)] pub struct UnixConnection { socket: UnixStream, } @@ -27,16 +21,23 @@ impl Connection for UnixConnection { } fn ipc_path() -> PathBuf { - let tmp = env::var("XDG_RUNTIME_DIR") - .or_else(|_| env::var("TMPDIR")) - .or_else(|_| { - match env::temp_dir().to_str() { - None => Err("Failed to convert temp_dir"), - Some(tmp) => Ok(tmp.to_string()) + env::var_os("XDG_RUNTIME_DIR") + .map(PathBuf::from) + .map(|mut p| { + // try flatpak dir + p.push("app/com.discordapp.Discord"); + if !p.exists() { + p.pop(); + p.pop(); } + p }) - .unwrap_or("/tmp".to_string()); - PathBuf::from(tmp) + .or_else(|| env::var_os("TMPDIR").map(PathBuf::from)) + .or_else(|| match env::temp_dir().to_str() { + None => None, + Some(tmp) => Some(PathBuf::from(tmp)), + }) + .unwrap_or_else(|| PathBuf::from("/tmp")) } fn socket(&mut self) -> &mut Self::Socket { @@ -46,7 +47,8 @@ impl Connection for UnixConnection { impl Drop for UnixConnection { fn drop(&mut self) { - self.socket.shutdown(Shutdown::Both) + self.socket + .shutdown(Shutdown::Both) .expect("Failed to properly shut down socket"); } } diff --git a/src/connection/windows.rs b/src/connection/windows.rs index 9cc81be..43e966e 100644 --- a/src/connection/windows.rs +++ b/src/connection/windows.rs @@ -1,13 +1,11 @@ -use std::{ - time, - path::PathBuf, -}; +use std::{path::PathBuf, time}; use super::base::Connection; use error::Result; use named_pipe::PipeClient; +#[derive(Debug)] pub struct WindowsConnection { socket: PipeClient, } diff --git a/src/error.rs b/src/error.rs index 16a24f7..a154304 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,16 +1,14 @@ +use crossbeam_channel::SendError; +use serde_json::Error as JsonError; use std::{ error::Error as StdError, + fmt::{self, Display, Formatter}, io::Error as IoError, result::Result as StdResult, sync::mpsc::RecvTimeoutError as ChannelTimeout, - fmt::{ - self, - Display, - Formatter - }, }; -use serde_json::Error as JsonError; +use crate::models::Message; #[derive(Debug)] pub enum Error { @@ -20,42 +18,48 @@ pub enum Error { Conversion, SubscriptionFailed, ConnectionClosed, + ConnectionClosedWhileSending(Message), + Busy, } impl Display for Error { fn fmt(&self, f: &mut Formatter) -> fmt::Result { - f.write_str(self.description()) - } -} - -impl StdError for Error { - fn description(&self) -> &str { - match *self { - Error::Conversion => "Failed to convert values", - Error::SubscriptionFailed => "Failed to subscribe to event", - Error::ConnectionClosed => "Connection closed", - Error::IoError(ref err) => err.description(), - Error::JsonError(ref err) => err.description(), - Error::Timeout(ref err) => err.description(), + match self { + Self::Conversion => f.write_str("Failed to convert values"), + Self::SubscriptionFailed => f.write_str("Failed to subscribe to event"), + Self::ConnectionClosed => f.write_str("Connection closed"), + Self::ConnectionClosedWhileSending(msg) => write!(f, "Connection closed while sending {:?}", msg), + Self::Busy => f.write_str("A resource was busy"), + Self::IoError(err) => write!(f, "{}", err), + Self::JsonError(err) => write!(f, "{}", err), + Self::Timeout(err) => write!(f, "{}", err), } } } +impl StdError for Error {} + impl From for Error { fn from(err: IoError) -> Self { - Error::IoError(err) + Self::IoError(err) } } impl From for Error { fn from(err: JsonError) -> Self { - Error::JsonError(err) + Self::JsonError(err) } } impl From for Error { fn from(err: ChannelTimeout) -> Self { - Error::Timeout(err) + Self::Timeout(err) + } +} + +impl From> for Error { + fn from(err: SendError) -> Self { + Self::ConnectionClosedWhileSending(err.0) } } diff --git a/src/lib.rs b/src/lib.rs index f2d3a9d..3375196 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,20 +6,22 @@ extern crate serde; #[macro_use] extern crate serde_json; extern crate byteorder; -extern crate uuid; extern crate bytes; -extern crate parking_lot; extern crate crossbeam_channel; #[cfg(windows)] extern crate named_pipe; +extern crate parking_lot; +extern crate uuid; #[macro_use] mod macros; -mod error; -mod utils; -mod connection; -pub mod models; pub mod client; +mod connection; +mod error; +pub mod models; +mod utils; pub use client::Client; pub use connection::{Connection, SocketConnection}; + +pub use error::*; diff --git a/src/models/commands.rs b/src/models/commands.rs index 7e0a49e..e5b3cf2 100644 --- a/src/models/commands.rs +++ b/src/models/commands.rs @@ -1,11 +1,10 @@ use super::shared::PartialUser; - -builder!{SubscriptionArgs +builder! {SubscriptionArgs secret: String, // Activity{Join,Spectate} user: PartialUser, // ActivityJoinRequest } -builder!{Subscription +builder! {Subscription evt: String, } diff --git a/src/models/events.rs b/src/models/events.rs index d86f0e2..bad195c 100644 --- a/src/models/events.rs +++ b/src/models/events.rs @@ -1,18 +1,17 @@ use super::shared::PartialUser; - -builder!{ReadyEvent +builder! {ReadyEvent v: u32, config: RpcServerConfiguration, user: PartialUser, } -builder!{ErrorEvent +builder! {ErrorEvent code: u32, message: String, } -builder!{RpcServerConfiguration +builder! {RpcServerConfiguration cdn_host: String, api_endpoint: String, environment: String, diff --git a/src/models/message.rs b/src/models/message.rs index 9bb93b5..28736be 100644 --- a/src/models/message.rs +++ b/src/models/message.rs @@ -1,11 +1,10 @@ -use std::io::{self, Write, Read}; +use std::io::{self, Read, Write}; -use byteorder::{WriteBytesExt, ReadBytesExt, LittleEndian}; -use serde_json; +use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; use serde::Serialize; +use serde_json; -use error::{Result, Error}; - +use error::{Error, Result}; #[derive(Debug, Copy, Clone, PartialEq)] pub enum OpCode { @@ -25,7 +24,7 @@ impl OpCode { 2 => Ok(OpCode::Close), 3 => Ok(OpCode::Ping), 4 => Ok(OpCode::Pong), - _ => Err(Error::Conversion) + _ => Err(Error::Conversion), } } } @@ -38,9 +37,13 @@ pub struct Message { impl Message { pub fn new(opcode: OpCode, payload: T) -> Self - where T: Serialize + where + T: Serialize, { - Self { opcode, payload: serde_json::to_string(&payload).unwrap() } + Self { + opcode, + payload: serde_json::to_string(&payload).unwrap(), + } } pub fn encode(&self) -> Result> { @@ -65,14 +68,13 @@ impl Message { } } - #[cfg(test)] mod tests { use super::*; #[derive(Debug, PartialEq, Serialize, Deserialize)] struct Something { - empty: bool + empty: bool, } #[test] diff --git a/src/models/mod.rs b/src/models/mod.rs index 60eb1dc..1074e90 100644 --- a/src/models/mod.rs +++ b/src/models/mod.rs @@ -1,10 +1,9 @@ -mod shared; -pub mod message; -pub mod payload; pub mod commands; pub mod events; +pub mod message; +pub mod payload; pub mod rich_presence; - +mod shared; #[derive(Debug, PartialEq, Deserialize, Serialize)] #[serde(rename_all = "SCREAMING_SNAKE_CASE")] @@ -34,30 +33,21 @@ pub enum Event { ActivityJoinRequest, } -pub use self::message::{Message, OpCode}; pub use self::commands::*; pub use self::events::*; +pub use self::message::{Message, OpCode}; #[cfg(feature = "rich_presence")] pub use self::rich_presence::*; pub mod prelude { - pub use super::Command; - pub use super::Event; + pub use super::commands::{Subscription, SubscriptionArgs}; + pub use super::events::{ErrorEvent, ReadyEvent}; #[cfg(feature = "rich_presence")] pub use super::rich_presence::{ - SetActivityArgs, - SendActivityJoinInviteArgs, - CloseActivityRequestArgs, - ActivityJoinEvent, - ActivitySpectateEvent, - ActivityJoinRequestEvent - }; - pub use super::commands::{ - SubscriptionArgs, Subscription - }; - pub use super::events::{ - ReadyEvent, - ErrorEvent, + ActivityJoinEvent, ActivityJoinRequestEvent, ActivitySpectateEvent, + CloseActivityRequestArgs, SendActivityJoinInviteArgs, SetActivityArgs, }; + pub use super::Command; + pub use super::Event; } diff --git a/src/models/payload.rs b/src/models/payload.rs index 3e604fa..23cc0c1 100644 --- a/src/models/payload.rs +++ b/src/models/payload.rs @@ -1,17 +1,15 @@ -use std::{ - convert::From, -}; +use std::convert::From; -use serde::{Serialize, de::DeserializeOwned}; +use serde::{de::DeserializeOwned, Serialize}; use serde_json; use super::{Command, Event, Message}; use utils; - #[derive(Debug, PartialEq, Deserialize, Serialize)] pub struct Payload - where T: Serialize +where + T: Serialize, { pub cmd: Command, @@ -29,15 +27,23 @@ pub struct Payload } impl Payload - where T: Serialize +where + T: Serialize, { pub fn with_nonce(cmd: Command, args: Option, data: Option, evt: Option) -> Self { - Self { cmd, args, data, evt, nonce: Some(utils::nonce()) } + Self { + cmd, + args, + data, + evt, + nonce: Some(utils::nonce()), + } } } impl From for Payload - where T: Serialize + DeserializeOwned +where + T: Serialize + DeserializeOwned, { fn from(message: Message) -> Self { serde_json::from_str(&message.payload).unwrap() diff --git a/src/models/rich_presence.rs b/src/models/rich_presence.rs index 05f5221..5bc4d92 100644 --- a/src/models/rich_presence.rs +++ b/src/models/rich_presence.rs @@ -5,7 +5,6 @@ use std::default::Default; use super::shared::PartialUser; use utils; - #[derive(Debug, PartialEq, Deserialize, Serialize)] pub struct SetActivityArgs { pid: u32, @@ -15,16 +14,20 @@ pub struct SetActivityArgs { } impl SetActivityArgs { - pub fn new(f: F) -> Self - where F: FnOnce(Activity) -> Activity - { - Self { pid: utils::pid(), activity: Some(f(Activity::new())) } + pub fn new(activity: Activity) -> Self { + Self { + pid: utils::pid(), + activity: Some(activity), + } } } impl Default for SetActivityArgs { fn default() -> Self { - Self { pid: utils::pid(), activity: None } + Self { + pid: utils::pid(), + activity: None, + } } } @@ -37,24 +40,26 @@ pub type CloseActivityRequestArgs = SendActivityJoinInviteArgs; impl SendActivityJoinInviteArgs { pub fn new(user_id: u64) -> Self { - Self { user_id: user_id.to_string() } + Self { + user_id: user_id.to_string(), + } } } -builder!{ActivityJoinEvent +builder! {ActivityJoinEvent secret: String, } -builder!{ActivitySpectateEvent +builder! {ActivitySpectateEvent secret: String, } -builder!{ActivityJoinRequestEvent +builder! {ActivityJoinRequestEvent user: PartialUser, } - -builder!{Activity +// TODO: remove this stupid builder func pattern. +builder! {Activity state: String, details: String, instance: bool, @@ -64,37 +69,35 @@ builder!{Activity secrets: ActivitySecrets func, } -builder!{ActivityTimestamps +builder! {ActivityTimestamps start: u64, end: u64, } -builder!{ActivityAssets +builder! {ActivityAssets large_image: String, large_text: String, small_image: String, small_text: String, } -builder!{ActivityParty +builder! {ActivityParty id: u32, size: (u32, u32), } -builder!{ActivitySecrets +builder! {ActivitySecrets join: String, spectate: String, game: String alias = "match", } - #[cfg(test)] mod tests { use super::*; use serde_json; - const FULL_JSON: &'static str = -r###"{ + const FULL_JSON: &'static str = r###"{ "state": "rusting", "details": "detailed", "instance": true, @@ -128,21 +131,19 @@ r###"{ .state("rusting") .details("detailed") .instance(true) - .timestamps(|t| t - .start(1000) - .end(2000)) - .assets(|a| a - .large_image("ferris") - .large_text("Ferris") - .small_image("rusting") - .small_text("Rusting...")) - .party(|p| p - .id(1) - .size((3, 6))) - .secrets(|s| s - .join("025ed05c71f639de8bfaa0d679d7c94b2fdce12f") - .spectate("e7eb30d2ee025ed05c71ea495f770b76454ee4e0") - .game("4b2fdce12f639de8bfa7e3591b71a0d679d7c93f")); + .timestamps(|t| t.start(1000).end(2000)) + .assets(|a| { + a.large_image("ferris") + .large_text("Ferris") + .small_image("rusting") + .small_text("Rusting...") + }) + .party(|p| p.id(1).size((3, 6))) + .secrets(|s| { + s.join("025ed05c71f639de8bfaa0d679d7c94b2fdce12f") + .spectate("e7eb30d2ee025ed05c71ea495f770b76454ee4e0") + .game("4b2fdce12f639de8bfa7e3591b71a0d679d7c93f") + }); let json = serde_json::to_string_pretty(&activity).unwrap(); diff --git a/src/models/shared.rs b/src/models/shared.rs index 8fd8b89..ed71894 100644 --- a/src/models/shared.rs +++ b/src/models/shared.rs @@ -1,4 +1,4 @@ -builder!{PartialUser +builder! {PartialUser id: String, username: String, discriminator: String, diff --git a/src/utils.rs b/src/utils.rs index e8b1952..e274fd2 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,6 +1,5 @@ use uuid::Uuid; - #[allow(unused)] pub fn pid() -> u32 { std::process::id()