Skip to content

Commit

Permalink
docs(rust): document crucial parts of the codebase
Browse files Browse the repository at this point in the history
  • Loading branch information
vyfor committed Dec 6, 2024
1 parent d55a6b1 commit 7f217a7
Show file tree
Hide file tree
Showing 14 changed files with 189 additions and 5 deletions.
23 changes: 20 additions & 3 deletions src/cord.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,16 @@ use crate::{
},
};

/// Core application managing configuration, sessions, IPC with Discord, and logging.
///
/// # Fields
/// * `config`: Configuration settings.
/// * `session_manager`: Manages user sessions.
/// * `rich_client`: Handles communication with Discord.
/// * `pipe`: Server-side communication pipe.
/// * `tx`, `rx`: Channels for message passing.
/// * `logger`: Logs application events.
/// * `_lock`: Ensures single instance operation.
pub struct Cord {
pub config: Config,
pub session_manager: Arc<SessionManager>,
Expand All @@ -39,6 +49,7 @@ pub struct Cord {
}

impl Cord {
/// Initializes the Cord application.
pub fn new(config: Config) -> crate::Result<Self> {
let lock = ServerLock::new()?;

Expand All @@ -60,6 +71,7 @@ impl Cord {
})
}

/// Runs the application.
pub fn run(&mut self) -> crate::Result<()> {
self.start_rpc()?;
self.pipe.start()?;
Expand All @@ -68,7 +80,8 @@ impl Cord {
Ok(())
}

fn start_event_loop(&mut self) -> crate::Result<()> {
/// Starts the event loop.
pub fn start_event_loop(&mut self) -> crate::Result<()> {
loop {
if let Ok(msg) = self
.rx
Expand All @@ -87,7 +100,8 @@ impl Cord {
Ok(())
}

fn start_rpc(&self) -> crate::Result<()> {
/// Starts RPC with Discord.
pub fn start_rpc(&self) -> crate::Result<()> {
self.rich_client.handshake()?;
let rich_client = self.rich_client.clone();
let tx = self.tx.clone();
Expand All @@ -103,7 +117,8 @@ impl Cord {
Ok(())
}

fn cleanup(&mut self) {
/// Cleans up before shutdown.
pub fn cleanup(&mut self) {
if let Some(client) = Arc::get_mut(&mut self.rich_client) {
client.close();
}
Expand All @@ -112,13 +127,15 @@ impl Cord {
}
}

/// Manages application settings required for initialization.
pub struct Config {
pub pipe_name: String,
pub client_id: u64,
pub timeout: u64,
}

impl Config {
/// Creates a new configuration.
pub fn new(pipe_name: String, client_id: u64, timeout: u64) -> Self {
Self {
pipe_name,
Expand Down
12 changes: 12 additions & 0 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,33 @@ use std::string::ParseError;
use crate::cli::error::CliError;
use crate::protocol::error::ProtocolError;

/// Enumerates error types: IO, parsing, protocol, CLI, and others.
#[derive(Debug)]
pub enum CordErrorKind {
/// Errors related to input/output operations.
Io,
/// Errors related to parsing.
Parse,
/// Errors related to protocol operations.
Protocol,
/// Errors related to CLI operations.
Cli,
/// Other unspecified errors.
Other,
}

/// Represents detailed application errors.
///
/// The `CordError` struct encapsulates an error, providing detailed information
/// about the error kind and its source.
#[derive(Debug)]
pub struct CordError {
kind: CordErrorKind,
source: Box<dyn Error + Send + Sync + 'static>,
}

impl CordError {
/// Creates a new `CordError` instance.
pub fn new<E>(kind: CordErrorKind, error: E) -> Self
where
E: Into<Box<dyn Error + Send + Sync + 'static>>,
Expand Down Expand Up @@ -91,4 +102,5 @@ impl From<&str> for CordError {
}
}

/// Alias for `std::result::Result<T, CordError>`.
pub type Result<T> = std::result::Result<T, CordError>;
15 changes: 15 additions & 0 deletions src/ipc/discord/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,13 @@ use crate::protocol::json::Json;
use std::io::{Read, Write};
use std::sync::atomic::AtomicBool;

/// Manages the connection to Discord for sending and receiving data.
///
/// # Fields
/// * `client_id`: The ID of the Discord client.
/// * `pipe`: The communication pipe (platform-specific).
/// * `pid`: Process ID.
/// * `is_ready`: Indicates if the client is ready.
pub struct RichClient {
pub client_id: u64,
#[cfg(target_os = "windows")]
Expand All @@ -14,12 +21,16 @@ pub struct RichClient {
pub is_ready: AtomicBool,
}

/// Defines methods for connecting and closing the client.
pub trait Connection {
/// Connects to Discord using the given client ID.
fn connect(client_id: u64) -> crate::Result<RichClient>;
/// Closes the connection to Discord.
fn close(&mut self);
}

impl RichClient {
/// Sends data to Discord.
pub fn write(&self, opcode: u32, data: Option<&[u8]>) -> crate::Result<()> {
self.pipe
.as_ref()
Expand All @@ -38,6 +49,7 @@ impl RichClient {
})
}

/// Receives data from Discord.
pub fn read(&self) -> crate::Result<Vec<u8>> {
self.pipe
.as_ref()
Expand All @@ -51,20 +63,23 @@ impl RichClient {
})
}

/// Establishes a connection with Discord.
pub fn handshake(&self) -> crate::Result<()> {
self.write(
0,
Some(format!("{{\"v\": 1,\"client_id\":\"{}\"}}", self.client_id).as_bytes()),
)
}

/// Updates the client's rich presence.
pub fn update(&self, packet: &Packet) -> crate::Result<()> {
let encoded = Json::serialize(packet)?;
self.write(1, Some(encoded.as_bytes()))?;

Ok(())
}

/// Clears the current rich presence.
pub fn clear(&self) -> crate::Result<()> {
let packet = Packet::empty();
let encoded = Json::serialize(&packet)?;
Expand Down
13 changes: 13 additions & 0 deletions src/ipc/discord/platform/unix.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,19 @@ use std::os::unix::net::UnixStream;
use crate::ipc::discord::client::{Connection, RichClient};

impl Connection for RichClient {
/// Pipe path can be in any of the following directories:
/// * `XDG_RUNTIME_DIR`
/// * `TMPDIR`
/// * `TMP`
/// * `TEMP`
/// * `/tmp`
///
/// Followed by:
/// * `/app/com.discordapp.Discord` - for flatpak
/// * `/snap.discord` - for snap
///
/// Followed by:
/// * `/discord-ipc-{i}` - where `i` is a number from 0 to 9
fn connect(client_id: u64) -> crate::Result<Self> {
let dirs = ["XDG_RUNTIME_DIR", "TMPDIR", "TMP", "TEMP"]
.iter()
Expand Down
1 change: 1 addition & 0 deletions src/ipc/discord/platform/windows.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use std::{fs::OpenOptions, io};
use crate::ipc::discord::client::{Connection, RichClient};

impl Connection for RichClient {
/// Pipe path can be under the directory `\\\\.\\pipe\\discord-ipc-{i}` where `i` is a number from 0 to 9.
fn connect(client_id: u64) -> crate::Result<Self> {
for i in 0..10 {
match OpenOptions::new()
Expand Down
2 changes: 2 additions & 0 deletions src/ipc/discord/utils.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
use std::convert::TryInto;

/// Combines an opcode and data length into a byte vector for transmission.
pub fn encode(opcode: u32, data_length: u32) -> Vec<u8> {
[opcode.to_le_bytes(), data_length.to_le_bytes()].concat()
}

/// Extracts the data length from a byte slice.
pub fn decode(data: &[u8]) -> u32 {
u32::from_le_bytes(data[4..8].try_into().unwrap())
}
41 changes: 41 additions & 0 deletions src/ipc/pipe/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,28 +10,69 @@ use crate::{
session::SessionManager,
};

/// Trait for server-side pipe operations.
///
/// This trait defines methods for managing a pipe server, including starting,
/// stopping, broadcasting, and writing to clients.
pub trait PipeServerImpl {
/// Creates a new pipe server instance.
///
/// # Arguments
///
/// * `pipe_name` - The name of the pipe.
/// * `tx` - A channel sender for sending messages.
/// * `session_manager` - A session manager for handling client sessions.
fn new(pipe_name: &str, tx: Sender<Message>, session_manager: Arc<SessionManager>) -> Self
where
Self: Sized;

/// Starts the pipe server.
fn start(&mut self) -> io::Result<()>;

/// Stops the pipe server.
fn stop(&mut self);

/// Broadcasts data to all connected clients.
fn broadcast(&self, data: &[u8]) -> io::Result<()>;

/// Writes data to a specific client.
fn write_to(&self, client_id: u32, data: &[u8]) -> io::Result<()>;

/// Disconnects a specific client.
#[allow(dead_code)]
fn disconnect(&self, client_id: u32) -> io::Result<()>;
}

/// Trait for client-side pipe operations.
///
/// This trait defines methods for managing a pipe client, including writing data
/// and starting a read thread.
pub trait PipeClientImpl {
/// Creates a new pipe client instance.
///
/// # Arguments
///
/// * `id` - The ID of the client.
/// * `pipe` - The pipe type used for communication.
/// * `tx` - A channel sender for sending messages.
fn new(id: u32, pipe: Self::PipeType, tx: Sender<Message>) -> Self
where
Self: Sized;

/// Writes data to the pipe.
fn write(&mut self, data: &[u8]) -> io::Result<()>;

/// Starts a thread for reading data from the pipe.
fn start_read_thread(&mut self) -> io::Result<()>;

/// The type of pipe used for communication.
type PipeType;
}

/// Handles error reporting for a specific client.
///
/// Sends an error event message when an error occurs, managing broken pipe errors
/// by sending a disconnect event.
fn report_error(id: u32, tx: &Sender<Message>, error: io::Error) {
if error.kind() == io::ErrorKind::BrokenPipe {
tx.send(client_event!(id, Disconnect)).ok();
Expand Down
3 changes: 3 additions & 0 deletions src/messages/events/client/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ pub enum ClientEvent {
Disconnect(DisconnectEvent),
}

/// Extracts the 'data' field from a map and returns an error if it is missing or invalid.
///
/// This macro is useful for extracting and validating the 'data' field from a map-like structure.
macro_rules! data {
($map:expr) => {
$map.remove("data")
Expand Down
12 changes: 12 additions & 0 deletions src/messages/message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ impl Message {
}
}

/// Constructs a `Message` for client events.
///
/// This macro simplifies the creation of a `Message` by wrapping a `ClientEvent`.
/// It accepts a client ID, event type, and optional arguments for the event.
#[macro_export]
macro_rules! client_event {
($id:expr, $type:ident, $args:expr) => {
Expand All @@ -36,6 +40,10 @@ macro_rules! client_event {
};
}

/// Constructs a `Message` for local events.
///
/// This macro simplifies the creation of a `Message` by wrapping a `LocalEvent`.
/// It accepts a client ID, event type, and optional arguments for the event.
#[macro_export]
macro_rules! local_event {
($id:expr, $type:ident, $args:expr) => {
Expand All @@ -57,6 +65,10 @@ macro_rules! local_event {
};
}

/// Constructs a `Message` for server events.
///
/// This macro simplifies the creation of a `Message` by wrapping a `ServerEvent`.
/// It accepts a client ID, event type, and optional arguments for the event.
#[macro_export]
macro_rules! server_event {
($id:expr, $type:ident, $args:expr) => {
Expand Down
14 changes: 14 additions & 0 deletions src/protocol/json/deserialize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,21 @@ use std::str::from_utf8_unchecked;
use super::{value::Value, Json};
use crate::protocol::error::ProtocolError;

/// Trait for deserializing JSON data into Rust types.
///
/// This trait defines a method for converting a JSON representation into
/// a Rust data structure. It requires implementing the `deserialize` method
/// that takes a reference to a `HashMap` and returns the desired type.
pub trait Deserialize: Sized {
/// Deserializes a JSON object into a Rust type.
///
/// # Arguments
///
/// * `input` - A reference to a `HashMap` containing JSON key-value pairs.
///
/// # Returns
///
/// A result containing the deserialized Rust type or an error.
fn deserialize<'a>(input: &HashMap<&'a str, Value<'a>>) -> crate::Result<Self>;
}

Expand Down
15 changes: 15 additions & 0 deletions src/protocol/json/serialize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,22 @@ use super::{value::ValueRef, Json};

pub type SerializeFn<'a> = fn(&'a str, ValueRef<'a>, &mut SerializeState) -> crate::Result<()>;

/// Trait for serializing Rust types into JSON data.
///
/// This trait defines a method for converting a Rust data structure into
/// a JSON representation. It requires implementing the `serialize` method
/// that uses a `SerializeFn` and `SerializeState` to produce the JSON output.
pub trait Serialize {
/// Serializes a Rust type into JSON data.
///
/// # Arguments
///
/// * `f` - A function for serializing key-value pairs.
/// * `state` - A mutable reference to the serialization state.
///
/// # Returns
///
/// A result indicating success or failure of the serialization process.
fn serialize<'a>(&'a self, f: SerializeFn<'a>, state: &mut SerializeState)
-> crate::Result<()>;
}
Expand Down
Loading

0 comments on commit 7f217a7

Please sign in to comment.