use std::any::TypeId;
use std::marker::PhantomData;
use crate::any::AnyLifetime;
use crate::values::Value;
pub struct Demand<'a, 'v> {
type_id_of_t: TypeId,
option: *mut (),
_marker: PhantomData<&'a mut &'v ()>,
}
impl<'a, 'v> Demand<'a, 'v> {
fn new<T: AnyLifetime<'v>>(option: &mut Option<T>) -> Demand<'a, 'v> {
Demand {
type_id_of_t: T::static_type_id(),
option: option as *mut _ as *mut (),
_marker: PhantomData,
}
}
pub fn provide_value<T: AnyLifetime<'v>>(&mut self, value: T) {
if self.type_id_of_t == T::static_type_id() {
unsafe { *(self.option as *mut Option<T>) = Some(value) };
}
}
pub(crate) fn provide_ref_static<T: 'static + ?Sized>(&mut self, value: &'v T) {
if self.type_id_of_t == TypeId::of::<&'static T>() {
unsafe { *(self.option as *mut Option<&'v T>) = Some(value) };
}
}
}
pub(crate) fn request_value_impl<'v, T: AnyLifetime<'v>>(value: Value<'v>) -> Option<T> {
let mut option = None;
value.get_ref().provide(&mut Demand::new(&mut option));
option
}
#[cfg(test)]
mod tests {
use allocative::Allocative;
use starlark_derive::starlark_value;
use starlark_derive::NoSerialize;
use crate as starlark;
use crate::any::ProvidesStaticType;
use crate::starlark_simple_value;
use crate::values::demand::Demand;
use crate::values::Heap;
use crate::values::StarlarkValue;
trait SomeTrait {
fn payload(&self) -> u32;
}
unsafe impl<'v> ProvidesStaticType<'v> for &'v dyn SomeTrait {
type StaticType = &'static dyn SomeTrait;
}
#[derive(
ProvidesStaticType,
derive_more::Display,
Debug,
NoSerialize,
Allocative
)]
#[display("SomeType")]
struct MyValue {
payload: u32,
}
impl SomeTrait for MyValue {
fn payload(&self) -> u32 {
self.payload
}
}
starlark_simple_value!(MyValue);
#[starlark_value(type = "MyValue")]
impl<'v> StarlarkValue<'v> for MyValue {
fn provide(&'v self, demand: &mut Demand<'_, 'v>) {
demand.provide_value::<&dyn SomeTrait>(self);
}
}
#[test]
fn test_trait_downcast() {
let heap = Heap::new();
let value = heap.alloc_simple(MyValue { payload: 17 });
assert!(value.request_value::<String>().is_none());
let some_trait = value.request_value::<&dyn SomeTrait>().unwrap();
assert_eq!(17, some_trait.payload());
}
}