From f216c00d0f4395a3120788be8b1e92581cba3cf7 Mon Sep 17 00:00:00 2001 From: Graeme Coupar Date: Sat, 10 Feb 2024 16:14:41 +0000 Subject: [PATCH] Prepare to release `next` api (#67) Not actually releasing it _yet_ but this gets it in a place where I can - Exposing it at the top-level - Deprecating the old code - Re-organising a bit to make cleaning up later easier - Updating examples --- CHANGELOG.md | 12 +++++ examples-wasm/examples/subscriptions.rs | 2 +- examples/examples/mulitiple-subscriptions.rs | 15 +++--- examples/examples/single-subscription.rs | 2 +- examples/examples/tokio.rs | 12 ++--- examples/src/lib.rs | 25 ---------- src/error.rs | 28 ++++++++++++ src/{ => legacy}/client.rs | 37 +++------------ src/legacy/mod.rs | 6 +++ src/{ => legacy}/wasm.rs | 5 +- src/{ => legacy}/websockets.rs | 4 ++ src/lib.rs | 48 ++++++++++++++++---- src/native.rs | 5 +- src/next/builder.rs | 6 +-- tests/cynic-tests.rs | 6 +-- 15 files changed, 119 insertions(+), 94 deletions(-) create mode 100644 src/error.rs rename src/{ => legacy}/client.rs (93%) create mode 100644 src/legacy/mod.rs rename src/{ => legacy}/wasm.rs (97%) rename src/{ => legacy}/websockets.rs (85%) diff --git a/CHANGELOG.md b/CHANGELOG.md index c049222..6f00124 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,18 @@ all APIs might be changed. ## Unreleased - xxxx-xx-xx +### Breaking Changes + +- The `next` api is now available at the top level rather than the `next` + module. + +### Deprecations + +These will be removed in a future version, probably in v0.9.0 + +- `AsyncWebsocketClient` and all its supporting traits and structs are now + deprecated. + ### New Features - Added a `streaming_operation` function to `next::ClientBuilder` to make diff --git a/examples-wasm/examples/subscriptions.rs b/examples-wasm/examples/subscriptions.rs index 1dbf9ed..adc091c 100644 --- a/examples-wasm/examples/subscriptions.rs +++ b/examples-wasm/examples/subscriptions.rs @@ -5,7 +5,7 @@ use std::future::IntoFuture; -use graphql_ws_client::{next::Client, ws_stream_wasm::Connection}; +use graphql_ws_client::{ws_stream_wasm::Connection, Client}; mod schema { cynic::use_schema!("../schemas/books.graphql"); diff --git a/examples/examples/mulitiple-subscriptions.rs b/examples/examples/mulitiple-subscriptions.rs index 9c0c7d0..47b9fdd 100644 --- a/examples/examples/mulitiple-subscriptions.rs +++ b/examples/examples/mulitiple-subscriptions.rs @@ -1,8 +1,11 @@ //! An example of running a multiple subscriptions on a single connection -//! using `graphql-ws-client` and `async-tungstenite` +//! using `graphql-ws-client`, `async-tungstenite` & the `async_std` +//! executor. //! //! Talks to the the tide subscription example in `async-graphql` +use std::future::IntoFuture; + mod schema { cynic::use_schema!("../schemas/books.graphql"); } @@ -38,7 +41,7 @@ struct BooksChangedSubscription { async fn main() { use async_tungstenite::tungstenite::{client::IntoClientRequest, http::HeaderValue}; use futures::StreamExt; - use graphql_ws_client::CynicClientBuilder; + use graphql_ws_client::Client; let mut request = "ws://localhost:8000/graphql".into_client_request().unwrap(); request.headers_mut().insert( @@ -52,12 +55,8 @@ async fn main() { println!("Connected"); - let (sink, stream) = connection.split(); - - let mut client = CynicClientBuilder::new() - .build(stream, sink, async_executors::AsyncStd) - .await - .unwrap(); + let (mut client, actor) = Client::build(connection).await.unwrap(); + async_std::task::spawn(actor.into_future()); // In reality you'd probably want to different subscriptions, but for the sake of this example // these are the same subscriptions diff --git a/examples/examples/single-subscription.rs b/examples/examples/single-subscription.rs index 6406f1a..4349a84 100644 --- a/examples/examples/single-subscription.rs +++ b/examples/examples/single-subscription.rs @@ -38,7 +38,7 @@ struct BooksChangedSubscription { async fn main() { use async_tungstenite::tungstenite::{client::IntoClientRequest, http::HeaderValue}; use futures::StreamExt; - use graphql_ws_client::next::Client; + use graphql_ws_client::Client; let mut request = "ws://localhost:8000/graphql".into_client_request().unwrap(); request.headers_mut().insert( diff --git a/examples/examples/tokio.rs b/examples/examples/tokio.rs index 48e9d5c..44452c5 100644 --- a/examples/examples/tokio.rs +++ b/examples/examples/tokio.rs @@ -3,7 +3,7 @@ //! //! Talks to the the tide subscription example in `async-graphql` -use examples::TokioSpawner; +use std::future::IntoFuture; mod schema { cynic::use_schema!("../schemas/books.graphql"); @@ -40,7 +40,7 @@ struct BooksChangedSubscription { async fn main() { use async_tungstenite::tungstenite::{client::IntoClientRequest, http::HeaderValue}; use futures::StreamExt; - use graphql_ws_client::CynicClientBuilder; + use graphql_ws_client::Client; let mut request = "ws://localhost:8000".into_client_request().unwrap(); request.headers_mut().insert( @@ -54,12 +54,8 @@ async fn main() { println!("Connected"); - let (sink, stream) = connection.split(); - - let mut client = CynicClientBuilder::new() - .build(stream, sink, TokioSpawner::current()) - .await - .unwrap(); + let (mut client, actor) = Client::build(connection).await.unwrap(); + tokio::spawn(actor.into_future()); let mut stream = client.streaming_operation(build_query()).await.unwrap(); println!("Running subscription apparently?"); diff --git a/examples/src/lib.rs b/examples/src/lib.rs index 7a5189a..8b13789 100644 --- a/examples/src/lib.rs +++ b/examples/src/lib.rs @@ -1,26 +1 @@ -#[cfg(not(target_arch = "wasm32"))] -mod tokio_spawner { - pub struct TokioSpawner(tokio::runtime::Handle); - impl TokioSpawner { - pub fn new(handle: tokio::runtime::Handle) -> Self { - TokioSpawner(handle) - } - - pub fn current() -> Self { - TokioSpawner::new(tokio::runtime::Handle::current()) - } - } - - impl futures::task::Spawn for TokioSpawner { - fn spawn_obj( - &self, - obj: futures::task::FutureObj<'static, ()>, - ) -> Result<(), futures::task::SpawnError> { - self.0.spawn(obj); - Ok(()) - } - } -} -#[cfg(not(target_arch = "wasm32"))] -pub use tokio_spawner::TokioSpawner; diff --git a/src/error.rs b/src/error.rs new file mode 100644 index 0000000..605818c --- /dev/null +++ b/src/error.rs @@ -0,0 +1,28 @@ +#[derive(thiserror::Error, Debug)] +/// Error type +pub enum Error { + /// Unknown error + #[error("unknown: {0}")] + Unknown(String), + /// Custom error + #[error("{0}: {1}")] + Custom(String, String), + /// Unexpected close frame + #[error("got close frame. code: {0}, reason: {1}")] + Close(u16, String), + /// Decoding / parsing error + #[error("message decode error, reason: {0}")] + Decode(String), + /// Serializing error + #[error("couldn't serialize message, reason: {0}")] + Serializing(String), + /// Sending error + #[error("message sending error, reason: {0}")] + Send(String), + /// Futures spawn error + #[error("futures spawn error, reason: {0}")] + SpawnHandle(String), + /// Sender shutdown error + #[error("sender shutdown error, reason: {0}")] + SenderShutdown(String), +} diff --git a/src/client.rs b/src/legacy/client.rs similarity index 93% rename from src/client.rs rename to src/legacy/client.rs index 4e3be8a..ba3d422 100644 --- a/src/client.rs +++ b/src/legacy/client.rs @@ -18,55 +18,32 @@ use futures::{ use serde::Serialize; use serde_json::json; -use super::{ +use crate::{ graphql::GraphqlOperation, logging::trace, protocol::{ConnectionInit, Event, Message}, websockets::WebsocketMessage, + Error, }; const SUBSCRIPTION_BUFFER_SIZE: usize = 5; /// A websocket client +#[deprecated(since = "0.8.0-rc.1", note = "use Client instead")] pub struct AsyncWebsocketClient { inner: Arc, sender_sink: mpsc::Sender, next_id: AtomicU64, } -#[derive(thiserror::Error, Debug)] -/// Error type -pub enum Error { - /// Unknown error - #[error("unknown: {0}")] - Unknown(String), - /// Custom error - #[error("{0}: {1}")] - Custom(String, String), - /// Unexpected close frame - #[error("got close frame. code: {0}, reason: {1}")] - Close(u16, String), - /// Decoding / parsing error - #[error("message decode error, reason: {0}")] - Decode(String), - /// Serializing error - #[error("couldn't serialize message, reason: {0}")] - Serializing(String), - /// Sending error - #[error("message sending error, reason: {0}")] - Send(String), - /// Futures spawn error - #[error("futures spawn error, reason: {0}")] - SpawnHandle(String), - /// Sender shutdown error - #[error("sender shutdown error, reason: {0}")] - SenderShutdown(String), -} - #[derive(Serialize)] pub enum NoPayload {} /// A websocket client builder +#[deprecated( + since = "0.8.0-rc.1", + note = "use ClientBuilder (via Client::build) instead" +)] pub struct AsyncWebsocketClientBuilder { payload: Option, } diff --git a/src/legacy/mod.rs b/src/legacy/mod.rs new file mode 100644 index 0000000..05206a3 --- /dev/null +++ b/src/legacy/mod.rs @@ -0,0 +1,6 @@ +#![allow(deprecated)] + +pub mod client; +#[cfg(feature = "ws_stream_wasm")] +pub mod wasm; +pub mod websockets; diff --git a/src/wasm.rs b/src/legacy/wasm.rs similarity index 97% rename from src/wasm.rs rename to src/legacy/wasm.rs index b286296..383d3ff 100644 --- a/src/wasm.rs +++ b/src/legacy/wasm.rs @@ -8,8 +8,6 @@ use pharos::{Observable, ObserveConfig}; use pin_project_lite::pin_project; use ws_stream_wasm::{WsErr, WsEvent, WsMessage, WsMeta, WsStream}; -use crate::websockets::WebsocketMessage; - /// Creates a new pair of sink and stream which operate on [`WasmWebsocketMessage`] instead of `WsMessage` and `WsEvent` separately. pub async fn wasm_websocket_combined_split( mut ws_meta: WsMeta, @@ -45,7 +43,8 @@ pub enum WasmWebsocketMessage { WsEvent(ws_stream_wasm::WsEvent), } -impl WebsocketMessage for WasmWebsocketMessage { +#[allow(deprecated)] +impl crate::legacy::websockets::WebsocketMessage for WasmWebsocketMessage { type Error = ws_stream_wasm::WsErr; fn new(text: String) -> Self { diff --git a/src/websockets.rs b/src/legacy/websockets.rs similarity index 85% rename from src/websockets.rs rename to src/legacy/websockets.rs index 4461cf1..1c8769e 100644 --- a/src/websockets.rs +++ b/src/legacy/websockets.rs @@ -4,6 +4,10 @@ /// /// graphql-ws-client doesn't implement the websocket protocol itself. /// This trait provides part of the integration with websocket client libraries. +#[deprecated( + since = "0.8.0-rc.1", + note = "WebsockeMessage is only used for the deprecated AsyncWebsocketClient. You should update to use Client instead" +)] pub trait WebsocketMessage: std::fmt::Debug { /// The `Error` type for this websocket client. type Error: std::error::Error + Send + 'static; diff --git a/src/lib.rs b/src/lib.rs index cd28cd8..d5f1bcf 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -22,7 +22,8 @@ #![warn(missing_docs)] -mod client; +mod error; +mod legacy; mod logging; mod protocol; @@ -31,16 +32,10 @@ mod protocol; pub mod __doc_utils; pub mod graphql; -pub mod websockets; // TODO: next shouldn't be public really, and shouldn't allow missing_docs #[allow(missing_docs)] -pub mod next; - -#[cfg(feature = "ws_stream_wasm")] -mod wasm; -#[cfg(feature = "ws_stream_wasm")] -pub use wasm::{wasm_websocket_combined_split, FusedWasmWebsocketSink, WasmWebsocketMessage}; +mod next; #[cfg(feature = "ws_stream_wasm")] /// Integration with the ws_stream_wasm library @@ -49,18 +44,53 @@ pub mod ws_stream_wasm; #[cfg(feature = "async-tungstenite")] mod native; -pub use client::{AsyncWebsocketClient, AsyncWebsocketClientBuilder, Error, SubscriptionStream}; +#[allow(deprecated)] +pub use legacy::{ + client::{AsyncWebsocketClient, AsyncWebsocketClientBuilder, SubscriptionStream}, + websockets, +}; + +#[cfg(feature = "ws_stream_wasm")] +pub use legacy::wasm::{ + wasm_websocket_combined_split, FusedWasmWebsocketSink, WasmWebsocketMessage, +}; + +pub use next::*; + +pub use error::Error; /// A websocket client for the cynic graphql crate #[cfg(feature = "cynic")] +#[allow(deprecated)] +#[deprecated( + since = "0.8.0-rc.1", + note = "graphql-ws-client no longer needs client specific types. Use the general purpose Client instead" +)] pub type CynicClient = AsyncWebsocketClient; + /// A websocket client builder for the cynic graphql crate #[cfg(feature = "cynic")] +#[allow(deprecated)] +#[deprecated( + since = "0.8.0-rc.1", + note = "graphql-ws-client no longer needs client specific types. Use the general purpose Client instead" +)] pub type CynicClientBuilder = AsyncWebsocketClientBuilder; /// A websocket client for the graphql_client graphql crate #[cfg(feature = "client-graphql-client")] +#[allow(deprecated)] +#[deprecated( + since = "0.8.0-rc.1", + note = "graphql-ws-client no longer needs client specific types. Use the general purpose Client instead" +)] pub type GraphQLClientClient = AsyncWebsocketClient; + /// A websocket client builder for the graphql_client graphql crate #[cfg(feature = "client-graphql-client")] +#[allow(deprecated)] +#[deprecated( + since = "0.8.0-rc.1", + note = "graphql-ws-client no longer needs client specific types. Use the general purpose Client instead" +)] pub type GraphQLClientClientBuilder = AsyncWebsocketClientBuilder; diff --git a/src/native.rs b/src/native.rs index 01fd2e1..4ed4f74 100644 --- a/src/native.rs +++ b/src/native.rs @@ -1,9 +1,10 @@ use async_tungstenite::tungstenite::{self, protocol::CloseFrame}; use futures::{AsyncRead, AsyncWrite, SinkExt, StreamExt}; -use crate::{websockets::WebsocketMessage, Error}; +use crate::Error; -impl WebsocketMessage for tungstenite::Message { +#[allow(deprecated)] +impl crate::legacy::websockets::WebsocketMessage for tungstenite::Message { type Error = tungstenite::Error; fn new(text: String) -> Self { diff --git a/src/next/builder.rs b/src/next/builder.rs index 040b3c7..cd9b718 100644 --- a/src/next/builder.rs +++ b/src/next/builder.rs @@ -14,7 +14,7 @@ use super::{ /// Builder for Clients. /// /// ```rust -/// use graphql_ws_client::next::{Client}; +/// use graphql_ws_client::Client; /// use std::future::IntoFuture; /// # /// # async fn example() -> Result<(), graphql_ws_client::Error> { @@ -33,7 +33,7 @@ impl super::Client { /// Creates a ClientBuilder with the given connection. /// /// ```rust - /// use graphql_ws_client::next::{Client}; + /// use graphql_ws_client::Client; /// use std::future::IntoFuture; /// # async fn example() -> Result<(), graphql_ws_client::Error> { /// # let connection = graphql_ws_client::__doc_utils::Conn; @@ -78,7 +78,7 @@ impl ClientBuilder { /// Initialise a Client and use it to run a single streaming operation /// /// ```rust - /// use graphql_ws_client::next::{Client}; + /// use graphql_ws_client::Client; /// use std::future::IntoFuture; /// # async fn example() -> Result<(), graphql_ws_client::Error> { /// # let connection = graphql_ws_client::__doc_utils::Conn; diff --git a/tests/cynic-tests.rs b/tests/cynic-tests.rs index 5441c47..2448a28 100644 --- a/tests/cynic-tests.rs +++ b/tests/cynic-tests.rs @@ -72,9 +72,7 @@ async fn main_test() { println!("Connected"); - let (mut client, actor) = graphql_ws_client::next::Client::build(connection) - .await - .unwrap(); + let (mut client, actor) = graphql_ws_client::Client::build(connection).await.unwrap(); tokio::spawn(actor.into_future()); @@ -137,7 +135,7 @@ async fn oneshot_operation_test() { println!("Connected"); - let stream = graphql_ws_client::next::Client::build(connection) + let stream = graphql_ws_client::Client::build(connection) .streaming_operation(build_query()) .await .unwrap();