Skip to content

Commit

Permalink
optional serde support, fix doc tests
Browse files Browse the repository at this point in the history
  • Loading branch information
museun committed Mar 11, 2019
1 parent b10e86b commit 0e93aba
Show file tree
Hide file tree
Showing 8 changed files with 82 additions and 48 deletions.
8 changes: 5 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,10 @@ license = "0BSD"
[dependencies]
parking_lot = "0.7.1"
uuid = { version = "0.7.2", features = ["v4"] }
hashbrown = { version = "0.1.8", optional = true }
hashbrown = { version = "0.1.8", features = ["serde"], optional = true }
serde = { version = "1.0.89", features = ["derive"], optional = true }

[features]
default = [ "hashbrown", "teststream" ]
teststream = [ ]
default = ["hashbrown", "teststream"]
teststream = []
serde_derive = ["serde"]
5 changes: 0 additions & 5 deletions rustfmt.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1 @@
wrap_comments = true
format_strings = true
format_macro_matchers = true
fn_single_line = false
imports_layout = "HorizontalVertical"
edition = "2018"
4 changes: 4 additions & 0 deletions src/irc/tags.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};

#[cfg(feature = "hashbrown")]
use hashbrown::HashMap;

Expand All @@ -6,6 +9,7 @@ use std::collections::HashMap;

/// Tags are IRCv3 message tags. Twitch uses them extensively
#[derive(Debug, Default, PartialEq, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Tags(pub(crate) HashMap<String, String>);

impl Tags {
Expand Down
79 changes: 44 additions & 35 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,70 +1,80 @@
//! # A simple "connection-less" twitch chat crate
//! This crate simply read lines from an [std::io::Read]() and produces data types
//! for the corresponding messages, and takes an [std::io::Write]() which can
//! produce messages that twitch understands.
//! This crate simply read lines from an [std::io::Read]() and produces data
//! types for the corresponding messages, and takes an [std::io::Write]() which
//! can produce messages that twitch understands.
//!
//! # Organization of project
//! This crate is split into two top-level modules, `irc` and `twitch`.
//!
//! The [`irc`](./irc/index.html) module contains a **very** simplistic representation of the IRC
//! protocol, but can be used to write simplistic clients for interacting with
//! the twitch module
//! The [`irc`](./irc/index.html) module contains a **very** simplistic
//! representation of the IRC protocol, but can be used to write simplistic
//! clients for interacting with the twitch module
//!
//! The [`twitch`](./twitch/index.html) module contains many data types that encapsulate the various
//! functionality of the irc-portion of Twitch's chat system
//! The [`twitch`](./twitch/index.html) module contains many data types that
//! encapsulate the various functionality of the irc-portion of Twitch's chat
//! system
//!
//! # 'Theory' of operation
//! First, by creating a [`Client`](./twitch/struct.Client.html) from a Read/Write pair
//! (such as a cloned TcpStream) then calling
//! [`Client::register`](./twitch/struct.Client.html#method.register) with a filled-out
//! [`UserConfig`](./struct.UserConfig.html) will connect you to Twitch. Once
//! connected, you can
//! [`Client::wait_for_ready`](./twitch/struct.Client.html#method.wait_for_ready)
//! and the client will read (blocking) until Twitch sends a
//! First, by creating a [`Client`](./twitch/struct.Client.html) from a
//! Read/Write pair (such as a cloned TcpStream) then calling
//! [`Client::register`](./twitch/struct.Client.html#method.register) with a
//! filled-out [`UserConfig`](./struct.UserConfig.html) will connect you to
//! Twitch. Once connected, you can
//! [`Client::wait_for_ready`](./twitch/struct.Client.html#method.
//! wait_for_ready) and the client will read (blocking) until Twitch sends a
//! [`GlobalUserState`](twitch/commands/struct.GlobalUserState.html) message,
//! which it'll fill out a [`LocalUser`](./twitch/struct.LocalUser.html) with
//! various information.
//!
//! Once connected, you can
//! - use [`Client::join`](./twitch/struct.Client.html#method.join) to join a channel.
//! - use [`Client::on`](./twitch/struct.Client.html#method.on) to set up a message filter.
//! - use [`Client::read_message`](./twitch/struct.Client.html#method.read_message) to read a message (and pump the filters).
//! - use [`Client::join`](./twitch/struct.Client.html#method.join) to join a
//! channel.
//! - use [`Client::on`](./twitch/struct.Client.html#method.on) to set up a
//! message filter.
//! - use [`Client::read_message`](./twitch/struct.Client.html#method.
//! read_message) to read a message (and pump the filters).
//! - or do various [*other things*](./twitch/struct.Client.html#method.host)
//!
//! # Message filters, and why blocking with this is a bad idea
//! The client provides a very simplistic callback registration system
//!
//! To use it, you simply just `register` a closure with the client via its [`Client::on`](./twitch/struct.Client.html#method.on) method.
//! It uses the type of the closures argument, one of [*these*](./twitch/commands/index.html#structs) to create a filter
//! When
//! To use it, you simply just `register` a closure with the client via its
//! [`Client::on`](./twitch/struct.Client.html#method.on) method. It uses the
//! type of the closures argument, one of
//! [*these*](./twitch/commands/index.html#structs) to create a filter When
//! [`Client::read_message`](./twitch/struct.Client.html#method.read_message) is
//! called, it'll check these filters and send a clone of the requested message
//! to the callback. Because it does this on same thread as the [`Client::read_message`](./twitch/struct.Client.html#method.read_message)
//! to the callback. Because it does this on same thread as the
//! [`Client::read_message`](./twitch/struct.Client.html#method.read_message)
//! call, you can lock up the system by simplying diverging.
//!
//! The client is thread safe, and clonable so one could call [`Client::read_message`](./twitch/struct.Client.html#method.read_message)
//! The client is thread safe, and clonable so one could call
//! [`Client::read_message`](./twitch/struct.Client.html#method.read_message)
//! with ones own sychronization scheme to allow for a simplistic thread pool,
//! but its best just to send the message off to a channel elsehwere
//!
//! # A simple example
//! ```no_run
//! use twitchchat::{UserConfig};
//! use twitchchat::
//! use std::net::TcpStream;
//! use twitchchat::twitch::{commands::PrivMsg, Capability, Client};
//! use twitchchat::UserConfig;
//! # fn main() {
//! // create a simple TcpStream
//! let read = TcpStream::connect("irc.chat.twitch.tv:6667").expect("to connect");
//! let write = read.try_clone().expect("must be able to clone the tcpstream")
//! let write = read
//! .try_clone()
//! .expect("must be able to clone the tcpstream");
//!
//! // your password and your nickname
//! // the twitch oauth token must be prefixed with `oauth:your_token_here`
//! let (nick, pass) = (env!("MY_TWITCH_OAUTH_TOKEN"), "my_name");
//! let config = UserConfig{
//! let (nick, pass) = (std::env::var("MY_TWITCH_OAUTH_TOKEN").unwrap(), "my_name");
//! let config = UserConfig {
//! token: pass.to_string(),
//! nick: nick.to_string(),
//! caps: vec![
//! Capability::Membership, // enable seeing whom is on the channel
//! Capability::Commands, // enable sending twitch-specific commands (and receiving them)
//! Capability::Tags // enable metadata (`tags`) on many of the messages
//! Capability::Tags, // enable metadata (`tags`) on many of the messages
//! ],
//! };
//!
Expand All @@ -75,7 +85,7 @@
//! client.register(config).unwrap();
//!
//! // wait for everything to be ready (blocks)
//! let user = client.wait_for_ready().unwrap()
//! let user = client.wait_for_ready().unwrap();
//! println!(
//! "connected with {} (id: {}). our color is: {}",
//! user.display_name.unwrap(),
Expand All @@ -84,18 +94,17 @@
//! );
//!
//! // when we receive a commands::PrivMsg print out who sent it, and the message
//! client.on(|msg: commands::PrivMsg| {
//! println!("{}: {}", msg.display_name().unwrap(), msg.message())
//! });
//! client.on(|msg: PrivMsg| println!("{}: {}", msg.display_name().unwrap(), msg.message()));
//!
//! // blocks the thread, but any callbacks set in the .on handlers will get their messages
//! client.run();
//! # }
//! ```
//!
//! # TestStream
//! [`TestStream`](./struct.TestStream.html) is a simple TcpStream-like thing that lets you inject/read its
//! internal buffers, allowing you to easily write unit tests for the [`Client`](./twitch/struct.Client.html)
//! [`TestStream`](./struct.TestStream.html) is a simple TcpStream-like thing
//! that lets you inject/read its internal buffers, allowing you to easily write
//! unit tests for the [`Client`](./twitch/struct.Client.html)
/// IRC-related stuff
pub mod irc;
Expand Down
5 changes: 5 additions & 0 deletions src/twitch/badge.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};

#[derive(Debug, PartialEq, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum BadgeKind {
Admin,
Bits,
Expand All @@ -13,6 +17,7 @@ pub enum BadgeKind {

/// Badges attached to a message
#[derive(Debug, PartialEq, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Badge {
pub kind: BadgeKind,
pub data: String,
Expand Down
14 changes: 13 additions & 1 deletion src/twitch/color.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};

/// A 24-bit triplet for hex colors. Defaults to White (0xFF,0xFF,0xFF)
#[derive(Debug, PartialEq, Copy, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct RGB(pub u8, pub u8, pub u8);

impl Default for RGB {
Expand All @@ -16,7 +20,8 @@ impl std::fmt::Display for RGB {
}

impl RGB {
/// Tries to parse a string (`'#FFFFFF'` or `'FFFFFF'`) into the RGB, `default`s if it can't
/// Tries to parse a string (`'#FFFFFF'` or `'FFFFFF'`) into the RGB,
/// `default`s if it can't
pub fn from_hex(input: &str) -> Self {
let input = input.trim();
let input = match (input.chars().next(), input.len()) {
Expand Down Expand Up @@ -68,6 +73,7 @@ impl From<Twitch> for RGB {

/// These are the default Twitch colors
#[derive(Debug, PartialEq, Copy, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum Twitch {
/// RGB (hex): #0000FF
Blue,
Expand Down Expand Up @@ -103,6 +109,12 @@ pub enum Twitch {
Turbo(RGB),
}

impl Default for Twitch {
fn default() -> Self {
Twitch::Turbo(RGB::default())
}
}

impl From<&str> for Twitch {
fn from(input: &str) -> Self {
use Twitch::*;
Expand Down
12 changes: 9 additions & 3 deletions src/twitch/emotes.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,25 @@
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};

use std::ops::Range;

/// Emotes are little pictograms used inline in twitch messages
///
/// They are presented (to the irc connection) in a `id1:range1,range2/id2:range1,..` form
/// which marks the byte position that the emote is at.
/// They are presented (to the irc connection) in a
/// `id1:range1,range2/id2:range1,..` form which marks the byte position that
/// the emote is at.
///
/// Examples:
///
/// `"testing Kappa"` would be `25:8-13`
/// `"Kappa testing Kappa"` would be `25:0-5,14-19`
#[derive(Debug, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Emotes {
/// The emote id, e.g. Kappa = 25
pub id: usize,
/// A list of [Range](std::ops::Range) in the message where this emote is found
/// A list of [Range](std::ops::Range) in the message where this emote is
/// found
pub ranges: Vec<Range<u16>>,
}

Expand Down
3 changes: 2 additions & 1 deletion src/twitch/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,8 @@ pub enum Message {
Reconnect(commands::Reconnect),
/// Identifies the channel's chat settings (e.g., slow mode duration).
RoomState(commands::RoomState),
/// Announces Twitch-specific events to the channel (e.g., a user's subscription notification).
/// Announces Twitch-specific events to the channel (e.g., a user's
/// subscription notification).
UserNotice(commands::UserNotice),
/// Identifies a user's chat settings or properties (e.g., chat color)..
UserState(commands::UserState),
Expand Down

0 comments on commit 0e93aba

Please sign in to comment.