Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Router v2 #326

Draft
wants to merge 70 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
70 commits
Select commit Hold shift + click to select a range
81ddf80
plan router v2
Dec 2, 2024
e21b9fb
Merge remote-tracking branch 'origin/main' into router-v2
Dec 3, 2024
27afad0
change 'merge' to 'nest' and key procedures by vec of strings
Brendonovich Dec 4, 2024
4016c8e
IntoIter + Debug
Brendonovich Dec 4, 2024
2286acf
legacy to modern router interop
Dec 4, 2024
a61abb3
`Router2` type exporting
Dec 4, 2024
c288153
`Router::merge` + some cleanup
Dec 4, 2024
62da9bd
d /Users/oscar/exile/specta-website-solidbased; clear; lazygit
Dec 4, 2024
aa9fe29
`rspc_axum` backend for `rspc_core` + `Router2` include the legacy bi…
Dec 4, 2024
2695450
fix axum and tauri
Dec 4, 2024
eb44eab
fix examples
Dec 4, 2024
2eca8ea
core crate publishing metadata
Dec 4, 2024
471d9ef
fix header + upgrade Specta
Dec 4, 2024
e2a381d
`rspc-client` prototype + new type exporting setup
oscartbeaumont Dec 6, 2024
f4fb572
jump to definition working
oscartbeaumont Dec 6, 2024
7792a08
new procedure and middleware syntax
oscartbeaumont Dec 6, 2024
1fa1905
migrate middleware from `main-rewrite`
oscartbeaumont Dec 6, 2024
b198448
virtual workspace
oscartbeaumont Dec 6, 2024
c4a6d15
add `unstable` and `nolegacy` features to rspc
oscartbeaumont Dec 6, 2024
6c7e5cc
remove `tracing` feature
oscartbeaumont Dec 6, 2024
377161c
workspace lints + wip `rspc-devtools`
oscartbeaumont Dec 6, 2024
06279ea
init cache middleware
oscartbeaumont Dec 7, 2024
c1952c3
expose window in tauri ctx + use ipc channel
Brendonovich Dec 7, 2024
7238008
trim down tauri integration + enumify DynInput
Brendonovich Dec 8, 2024
bc0e1f0
simplify subscription stream handling
Brendonovich Dec 8, 2024
bd7f922
better document tauri integration
Brendonovich Dec 8, 2024
c2f4ac1
tauri spaawn
Brendonovich Dec 8, 2024
ca5f31d
handle first value properly
Brendonovich Dec 8, 2024
5859923
improve Tauri integration even more
oscartbeaumont Dec 8, 2024
42b6948
more efficent Tauri integration
oscartbeaumont Dec 8, 2024
76ec525
more efficent Tauri deserialisation
oscartbeaumont Dec 8, 2024
f4f27fe
init placeholder binario crate
oscartbeaumont Dec 8, 2024
598c85e
cleanup and generalise `ProcedureStream` + break middleware
oscartbeaumont Dec 9, 2024
21f7212
fix middleware api
oscartbeaumont Dec 9, 2024
1efb19b
drop `ProcedureError::Serialize` + properly handle in Tauri integration
oscartbeaumont Dec 9, 2024
dd6a76e
gracefully handle panics in procedures
oscartbeaumont Dec 9, 2024
2a42867
cleanup `Procedures`
oscartbeaumont Dec 9, 2024
d5e17d7
access `State` in procedure + wip `rspc-cache`
oscartbeaumont Dec 9, 2024
7e95b09
working memory cache
oscartbeaumont Dec 9, 2024
f0965ef
make cache work
oscartbeaumont Dec 9, 2024
a413734
cleanup cache middleware
oscartbeaumont Dec 9, 2024
5ccc33a
tauri example + handleRpc
Brendonovich Dec 10, 2024
c8f8054
drop `rspc::Infallible` + untagged on `tauri-plugin-rspc::Response`
oscartbeaumont Dec 10, 2024
a263580
duplicate procedure errors
Brendonovich Dec 11, 2024
cc19437
`Procedures` as struct & include line number on duplicate procedure k…
oscartbeaumont Dec 11, 2024
6a49b0d
basic tauri integration query/mutation working
Brendonovich Dec 16, 2024
b8f10d3
simplify wire format
Brendonovich Dec 17, 2024
2de248f
`rspc_axum::Endpoint` wip
oscartbeaumont Dec 18, 2024
209d66d
Merge branch 'router-v2' of https://github.com/specta-rs/rspc into ro…
oscartbeaumont Dec 18, 2024
62ba199
cleanup
oscartbeaumont Dec 18, 2024
01a0980
`example-core` + `rspc-actix-web` wip
oscartbeaumont Dec 18, 2024
38024c2
init sfm example
oscartbeaumont Dec 20, 2024
38328f2
Extensions + SFM wip & `rspc_axum` in userspace
oscartbeaumont Dec 23, 2024
8a7dc86
remove HTTP status codes from core + comment out broken stuff
oscartbeaumont Dec 23, 2024
d45ded7
basically working `rspc_invalidation`
oscartbeaumont Dec 23, 2024
43d0858
`ProcedureStream` advanced queuing and flushing
oscartbeaumont Dec 23, 2024
792e7a6
use extension for openapi
Brendonovich Dec 24, 2024
5ed8808
major `ProcedureStream` work + add `DynOutput`
oscartbeaumont Dec 25, 2024
f485eea
upgrade dependencies
oscartbeaumont Dec 25, 2024
86cf6e0
@Brendonovich decreed we have `crates/`
oscartbeaumont Dec 25, 2024
3b642e7
`README`'s for crates
oscartbeaumont Dec 25, 2024
5696ef2
fix `ProcedureStream` error handling + `rspc-validator`
oscartbeaumont Dec 25, 2024
a6889ca
`rspc-zer` prototype
oscartbeaumont Dec 25, 2024
67d48d4
`rspc-legacy` crate
oscartbeaumont Dec 26, 2024
f5e3e92
drop `rspc::legacy` in favor of `rspc_legacy`
oscartbeaumont Dec 26, 2024
36dfe67
cleanup naming of modern stuff
oscartbeaumont Dec 26, 2024
6081d0e
flatten `rspc::modern` into root
oscartbeaumont Dec 26, 2024
322c8dc
`rspc-binario` prototype
oscartbeaumont Dec 26, 2024
b782394
`example-binario` + remove `block_on` decoding input
oscartbeaumont Dec 26, 2024
07ac2f0
`rpc-binario` streaming
oscartbeaumont Dec 27, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,7 @@ updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
interval: "weekly"

# TODO: Rust
# TODO: npm
3 changes: 0 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,6 @@ Cargo.lock
# System Files
.DS_Store

# Typescript bindings exported by the examples
bindings.ts

# Node
node_modules
.pnpm-debug.log*
Expand Down
87 changes: 42 additions & 45 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,51 +1,48 @@
[package]
name = "rspc"
description = "A blazing fast and easy to use TRPC server for Rust."
version = "0.3.1"
authors = ["Oscar Beaumont <[email protected]>"]
edition = "2021"
license = "MIT"
include = ["/src", "/LICENCE", "/README.md"]
repository = "https://github.com/specta-rs/rspc"
documentation = "https://docs.rs/rspc/latest/rspc"
keywords = ["async", "specta", "rust-to-ts", "typescript", "typesafe"]
categories = ["web-programming", "asynchronous"]

# /bin/sh RUSTDOCFLAGS="--cfg docsrs" cargo +nightly doc --all-features
[package.metadata."docs.rs"]
all-features = true
rustdoc-args = ["--cfg", "docsrs"]
[workspace]
resolver = "2"
members = [
"./crates/*",
"./rspc",
"./integrations/*",
"./examples/core",
"./examples/axum",
"./examples/actix-web",
"./examples/client",
"./examples/tauri/src-tauri",
"./examples/legacy",
"./examples/legacy-compat",
"./examples/binario",
]

[features]
default = []
tracing = ["dep:tracing"]
[workspace.dependencies]
# Private
specta-typescript = { version = "0.0.7", default-features = false }
pin-project-lite = { version = "0.2", default-features = false }
erased-serde = { version = "0.4", default-features = false }

[dependencies]
# Public
serde = { version = "1", features = ["derive"] } # TODO: Remove features
futures = "0.3"
specta = { version = "=2.0.0-rc.20", features = [
"derive",
"serde",
"serde_json",
] } # TODO: Drop all features
specta-util = "0.0.7"
specta = { version = "=2.0.0-rc.20", default-features = false }
serde = { version = "1", default-features = false }
serde_json = { version = "1", default-features = false }
futures = { version = "0.3", default-features = false }
futures-core = { version = "0.3", default-features = false }
futures-util = { version = "0.3", default-features = false }
tracing = { version = "0.1", default-features = false }

# Private
serde-value = "0.7"
erased-serde = "0.4"

# Temporary # TODO: Remove
specta-typescript = { version = "=0.0.7", features = [] }
serde_json = "1.0.133" # TODO: Drop this
thiserror = "2.0.3"
tokio = { version = "1.41.1", features = ["sync", "rt", "macros"] }
tracing = { version = "0.1.40", optional = true }
transient = "0.4.1"
better_any = "0.2.0"
[workspace.lints.clippy]
all = { level = "warn", priority = -1 }
cargo = { level = "warn", priority = -1 }
unwrap_used = { level = "warn", priority = -1 }
panic = { level = "warn", priority = -1 }
todo = { level = "warn", priority = -1 }
panic_in_result_fn = { level = "warn", priority = -1 }

# https://github.com/rust-lang/rust/issues/77125
typeid = "1.0.2"
[patch.crates-io]
specta = { git = "https://github.com/specta-rs/specta", rev = "bf3a0937cceb29eca11df207076b9e1b942ba7bb" }
specta-serde = { git = "https://github.com/specta-rs/specta", rev = "bf3a0937cceb29eca11df207076b9e1b942ba7bb" }
specta-typescript = { git = "https://github.com/specta-rs/specta", rev = "bf3a0937cceb29eca11df207076b9e1b942ba7bb" }

[workspace]
members = ["./crates/*", "./examples", "./examples/axum", "crates/core"]
# specta = { path = "/Users/oscar/Desktop/specta/specta" }
# specta-typescript = { path = "/Users/oscar/Desktop/specta/specta-typescript" }
# specta-serde = { path = "/Users/oscar/Desktop/specta/specta-serde" }
# specta-util = { path = "/Users/oscar/Desktop/specta/specta-util" }
21 changes: 21 additions & 0 deletions crates/binario/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
[package]
name = "rspc-binario"
description = "Binario support for rspc"
version = "0.0.0"
edition = "2021"
publish = false # TODO: Crate metadata & publish

[dependencies]
binario = "0.0.2"
futures-util.workspace = true
rspc = { path = "../../rspc" }
specta = { workspace = true }
tokio = "1.42.0"

# /bin/sh RUSTDOCFLAGS="--cfg docsrs" cargo +nightly doc --all-features
[package.metadata."docs.rs"]
all-features = true
rustdoc-args = ["--cfg", "docsrs"]

[lints]
workspace = true
12 changes: 12 additions & 0 deletions crates/binario/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# rspc 🤝 Binario

[![docs.rs](https://img.shields.io/crates/v/rspc-binario)](https://docs.rs/rspc-binario)

> [!CAUTION]
> This crate is a proof of concept.
>
> It is not intended for production use and will likely remain that way.

Use [Binario](https://github.com/oscartbeaumont/binario) instead of [Serde](https://serde.rs) for serialization and deserialization.

This is a proof of concept to show that rspc has the ability to support any serialization libraries.
172 changes: 172 additions & 0 deletions crates/binario/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
//! rspc-binario: Binario support for rspc
//!
//! TODO:
//! - Support for streaming the result. Right now we encode into a buffer.
//! - `BinarioDeserializeError` should end up as a `ProcedureError::Deserialize` not `ProcedureError::Resolver`
//! - Binario needs impl for `()` for procedures with no input.
//! - Client integration
//! - Cleanup HTTP endpoint on `example-binario`. Maybe don't use HTTP cause Axum's model doesn't mesh with Binario?
//! - Maybe actix-web example to show portability. Might be interesting with the fact that Binario depends on `tokio::AsyncRead`.
//!
#![forbid(unsafe_code)]
#![cfg_attr(docsrs, feature(doc_cfg))]
#![doc(
html_logo_url = "https://github.com/specta-rs/rspc/blob/main/.github/logo.png?raw=true",
html_favicon_url = "https://github.com/specta-rs/rspc/blob/main/.github/logo.png?raw=true"
)]

use std::{error, fmt, marker::PhantomData, pin::Pin};

use binario::{encode, Decode, Encode};
use futures_util::{stream, Stream, StreamExt};
use rspc::{
middleware::Middleware, DynInput, ProcedureError, ProcedureStream, ResolverInput,
ResolverOutput,
};
use specta::{datatype::DataType, Generics, Type, TypeCollection};
use tokio::io::AsyncRead;

enum Repr {
Bytes(Vec<u8>),
Stream(Pin<Box<dyn AsyncRead + Send>>),
}

pub struct BinarioInput(Repr);

impl BinarioInput {
pub fn from_bytes(bytes: Vec<u8>) -> Self {
Self(Repr::Bytes(bytes))
}

pub fn from_stream(stream: impl AsyncRead + Send + 'static) -> Self {
Self(Repr::Stream(Box::pin(stream)))
}
}

pub struct TypedBinarioInput<T>(pub BinarioInput, pub PhantomData<T>);

impl<T: Decode + Type + Send + 'static> ResolverInput for TypedBinarioInput<T> {
fn data_type(types: &mut TypeCollection) -> DataType {
T::inline(types, Generics::Definition)
}

fn from_input(input: DynInput) -> Result<Self, ProcedureError> {
Ok(Self(input.value()?, PhantomData))
}
}

// TODO: This should probs be a stream not a buffer.
// Binario currently only supports `impl AsyncRead` not `impl Stream`
pub struct BinarioOutput(pub Vec<u8>);
pub struct TypedBinarioOutput<T, M>(pub T, pub PhantomData<fn() -> M>);

pub(crate) mod sealed {
use super::*;

pub trait ValidBinarioOutput<M>: Send + 'static {
type T: Encode + Send + Sync + 'static;
fn data_type(types: &mut TypeCollection) -> DataType;
fn into_stream(
self,
) -> impl Stream<Item = Result<Self::T, ProcedureError>> + Send + 'static;
}
pub enum ValueMarker {}
impl<T: Encode + Type + Send + Sync + 'static> ValidBinarioOutput<ValueMarker> for T {
type T = T;
fn data_type(types: &mut TypeCollection) -> DataType {
T::inline(types, Generics::Definition)
}
fn into_stream(
self,
) -> impl Stream<Item = Result<Self::T, ProcedureError>> + Send + 'static {
stream::once(async move { Ok(self) })
}
}
pub enum StreamMarker {}
impl<S: Stream + Send + Sync + 'static> ValidBinarioOutput<StreamMarker> for rspc::Stream<S>
where
S::Item: Encode + Type + Send + Sync + 'static,
{
type T = S::Item;

fn data_type(types: &mut TypeCollection) -> DataType {
S::Item::inline(types, Generics::Definition)
}
fn into_stream(
self,
) -> impl Stream<Item = Result<Self::T, ProcedureError>> + Send + 'static {
self.0.map(|v| Ok(v))
}
}
}

impl<TError, M: 'static, T: sealed::ValidBinarioOutput<M>> ResolverOutput<TError>
for TypedBinarioOutput<T, M>
{
type T = BinarioOutput;

fn data_type(types: &mut TypeCollection) -> DataType {
T::data_type(types)
}

fn into_stream(self) -> impl Stream<Item = Result<Self::T, ProcedureError>> + Send + 'static {
// TODO: Encoding into a buffer is not how Binario is intended to work but it's how rspc needs it.
self.0.into_stream().then(|v| async move {
let mut buf = Vec::new();
encode(&v?, &mut buf).await.unwrap(); // TODO: Error handling
Ok(BinarioOutput(buf))
})
}

fn into_procedure_stream(
stream: impl Stream<Item = Result<Self::T, ProcedureError>> + Send + 'static,
) -> ProcedureStream {
ProcedureStream::from_stream_value(stream)
}
}

pub fn binario<TError, TCtx, TInput, TResult, M>() -> Middleware<
TError,
TCtx,
TypedBinarioInput<TInput>,
TypedBinarioOutput<TResult, M>,
TCtx,
TInput,
TResult,
>
where
TError: From<DeserializeError> + Send + 'static,
TCtx: Send + 'static,
TInput: Decode + Send + 'static,
TResult: sealed::ValidBinarioOutput<M>,
{
Middleware::new(
move |ctx: TCtx, input: TypedBinarioInput<TInput>, next| async move {
let input = match input.0 .0 {
Repr::Bytes(bytes) => binario::decode::<TInput, _>(bytes.as_slice()).await,
Repr::Stream(stream) => binario::decode::<TInput, _>(stream).await,
}
.map_err(DeserializeError)?;

next.exec(ctx, input)
.await
.map(|v| TypedBinarioOutput(v, PhantomData))
},
)
}

pub struct DeserializeError(pub std::io::Error);

impl fmt::Debug for DeserializeError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt(f)
}
}

impl fmt::Display for DeserializeError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt(f)
}
}

impl error::Error for DeserializeError {}
18 changes: 18 additions & 0 deletions crates/cache/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
[package]
name = "rspc-cache"
version = "0.0.0"
edition = "2021"
publish = false

[dependencies]
moka = { version = "0.12.8", features = ["sync"] }
pin-project-lite = { workspace = true }
rspc = { path = "../../rspc" }

# /bin/sh RUSTDOCFLAGS="--cfg docsrs" cargo +nightly doc --all-features
[package.metadata."docs.rs"]
all-features = true
rustdoc-args = ["--cfg", "docsrs"]

[lints]
workspace = true
34 changes: 34 additions & 0 deletions crates/cache/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# rspc cache

[![docs.rs](https://img.shields.io/crates/v/rspc-cache)](https://docs.rs/rspc-cache)

> [!CAUTION]
> This crate is still a work in progress. You can use it but we can't guarantee that it's API won't change.

Provides a simple way to cache the results of rspc queries with pluggable backends.

Features:
- Simple to use
- Pluggable backends (memory, redis, etc.)
- Configurable cache TTL

## Example

```rust
// TODO: imports

fn todo() -> Router2<Ctx> {
Router2::new()
.setup(CacheState::builder(Memory::new()).mount())
.procedure("my_query", {
<BaseProcedure>::builder()
.with(cache())
.query(|_, _: ()| async {
// if input.some_arg {}
cache_ttl(10);

Ok(SystemTime::now())
})
})
}
```
Loading