From 7f217a7776ed5f7662bf3a5432493c73b49f2756 Mon Sep 17 00:00:00 2001 From: vyfor Date: Mon, 2 Dec 2024 22:42:53 +0500 Subject: [PATCH] docs(rust): document crucial parts of the codebase --- src/cord.rs | 23 +++++++++++++--- src/error.rs | 12 +++++++++ src/ipc/discord/client.rs | 15 +++++++++++ src/ipc/discord/platform/unix.rs | 13 +++++++++ src/ipc/discord/platform/windows.rs | 1 + src/ipc/discord/utils.rs | 2 ++ src/ipc/pipe/mod.rs | 41 +++++++++++++++++++++++++++++ src/messages/events/client/mod.rs | 3 +++ src/messages/message.rs | 12 +++++++++ src/protocol/json/deserialize.rs | 14 ++++++++++ src/protocol/json/serialize.rs | 15 +++++++++++ src/protocol/msgpack/deserialize.rs | 14 ++++++++++ src/protocol/msgpack/serialize.rs | 15 +++++++++++ src/util/utils.rs | 14 ++++++++-- 14 files changed, 189 insertions(+), 5 deletions(-) diff --git a/src/cord.rs b/src/cord.rs index 5128be44..f7ca6be6 100644 --- a/src/cord.rs +++ b/src/cord.rs @@ -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, @@ -39,6 +49,7 @@ pub struct Cord { } impl Cord { + /// Initializes the Cord application. pub fn new(config: Config) -> crate::Result { let lock = ServerLock::new()?; @@ -60,6 +71,7 @@ impl Cord { }) } + /// Runs the application. pub fn run(&mut self) -> crate::Result<()> { self.start_rpc()?; self.pipe.start()?; @@ -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 @@ -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(); @@ -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(); } @@ -112,6 +127,7 @@ impl Cord { } } +/// Manages application settings required for initialization. pub struct Config { pub pipe_name: String, pub client_id: u64, @@ -119,6 +135,7 @@ pub struct Config { } impl Config { + /// Creates a new configuration. pub fn new(pipe_name: String, client_id: u64, timeout: u64) -> Self { Self { pipe_name, diff --git a/src/error.rs b/src/error.rs index 4d87febd..03e2ced3 100644 --- a/src/error.rs +++ b/src/error.rs @@ -7,15 +7,25 @@ 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, @@ -23,6 +33,7 @@ pub struct CordError { } impl CordError { + /// Creates a new `CordError` instance. pub fn new(kind: CordErrorKind, error: E) -> Self where E: Into>, @@ -91,4 +102,5 @@ impl From<&str> for CordError { } } +/// Alias for `std::result::Result`. pub type Result = std::result::Result; diff --git a/src/ipc/discord/client.rs b/src/ipc/discord/client.rs index 3f5cad6f..8122ca75 100644 --- a/src/ipc/discord/client.rs +++ b/src/ipc/discord/client.rs @@ -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")] @@ -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; + /// 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() @@ -38,6 +49,7 @@ impl RichClient { }) } + /// Receives data from Discord. pub fn read(&self) -> crate::Result> { self.pipe .as_ref() @@ -51,6 +63,7 @@ impl RichClient { }) } + /// Establishes a connection with Discord. pub fn handshake(&self) -> crate::Result<()> { self.write( 0, @@ -58,6 +71,7 @@ impl RichClient { ) } + /// 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()))?; @@ -65,6 +79,7 @@ impl RichClient { Ok(()) } + /// Clears the current rich presence. pub fn clear(&self) -> crate::Result<()> { let packet = Packet::empty(); let encoded = Json::serialize(&packet)?; diff --git a/src/ipc/discord/platform/unix.rs b/src/ipc/discord/platform/unix.rs index dbe8ed98..49a6f1eb 100644 --- a/src/ipc/discord/platform/unix.rs +++ b/src/ipc/discord/platform/unix.rs @@ -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 { let dirs = ["XDG_RUNTIME_DIR", "TMPDIR", "TMP", "TEMP"] .iter() diff --git a/src/ipc/discord/platform/windows.rs b/src/ipc/discord/platform/windows.rs index a8de86e6..7b037aaf 100644 --- a/src/ipc/discord/platform/windows.rs +++ b/src/ipc/discord/platform/windows.rs @@ -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 { for i in 0..10 { match OpenOptions::new() diff --git a/src/ipc/discord/utils.rs b/src/ipc/discord/utils.rs index e1d88cf9..3c9e18a1 100644 --- a/src/ipc/discord/utils.rs +++ b/src/ipc/discord/utils.rs @@ -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 { [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()) } diff --git a/src/ipc/pipe/mod.rs b/src/ipc/pipe/mod.rs index e4b12318..22a430ab 100644 --- a/src/ipc/pipe/mod.rs +++ b/src/ipc/pipe/mod.rs @@ -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, session_manager: Arc) -> 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) -> 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, error: io::Error) { if error.kind() == io::ErrorKind::BrokenPipe { tx.send(client_event!(id, Disconnect)).ok(); diff --git a/src/messages/events/client/mod.rs b/src/messages/events/client/mod.rs index 2e09f38f..5d797831 100644 --- a/src/messages/events/client/mod.rs +++ b/src/messages/events/client/mod.rs @@ -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") diff --git a/src/messages/message.rs b/src/messages/message.rs index ff1eab28..b198faa0 100644 --- a/src/messages/message.rs +++ b/src/messages/message.rs @@ -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) => { @@ -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) => { @@ -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) => { diff --git a/src/protocol/json/deserialize.rs b/src/protocol/json/deserialize.rs index 1d8d02f5..aa7427ee 100644 --- a/src/protocol/json/deserialize.rs +++ b/src/protocol/json/deserialize.rs @@ -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; } diff --git a/src/protocol/json/serialize.rs b/src/protocol/json/serialize.rs index cbd1ca77..97700893 100644 --- a/src/protocol/json/serialize.rs +++ b/src/protocol/json/serialize.rs @@ -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<()>; } diff --git a/src/protocol/msgpack/deserialize.rs b/src/protocol/msgpack/deserialize.rs index ec79a044..f9972aea 100644 --- a/src/protocol/msgpack/deserialize.rs +++ b/src/protocol/msgpack/deserialize.rs @@ -8,7 +8,21 @@ use super::{ use crate::protocol::error::ProtocolError; use std::collections::HashMap; +/// Trait for deserializing MsgPack data into Rust types. +/// +/// This trait defines a method for converting a MsgPack representation into +/// a Rust data structure. It requires implementing the `deserialize` method +/// that takes a `Value` and returns the desired type. pub trait Deserialize: Sized { + /// Deserializes a MsgPack value into a Rust type. + /// + /// # Arguments + /// + /// * `input` - A `Value` representing the MsgPack data. + /// + /// # Returns + /// + /// A result containing the deserialized Rust type or an error. fn deserialize(input: Value) -> crate::Result; } diff --git a/src/protocol/msgpack/serialize.rs b/src/protocol/msgpack/serialize.rs index ade17315..c8c175ea 100644 --- a/src/protocol/msgpack/serialize.rs +++ b/src/protocol/msgpack/serialize.rs @@ -9,7 +9,22 @@ use crate::protocol::error::ProtocolError; pub type SerializeFn<'a> = fn(&'a str, ValueRef<'a>, &mut SerializeState) -> crate::Result<()>; +/// Trait for serializing Rust types into MsgPack data. +/// +/// This trait defines a method for converting a Rust data structure into +/// a MsgPack representation. It requires implementing the `serialize` method +/// that uses a `SerializeFn` and `SerializeState` to produce the MsgPack output. pub trait Serialize { + /// Serializes a Rust type into MsgPack 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<()>; } diff --git a/src/util/utils.rs b/src/util/utils.rs index 6002d7c5..bb879e51 100644 --- a/src/util/utils.rs +++ b/src/util/utils.rs @@ -1,5 +1,6 @@ -#![allow(clippy::too_many_arguments)] - +/// Retrieves a field from a map, applies a transformation, and returns an error if the field is missing or invalid. +/// +/// This macro is useful for extracting and validating fields from a map-like structure. #[macro_export] macro_rules! get_field { ($map:expr, $field:expr, $expr:expr) => { @@ -11,6 +12,9 @@ macro_rules! get_field { }; } +/// Retrieves a field from a map and applies a transformation, returning `None` if the field is missing or invalid. +/// +/// This macro is useful for optional field extraction and transformation. #[macro_export] macro_rules! get_field_or_none { ($map:expr, $field:expr, $expr:expr) => { @@ -18,6 +22,9 @@ macro_rules! get_field_or_none { }; } +/// Removes a field from a map, applies a transformation, and returns an error if the field is missing or invalid. +/// +/// This macro is useful for extracting, validating, and removing fields from a map-like structure. #[macro_export] macro_rules! remove_field { ($map:expr, $field:expr, $expr:expr) => { @@ -29,6 +36,9 @@ macro_rules! remove_field { }; } +/// Removes a field from a map and applies a transformation, returning `None` if the field is missing or invalid. +/// +/// This macro is useful for optional field extraction, transformation, and removal. #[macro_export] macro_rules! remove_field_or_none { ($map:expr, $field:expr, $expr:expr) => {