Overhaul (part 1)
This commit is contained in:
parent
6ea1c93bae
commit
326354d3b4
|
@ -1,17 +1,12 @@
|
||||||
[package]
|
[package]
|
||||||
authors = ["Patrick Auernig <dev.patrick.auernig@gmail.com>"]
|
authors = ["Patrick Auernig <dev.patrick.auernig@gmail.com>", "Michael Pfaff <michael@pfaff.dev>"]
|
||||||
name = "discord-rpc-client"
|
name = "discord-rpc-client"
|
||||||
description = "A Rust client for Discord RPC."
|
description = "A Rust client for Discord RPC."
|
||||||
keywords = ["discord", "rpc", "ipc"]
|
keywords = ["discord", "rpc", "ipc"]
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
repository = "https://gitlab.com/valeth/discord-rpc-client.rs.git"
|
repository = "https://gitlab.com/valeth/discord-rpc-client.rs.git"
|
||||||
version = "0.3.0"
|
version = "0.4.0"
|
||||||
|
|
||||||
[badges]
|
|
||||||
travis-ci = { repository = "valeth/discord-rpc-client.rs" }
|
|
||||||
appveyor = { repository = "valeth/discord-rpc-client.rs", service = "gitlab" }
|
|
||||||
maintenance = { status = "experimental" }
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
serde = "^1.0"
|
serde = "^1.0"
|
||||||
|
|
|
@ -1,16 +1,16 @@
|
||||||
extern crate simplelog;
|
|
||||||
extern crate discord_rpc_client;
|
extern crate discord_rpc_client;
|
||||||
|
extern crate simplelog;
|
||||||
|
|
||||||
use std::io;
|
use discord_rpc_client::{models::Activity, Client as DiscordRPC};
|
||||||
use simplelog::*;
|
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();
|
TermLogger::init(LevelFilter::Debug, Config::default()).unwrap();
|
||||||
|
|
||||||
let mut drpc = DiscordRPC::new(425407036495495169);
|
let mut drpc = DiscordRPC::new(425407036495495169);
|
||||||
|
|
||||||
drpc.start();
|
drpc.connect()?;
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let mut buf = String::new();
|
let mut buf = String::new();
|
||||||
|
@ -23,16 +23,14 @@ fn main() {
|
||||||
println!("Failed to clear presence: {}", why);
|
println!("Failed to clear presence: {}", why);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if let Err(why) = drpc.set_activity(|a| a
|
if let Err(why) = drpc.set_activity(Activity::new().state(buf).assets(|ass| {
|
||||||
.state(buf)
|
ass.large_image("ferris_wat")
|
||||||
.assets(|ass| ass
|
|
||||||
.large_image("ferris_wat")
|
|
||||||
.large_text("wat.")
|
.large_text("wat.")
|
||||||
.small_image("rusting")
|
.small_image("rusting")
|
||||||
.small_text("rusting...")))
|
.small_text("rusting...")
|
||||||
{
|
})) {
|
||||||
println!("Failed to set presence: {}", why);
|
println!("Failed to set presence: {}", why);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,26 +1,21 @@
|
||||||
extern crate simplelog;
|
|
||||||
extern crate discord_rpc_client;
|
extern crate discord_rpc_client;
|
||||||
|
extern crate simplelog;
|
||||||
|
|
||||||
use std::{thread, time};
|
use discord_rpc_client::{models::Event, Client as DiscordRPC};
|
||||||
use simplelog::*;
|
use simplelog::*;
|
||||||
use discord_rpc_client::{
|
use std::{thread, time};
|
||||||
Client as DiscordRPC,
|
|
||||||
models::Event,
|
|
||||||
};
|
|
||||||
|
|
||||||
fn main() {
|
fn main() -> discord_rpc_client::Result<()> {
|
||||||
TermLogger::init(LevelFilter::Debug, Config::default()).unwrap();
|
TermLogger::init(LevelFilter::Debug, Config::default()).unwrap();
|
||||||
|
|
||||||
let mut drpc = DiscordRPC::new(425407036495495169);
|
let mut drpc = DiscordRPC::new(425407036495495169);
|
||||||
|
|
||||||
drpc.start();
|
drpc.connect()?;
|
||||||
|
|
||||||
drpc.subscribe(Event::ActivityJoin, |j| j
|
drpc.subscribe(Event::ActivityJoin, |j| j.secret("123456"))
|
||||||
.secret("123456"))
|
|
||||||
.expect("Failed to subscribe to event");
|
.expect("Failed to subscribe to event");
|
||||||
|
|
||||||
drpc.subscribe(Event::ActivitySpectate, |s| s
|
drpc.subscribe(Event::ActivitySpectate, |s| s.secret("123456"))
|
||||||
.secret("123456"))
|
|
||||||
.expect("Failed to subscribe to event");
|
.expect("Failed to subscribe to event");
|
||||||
|
|
||||||
drpc.subscribe(Event::ActivityJoinRequest, |s| s)
|
drpc.subscribe(Event::ActivityJoinRequest, |s| s)
|
||||||
|
@ -29,5 +24,7 @@ fn main() {
|
||||||
drpc.unsubscribe(Event::ActivityJoinRequest, |j| j)
|
drpc.unsubscribe(Event::ActivityJoinRequest, |j| j)
|
||||||
.expect("Failed to unsubscribe from event");
|
.expect("Failed to unsubscribe from event");
|
||||||
|
|
||||||
loop { thread::sleep(time::Duration::from_millis(500)); }
|
loop {
|
||||||
|
thread::sleep(time::Duration::from_millis(500));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,27 +1,22 @@
|
||||||
use serde::{Serialize, de::DeserializeOwned};
|
use serde::{de::DeserializeOwned, Serialize};
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
|
|
||||||
use connection::Manager as ConnectionManager;
|
use connection::Manager as ConnectionManager;
|
||||||
use models::{
|
use error::{Error, Result};
|
||||||
OpCode,
|
|
||||||
Command,
|
|
||||||
Event,
|
|
||||||
payload::Payload,
|
|
||||||
message::Message,
|
|
||||||
commands::{SubscriptionArgs, Subscription},
|
|
||||||
};
|
|
||||||
#[cfg(feature = "rich_presence")]
|
#[cfg(feature = "rich_presence")]
|
||||||
use models::rich_presence::{
|
use models::rich_presence::{
|
||||||
SetActivityArgs,
|
Activity, CloseActivityRequestArgs, SendActivityJoinInviteArgs, SetActivityArgs,
|
||||||
Activity,
|
};
|
||||||
SendActivityJoinInviteArgs,
|
use models::{
|
||||||
CloseActivityRequestArgs,
|
commands::{Subscription, SubscriptionArgs},
|
||||||
|
message::Message,
|
||||||
|
payload::Payload,
|
||||||
|
Command, Event, OpCode,
|
||||||
};
|
};
|
||||||
use error::{Result, Error};
|
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
#[derive(Clone)]
|
#[repr(transparent)]
|
||||||
pub struct Client {
|
pub struct Client {
|
||||||
connection_manager: ConnectionManager,
|
connection_manager: ConnectionManager,
|
||||||
}
|
}
|
||||||
|
@ -32,30 +27,40 @@ impl Client {
|
||||||
Self { connection_manager }
|
Self { connection_manager }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn start(&mut self) {
|
pub fn client_id(&self) -> u64 {
|
||||||
self.connection_manager.start();
|
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<A, E>(&mut self, cmd: Command, args: A, evt: Option<Event>) -> Result<Payload<E>>
|
fn execute<A, E>(&mut self, cmd: Command, args: A, evt: Option<Event>) -> Result<Payload<E>>
|
||||||
where A: Serialize + Send + Sync,
|
where
|
||||||
E: Serialize + DeserializeOwned + Send + Sync
|
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)?;
|
self.connection_manager.send(message)?;
|
||||||
let Message { payload, .. } = self.connection_manager.recv()?;
|
let Message { payload, .. } = self.connection_manager.recv()?;
|
||||||
let response: Payload<E> = serde_json::from_str(&payload)?;
|
let response: Payload<E> = serde_json::from_str(&payload)?;
|
||||||
|
|
||||||
match response.evt {
|
match response.evt {
|
||||||
Some(Event::Error) => Err(Error::SubscriptionFailed),
|
Some(Event::Error) => Err(Error::SubscriptionFailed),
|
||||||
_ => Ok(response)
|
_ => Ok(response),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "rich_presence")]
|
#[cfg(feature = "rich_presence")]
|
||||||
pub fn set_activity<F>(&mut self, f: F) -> Result<Payload<Activity>>
|
pub fn set_activity(&mut self, activity: Activity) -> Result<Payload<Activity>> {
|
||||||
where F: FnOnce(Activity) -> Activity
|
self.execute(Command::SetActivity, SetActivityArgs::new(activity), None)
|
||||||
{
|
|
||||||
self.execute(Command::SetActivity, SetActivityArgs::new(f), None)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "rich_presence")]
|
#[cfg(feature = "rich_presence")]
|
||||||
|
@ -68,22 +73,32 @@ impl Client {
|
||||||
// they are not documented.
|
// they are not documented.
|
||||||
#[cfg(feature = "rich_presence")]
|
#[cfg(feature = "rich_presence")]
|
||||||
pub fn send_activity_join_invite(&mut self, user_id: u64) -> Result<Payload<Value>> {
|
pub fn send_activity_join_invite(&mut self, user_id: u64) -> Result<Payload<Value>> {
|
||||||
self.execute(Command::SendActivityJoinInvite, SendActivityJoinInviteArgs::new(user_id), None)
|
self.execute(
|
||||||
|
Command::SendActivityJoinInvite,
|
||||||
|
SendActivityJoinInviteArgs::new(user_id),
|
||||||
|
None,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "rich_presence")]
|
#[cfg(feature = "rich_presence")]
|
||||||
pub fn close_activity_request(&mut self, user_id: u64) -> Result<Payload<Value>> {
|
pub fn close_activity_request(&mut self, user_id: u64) -> Result<Payload<Value>> {
|
||||||
self.execute(Command::CloseActivityRequest, CloseActivityRequestArgs::new(user_id), None)
|
self.execute(
|
||||||
|
Command::CloseActivityRequest,
|
||||||
|
CloseActivityRequestArgs::new(user_id),
|
||||||
|
None,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn subscribe<F>(&mut self, evt: Event, f: F) -> Result<Payload<Subscription>>
|
pub fn subscribe<F>(&mut self, evt: Event, f: F) -> Result<Payload<Subscription>>
|
||||||
where F: FnOnce(SubscriptionArgs) -> SubscriptionArgs
|
where
|
||||||
|
F: FnOnce(SubscriptionArgs) -> SubscriptionArgs,
|
||||||
{
|
{
|
||||||
self.execute(Command::Subscribe, f(SubscriptionArgs::new()), Some(evt))
|
self.execute(Command::Subscribe, f(SubscriptionArgs::new()), Some(evt))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn unsubscribe<F>(&mut self, evt: Event, f: F) -> Result<Payload<Subscription>>
|
pub fn unsubscribe<F>(&mut self, evt: Event, f: F) -> Result<Payload<Subscription>>
|
||||||
where F: FnOnce(SubscriptionArgs) -> SubscriptionArgs
|
where
|
||||||
|
F: FnOnce(SubscriptionArgs) -> SubscriptionArgs,
|
||||||
{
|
{
|
||||||
self.execute(Command::Unsubscribe, f(SubscriptionArgs::new()), Some(evt))
|
self.execute(Command::Unsubscribe, f(SubscriptionArgs::new()), Some(evt))
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,17 +1,15 @@
|
||||||
use std::{
|
use std::{
|
||||||
io::{Write, Read, ErrorKind},
|
io::{ErrorKind, Read, Write},
|
||||||
marker::Sized,
|
marker::Sized,
|
||||||
path::PathBuf,
|
path::PathBuf,
|
||||||
thread,
|
thread, time,
|
||||||
time,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use bytes::BytesMut;
|
use bytes::BytesMut;
|
||||||
|
|
||||||
use utils;
|
|
||||||
use models::message::{Message, OpCode};
|
|
||||||
use error::{Error, Result};
|
use error::{Error, Result};
|
||||||
|
use models::message::{Message, OpCode};
|
||||||
|
use utils;
|
||||||
|
|
||||||
/// Wait for a non-blocking connection until it's complete.
|
/// Wait for a non-blocking connection until it's complete.
|
||||||
macro_rules! try_until_done {
|
macro_rules! try_until_done {
|
||||||
|
@ -28,7 +26,6 @@ macro_rules! try_until_done {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub trait Connection: Sized {
|
pub trait Connection: Sized {
|
||||||
type Socket: Write + Read;
|
type Socket: Write + Read;
|
||||||
|
|
||||||
|
|
|
@ -1,138 +1,166 @@
|
||||||
use std::{
|
use crossbeam_channel::{unbounded, Receiver, Sender, TryRecvError};
|
||||||
thread,
|
use parking_lot::{RwLock, RwLockUpgradableReadGuard};
|
||||||
sync::{
|
use std::{io::ErrorKind, sync::Arc, thread, time};
|
||||||
Arc,
|
|
||||||
},
|
|
||||||
time,
|
|
||||||
io::ErrorKind
|
|
||||||
};
|
|
||||||
use crossbeam_channel::{unbounded, Receiver, Sender};
|
|
||||||
use parking_lot::Mutex;
|
|
||||||
|
|
||||||
use super::{
|
use super::{Connection, SocketConnection};
|
||||||
Connection,
|
use error::{Error, Result};
|
||||||
SocketConnection,
|
|
||||||
};
|
|
||||||
use models::Message;
|
use models::Message;
|
||||||
use error::{Result, Error};
|
|
||||||
|
|
||||||
|
|
||||||
type Tx = Sender<Message>;
|
type Tx = Sender<Message>;
|
||||||
type Rx = Receiver<Message>;
|
type Rx = Receiver<Message>;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
|
enum ConnectionState {
|
||||||
|
Disconnected,
|
||||||
|
Connecting,
|
||||||
|
Connected,
|
||||||
|
Disconnecting,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
pub struct Manager {
|
pub struct Manager {
|
||||||
connection: Arc<Option<Mutex<SocketConnection>>>,
|
state: Arc<RwLock<ConnectionState>>,
|
||||||
client_id: u64,
|
client_id: u64,
|
||||||
outbound: (Rx, Tx),
|
outbound: (Rx, Tx),
|
||||||
inbound: (Rx, Tx),
|
inbound: (Rx, Tx),
|
||||||
handshake_completed: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Manager {
|
impl Manager {
|
||||||
pub fn new(client_id: u64) -> Self {
|
pub fn new(client_id: u64) -> Self {
|
||||||
let connection = Arc::new(None);
|
|
||||||
let (sender_o, receiver_o) = unbounded();
|
let (sender_o, receiver_o) = unbounded();
|
||||||
let (sender_i, receiver_i) = unbounded();
|
let (sender_i, receiver_i) = unbounded();
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
connection,
|
state: Arc::new(RwLock::new(ConnectionState::Disconnected)),
|
||||||
client_id,
|
client_id,
|
||||||
handshake_completed: false,
|
|
||||||
inbound: (receiver_i, sender_i),
|
inbound: (receiver_i, sender_i),
|
||||||
outbound: (receiver_o, sender_o),
|
outbound: (receiver_o, sender_o),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn start(&mut self) {
|
pub fn client_id(&self) -> u64 {
|
||||||
let manager_inner = self.clone();
|
self.client_id
|
||||||
thread::spawn(move || {
|
|
||||||
send_and_receive_loop(manager_inner);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn send(&self, message: Message) -> Result<()> {
|
pub fn send(&self, message: Message) -> Result<()> {
|
||||||
self.outbound.1.send(message).unwrap();
|
self.outbound.1.send(message)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: timeout
|
||||||
pub fn recv(&self) -> Result<Message> {
|
pub fn recv(&self) -> Result<Message> {
|
||||||
let message = self.inbound.0.recv().unwrap();
|
while *self.state.read() == ConnectionState::Connected {
|
||||||
Ok(message)
|
match self.inbound.0.try_recv() {
|
||||||
}
|
Ok(message) => return Ok(message),
|
||||||
|
Err(TryRecvError::Empty) => {}
|
||||||
fn connect(&mut self) -> Result<()> {
|
Err(TryRecvError::Disconnected) => break
|
||||||
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,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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<()> {
|
fn send_and_receive_loop(
|
||||||
while let Ok(msg) = outbound.try_recv() {
|
state: Arc<RwLock<ConnectionState>>,
|
||||||
connection.send(msg).expect("Failed to send outgoing data");
|
mut inbound: Sender<Message>,
|
||||||
|
outbound: Receiver<Message>,
|
||||||
|
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()?;
|
let msg = connection.recv()?;
|
||||||
inbound.send(msg).expect("Failed to send received data");
|
inbound.send(msg)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@ mod unix;
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
mod windows;
|
mod windows;
|
||||||
|
|
||||||
pub use self::base::Connection as Connection;
|
pub use self::base::Connection;
|
||||||
pub use self::manager::Manager;
|
pub use self::manager::Manager;
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
pub use self::unix::UnixConnection as SocketConnection;
|
pub use self::unix::UnixConnection as SocketConnection;
|
||||||
|
|
|
@ -1,15 +1,9 @@
|
||||||
use std::{
|
use std::{env, net::Shutdown, os::unix::net::UnixStream, path::PathBuf, time};
|
||||||
time,
|
|
||||||
path::PathBuf,
|
|
||||||
env,
|
|
||||||
os::unix::net::UnixStream,
|
|
||||||
net::Shutdown,
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::base::Connection;
|
use super::base::Connection;
|
||||||
use error::Result;
|
use error::Result;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct UnixConnection {
|
pub struct UnixConnection {
|
||||||
socket: UnixStream,
|
socket: UnixStream,
|
||||||
}
|
}
|
||||||
|
@ -27,16 +21,23 @@ impl Connection for UnixConnection {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ipc_path() -> PathBuf {
|
fn ipc_path() -> PathBuf {
|
||||||
let tmp = env::var("XDG_RUNTIME_DIR")
|
env::var_os("XDG_RUNTIME_DIR")
|
||||||
.or_else(|_| env::var("TMPDIR"))
|
.map(PathBuf::from)
|
||||||
.or_else(|_| {
|
.map(|mut p| {
|
||||||
match env::temp_dir().to_str() {
|
// try flatpak dir
|
||||||
None => Err("Failed to convert temp_dir"),
|
p.push("app/com.discordapp.Discord");
|
||||||
Some(tmp) => Ok(tmp.to_string())
|
if !p.exists() {
|
||||||
|
p.pop();
|
||||||
|
p.pop();
|
||||||
}
|
}
|
||||||
|
p
|
||||||
})
|
})
|
||||||
.unwrap_or("/tmp".to_string());
|
.or_else(|| env::var_os("TMPDIR").map(PathBuf::from))
|
||||||
PathBuf::from(tmp)
|
.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 {
|
fn socket(&mut self) -> &mut Self::Socket {
|
||||||
|
@ -46,7 +47,8 @@ impl Connection for UnixConnection {
|
||||||
|
|
||||||
impl Drop for UnixConnection {
|
impl Drop for UnixConnection {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
self.socket.shutdown(Shutdown::Both)
|
self.socket
|
||||||
|
.shutdown(Shutdown::Both)
|
||||||
.expect("Failed to properly shut down socket");
|
.expect("Failed to properly shut down socket");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,11 @@
|
||||||
use std::{
|
use std::{path::PathBuf, time};
|
||||||
time,
|
|
||||||
path::PathBuf,
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::base::Connection;
|
use super::base::Connection;
|
||||||
use error::Result;
|
use error::Result;
|
||||||
|
|
||||||
use named_pipe::PipeClient;
|
use named_pipe::PipeClient;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct WindowsConnection {
|
pub struct WindowsConnection {
|
||||||
socket: PipeClient,
|
socket: PipeClient,
|
||||||
}
|
}
|
||||||
|
|
48
src/error.rs
48
src/error.rs
|
@ -1,16 +1,14 @@
|
||||||
|
use crossbeam_channel::SendError;
|
||||||
|
use serde_json::Error as JsonError;
|
||||||
use std::{
|
use std::{
|
||||||
error::Error as StdError,
|
error::Error as StdError,
|
||||||
|
fmt::{self, Display, Formatter},
|
||||||
io::Error as IoError,
|
io::Error as IoError,
|
||||||
result::Result as StdResult,
|
result::Result as StdResult,
|
||||||
sync::mpsc::RecvTimeoutError as ChannelTimeout,
|
sync::mpsc::RecvTimeoutError as ChannelTimeout,
|
||||||
fmt::{
|
|
||||||
self,
|
|
||||||
Display,
|
|
||||||
Formatter
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
use serde_json::Error as JsonError;
|
|
||||||
|
|
||||||
|
use crate::models::Message;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
|
@ -20,42 +18,48 @@ pub enum Error {
|
||||||
Conversion,
|
Conversion,
|
||||||
SubscriptionFailed,
|
SubscriptionFailed,
|
||||||
ConnectionClosed,
|
ConnectionClosed,
|
||||||
|
ConnectionClosedWhileSending(Message),
|
||||||
|
Busy,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for Error {
|
impl Display for Error {
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
f.write_str(self.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"),
|
||||||
impl StdError for Error {
|
Self::ConnectionClosedWhileSending(msg) => write!(f, "Connection closed while sending {:?}", msg),
|
||||||
fn description(&self) -> &str {
|
Self::Busy => f.write_str("A resource was busy"),
|
||||||
match *self {
|
Self::IoError(err) => write!(f, "{}", err),
|
||||||
Error::Conversion => "Failed to convert values",
|
Self::JsonError(err) => write!(f, "{}", err),
|
||||||
Error::SubscriptionFailed => "Failed to subscribe to event",
|
Self::Timeout(err) => write!(f, "{}", err),
|
||||||
Error::ConnectionClosed => "Connection closed",
|
|
||||||
Error::IoError(ref err) => err.description(),
|
|
||||||
Error::JsonError(ref err) => err.description(),
|
|
||||||
Error::Timeout(ref err) => err.description(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl StdError for Error {}
|
||||||
|
|
||||||
impl From<IoError> for Error {
|
impl From<IoError> for Error {
|
||||||
fn from(err: IoError) -> Self {
|
fn from(err: IoError) -> Self {
|
||||||
Error::IoError(err)
|
Self::IoError(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<JsonError> for Error {
|
impl From<JsonError> for Error {
|
||||||
fn from(err: JsonError) -> Self {
|
fn from(err: JsonError) -> Self {
|
||||||
Error::JsonError(err)
|
Self::JsonError(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<ChannelTimeout> for Error {
|
impl From<ChannelTimeout> for Error {
|
||||||
fn from(err: ChannelTimeout) -> Self {
|
fn from(err: ChannelTimeout) -> Self {
|
||||||
Error::Timeout(err)
|
Self::Timeout(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<SendError<Message>> for Error {
|
||||||
|
fn from(err: SendError<Message>) -> Self {
|
||||||
|
Self::ConnectionClosedWhileSending(err.0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
14
src/lib.rs
14
src/lib.rs
|
@ -6,20 +6,22 @@ extern crate serde;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate serde_json;
|
extern crate serde_json;
|
||||||
extern crate byteorder;
|
extern crate byteorder;
|
||||||
extern crate uuid;
|
|
||||||
extern crate bytes;
|
extern crate bytes;
|
||||||
extern crate parking_lot;
|
|
||||||
extern crate crossbeam_channel;
|
extern crate crossbeam_channel;
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
extern crate named_pipe;
|
extern crate named_pipe;
|
||||||
|
extern crate parking_lot;
|
||||||
|
extern crate uuid;
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
mod macros;
|
mod macros;
|
||||||
mod error;
|
|
||||||
mod utils;
|
|
||||||
mod connection;
|
|
||||||
pub mod models;
|
|
||||||
pub mod client;
|
pub mod client;
|
||||||
|
mod connection;
|
||||||
|
mod error;
|
||||||
|
pub mod models;
|
||||||
|
mod utils;
|
||||||
|
|
||||||
pub use client::Client;
|
pub use client::Client;
|
||||||
pub use connection::{Connection, SocketConnection};
|
pub use connection::{Connection, SocketConnection};
|
||||||
|
|
||||||
|
pub use error::*;
|
||||||
|
|
|
@ -1,11 +1,10 @@
|
||||||
use super::shared::PartialUser;
|
use super::shared::PartialUser;
|
||||||
|
|
||||||
|
builder! {SubscriptionArgs
|
||||||
builder!{SubscriptionArgs
|
|
||||||
secret: String, // Activity{Join,Spectate}
|
secret: String, // Activity{Join,Spectate}
|
||||||
user: PartialUser, // ActivityJoinRequest
|
user: PartialUser, // ActivityJoinRequest
|
||||||
}
|
}
|
||||||
|
|
||||||
builder!{Subscription
|
builder! {Subscription
|
||||||
evt: String,
|
evt: String,
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,18 +1,17 @@
|
||||||
use super::shared::PartialUser;
|
use super::shared::PartialUser;
|
||||||
|
|
||||||
|
builder! {ReadyEvent
|
||||||
builder!{ReadyEvent
|
|
||||||
v: u32,
|
v: u32,
|
||||||
config: RpcServerConfiguration,
|
config: RpcServerConfiguration,
|
||||||
user: PartialUser,
|
user: PartialUser,
|
||||||
}
|
}
|
||||||
|
|
||||||
builder!{ErrorEvent
|
builder! {ErrorEvent
|
||||||
code: u32,
|
code: u32,
|
||||||
message: String,
|
message: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
builder!{RpcServerConfiguration
|
builder! {RpcServerConfiguration
|
||||||
cdn_host: String,
|
cdn_host: String,
|
||||||
api_endpoint: String,
|
api_endpoint: String,
|
||||||
environment: String,
|
environment: String,
|
||||||
|
|
|
@ -1,11 +1,10 @@
|
||||||
use std::io::{self, Write, Read};
|
use std::io::{self, Read, Write};
|
||||||
|
|
||||||
use byteorder::{WriteBytesExt, ReadBytesExt, LittleEndian};
|
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
|
||||||
use serde_json;
|
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
use serde_json;
|
||||||
|
|
||||||
use error::{Result, Error};
|
use error::{Error, Result};
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||||
pub enum OpCode {
|
pub enum OpCode {
|
||||||
|
@ -25,7 +24,7 @@ impl OpCode {
|
||||||
2 => Ok(OpCode::Close),
|
2 => Ok(OpCode::Close),
|
||||||
3 => Ok(OpCode::Ping),
|
3 => Ok(OpCode::Ping),
|
||||||
4 => Ok(OpCode::Pong),
|
4 => Ok(OpCode::Pong),
|
||||||
_ => Err(Error::Conversion)
|
_ => Err(Error::Conversion),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -38,9 +37,13 @@ pub struct Message {
|
||||||
|
|
||||||
impl Message {
|
impl Message {
|
||||||
pub fn new<T>(opcode: OpCode, payload: T) -> Self
|
pub fn new<T>(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<Vec<u8>> {
|
pub fn encode(&self) -> Result<Vec<u8>> {
|
||||||
|
@ -65,14 +68,13 @@ impl Message {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Serialize, Deserialize)]
|
#[derive(Debug, PartialEq, Serialize, Deserialize)]
|
||||||
struct Something {
|
struct Something {
|
||||||
empty: bool
|
empty: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
@ -1,10 +1,9 @@
|
||||||
mod shared;
|
|
||||||
pub mod message;
|
|
||||||
pub mod payload;
|
|
||||||
pub mod commands;
|
pub mod commands;
|
||||||
pub mod events;
|
pub mod events;
|
||||||
|
pub mod message;
|
||||||
|
pub mod payload;
|
||||||
pub mod rich_presence;
|
pub mod rich_presence;
|
||||||
|
mod shared;
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Deserialize, Serialize)]
|
#[derive(Debug, PartialEq, Deserialize, Serialize)]
|
||||||
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
|
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
|
||||||
|
@ -34,30 +33,21 @@ pub enum Event {
|
||||||
ActivityJoinRequest,
|
ActivityJoinRequest,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub use self::message::{Message, OpCode};
|
|
||||||
pub use self::commands::*;
|
pub use self::commands::*;
|
||||||
pub use self::events::*;
|
pub use self::events::*;
|
||||||
|
pub use self::message::{Message, OpCode};
|
||||||
|
|
||||||
#[cfg(feature = "rich_presence")]
|
#[cfg(feature = "rich_presence")]
|
||||||
pub use self::rich_presence::*;
|
pub use self::rich_presence::*;
|
||||||
|
|
||||||
pub mod prelude {
|
pub mod prelude {
|
||||||
pub use super::Command;
|
pub use super::commands::{Subscription, SubscriptionArgs};
|
||||||
pub use super::Event;
|
pub use super::events::{ErrorEvent, ReadyEvent};
|
||||||
#[cfg(feature = "rich_presence")]
|
#[cfg(feature = "rich_presence")]
|
||||||
pub use super::rich_presence::{
|
pub use super::rich_presence::{
|
||||||
SetActivityArgs,
|
ActivityJoinEvent, ActivityJoinRequestEvent, ActivitySpectateEvent,
|
||||||
SendActivityJoinInviteArgs,
|
CloseActivityRequestArgs, SendActivityJoinInviteArgs, SetActivityArgs,
|
||||||
CloseActivityRequestArgs,
|
|
||||||
ActivityJoinEvent,
|
|
||||||
ActivitySpectateEvent,
|
|
||||||
ActivityJoinRequestEvent
|
|
||||||
};
|
|
||||||
pub use super::commands::{
|
|
||||||
SubscriptionArgs, Subscription
|
|
||||||
};
|
|
||||||
pub use super::events::{
|
|
||||||
ReadyEvent,
|
|
||||||
ErrorEvent,
|
|
||||||
};
|
};
|
||||||
|
pub use super::Command;
|
||||||
|
pub use super::Event;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,17 +1,15 @@
|
||||||
use std::{
|
use std::convert::From;
|
||||||
convert::From,
|
|
||||||
};
|
|
||||||
|
|
||||||
use serde::{Serialize, de::DeserializeOwned};
|
use serde::{de::DeserializeOwned, Serialize};
|
||||||
use serde_json;
|
use serde_json;
|
||||||
|
|
||||||
use super::{Command, Event, Message};
|
use super::{Command, Event, Message};
|
||||||
use utils;
|
use utils;
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Deserialize, Serialize)]
|
#[derive(Debug, PartialEq, Deserialize, Serialize)]
|
||||||
pub struct Payload<T>
|
pub struct Payload<T>
|
||||||
where T: Serialize
|
where
|
||||||
|
T: Serialize,
|
||||||
{
|
{
|
||||||
pub cmd: Command,
|
pub cmd: Command,
|
||||||
|
|
||||||
|
@ -29,15 +27,23 @@ pub struct Payload<T>
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Payload<T>
|
impl<T> Payload<T>
|
||||||
where T: Serialize
|
where
|
||||||
|
T: Serialize,
|
||||||
{
|
{
|
||||||
pub fn with_nonce(cmd: Command, args: Option<T>, data: Option<T>, evt: Option<Event>) -> Self {
|
pub fn with_nonce(cmd: Command, args: Option<T>, data: Option<T>, evt: Option<Event>) -> Self {
|
||||||
Self { cmd, args, data, evt, nonce: Some(utils::nonce()) }
|
Self {
|
||||||
|
cmd,
|
||||||
|
args,
|
||||||
|
data,
|
||||||
|
evt,
|
||||||
|
nonce: Some(utils::nonce()),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> From<Message> for Payload<T>
|
impl<T> From<Message> for Payload<T>
|
||||||
where T: Serialize + DeserializeOwned
|
where
|
||||||
|
T: Serialize + DeserializeOwned,
|
||||||
{
|
{
|
||||||
fn from(message: Message) -> Self {
|
fn from(message: Message) -> Self {
|
||||||
serde_json::from_str(&message.payload).unwrap()
|
serde_json::from_str(&message.payload).unwrap()
|
||||||
|
|
|
@ -5,7 +5,6 @@ use std::default::Default;
|
||||||
use super::shared::PartialUser;
|
use super::shared::PartialUser;
|
||||||
use utils;
|
use utils;
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Deserialize, Serialize)]
|
#[derive(Debug, PartialEq, Deserialize, Serialize)]
|
||||||
pub struct SetActivityArgs {
|
pub struct SetActivityArgs {
|
||||||
pid: u32,
|
pid: u32,
|
||||||
|
@ -15,16 +14,20 @@ pub struct SetActivityArgs {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SetActivityArgs {
|
impl SetActivityArgs {
|
||||||
pub fn new<F>(f: F) -> Self
|
pub fn new(activity: Activity) -> Self {
|
||||||
where F: FnOnce(Activity) -> Activity
|
Self {
|
||||||
{
|
pid: utils::pid(),
|
||||||
Self { pid: utils::pid(), activity: Some(f(Activity::new())) }
|
activity: Some(activity),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for SetActivityArgs {
|
impl Default for SetActivityArgs {
|
||||||
fn default() -> Self {
|
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 {
|
impl SendActivityJoinInviteArgs {
|
||||||
pub fn new(user_id: u64) -> Self {
|
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,
|
secret: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
builder!{ActivitySpectateEvent
|
builder! {ActivitySpectateEvent
|
||||||
secret: String,
|
secret: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
builder!{ActivityJoinRequestEvent
|
builder! {ActivityJoinRequestEvent
|
||||||
user: PartialUser,
|
user: PartialUser,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: remove this stupid builder func pattern.
|
||||||
builder!{Activity
|
builder! {Activity
|
||||||
state: String,
|
state: String,
|
||||||
details: String,
|
details: String,
|
||||||
instance: bool,
|
instance: bool,
|
||||||
|
@ -64,37 +69,35 @@ builder!{Activity
|
||||||
secrets: ActivitySecrets func,
|
secrets: ActivitySecrets func,
|
||||||
}
|
}
|
||||||
|
|
||||||
builder!{ActivityTimestamps
|
builder! {ActivityTimestamps
|
||||||
start: u64,
|
start: u64,
|
||||||
end: u64,
|
end: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
builder!{ActivityAssets
|
builder! {ActivityAssets
|
||||||
large_image: String,
|
large_image: String,
|
||||||
large_text: String,
|
large_text: String,
|
||||||
small_image: String,
|
small_image: String,
|
||||||
small_text: String,
|
small_text: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
builder!{ActivityParty
|
builder! {ActivityParty
|
||||||
id: u32,
|
id: u32,
|
||||||
size: (u32, u32),
|
size: (u32, u32),
|
||||||
}
|
}
|
||||||
|
|
||||||
builder!{ActivitySecrets
|
builder! {ActivitySecrets
|
||||||
join: String,
|
join: String,
|
||||||
spectate: String,
|
spectate: String,
|
||||||
game: String alias = "match",
|
game: String alias = "match",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use serde_json;
|
use serde_json;
|
||||||
|
|
||||||
const FULL_JSON: &'static str =
|
const FULL_JSON: &'static str = r###"{
|
||||||
r###"{
|
|
||||||
"state": "rusting",
|
"state": "rusting",
|
||||||
"details": "detailed",
|
"details": "detailed",
|
||||||
"instance": true,
|
"instance": true,
|
||||||
|
@ -128,21 +131,19 @@ r###"{
|
||||||
.state("rusting")
|
.state("rusting")
|
||||||
.details("detailed")
|
.details("detailed")
|
||||||
.instance(true)
|
.instance(true)
|
||||||
.timestamps(|t| t
|
.timestamps(|t| t.start(1000).end(2000))
|
||||||
.start(1000)
|
.assets(|a| {
|
||||||
.end(2000))
|
a.large_image("ferris")
|
||||||
.assets(|a| a
|
.large_text("Ferris")
|
||||||
.large_image("ferris")
|
.small_image("rusting")
|
||||||
.large_text("Ferris")
|
.small_text("Rusting...")
|
||||||
.small_image("rusting")
|
})
|
||||||
.small_text("Rusting..."))
|
.party(|p| p.id(1).size((3, 6)))
|
||||||
.party(|p| p
|
.secrets(|s| {
|
||||||
.id(1)
|
s.join("025ed05c71f639de8bfaa0d679d7c94b2fdce12f")
|
||||||
.size((3, 6)))
|
.spectate("e7eb30d2ee025ed05c71ea495f770b76454ee4e0")
|
||||||
.secrets(|s| s
|
.game("4b2fdce12f639de8bfa7e3591b71a0d679d7c93f")
|
||||||
.join("025ed05c71f639de8bfaa0d679d7c94b2fdce12f")
|
});
|
||||||
.spectate("e7eb30d2ee025ed05c71ea495f770b76454ee4e0")
|
|
||||||
.game("4b2fdce12f639de8bfa7e3591b71a0d679d7c93f"));
|
|
||||||
|
|
||||||
let json = serde_json::to_string_pretty(&activity).unwrap();
|
let json = serde_json::to_string_pretty(&activity).unwrap();
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
builder!{PartialUser
|
builder! {PartialUser
|
||||||
id: String,
|
id: String,
|
||||||
username: String,
|
username: String,
|
||||||
discriminator: String,
|
discriminator: String,
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
pub fn pid() -> u32 {
|
pub fn pid() -> u32 {
|
||||||
std::process::id()
|
std::process::id()
|
||||||
|
|
Loading…
Reference in New Issue