use std::fmt::{Debug, Display};
use std::future::{Future, IntoFuture};
use std::hash::Hash;
use std::ops::{Deref, DerefMut};
use std::pin::Pin;
use std::{mem, ptr};
pub struct Bound<G, L> {
derived: Option<G>,
source_ptr: *mut L,
}
impl<G, L> Bound<G, L> {
fn decompose(mut self) -> (G, *mut L) {
let g = unsafe { self.derived.take().unwrap_unchecked() };
let source_ptr = self.source_ptr;
self.source_ptr = ptr::null_mut();
(g, source_ptr)
}
pub fn unbind(self) -> Box<L> {
let (derived, source_ptr) = self.decompose();
mem::drop(derived);
unsafe { Box::from_raw(source_ptr) }
}
pub fn wrapped(&self) -> &G {
unsafe { self.derived.as_ref().unwrap_unchecked() }
}
pub fn wrapped_mut(&mut self) -> Pin<&mut G> {
unsafe {
let internal_ref = self.derived.as_mut().unwrap_unchecked();
Pin::new_unchecked(internal_ref)
}
}
}
impl<G, L: 'static> Bound<G, L> {
pub fn new<'a, F: FnOnce(&'a mut L) -> G>(source: L, func: F) -> Self
where G: 'a {
let source_ref = Box::leak(Box::new(source));
let source_ptr = source_ref as *mut L;
let derived = func(source_ref);
Self { derived: Some(derived), source_ptr }
}
pub fn try_new<'a, E, F>(source: L, func: F) -> Result<Self, Bound<E, L>>
where
F: FnOnce(&'a mut L) -> Result<G, E>,
G: 'a,
{
let source_ref = Box::leak(Box::new(source));
let source_ptr = source_ref as *mut L;
func(source_ref)
.map(|res| Bound { derived: Some(res), source_ptr })
.map_err(|e| Bound { derived: Some(e), source_ptr })
}
pub fn async_new<'a, Fut, F>(source: L, func: F) -> impl Future<Output = Self>
where
F: FnOnce(&'a mut L) -> Fut,
Fut: IntoFuture<Output = G>,
G: 'a,
{
let source_ref = Box::leak(Box::new(source));
let source_ptr = source_ref as *mut L;
let mut instance = Self { source_ptr, derived: None };
let fut = func(source_ref);
async move {
let derived = fut.await;
instance.derived = Some(derived);
instance
}
}
pub fn async_try_new<'a, E, Fut, F>(
source: L,
func: F,
) -> impl Future<Output = Result<Self, Bound<E, L>>>
where
F: FnOnce(&'a mut L) -> Fut,
Fut: IntoFuture<Output = Result<G, E>>,
G: 'a,
L: Send,
{
let source_ref = Box::leak(Box::new(source));
let source_ptr = source_ref as *mut L;
let fut = func(source_ref);
let sendable_source_ptr = unsafe { source_ptr.as_mut().unwrap() };
async move {
let res = fut.await;
let source_ptr = sendable_source_ptr as *mut L;
res
.map(|derived| Bound { derived: Some(derived), source_ptr })
.map_err(|e| Bound { derived: Some(e), source_ptr })
}
}
pub fn map<G2, F>(self, func: F) -> Bound<G2, L>
where F: FnOnce(G) -> G2 {
let (derived, source_ptr) = self.decompose();
let new = func(derived);
Bound { derived: Some(new), source_ptr }
}
pub fn try_map<G2, E, F>(self, func: F) -> Result<Bound<G2, L>, Bound<E, L>>
where F: FnOnce(G) -> Result<G2, E> {
let (derived, source_ptr) = self.decompose();
func(derived)
.map(|new| Bound { derived: Some(new), source_ptr })
.map_err(|e| Bound { derived: Some(e), source_ptr })
}
pub fn async_map<G2, Fut, F>(self, func: F) -> impl Future<Output = Bound<G2, L>>
where
F: FnOnce(G) -> Fut,
Fut: IntoFuture<Output = G2>,
{
let (derived, source_ptr) = self.decompose();
let fut = func(derived);
let mut next_instance = Bound { derived: None, source_ptr };
async move {
let new = fut.await;
next_instance.derived = Some(new);
next_instance
}
}
pub fn async_try_map<G2, E, Fut, F>(
self,
func: F,
) -> impl Future<Output = Result<Bound<G2, L>, Bound<E, L>>>
where
F: FnOnce(G) -> Fut,
Fut: IntoFuture<Output = Result<G2, E>>,
{
let (derived, source_ptr) = self.decompose();
let fut = func(derived);
let sendable_source_ptr = unsafe { source_ptr.as_mut().unwrap() };
async move {
let res = fut.await;
let source_ptr = sendable_source_ptr as *mut L;
res
.map(|new| Bound { derived: Some(new), source_ptr })
.map_err(|e| Bound { derived: Some(e), source_ptr })
}
}
}
unsafe impl<G, L> Send for Bound<G, L> where G: Send {}
unsafe impl<G, L> Sync for Bound<G, L> where G: Sync {}
impl<G, L> Drop for Bound<G, L> {
fn drop(&mut self) {
mem::drop(self.derived.take());
if !self.source_ptr.is_null() {
let source = unsafe { Box::from_raw(self.source_ptr) };
mem::drop(source)
};
}
}
impl<T: ?Sized, G, U> Deref for Bound<G, U>
where G: Deref<Target = T>
{
type Target = T;
fn deref(&self) -> &Self::Target { self.wrapped().deref() }
}
impl<T: ?Sized, G, U> DerefMut for Bound<G, U>
where G: DerefMut<Target = T>
{
fn deref_mut(&mut self) -> &mut Self::Target {
unsafe { self.wrapped_mut().get_unchecked_mut() }.deref_mut()
}
}
impl<T: ?Sized, G: Deref, L> AsRef<T> for Bound<G, L>
where <G as Deref>::Target: AsRef<T>
{
fn as_ref(&self) -> &T { self.deref().as_ref() }
}
impl<T: ?Sized, G: DerefMut, L> AsMut<T> for Bound<G, L>
where <G as Deref>::Target: AsMut<T>
{
fn as_mut(&mut self) -> &mut T {
unsafe { self.wrapped_mut().get_unchecked_mut() }.as_mut()
}
}
impl<G, L> Debug for Bound<G, L>
where G: Debug
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_tuple("Bound").field(&self.wrapped()).finish()
}
}
impl<T: ?Sized, G, L> Display for Bound<G, L>
where
G: Deref<Target = T>,
T: Display,
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { self.deref().fmt(f) }
}
impl<T: ?Sized, G, L> Hash for Bound<G, L>
where
G: Deref<Target = T>,
T: Hash,
{
fn hash<H: std::hash::Hasher>(&self, state: &mut H) { self.deref().hash(state) }
}
impl<T: ?Sized, G, L> Eq for Bound<G, L>
where
G: Deref<Target = T>,
T: Eq,
{
}
impl<T: ?Sized, G, L, T2: ?Sized, G2, L2> PartialEq<Bound<G2, L2>> for Bound<G, L>
where
G: Deref<Target = T>,
G2: Deref<Target = T2>,
T: PartialEq<T2>,
{
fn eq(&self, other: &Bound<G2, L2>) -> bool { self.deref().eq(other.deref()) }
}
impl<T: ?Sized, G, L, T2: ?Sized, G2, L2> PartialOrd<Bound<G2, L2>> for Bound<G, L>
where
G: Deref<Target = T>,
G2: Deref<Target = T2>,
T: PartialOrd<T2>,
{
fn partial_cmp(&self, other: &Bound<G2, L2>) -> Option<std::cmp::Ordering> {
self.deref().partial_cmp(other)
}
}
impl<T: ?Sized, G, L> Ord for Bound<G, L>
where
G: Deref<Target = T>,
T: Ord,
{
fn cmp(&self, other: &Self) -> std::cmp::Ordering { self.deref().cmp(other.deref()) }
}
#[cfg(test)]
mod tests {
use std::ops::Deref;
use std::sync::{Arc, RwLock, RwLockWriteGuard};
use async_lock::RwLock as ARwLock;
use test_executors::spin_on;
use super::*;
struct MockStore(&'static str);
impl Drop for MockStore {
fn drop(&mut self) {
println!("Store {} dropped", self.0);
self.0 = "FAIL"; }
}
struct MockLock<'a>(&'a MockStore);
impl Drop for MockLock<'_> {
fn drop(&mut self) { println!("Lock for {} dropped", self.0.0) }
}
impl Deref for MockLock<'_> {
type Target = str;
fn deref(&self) -> &Self::Target { self.0.0 }
}
fn mk_store() -> MockStore { MockStore("flag") }
fn mk_lock() -> Bound<MockLock<'static>, MockStore> { Bound::new(mk_store(), |s| MockLock(s)) }
#[test]
fn test_functionality() {
println!("-- testing basic functionality --");
let bound = mk_lock();
assert_eq!(bound.wrapped().0.0, "flag"); assert_eq!(bound.deref(), "flag"); }
#[test]
fn test_unbind() {
println!("-- testing unbind --");
let source = {
let bound = mk_lock();
bound.unbind()
};
println!("derived should be dropped but source is still alive");
assert_eq!(source.0, "flag"); }
fn get_writer() -> Bound<RwLockWriteGuard<'static, usize>, Arc<RwLock<usize>>> {
let shared_data = Arc::new(RwLock::new(1));
Bound::new(shared_data.clone(), |a| a.write().unwrap())
}
#[test]
fn test_rwlock() {
println!("-- testing returning RwLockWriteGuard referencing Arc<RwLock> --");
let mut writer = get_writer();
*writer = 2;
}
struct MockLock2<'a>(&'a MockStore);
impl Drop for MockLock2<'_> {
fn drop(&mut self) { println!("Lock2 for {} dropped", self.0.0) }
}
#[test]
fn test_map() {
println!("-- testing mapping one type of lock into another --");
let first = mk_lock();
let second = first.map(|MockLock(store)| MockLock2(store));
println!("The first lock should be gcd now");
assert_eq!(second.wrapped().0.0, "flag");
}
#[allow(unused)]
fn works_with_async_lock() -> impl Sized {
let lock = Arc::new(ARwLock::new(1));
Bound::async_new(lock.clone(), |l| l.read())
}
#[allow(unused)]
fn works_with_async_lock_of_async_lock() -> impl Sized {
let lock = Arc::new(ARwLock::new(Arc::new(ARwLock::new(1))));
spin_on(async move {
let inner = Bound::async_new(lock.clone(), |l| l.read()).await;
Bound::async_new(inner, |g| g.read()).await
})
}
}