use core::future::Future;
use core::pin::pin;
use core::task::{Context, Poll, Waker};
use closed_trait::{enumerate, sealed};
#[derive(Clone)]
pub struct Square {
pub side: i32,
}
#[derive(Clone)]
pub struct Circle {
pub radius: i32,
}
#[allow(async_fn_in_trait)]
#[enumerate(match_any)]
#[sealed(Square, Circle)]
pub trait Shape {
fn area(&self) -> i32;
fn doubled(self) -> Self
where
Self: Sized;
async fn area_later(&self) -> i32;
unsafe fn area_unchecked(&self) -> i32;
fn duplicate(&self) -> Self
where
Self: Clone;
}
impl Shape for Square {
fn area(&self) -> i32 {
self.side * self.side
}
fn doubled(self) -> Self {
Square {
side: self.side * 2,
}
}
async fn area_later(&self) -> i32 {
self.area()
}
unsafe fn area_unchecked(&self) -> i32 {
self.area()
}
fn duplicate(&self) -> Self {
self.clone()
}
}
impl Shape for Circle {
fn area(&self) -> i32 {
3 * self.radius * self.radius
}
fn doubled(self) -> Self {
Circle {
radius: self.radius * 2,
}
}
async fn area_later(&self) -> i32 {
self.area()
}
unsafe fn area_unchecked(&self) -> i32 {
self.area()
}
fn duplicate(&self) -> Self {
self.clone()
}
}
impl AnyShape {
pub fn area(&self) -> i32 {
match_any_shape!(self, s => s.area())
}
pub fn doubled(self) -> Self {
match_any_shape!(self, s => AnyShape::from(s.doubled()))
}
pub async fn area_later(&self) -> i32 {
match_any_shape!(self, s => s.area_later().await)
}
pub unsafe fn area_unchecked(&self) -> i32 {
match_any_shape!(self, s => unsafe { s.area_unchecked() })
}
pub fn duplicate(&self) -> Self {
match_any_shape!(self, s => AnyShape::from(s.duplicate()))
}
}
fn block_on<F: Future>(future: F) -> F::Output {
let mut future = pin!(future);
let mut context = Context::from_waker(Waker::noop());
match future.as_mut().poll(&mut context) {
Poll::Ready(value) => value,
Poll::Pending => panic!("the forwarded future yielded, which none of these do"),
}
}
#[test]
fn plain_and_self_returning() {
let shape = AnyShape::from(Square { side: 3 });
assert_eq!(shape.area(), 9);
match AnyShape::from(Square { side: 3 }).doubled() {
AnyShape::Square(square) => assert_eq!(square.side, 6),
AnyShape::Circle(_) => unreachable!(),
}
}
#[test]
fn async_and_unsafe() {
let shape = AnyShape::from(Circle { radius: 2 });
assert_eq!(block_on(shape.area_later()), 12);
assert_eq!(unsafe { shape.area_unchecked() }, 12);
}
#[test]
fn a_self_bounded_method() {
let shape = AnyShape::from(Square { side: 4 });
assert_eq!(shape.duplicate().area(), 16);
}