Skip to content

Commit

Permalink
🎉 Release 0.1.4
Browse files Browse the repository at this point in the history
  • Loading branch information
oscartbeaumont committed Mar 17, 2024
1 parent 45d3821 commit dc8b8f6
Show file tree
Hide file tree
Showing 8 changed files with 106 additions and 37 deletions.
9 changes: 8 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]
name = "rspc"
description = "A blazing fast and easy to use TRPC server for Rust."
version = "0.1.3"
version = "0.1.4"
authors = ["Oscar Beaumont <[email protected]>"]
edition = "2021"
license = "MIT"
Expand Down Expand Up @@ -41,6 +41,9 @@ mac_address = ["specta/mac_address"]
bit-vec = ["specta/bit-vec"]
bson = ["specta/bson"]

# Internal (doesn't follow semver so don't use directly)
internal_axum_07 = ["dep:axum"]

[dependencies]
specta = { version = "1.0.5", features = ["serde", "typescript"] }
httpz = { version = "0.0.6", optional = true } # TODO: Move back to crates.io release
Expand All @@ -52,6 +55,10 @@ tokio = { version = "1.36.0", features = ["sync", "rt", "macros"] }
tauri = { version = "1.6.1", optional = true }
tracing = { version = "0.1.40", optional = true }


# TODO: This is temporary. It won't stay in the core long term.
axum = { version = "0.7.4", optional = true }

[dev-dependencies]
async-stream = "0.3.5"

Expand Down
2 changes: 1 addition & 1 deletion crates/axum/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,4 @@ categories = ["web-programming", "asynchronous"]
[dependencies]
axum = "0.7.4"
httpz = { version = "0.0.6", features = ["axum"] }
rspc = { version = "0.1.3", path = "../.." }
rspc = { version = "0.1.4", path = "../..", features = ["internal_axum_07"] }
14 changes: 11 additions & 3 deletions examples/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,21 @@ publish = false

[dependencies]
rspc = { path = "../", features = ["axum"] }
rspc-axum = { path = "../crates/axum" }
async-stream = "0.3.5"
axum = "0.7.4"
chrono = { version = "0.4.35", features = ["serde"] }
serde = { version = "1.0.197", features = ["derive"] }
time = "0.3.34"
tokio = { version = "1.36.0", features = ["rt-multi-thread", "macros", "time", "sync"], default-features = false }
tower-cookies = "0.10.0"
tower-http = { version = "0.5.2", default-features = false, features = ["cors"] }
tokio = { version = "1.36.0", features = [
"rt-multi-thread",
"macros",
"time",
"sync",
], default-features = false }
tower-cookies = { version = "0.10.0", features = ["axum-core"] }
tower-http = { version = "0.5.2", default-features = false, features = [
"cors",
] }
uuid = { version = "1.7.0", features = ["v4", "serde"] }
serde_json = "1.0.114"
33 changes: 15 additions & 18 deletions examples/src/bin/axum.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,24 +36,22 @@ async fn main() {
// Attach the rspc router to your axum router. The closure is used to generate the request context for each request.
.nest(
"/rspc",
router
.endpoint(|mut req: Request| {
// Official rspc API
println!("Client requested operation '{}'", req.uri().path());
rspc_axum::endpoint(router.endpoint(|mut req: Request| {
// Official rspc API
println!("Client requested operation '{}'", req.uri().path());

// Deprecated Axum extractors - this API will be removed in the future
// The first generic is the Axum extractor and the second is the type of your Axum state.
// If the state generic is wrong you will get a **RUNTIME** error so be careful!
// TODO: Be aware these will NOT work for websockets. If this is a problem for you open an issue on GitHub!
let path = req
.deprecated_extract::<Path<String>, ()>()
.expect("I got the Axum state type wrong!")
.unwrap();
println!("Client requested operation '{}'", path.0);
// Deprecated Axum extractors - this API will be removed in the future
// The first generic is the Axum extractor and the second is the type of your Axum state.
// If the state generic is wrong you will get a **RUNTIME** error so be careful!
// TODO: Be aware these will NOT work for websockets. If this is a problem for you open an issue on GitHub!
let path = req
.deprecated_extract::<Path<String>, ()>()
.expect("I got the Axum state type wrong!")
.unwrap();
println!("Client requested operation '{}'", path.0);

()
})
.axum(),
()
})),
)
// We disable CORS because this is just an example. DON'T DO THIS IN PRODUCTION!
.layer(
Expand All @@ -65,8 +63,7 @@ async fn main() {

let addr = "[::]:4000".parse::<std::net::SocketAddr>().unwrap(); // This listens on IPv6 and IPv4
println!("listening on http://{}/rspc/version", addr);
axum::Server::bind(&addr)
.serve(app.into_make_service())
axum::serve(tokio::net::TcpListener::bind(addr).await.unwrap(), app)
.await
.unwrap();
}
6 changes: 3 additions & 3 deletions examples/src/bin/cookies.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ async fn main() {
// Attach the rspc router to your axum router. The closure is used to generate the request context for each request.
.nest(
"/rspc",
rspc_axum::endpoint(
router
.endpoint(|mut req: Request| {
// TODO: This API is going to be replaced with a httpz cookie manager in the next release to deal with Axum's recent changes.
Expand All @@ -52,7 +53,7 @@ async fn main() {
.unwrap();
Ctx { cookies }
})
.axum(),
),
)
.layer(CookieManagerLayer::new())
// We disable CORS because this is just an example. DON'T DO THIS IN PRODUCTION!
Expand All @@ -65,8 +66,7 @@ async fn main() {

let addr = "[::]:4000".parse::<std::net::SocketAddr>().unwrap(); // This listens on IPv6 and IPv4
println!("listening on http://{}/rspc/version", addr);
axum::Server::bind(&addr)
.serve(app.into_make_service())
axum::serve(tokio::net::TcpListener::bind(addr).await.unwrap(), app)
.await
.unwrap();
}
8 changes: 5 additions & 3 deletions examples/src/bin/global_context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,14 @@ async fn main() {
// `Arc<Mutex<T>>`. This could be your database connecton or any other value.
let count = Arc::new(AtomicU16::new(0));

let app = axum::Router::new().nest("/rspc", router.endpoint(move || MyCtx { count }).axum());
let app = axum::Router::new().nest(
"/rspc",
rspc_axum::endpoint(router.endpoint(move || MyCtx { count })),
);

let addr = "[::]:4000".parse::<std::net::SocketAddr>().unwrap(); // This listens on IPv6 and IPv4
println!("listening on http://{}/rspc/hit", addr);
axum::Server::bind(&addr)
.serve(app.into_make_service())
axum::serve(tokio::net::TcpListener::bind(addr).await.unwrap(), app)
.await
.unwrap();
}
11 changes: 4 additions & 7 deletions examples/src/bin/middleware.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,11 +108,9 @@ async fn main() {
// Attach the rspc router to your axum router. The closure is used to generate the request context for each request.
.nest(
"/rspc",
router
.endpoint(|| UnauthenticatedContext {
session_id: Some("abc".into()), // Change this line to control whether you are authenticated and can access the "another" query.
})
.axum(),
rspc_axum::endpoint(router.endpoint(|| UnauthenticatedContext {
session_id: Some("abc".into()), // Change this line to control whether you are authenticated and can access the "another" query.
})),
)
// We disable CORS because this is just an example. DON'T DO THIS IN PRODUCTION!
.layer(
Expand All @@ -124,8 +122,7 @@ async fn main() {

let addr = "[::]:4000".parse::<std::net::SocketAddr>().unwrap(); // This listens on IPv6 and IPv4
println!("listening on http://{}/rspc/version", addr);
axum::Server::bind(&addr)
.serve(app.into_make_service())
axum::serve(tokio::net::TcpListener::bind(addr).await.unwrap(), app)
.await
.unwrap();
}
60 changes: 59 additions & 1 deletion src/integrations/httpz.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use axum::http::request::Parts;
use futures::{SinkExt, StreamExt};
use httpz::{
axum::axum::extract::FromRequestParts,
Expand Down Expand Up @@ -221,7 +222,7 @@ impl Request {
/// This methods allows using Axum extractors.
/// This was previously supported but in Axum 0.6 it's not typesafe anymore so we are going to remove this API.
// TODO: Remove this API once rspc's official cookie API is more stabilised.
#[cfg(feature = "axum")]
#[cfg(all(not(feature = "internal_axum_07"), feature = "axum"))]
pub fn deprecated_extract<E, S>(&mut self) -> Option<Result<E, E::Rejection>>
where
E: FromRequestParts<S>,
Expand All @@ -240,6 +241,63 @@ impl Request {
resp
}))
}

/// This methods allows using Axum extractors.
/// This was previously supported but in Axum 0.6 it's not typesafe anymore so we are going to remove this API.
// TODO: Remove this API once rspc's official cookie API is more stabilised.
#[cfg(all(feature = "internal_axum_07", feature = "axum"))]
pub fn deprecated_extract<E, S>(&mut self) -> Option<Result<E, E::Rejection>>
where
E: axum::extract::FromRequestParts<S>,
S: Clone + Send + Sync + 'static,
{
let parts = self.0.parts_mut();

let state = parts
.extensions
.remove::<httpz::axum::axum::extract::State<S>>()?;

// This is bad but it's a temporary API so I don't care.
Some(futures::executor::block_on(async {
let (mut new_parts, _) = axum::http::Request::new(()).into_parts();
new_parts.method = match parts.method {
httpz::http::Method::OPTIONS => axum::http::Method::OPTIONS,
httpz::http::Method::GET => axum::http::Method::GET,
httpz::http::Method::POST => axum::http::Method::POST,
httpz::http::Method::PUT => axum::http::Method::PUT,
httpz::http::Method::DELETE => axum::http::Method::DELETE,
httpz::http::Method::HEAD => axum::http::Method::HEAD,
httpz::http::Method::TRACE => axum::http::Method::TRACE,
httpz::http::Method::CONNECT => axum::http::Method::CONNECT,
httpz::http::Method::PATCH => axum::http::Method::PATCH,
_ => unreachable!(),
};
new_parts.uri = axum::http::Uri::try_from(parts.uri.to_string()).expect("unreachable");
new_parts.version = match parts.version {
httpz::http::Version::HTTP_10 => axum::http::Version::HTTP_10,
httpz::http::Version::HTTP_11 => axum::http::Version::HTTP_11,
httpz::http::Version::HTTP_2 => axum::http::Version::HTTP_2,
httpz::http::Version::HTTP_3 => axum::http::Version::HTTP_3,
_ => unreachable!(),
};
for (key, value) in parts.headers.iter() {
new_parts.headers.insert(
axum::http::HeaderName::from_bytes(key.as_str().as_bytes())
.expect("unreachable"),
axum::http::HeaderValue::from_bytes(value.as_bytes()).expect("unreachable"),
);
}
// parts.extensions: Extensions, // TODO: This can't be converted.

let resp = <E as axum::extract::FromRequestParts<S>>::from_request_parts(
&mut new_parts,
&state.0,
)
.await;
new_parts.extensions.insert(state);
resp
}))
}
}

impl<TCtx> Router<TCtx>
Expand Down

0 comments on commit dc8b8f6

Please sign in to comment.