Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
69e89b5
commit 398c957
Showing
6 changed files
with
371 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
[package] | ||
name = "rspc2" | ||
version = "0.2.0" | ||
edition = "2021" | ||
|
||
[dependencies] | ||
# Public | ||
serde = { version = "1", default-features = false } | ||
specta = { version = "1", default-features = false } | ||
|
||
# Private | ||
erased-serde = { version = "0.4.2", default-features = false, features = ["std"] } | ||
serde-value = "0.7.0" | ||
|
||
# Temporary | ||
serde_json = "1" | ||
tokio = "1" | ||
futures = "0.3.30" | ||
|
||
[dev-dependencies] | ||
tokio = { version = "1", features = ["full"] } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
use std::convert::Infallible; | ||
|
||
use rspc2::{serde, Procedure, Router}; | ||
|
||
// TODO | ||
use rspc2::serde::de::IntoDeserializer; | ||
|
||
pub struct Primitive<T>(T); | ||
|
||
// The error type here is just a placeholder, to contrain it. | ||
impl<'de, T: IntoDeserializer<'de, serde::de::value::Error>> | ||
IntoDeserializer<'de, serde::de::value::Error> for Primitive<T> | ||
{ | ||
type Deserializer = T::Deserializer; | ||
|
||
fn into_deserializer(self) -> Self::Deserializer { | ||
self.0.into_deserializer() | ||
} | ||
} | ||
|
||
#[tokio::main] | ||
async fn main() { | ||
let p = <Procedure>::new::<Infallible>() | ||
.error::<String>() | ||
.query(|ctx, input: i32| async move { Ok(()) }); | ||
|
||
let router = <Router>::new().procedure("a", p).build().unwrap(); | ||
|
||
let result = router | ||
.exec( | ||
"a", | ||
(), | ||
Primitive(42).into_deserializer(), | ||
rspc2::serde_json::value::Serializer, | ||
) | ||
// .await // TODO | ||
.unwrap(); | ||
println!("{:?}", result); | ||
|
||
router | ||
.exec( | ||
"a", | ||
(), | ||
Primitive(42).into_deserializer(), | ||
&mut rspc2::serde_json::Serializer::new(std::io::stdout()), | ||
) | ||
// .await // TODO | ||
.unwrap(); | ||
|
||
// let value = rspc::serde_json::json!(43); | ||
// let result = router | ||
// .exec::<JsonValue, TokioRuntime>("a", (), value.into_deserializer()) | ||
// .await | ||
// .unwrap(); | ||
// println!("{:?}", result); | ||
|
||
// let result = router | ||
// .exec::<JsonValue, TokioRuntime>( | ||
// "a", | ||
// (), | ||
// &mut rspc::serde_json::Deserializer::new(rspc::serde_json::de::StrRead::new("44")), | ||
// ) | ||
// .await | ||
// .unwrap(); | ||
// println!("{:?}", result); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
// use serde::Serialize; | ||
|
||
// // TODO: Drop this trait if still unused | ||
|
||
// /// TODO | ||
// pub trait Format { | ||
// type Output; | ||
|
||
// fn new() -> Self; | ||
|
||
// fn serialize<T: Serialize>(&mut self, v: T); | ||
|
||
// fn take(self) -> Self::Output; | ||
// } | ||
|
||
pub(crate) trait DynFormat { | ||
fn serialize(&mut self, v: &dyn erased_serde::Serialize); | ||
} | ||
|
||
// impl<T: Format> DynFormat for T { | ||
// fn serialize(&mut self, v: &dyn erased_serde::Serialize) { | ||
// <T as Format>::serialize(self, v) | ||
// } | ||
// } | ||
|
||
// // TODO: Move into another crate | ||
|
||
// /// TODO | ||
// pub struct JsonValue { | ||
// result: serde_json::Value, | ||
// } | ||
|
||
// impl JsonValue { | ||
// pub fn new() -> Self { | ||
// Self { | ||
// result: serde_json::Value::Null, | ||
// } | ||
// } | ||
// } | ||
|
||
// impl Format for JsonValue { | ||
// type Output = serde_json::Value; | ||
|
||
// fn new() -> Self { | ||
// Self { | ||
// result: serde_json::Value::Null, | ||
// } | ||
// } | ||
|
||
// fn serialize<T: Serialize>(&mut self, v: T) { | ||
// self.result = serde_json::to_value(v).unwrap(); // TODO: Error handling | ||
// } | ||
|
||
// fn take(self) -> Self::Output { | ||
// self.result | ||
// } | ||
// } | ||
|
||
// // TODO: Json into Vec<u8> buffer | ||
// // TODO: FormData |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
//! rspc: A blazingly fast and easy to use tRPC-like server for Rust. | ||
//! | ||
//! Checkout the official docs <https://rspc.dev> | ||
//! | ||
|
||
// TODO: Clippy lints + `Cargo.toml` | ||
|
||
mod format; | ||
mod procedure; | ||
mod router; | ||
|
||
// pub use format::Format; | ||
pub use procedure::{Procedure, ProcedureBuilder, ProcedureFunc}; | ||
pub use router::{Router, RouterBuilder}; | ||
|
||
// TODO: Remove this from public API | ||
// pub use format::JsonValue; | ||
pub use serde; | ||
pub use serde_json; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
use std::{borrow::Cow, future::Future, marker::PhantomData}; | ||
|
||
use crate::Router; | ||
|
||
/// TODO | ||
pub struct Procedure<TCtx = ()> { | ||
#[cfg(debug_assertions)] | ||
pub(crate) location: std::panic::Location<'static>, | ||
pub(crate) build: Box<dyn FnOnce(Cow<'static, str>, &mut Router<TCtx>)>, | ||
} | ||
|
||
impl<TCtx> Procedure<TCtx> { | ||
pub fn new<TError>() -> ProcedureBuilder<Generics<TCtx, TError, ()>> { | ||
ProcedureBuilder(Generics(PhantomData)) | ||
} | ||
} | ||
|
||
mod private { | ||
use super::*; | ||
pub struct Generics<TCtx, TError, TStream>(pub(super) PhantomData<(TCtx, TError, TStream)>); | ||
} | ||
use private::Generics; | ||
|
||
/// TODO | ||
pub struct ProcedureBuilder<G>(G); | ||
|
||
impl<TCtx, TError, TStream> ProcedureBuilder<Generics<TCtx, TError, TStream>> { | ||
/// Manually override the error type of this procedure. | ||
/// By default the error type will be infered from the [Router](rspc::Router). | ||
pub fn error<T>(self) -> ProcedureBuilder<Generics<TCtx, T, TStream>> { | ||
ProcedureBuilder(Generics(PhantomData)) | ||
} | ||
|
||
// TODO: Middleware working | ||
pub fn with(self) -> Self { | ||
todo!(); | ||
} | ||
|
||
#[track_caller] | ||
pub fn query<F, I>(self, handler: F) -> Procedure<TCtx> | ||
where | ||
F: ProcedureFunc<TCtx, TError, I>, | ||
{ | ||
let p: InternalProcedure<TCtx, TError, F, I> = InternalProcedure { | ||
handler, // Box::new(|a, b| handler.exec(a, b)), | ||
phantom: PhantomData, | ||
}; | ||
// Procedure { | ||
// #[cfg(debug_assertions)] | ||
// location: *std::panic::Location::caller(), | ||
// build: Box::new(move |key, router| { | ||
// router.0.insert( | ||
// key, | ||
// // TODO: Async procedures | ||
// Box::new(move |serializer, ctx, input| { | ||
// let input: i32 = erased_serde::deserialize(input).unwrap(); | ||
|
||
// serializer.serialize(&format!("Hello {:?}", input)); | ||
// }), | ||
// ); | ||
// }), | ||
// } | ||
|
||
todo!(); | ||
} | ||
|
||
#[track_caller] | ||
pub fn mutation<F, I>(self, handler: F) -> Procedure<TCtx> | ||
where | ||
F: ProcedureFunc<TCtx, TError, I>, | ||
{ | ||
todo!(); | ||
} | ||
} | ||
|
||
// TODO: Prevent downstream impls by inherinting from sealed trait. | ||
/// TODO | ||
// `TInput` mostly exists as a generic to contrain the impl. | ||
pub trait ProcedureFunc<TCtx, TError, TInput> { | ||
type Result; | ||
type Future: Future<Output = Result<Self::Result, TError>>; | ||
|
||
fn exec(&self, ctx: TCtx, input: TInput) -> Self::Future; | ||
} | ||
impl< | ||
TCtx, | ||
TInput, | ||
TResult, | ||
TError, | ||
F: Fn(TCtx, TInput) -> Fu, | ||
Fu: Future<Output = Result<TResult, TError>>, | ||
> ProcedureFunc<TCtx, TError, TInput> for F | ||
{ | ||
type Result = TResult; | ||
type Future = Fu; | ||
|
||
fn exec(&self, ctx: TCtx, input: TInput) -> Self::Future { | ||
(self)(ctx, input) | ||
} | ||
} | ||
|
||
// TODO: Probs break out file | ||
|
||
pub(crate) trait AnyProcedure { | ||
// fn exec<T: Serializer>(); // TODO: It being generic means we can't box it. | ||
} | ||
|
||
// TODO: Remove generics | ||
pub(crate) struct InternalProcedure<TCtx, TError, F, I> { | ||
pub(crate) handler: F, | ||
pub(crate) phantom: PhantomData<(TCtx, TError, I)>, | ||
} | ||
|
||
impl<TCtx, TError, F, I> AnyProcedure for InternalProcedure<TCtx, TError, F, I> where | ||
F: ProcedureFunc<TCtx, TError, I> | ||
{ | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
use std::{borrow::Cow, collections::HashMap, convert::Infallible}; | ||
|
||
use serde::{ | ||
de::{value::I32Deserializer, IntoDeserializer}, | ||
Deserialize, Deserializer, Serialize, Serializer, | ||
}; | ||
|
||
use crate::{format::DynFormat, Procedure}; | ||
|
||
/// TODO | ||
pub struct RouterBuilder<TCtx>(Vec<(Cow<'static, str>, Procedure<TCtx>)>); | ||
|
||
impl<TCtx> RouterBuilder<TCtx> { | ||
/// TODO | ||
pub fn procedure(mut self, key: &'static str, procedure: Procedure<TCtx>) -> Self { | ||
self.0.push((Cow::Borrowed(key), procedure)); | ||
self | ||
} | ||
|
||
/// TODO: Register custom types to export | ||
|
||
/// TODO | ||
pub fn build(self) -> Result<Router<TCtx>, ()> { | ||
// TODO: Check for invalid or duplicate procedure names. | ||
|
||
let mut router = Router(Default::default()); | ||
for (key, procedure) in self.0 { | ||
(procedure.build)(key, &mut router); | ||
} | ||
Ok(router) | ||
} | ||
} | ||
|
||
pub(crate) type ExecutableProcedure<TCtx> = | ||
Box<dyn Fn(&mut dyn DynFormat, TCtx, &mut dyn erased_serde::Deserializer)>; | ||
|
||
/// TODO | ||
pub struct Router<TCtx = ()>(pub(crate) HashMap<Cow<'static, str>, ExecutableProcedure<TCtx>>); | ||
|
||
impl<TCtx> Router<TCtx> { | ||
/// TODO | ||
pub fn new() -> RouterBuilder<TCtx> { | ||
RouterBuilder(Vec::new()) | ||
} | ||
|
||
// TODO: Specta exporting but support any Specta exporter. | ||
|
||
/// TODO: Dump the router for custom integrations | ||
|
||
/// TODO | ||
pub fn exec<'de, S: Serializer>( | ||
&self, | ||
key: &str, | ||
ctx: TCtx, | ||
deserializer: impl Deserializer<'de>, | ||
serializer: S, | ||
// TODO: Custom error type | ||
// TODO: Returning `Value or Future` so we can avoid spawning if not required???? | ||
// // TODO: R::Output<> | ||
) -> Result<S::Ok, ()> { | ||
let procedure = self.0.get(key).unwrap(); // TODO: .ok_or(())?; | ||
|
||
let y = serde_value::Value::String("testing".into()); | ||
|
||
// let mut serializer = serde_value::ValueDeserializer::<Infallible>::new(y); | ||
|
||
// let y = y.serialize(serializer).unwrap(); // TODO: Error handling | ||
|
||
// serializer.deserialize(); | ||
|
||
// procedure( | ||
// &mut serializer, | ||
// ctx, | ||
// &mut <dyn erased_serde::Deserializer>::erase(input), | ||
// ); | ||
|
||
// TODO: With `async` in batches we need to ensure the `Serialize` is put somewhere. | ||
|
||
// TODO: Result before or after async runtime??? | ||
|
||
// Ok(serializer.take()) | ||
// todo!(); | ||
|
||
Ok(y.serialize(serializer).unwrap()) // TODO: Error handling | ||
} | ||
|
||
// TODO: Batching multiple exec's??? Can this just be done using the single `Exec` method and a shared runtime??? | ||
} |