1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
#![cfg_attr(docsrs, feature(doc_cfg))]
#![deny(
    missing_docs,
    missing_debug_implementations,
    rustdoc::broken_intra_doc_links,
    rustdoc::private_intra_doc_links
)]

//! A library for interacting with the [VTube Studio API].
//!
//! This crate exposes a [`Client`] for making requests to the VTube Studio websocket API, and
//! handles the work of mapping requests to responses (using [`tokio_tower::multiplex`]).
//!
//! The client wraps a set of configurable [`tower::Service`] middleware for handling the
//! [authentication flow](crate::service::Authentication), [retries](crate::service::RetryPolicy),
//! and [reconnects](crate::service::MakeApiService), and uses [`tokio_tungstenite`] as the
//! underlying websocket transport by default.
//!
//! [VTube Studio API]: https://github.com/DenchiSoft/VTubeStudio
//!
//! # Basic usage
//!
//! This example creates a [`Client`] using the provided [builder](ClientBuilder), which:
//!
//! * connects to `ws://localhost:8001` using [tokio_tungstenite](https://docs.rs/tokio_tungstenite)
//! * authenticates with an existing token (if present and valid)
//! * reconnects when disconnected, and retries the failed request on reconnection success
//! * requests a new auth token on receiving an auth error, and retries the initial failed
//!   request on authentication success
//!
#![cfg_attr(feature = "tokio-tungstenite", doc = "```no_run")]
#![cfg_attr(not(feature = "tokio-tungstenite"), doc = "```ignore")]
//! use vtubestudio::{Client, Error};
//! use vtubestudio::data::StatisticsRequest;
//!
//! #[tokio::main]
//! async fn main() -> Result<(), Error> {
//!     // An auth token from a previous successful authentication request
//!     let stored_token = Some("...".to_string());
//!
//!     let (mut client, mut new_tokens) = Client::builder()
//!         .auth_token(stored_token)
//!         .authentication("Plugin name", "Developer name", None)
//!         .build_tungstenite();
//!
//!     tokio::spawn(async move {
//!         // This returns whenever the authentication middleware receives a new auth token.
//!         // We can handle it by saving it somewhere, etc.
//!         while let Some(token) = new_tokens.next().await {
//!             println!("Got new auth token: {}", token);
//!         }
//!     });
//!
//!     // Use the client to send a `StatisticsRequest`, handling authentication if necessary.
//!     // The return type is inferred from the input type to be `StatisticsResponse`.
//!     let resp = client.send(&StatisticsRequest {}).await?;
//!     println!("VTube Studio has been running for {}ms", resp.uptime);
//!
//!     Ok(())
//! }
//! ```
//!
//! To send multiple outgoing requests at the same time without waiting for a request to come back,
//! you can clone the [`Client`] per request (by default, the client wraps a
//! [`tower::buffer::Buffer`] which adds an mpsc buffer in front of the underlying websocket
//! transport).
//!
//! # Project structure
//!
//! * [`client`] provides a high level API dealing with typed [`Request`]/[`Response`] types, which wraps a... ⏎
//!   * [`service`], a stack of [`tower::Service`]s that deal with
//!     [`RequestEnvelope`]/[`ResponseEnvelope`] pairs, and wraps a... ⏎
//!     * [`transport`], which describes the underlying websocket connection stream, using a... ⏎
//!       * [`codec`] to determine how to encode/decode websocket messages
//!
//! While the provided [`ClientBuilder`] should be sufficient for most users, each of these layers
//! can be modified to add custom behavior if needed. E.g.,
//!
//! * using a different combination of tower middleware
//! * using a different websocket library
//! * adding custom request/response types
//!   * as an escape hatch, if new request types or fields are added to the API and you don't feel
//!     like waiting for them to be added to this library
//!
//! [`Request`]: crate::data::Request
//! [`Response`]: crate::data::Response
//! [`RequestEnvelope`]: crate::data::RequestEnvelope
//! [`ResponseEnvelope`]: crate::data::ResponseEnvelope

/// Utilities for creating [`Client`]s.
pub mod client;

/// [`Service`](tower::Service) middleware used by [`Client`].
pub mod service;

/// Transport ([`Sink`]/[`Stream`]) types.
///
/// [`Sink`]: futures_sink::Sink
/// [`Stream`]: futures_util::Stream
pub mod transport;

/// Codecs for converting to/from websocket message types.
pub mod codec;

/// Request/response types for the VTube Studio API.
pub mod data;

/// Types related to error handling.
pub mod error;

// Macro for enabling `doc_cfg` on docs.rs
macro_rules! cfg_feature {
    (
        #![$meta:meta]
        $($item:item)+
    ) => {
        $(
            #[cfg($meta)]
            #[cfg_attr(docsrs, doc(cfg($meta)))]
            $item
        )*
    }
}

pub(crate) use cfg_feature;

pub use crate::client::{Client, ClientBuilder, TokenReceiver};
pub use crate::error::{Error, ErrorKind, Result};