Skip to content

Instantly share code, notes, and snippets.

@shurizzle
Last active February 6, 2024 18:58
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save shurizzle/1f676ede91630f37b0e4958a93e3700c to your computer and use it in GitHub Desktop.
Save shurizzle/1f676ede91630f37b0e4958a93e3700c to your computer and use it in GitHub Desktop.
Blocking and async
use core::fmt;
use std::{future::Future, pin::Pin};
use url::Url;
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
#[non_exhaustive]
pub struct OpaqueError;
impl fmt::Display for OpaqueError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "shit happens")
}
}
impl std::error::Error for OpaqueError {}
#[allow(async_fn_in_trait)]
pub trait HttpClient {
async fn get(&self, url: Url) -> Result<String, OpaqueError>;
}
impl HttpClient for reqwest::Client {
async fn get(&self, url: Url) -> Result<String, OpaqueError> {
let req = self.get(url).build().map_err(|_| OpaqueError)?;
let res = self.execute(req).await.map_err(|_| OpaqueError)?;
res.text().await.map_err(|_| OpaqueError)
}
}
impl HttpClient for ureq::Agent {
async fn get(&self, url: Url) -> Result<String, OpaqueError> {
self.request_url("GET", &url)
.call()
.map_err(|_| OpaqueError)?
.into_string()
.map_err(|_| OpaqueError)
}
}
struct ApiClient<Http: HttpClient>(Http);
impl<Http: HttpClient> ApiClient<Http> {
pub fn new(client: Http) -> Self {
Self(client)
}
pub async fn index(&self) -> Result<String, OpaqueError> {
self.0
.get(Url::parse("https://www.example.org/").unwrap())
.await
}
}
fn block_on<O, F: Future<Output = O>>(mut f: F) -> O {
use std::task::{Context, Poll::*, RawWaker, RawWakerVTable, Waker};
unsafe fn clone(_: *const ()) -> RawWaker {
RawWaker::new(core::ptr::null(), &WAKER_VTABLE)
}
unsafe fn wake(_: *const ()) {}
unsafe fn wake_by_ref(_: *const ()) {}
unsafe fn drop(_: *const ()) {}
const WAKER_VTABLE: RawWakerVTable = RawWakerVTable::new(clone, wake, wake_by_ref, drop);
let mut pin = unsafe { Pin::new_unchecked(&mut f) };
let raw_waker = RawWaker::new(core::ptr::null(), &WAKER_VTABLE);
let waker = unsafe { Waker::from_raw(raw_waker) };
let mut ctx = Context::from_waker(&waker);
loop {
match F::poll(pin.as_mut(), &mut ctx) {
Ready(res) => return res,
Pending => (),
}
}
}
pub struct AsyncClient(ApiClient<reqwest::Client>);
pub struct BlockingClient(ApiClient<ureq::Agent>);
impl AsyncClient {
#[inline]
pub fn new() -> Self {
Self(ApiClient::new(reqwest::Client::new()))
}
#[inline]
pub async fn index(&self) -> Result<String, OpaqueError> {
self.0.index().await
}
}
impl Default for AsyncClient {
#[inline]
fn default() -> Self {
Self::new()
}
}
impl BlockingClient {
#[inline]
pub fn new() -> Self {
Self(ApiClient::new(ureq::AgentBuilder::new().build()))
}
#[inline]
pub fn index(&self) -> Result<String, OpaqueError> {
block_on(self.0.index())
}
}
impl Default for BlockingClient {
#[inline]
fn default() -> Self {
Self::new()
}
}
// #[tokio::main]
// async fn main() {
// let client = AsyncClient::new();
// _ = dbg!(client.index().await);
// }
fn main() {
let client = BlockingClient::new();
_ = dbg!(client.index());
}
[package]
name = "blocking-async"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
reqwest = "0.11.24"
tokio = { version = "1.36.0", features = ["full"] }
ureq = "2.9.4"
url = "*"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment