forked from boos/BoosAPI
chore: sync current workspace changes
This commit is contained in:
+138
@@ -0,0 +1,138 @@
|
||||
//! A minimalist clone of the `async-stream` crate in 100% safe code, without proc macros.
|
||||
//!
|
||||
//! This was created initially to get around some weird compiler errors we were getting with
|
||||
//! `async-stream`, and now it'd just be more work to replace.
|
||||
|
||||
use std::future::Future;
|
||||
use std::pin::Pin;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::task::{Context, Poll};
|
||||
|
||||
use futures_core::future::BoxFuture;
|
||||
use futures_core::stream::Stream;
|
||||
use futures_core::FusedFuture;
|
||||
use futures_util::future::Fuse;
|
||||
use futures_util::FutureExt;
|
||||
|
||||
use crate::error::Error;
|
||||
|
||||
pub struct TryAsyncStream<'a, T> {
|
||||
yielder: Yielder<T>,
|
||||
future: Fuse<BoxFuture<'a, Result<(), Error>>>,
|
||||
}
|
||||
|
||||
impl<'a, T> TryAsyncStream<'a, T> {
|
||||
pub fn new<F, Fut>(f: F) -> Self
|
||||
where
|
||||
F: FnOnce(Yielder<T>) -> Fut + Send,
|
||||
Fut: 'a + Future<Output = Result<(), Error>> + Send,
|
||||
T: 'a + Send,
|
||||
{
|
||||
let yielder = Yielder::new();
|
||||
|
||||
let future = f(yielder.duplicate()).boxed().fuse();
|
||||
|
||||
Self { future, yielder }
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Yielder<T> {
|
||||
// This mutex should never have any contention in normal operation.
|
||||
// We're just using it because `Rc<Cell<Option<T>>>` would not be `Send`.
|
||||
value: Arc<Mutex<Option<T>>>,
|
||||
}
|
||||
|
||||
impl<T> Yielder<T> {
|
||||
fn new() -> Self {
|
||||
Yielder {
|
||||
value: Arc::new(Mutex::new(None)),
|
||||
}
|
||||
}
|
||||
|
||||
// Don't want to expose a `Clone` impl
|
||||
fn duplicate(&self) -> Self {
|
||||
Yielder {
|
||||
value: self.value.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
/// NOTE: may deadlock the task if called from outside the future passed to `TryAsyncStream`.
|
||||
pub async fn r#yield(&self, val: T) {
|
||||
let replaced = self
|
||||
.value
|
||||
.lock()
|
||||
.expect("BUG: panicked while holding a lock")
|
||||
.replace(val);
|
||||
|
||||
debug_assert!(
|
||||
replaced.is_none(),
|
||||
"BUG: previously yielded value not taken"
|
||||
);
|
||||
|
||||
let mut yielded = false;
|
||||
|
||||
// Allows the generating future to suspend its execution without changing the task priority,
|
||||
// which would happen with `tokio::task::yield_now()`.
|
||||
//
|
||||
// Note that because this has no way to schedule a wakeup, this could deadlock the task
|
||||
// if called in the wrong place.
|
||||
futures_util::future::poll_fn(|_cx| {
|
||||
if !yielded {
|
||||
yielded = true;
|
||||
Poll::Pending
|
||||
} else {
|
||||
Poll::Ready(())
|
||||
}
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
fn take(&self) -> Option<T> {
|
||||
self.value
|
||||
.lock()
|
||||
.expect("BUG: panicked while holding a lock")
|
||||
.take()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> Stream for TryAsyncStream<'a, T> {
|
||||
type Item = Result<T, Error>;
|
||||
|
||||
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
|
||||
if self.future.is_terminated() {
|
||||
return Poll::Ready(None);
|
||||
}
|
||||
|
||||
match self.future.poll_unpin(cx) {
|
||||
Poll::Ready(Ok(())) => {
|
||||
// Future returned without yielding another value,
|
||||
// or else it would have returned `Pending` instead.
|
||||
Poll::Ready(None)
|
||||
}
|
||||
Poll::Ready(Err(e)) => Poll::Ready(Some(Err(e))),
|
||||
Poll::Pending => self
|
||||
.yielder
|
||||
.take()
|
||||
.map_or(Poll::Pending, |val| Poll::Ready(Some(Ok(val)))),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! try_stream {
|
||||
($($block:tt)*) => {
|
||||
$crate::ext::async_stream::TryAsyncStream::new(move |yielder| ::tracing::Instrument::in_current_span(async move {
|
||||
// Anti-footgun: effectively pins `yielder` to this future to prevent any accidental
|
||||
// move to another task, which could deadlock.
|
||||
let yielder = &yielder;
|
||||
|
||||
macro_rules! r#yield {
|
||||
($v:expr) => {{
|
||||
yielder.r#yield($v).await;
|
||||
}}
|
||||
}
|
||||
|
||||
$($block)*
|
||||
}))
|
||||
}
|
||||
}
|
||||
Vendored
+4
@@ -0,0 +1,4 @@
|
||||
pub mod ustr;
|
||||
|
||||
#[macro_use]
|
||||
pub mod async_stream;
|
||||
Vendored
+121
@@ -0,0 +1,121 @@
|
||||
use std::borrow::Borrow;
|
||||
use std::fmt::{self, Debug, Display, Formatter};
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::ops::Deref;
|
||||
use std::sync::Arc;
|
||||
|
||||
// U meaning micro
|
||||
// a micro-string is either a reference-counted string or a static string
|
||||
// this guarantees these are cheap to clone everywhere
|
||||
#[derive(Clone, Eq)]
|
||||
pub enum UStr {
|
||||
Static(&'static str),
|
||||
Shared(Arc<str>),
|
||||
}
|
||||
|
||||
impl UStr {
|
||||
pub fn new(s: &str) -> Self {
|
||||
UStr::Shared(Arc::from(s.to_owned()))
|
||||
}
|
||||
|
||||
/// Apply [str::strip_prefix], without copying if possible.
|
||||
pub fn strip_prefix(this: &Self, prefix: &str) -> Option<Self> {
|
||||
match this {
|
||||
UStr::Static(s) => s.strip_prefix(prefix).map(Self::Static),
|
||||
UStr::Shared(s) => s.strip_prefix(prefix).map(|s| Self::Shared(s.into())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for UStr {
|
||||
type Target = str;
|
||||
|
||||
#[inline]
|
||||
fn deref(&self) -> &str {
|
||||
match self {
|
||||
UStr::Static(s) => s,
|
||||
UStr::Shared(s) => s,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Hash for UStr {
|
||||
#[inline]
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
// Forward the hash to the string representation of this
|
||||
// A derive(Hash) encodes the enum discriminant
|
||||
(**self).hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl Borrow<str> for UStr {
|
||||
#[inline]
|
||||
fn borrow(&self) -> &str {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<UStr> for UStr {
|
||||
fn eq(&self, other: &UStr) -> bool {
|
||||
(**self).eq(&**other)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&'static str> for UStr {
|
||||
#[inline]
|
||||
fn from(s: &'static str) -> Self {
|
||||
UStr::Static(s)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a UStr> for UStr {
|
||||
fn from(value: &'a UStr) -> Self {
|
||||
value.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<String> for UStr {
|
||||
#[inline]
|
||||
fn from(s: String) -> Self {
|
||||
UStr::Shared(s.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for UStr {
|
||||
#[inline]
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
f.pad(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for UStr {
|
||||
#[inline]
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
f.pad(self)
|
||||
}
|
||||
}
|
||||
|
||||
// manual impls because otherwise things get a little screwy with lifetimes
|
||||
|
||||
#[cfg(feature = "offline")]
|
||||
impl<'de> serde::Deserialize<'de> for UStr {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, <D as serde::Deserializer<'de>>::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
Ok(String::deserialize(deserializer)?.into())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "offline")]
|
||||
impl serde::Serialize for UStr {
|
||||
fn serialize<S>(
|
||||
&self,
|
||||
serializer: S,
|
||||
) -> Result<<S as serde::Serializer>::Ok, <S as serde::Serializer>::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
{
|
||||
serializer.serialize_str(self)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user