Skip to content

Instantly share code, notes, and snippets.

@oscartbeaumont
Last active February 26, 2024 18:01
Show Gist options
  • Save oscartbeaumont/1c7c4bea577d7579faf57600a427429b to your computer and use it in GitHub Desktop.
Save oscartbeaumont/1c7c4bea577d7579faf57600a427429b to your computer and use it in GitHub Desktop.
Content Types Idea
[package]
name = "testing"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
erased-serde = "0.4.3"
pin-project-lite = "0.2.13"
serde = { version = "1.0.197", features = ["derive"] }
serde_json = "1.0.114"
tokio = { version = "1.36.0", features = ["macros", "rt-multi-thread"] }
use std::{
future::{poll_fn, Future},
pin::Pin,
task::{Context, Poll},
};
use pin_project_lite::pin_project;
use serde::{Serialize, Serializer};
#[tokio::main]
async fn main() {
let p: Procedure = Box::new(|| {
Box::pin(ProcedureResult {
handler: async { 42 },
value: None,
})
});
let result = execute(&p, serde_json::value::Serializer).await.unwrap();
println!("{result:?}");
let mut buf = Vec::new();
execute(&p, &mut serde_json::Serializer::new(&mut buf))
.await
.unwrap();
println!("{:?}", String::from_utf8_lossy(&buf));
}
async fn execute<S: Serializer>(procedure: &Procedure, serializer: S) -> Result<S::Ok, S::Error> {
let mut serializer = Some(serializer);
let mut fut = (procedure)();
poll_fn(move |cx| {
fut.as_mut()
.poll(cx)
.map(|res| res.serialize(serializer.take().expect("Future yielded more than once")))
})
.await
}
type Procedure = Box<dyn Fn() -> Pin<Box<dyn AnyProcedureResult>>>;
// Can't be `Future` due to lack of GAT on `Output`.
pub trait AnyProcedureResult {
fn poll<'a>(
self: Pin<&'a mut Self>,
cx: &mut Context<'_>,
) -> Poll<&'a dyn erased_serde::Serialize>;
}
pin_project! {
pub struct ProcedureResult<TResult, F: Future<Output = TResult>> {
#[pin]
handler: F,
#[pin]
value: Option<TResult>,
}
}
impl<TResult: serde::Serialize + Unpin, F: Future<Output = TResult>> AnyProcedureResult
for ProcedureResult<TResult, F>
{
fn poll<'a>(
mut self: Pin<&'a mut Self>,
cx: &mut Context<'_>,
) -> Poll<&'a dyn erased_serde::Serialize> {
let mut this = self.as_mut().project();
match this.handler.poll(cx) {
Poll::Ready(value) => {
*this.value = Some(value);
// SAFETY: `value` of type `TResult` is `Unpin`.
let this = unsafe {
let v = self.get_unchecked_mut();
&v.value
};
Poll::Ready(this.as_ref().expect("set above"))
}
Poll::Pending => Poll::Pending,
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment