Skip to content

Commit

Permalink
wip: working types but runtime partially put together
Browse files Browse the repository at this point in the history
  • Loading branch information
oscartbeaumont committed Sep 10, 2023
1 parent 773c9bd commit 5e87606
Show file tree
Hide file tree
Showing 11 changed files with 192 additions and 319 deletions.
4 changes: 3 additions & 1 deletion examples/bindings.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
// This file was generated by [rspc](https://github.com/oscartbeaumont/rspc). Do not edit this file manually.
export type Procedures = { queries: { key: "X-Demo-Header"; input: never; result: null; error: null } | { key: "echo"; input: string; result: null; error: null } | { key: "echo2"; input: string; result: null; error: null } | { key: "error"; input: never; result: null; error: null } | { key: "transformMe"; input: never; result: null; error: null }; mutations: { key: "error"; input: never; result: null; error: null } | { key: "sendMsg"; input: string; result: null; error: null }; subscriptions: never }
export type Procedures = { queries: { key: "X-Demo-Header"; input: never; result: string; error: Error } | { key: "echo"; input: string; result: string; error: Error } | { key: "echo2"; input: string; result: string; error: Error } | { key: "error"; input: never; result: string; error: Error } | { key: "transformMe"; input: never; result: string; error: Error }; mutations: { key: "error"; input: never; result: string; error: Error } | { key: "sendMsg"; input: string; result: string; error: Error }; subscriptions: never }

export type Error = string
3 changes: 2 additions & 1 deletion src/internal/body.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,8 @@ impl Body for Box<dyn Body + Send + '_> {
pin_project! {
/// A stream which emits single element and then EOF.
#[must_use = "streams do nothing unless polled"]
pub(crate) struct Once<Fut> {
// TODO: `pub(crate)` again?
pub struct Once<Fut> {
#[pin]
future: Option<Fut>
}
Expand Down
38 changes: 32 additions & 6 deletions src/internal/layer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,38 @@
//!
//! This module is sealed.

use std::borrow::Cow;
use std::future::ready;

use serde_json::Value;
use specta::ts::TsExportError;
use specta::TypeMap;

use super::procedure::ProcedureDef;
use super::Body;
use crate::internal::middleware::RequestContext;

use crate::{internal::Once, ExecError};

pub trait DynLayer<TLCtx: 'static>: Send + Sync + 'static {
fn dyn_call(
fn into_procedure_def(
&self,
ctx: TLCtx,
input: Value,
req: RequestContext,
// TODO: Return `Result` too?
) -> Box<dyn Body + Send + '_>;
key: Cow<'static, str>,
ty_store: &mut TypeMap,
) -> Result<ProcedureDef, TsExportError>;

fn dyn_call(&self, ctx: TLCtx, input: Value, req: RequestContext) -> Box<dyn Body + Send + '_>;
}

impl<TLCtx: Send + 'static, L: Layer<TLCtx>> DynLayer<TLCtx> for L {
fn into_procedure_def(
&self,
key: Cow<'static, str>,
ty_store: &mut TypeMap,
) -> Result<ProcedureDef, TsExportError> {
Layer::into_procedure_def(self, key, ty_store)
}

fn dyn_call(&self, ctx: TLCtx, input: Value, req: RequestContext) -> Box<dyn Body + Send + '_> {
match self.call(ctx, input, req) {
Ok(stream) => Box::new(stream),
Expand All @@ -34,6 +46,14 @@ impl<TLCtx: Send + 'static, L: Layer<TLCtx>> DynLayer<TLCtx> for L {
impl<TLCtx: Send + 'static> Layer<TLCtx> for Box<dyn DynLayer<TLCtx>> {
type Stream<'a> = Box<dyn Body + Send + 'a>;

fn into_procedure_def(
&self,
key: Cow<'static, str>,
ty_store: &mut TypeMap,
) -> Result<ProcedureDef, TsExportError> {
(&**self).into_procedure_def(key, ty_store)
}

fn call(
&self,
ctx: TLCtx,
Expand All @@ -53,6 +73,12 @@ pub trait Layer<TLCtx: 'static>: Send + Sync + 'static {
// TODO: Rename `Body`
type Stream<'a>: Body + Send + 'a;

fn into_procedure_def(
&self,
key: Cow<'static, str>,
ty_store: &mut TypeMap,
) -> Result<ProcedureDef, TsExportError>;

fn call(
&self,
ctx: TLCtx,
Expand Down
23 changes: 17 additions & 6 deletions src/internal/middleware/middleware_layer.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
mod private {
use std::{
borrow::Cow,
marker::PhantomData,
pin::Pin,
task::{ready, Context, Poll},
Expand All @@ -8,31 +9,41 @@ mod private {
use futures::Future;
use pin_project_lite::pin_project;
use serde_json::Value;
use specta::{ts::TsExportError, TypeMap};

use crate::{
internal::{
middleware::Middleware,
middleware::{Executable2, MiddlewareContext, MwV2Result, RequestContext},
procedure::ProcedureDef,
Body, Layer, PinnedOption, PinnedOptionProj,
},
ExecError,
};

#[doc(hidden)]
pub struct MiddlewareLayer<TLayerCtx, TMiddleware, TNewMiddleware> {
pub(crate) next: TMiddleware,
pub struct MiddlewareLayer<TLayerCtx, TNextLayer, TNewMiddleware> {
pub(crate) next: TNextLayer,
pub(crate) mw: TNewMiddleware,
pub(crate) phantom: PhantomData<TLayerCtx>,
}

impl<TLayerCtx, TMiddleware, TNewMiddleware> Layer<TLayerCtx>
for MiddlewareLayer<TLayerCtx, TMiddleware, TNewMiddleware>
impl<TLayerCtx, TNextMiddleware, TNewMiddleware> Layer<TLayerCtx>
for MiddlewareLayer<TLayerCtx, TNextMiddleware, TNewMiddleware>
where
TLayerCtx: Send + Sync + 'static,
TMiddleware: Layer<TNewMiddleware::NewCtx> + Sync + 'static,
TNextMiddleware: Layer<TNewMiddleware::NewCtx> + Sync + 'static,
TNewMiddleware: Middleware<TLayerCtx> + Send + Sync + 'static,
{
type Stream<'a> = MiddlewareLayerFuture<'a, TLayerCtx, TNewMiddleware, TMiddleware>;
type Stream<'a> = MiddlewareLayerFuture<'a, TLayerCtx, TNewMiddleware, TNextMiddleware>;

fn into_procedure_def(
&self,
key: Cow<'static, str>,
ty_store: &mut TypeMap,
) -> Result<ProcedureDef, TsExportError> {
self.next.into_procedure_def(key, ty_store)
}

fn call(
&self,
Expand Down
50 changes: 3 additions & 47 deletions src/internal/procedure/procedure.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,7 @@
use std::{borrow::Cow, future::ready, marker::PhantomData};
use std::marker::PhantomData;

use futures::stream;

use crate::internal::{
boxed,
middleware::{ConstrainedMiddleware, MiddlewareBuilder, MiddlewareLayerBuilder, ProcedureKind},
procedure::BuildProceduresCtx,
resolver::{HasResolver, ResolverFunction, ResolverLayer, StreamAdapter},
use crate::internal::middleware::{
ConstrainedMiddleware, MiddlewareBuilder, MiddlewareLayerBuilder,
};

/// TODO: Explain
Expand Down Expand Up @@ -91,42 +86,3 @@ where
)
}
}

impl<F, M, TResult, TMiddleware> Procedure<HasResolver<F, TResult, M>, TMiddleware> {
pub(crate) fn build(
self,
key: Cow<'static, str>,
ctx: &mut BuildProceduresCtx<'_, TMiddleware::Ctx>,
)
// TODO: Applying these sorta bounds here is cursed but it helps for refactoring
where
HasResolver<F, TResult, M>: ResolverFunction<TMiddleware::LayerCtx>,
TMiddleware: MiddlewareBuilder,
{
let key_str = key.to_string();
let type_def = self
.resolver
.into_procedure_def(key, &mut ctx.ty_store)
.expect("error exporting types"); // TODO: Error handling using `#[track_caller]`

let m = match &self.resolver.kind {
ProcedureKind::Query => &mut ctx.queries,
ProcedureKind::Mutation => &mut ctx.mutations,
ProcedureKind::Subscription => &mut ctx.subscriptions,
};

let layer = ResolverLayer::new(move |ctx, value, req| {
// TODO: no shot this will fly
Ok(StreamAdapter {
stream: stream::once(ready(Ok(self.resolver.exec(ctx, value, req)))),
})
});

// TODO: Do this earlier when constructing `HasResolver`
// Trade runtime performance for reduced monomorphization
#[cfg(debug_assertions)]
let layer = boxed(layer);

m.append(key_str, self.mw.build(layer), type_def);
}
}
151 changes: 65 additions & 86 deletions src/internal/resolver/function.rs
Original file line number Diff line number Diff line change
@@ -1,98 +1,77 @@
use std::marker::PhantomData;
use std::{
future::{ready, Ready},
marker::PhantomData,
};

use serde::de::DeserializeOwned;
use specta::Type;

mod private {
use std::borrow::Cow;

use futures::Stream;
use serde::Serialize;
use serde_json::Value;
use specta::{ts::TsExportError, TypeMap};

use crate::{
internal::{
middleware::{ProcedureKind, RequestContext},
procedure::ProcedureDef,
resolver::IntoQueryMutationResponse,
},
IntoResolverError,
};

use super::*;

/// TODO
pub trait ResolverFunction<TLCtx>: Send + Sync + 'static {
// TODO: How da hell if this needs to be dyn-safe. It needs to end up boxed too, basically but then that prevents boxing anything refering to it
// type Stream<'a>: Body + Send + 'a;

fn into_procedure_def(
&self,
key: Cow<'static, str>,
ty_store: &mut TypeMap,
) -> Result<ProcedureDef, TsExportError>;

// TODO: The return type can't be `Value` cause streams and stuff
fn exec(&self, ctx: TLCtx, input: Value, req: RequestContext) -> Value;
}

// TODO: Allow transforming into a boxed variant of the function

// TODO: Rename `Resolver`?
pub struct HasResolver<F, TResult, M> {
resolver: F,
pub(crate) kind: ProcedureKind,
phantom: PhantomData<fn() -> (TResult, M)>,
}
use std::borrow::Cow;

use serde::Serialize;
use serde_json::Value;
use specta::{ts::TsExportError, TypeMap};

use crate::{
internal::{
middleware::{ProcedureKind, RequestContext},
procedure::ProcedureDef,
resolver::IntoQueryMutationResponse,
Layer, Once,
},
ExecError, IntoResolverError,
};

// TODO: Rename `Resolver`?
pub struct HasResolver<F, TErr, M> {
resolver: F,
pub(crate) kind: ProcedureKind,
phantom: PhantomData<fn() -> (TErr, M)>,
}

impl<F, TResult, M> HasResolver<F, TResult, M> {
pub(crate) fn new(resolver: F, kind: ProcedureKind) -> Self {
Self {
resolver,
kind,
phantom: PhantomData,
}
impl<F, E, M> HasResolver<F, E, M> {
pub(crate) fn new(resolver: F, kind: ProcedureKind) -> Self {
Self {
resolver,
kind,
phantom: PhantomData,
}
}
}

pub struct M<TArg>(PhantomData<TArg>);
impl<F, TLCtx, TResult, TArg> ResolverFunction<TLCtx> for HasResolver<F, TResult, M<TArg>>
where
F: Fn(TLCtx, TArg) -> TResult + Send + Sync + 'static,
TArg: DeserializeOwned + Type + 'static,
TLCtx: Send + Sync + 'static,
TResult: 'static,
{
// type Stream<'a> = Once<Ready<Result<Value, ExecError>>>;

fn into_procedure_def(
&self,
key: Cow<'static, str>,
ty_store: &mut TypeMap,
) -> Result<ProcedureDef, TsExportError> {
// ProcedureDef::from_tys::<TArg, TOk, TError>(key, ty_store)

// TODO: Fix this
ProcedureDef::from_tys::<TArg, (), ()>(key, ty_store)
}

fn exec(&self, ctx: TLCtx, input: Value, req: RequestContext) -> Value {
// TODO: Error handling
// serde_json::to_value((self.resolver)(ctx, serde_json::from_value(input).unwrap()))
// .unwrap()
todo!();
}
pub struct M<TArg, TResultMarker>(PhantomData<(TArg, TResultMarker)>);
impl<F, TLCtx, TErr, TArg, TResult, TResultMarker> Layer<TLCtx>
for HasResolver<F, TErr, M<TArg, TResultMarker>>
where
F: Fn(TLCtx, TArg) -> TResult + Send + Sync + 'static,
TArg: DeserializeOwned + Type + 'static,
TLCtx: Send + Sync + 'static,
TResult: IntoQueryMutationResponse<TResultMarker, TErr>,
TResult::Ok: Serialize + Type + 'static,
TErr: IntoResolverError + 'static,
TResultMarker: 'static,
{
// TODO: This is a placeholder
type Stream<'a> = Once<Ready<Result<Value, ExecError>>>;

fn into_procedure_def(
&self,
key: Cow<'static, str>,
ty_store: &mut TypeMap,
) -> Result<ProcedureDef, TsExportError> {
ProcedureDef::from_tys::<TArg, TResult::Ok, TErr>(key, ty_store)
}

pub trait QueryMutationFn<TErr, M> {}

impl<F, TResult, M, TMarker, TErr> QueryMutationFn<TErr, TMarker> for HasResolver<F, TResult, M>
where
TResult: IntoQueryMutationResponse<TMarker, TErr>,
TResult::Ok: Serialize + Type + 'static,
{
fn call(
&self,
ctx: TLCtx,
input: Value,
req: RequestContext,
) -> Result<Self::Stream<'_>, ExecError> {
// TODO: Error handling
let y = (self.resolver)(ctx, serde_json::from_value(input).unwrap());

// TODO: Make this actually work
Ok(Once::new(ready(Ok(Value::String("TODO".into())))))
}
}

pub(crate) use private::{HasResolver, QueryMutationFn, ResolverFunction};
Loading

0 comments on commit 5e87606

Please sign in to comment.