1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167
use std::sync::Arc;
use re_log_types::DataCell;
// ---
/// Uniquely identifies a [`Promise`].
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct PromiseId(pub(crate) re_tuid::Tuid);
impl std::fmt::Display for PromiseId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.0.fmt(f)
}
}
impl re_types_core::SizeBytes for PromiseId {
#[inline]
fn heap_size_bytes(&self) -> u64 {
self.0.heap_size_bytes()
}
}
impl PromiseId {
/// Create a new unique [`PromiseId`] based on the current time.
#[allow(clippy::new_without_default)]
#[inline]
pub fn new() -> Self {
Self(re_tuid::Tuid::new())
}
}
// ---
/// A [`Promise`] turns a source [`DataCell`] into a new [`DataCell`] with the helper of a
/// [`PromiseResolver`].
///
/// Each promise is uniquely identified via a [`PromiseId`].
///
/// [`Promise`]s can be cloned cheaply.
#[derive(Debug, Clone)]
pub struct Promise {
id: PromiseId,
source: DataCell,
}
static_assertions::assert_eq_size!(Promise, Option<Promise>);
impl re_types_core::SizeBytes for Promise {
#[inline]
fn heap_size_bytes(&self) -> u64 {
let Self { id, source } = self;
id.heap_size_bytes() + source.heap_size_bytes()
}
}
impl Promise {
#[inline]
pub fn new(source: DataCell) -> Self {
Self {
id: PromiseId::new(),
source,
}
}
}
// ---
/// Resolves and keeps track of [`Promise`]s.
#[derive(Default)]
pub struct PromiseResolver {}
impl PromiseResolver {
/// Resolves the given [`Promise`].
///
/// If the method returns [`PromiseResult::Pending`], you should call it again with the same
/// [`Promise`] until it either resolves it or fails irrecoverably.
///
/// Once a [`Promise`] has left the `Pending` state, `resolve`ing it again is cached and
/// idempotent (the [`PromiseResolver`] keeps track of the state of all [`Promise`]s, both
/// pending and already resolved).
#[inline]
pub fn resolve(&self, promise: &Promise) -> PromiseResult<DataCell> {
// NOTE: we're pretending there's gonna be some kind of interior mutability when
// everything's said and done.
_ = self;
_ = promise.id;
PromiseResult::Ready(promise.source.clone())
}
}
/// The result of resolving a [`Promise`] through a [`PromiseResolver`].
#[derive(Debug, Clone)]
pub enum PromiseResult<T> {
/// The resolution process is still in progress.
///
/// Try calling [`PromiseResolver::resolve`] again.
Pending,
/// The [`Promise`] failed to resolve due to an irrecoverable error.
Error(Arc<dyn std::error::Error + Send + Sync>),
/// The [`Promise`] has been fully resolved.
Ready(T),
}
impl<T> PromiseResult<T> {
/// Applies the given transformation to the [`PromiseResult`] iff it's `Ready`.
#[inline]
pub fn map<B, F>(self, mut f: F) -> PromiseResult<B>
where
F: FnMut(T) -> B,
{
match self {
PromiseResult::Ready(v) => PromiseResult::Ready(f(v)),
PromiseResult::Pending => PromiseResult::Pending,
PromiseResult::Error(err) => PromiseResult::Error(err),
}
}
/// Applies the given transformation to the [`PromiseResult`] iff it's `Ready`.
///
/// Able to modify the result itself, not just the value contained within.
#[inline]
pub fn remap<B, F>(self, mut f: F) -> PromiseResult<B>
where
F: FnMut(T) -> PromiseResult<B>,
{
match self {
PromiseResult::Ready(v) => f(v),
PromiseResult::Pending => PromiseResult::Pending,
PromiseResult::Error(err) => PromiseResult::Error(err),
}
}
/// Returns the inner value if it's ready.
#[inline]
pub fn ok(self) -> Option<T> {
match self {
PromiseResult::Ready(v) => Some(v),
_ => None,
}
}
/// Unwraps the resolved result if it's `Ready`, panics otherwise.
#[inline]
pub fn unwrap(self) -> T {
match self {
PromiseResult::Ready(v) => v,
PromiseResult::Pending => panic!("tried to unwrap a pending `PromiseResult`"),
PromiseResult::Error(err) => {
panic!("tried to unwrap an errored `PromiseResult`: {err}")
}
}
}
}
impl<T, E: 'static + std::error::Error + Send + Sync> PromiseResult<Result<T, E>> {
/// Given a [`PromiseResult`] of a `Result`, flattens it down to a single layer [`PromiseResult`].
#[inline]
pub fn flatten(self) -> PromiseResult<T> {
self.remap(|res| match res {
Ok(v) => PromiseResult::Ready(v),
Err(err) => PromiseResult::Error(Arc::new(err) as _),
})
}
}