Last active
October 18, 2024 17:06
-
-
Save shurizzle/1f676ede91630f37b0e4958a93e3700c to your computer and use it in GitHub Desktop.
Blocking and async
This file contains hidden or 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
use std::fmt; | |
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 AsyncHttpClient { | |
async fn get(&self, url: Url) -> Result<String, OpaqueError>; | |
} | |
pub trait BlockingHttpClient { | |
fn get(&self, url: Url) -> Result<String, OpaqueError>; | |
} | |
struct BlockingHttpBridge<T: BlockingHttpClient>(T); | |
impl<T: BlockingHttpClient> AsyncHttpClient for BlockingHttpBridge<T> { | |
#[inline(always)] | |
async fn get(&self, url: Url) -> Result<String, OpaqueError> { | |
self.0.get(url) | |
} | |
} | |
struct ApiClient<Http: AsyncHttpClient>(Http); | |
impl<Http: AsyncHttpClient> ApiClient<Http> { | |
pub fn new(client: Http) -> Self { | |
Self(client) | |
} | |
pub async fn get_ip(&self) -> Result<String, OpaqueError> { | |
self.0 | |
.get(Url::parse("https://ipinfo.io/ip").map_err(|_| OpaqueError)?) | |
.await | |
} | |
} | |
mod internal { | |
use std::{ | |
future::Future, | |
pin::Pin, | |
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); | |
const RAW_WAKER: RawWaker = RawWaker::new(core::ptr::null(), &WAKER_VTABLE); | |
static WAKER: Waker = unsafe { Waker::from_raw(RAW_WAKER) }; | |
#[inline(always)] | |
pub fn block_on<O, F: Future<Output = O>>(mut f: F) -> O { | |
let mut pin = unsafe { Pin::new_unchecked(&mut f) }; | |
let mut ctx = Context::from_waker(&WAKER); | |
loop { | |
match F::poll(pin.as_mut(), &mut ctx) { | |
Ready(res) => return res, | |
Pending => (), | |
} | |
} | |
} | |
} | |
use internal::block_on; | |
pub struct AsyncClient<Http: AsyncHttpClient>(ApiClient<Http>); | |
pub struct BlockingClient<Http: BlockingHttpClient>(ApiClient<BlockingHttpBridge<Http>>); | |
impl<Http: AsyncHttpClient> AsyncClient<Http> { | |
#[inline(always)] | |
pub fn new(http: Http) -> Self { | |
Self(ApiClient::new(http)) | |
} | |
#[inline(always)] | |
pub async fn get_ip(&self) -> Result<String, OpaqueError> { | |
self.0.get_ip().await | |
} | |
} | |
impl<Http: AsyncHttpClient + Default> Default for AsyncClient<Http> { | |
#[inline(always)] | |
fn default() -> Self { | |
Self::new(Http::default()) | |
} | |
} | |
impl<Http: BlockingHttpClient> BlockingClient<Http> { | |
#[inline(always)] | |
pub fn new(http: Http) -> Self { | |
Self(ApiClient::new(BlockingHttpBridge(http))) | |
} | |
#[inline(never)] | |
pub fn get_ip(&self) -> Result<String, OpaqueError> { | |
block_on(self.0.get_ip()) | |
} | |
} | |
impl<Http: BlockingHttpClient + Default> Default for BlockingClient<Http> { | |
#[inline(always)] | |
fn default() -> Self { | |
Self::new(Http::default()) | |
} | |
} | |
// 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 BlockingHttpClient for ureq::Agent { | |
#[inline(never)] | |
fn get(&self, url: Url) -> Result<String, OpaqueError> { | |
self.request_url("GET", &url) | |
.call() | |
.map_err(|_| OpaqueError)? | |
.into_string() | |
.map_err(|_| OpaqueError) | |
} | |
} | |
// #[tokio::main] | |
// async fn main() { | |
// let client = AsyncClient::new(); | |
// _ = dbg!(client.index().await); | |
// } | |
fn main() { | |
let client = BlockingClient::new(ureq::Agent::new()); | |
_ = dbg!(client.get_ip()); | |
} | |
This file contains hidden or 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
[package] | |
name = "blocking-async" | |
version = "0.1.0" | |
edition = "2021" | |
[[bin]] | |
name = "blocking_async" | |
path = "blocking_async.rs" | |
[profile.release] | |
panic = "abort" | |
[dependencies] | |
url = "2.5.2" | |
ureq = "2.10.0" | |
# reqwest = "0.12.5" | |
# tokio = { version = "1.38.1", features = ["full"] } | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment