use super::optionality::{AllowedOptionalityChain, Optional, Optionality, Required};
use crate::prelude::*;
use core::any::Any;
use core::any::TypeId;
use core::borrow::Borrow;
use core::fmt::{Debug, Display};
use core::iter::Peekable;
use core::marker::PhantomData;
use core::slice::IterMut;
use variadics_please::all_tuples;
#[derive(Debug)]
pub struct YarnValueWrapper {
raw: Option<YarnValue>,
converted: Option<Box<dyn Any>>,
}
#[doc(hidden)]
pub type YarnValueWrapperIter<'a> = Peekable<IterMut<'a, YarnValueWrapper>>;
impl From<YarnValue> for YarnValueWrapper {
fn from(value: YarnValue) -> Self {
Self {
raw: Some(value),
converted: None,
}
}
}
impl YarnValueWrapper {
fn convert<T>(&mut self)
where
T: TryFrom<YarnValue> + 'static,
<T as TryFrom<YarnValue>>::Error: Display,
{
let raw = core::mem::take(&mut self.raw).unwrap();
let converted: T = raw
.try_into()
.unwrap_or_else(|e| panic!("Parameter passed to Yarn has invalid type: {e}"));
self.converted.replace(Box::new(converted));
}
}
pub trait YarnFnParam {
type Item<'new>;
#[doc(hidden)]
type Optionality: Optionality;
#[doc(hidden)]
fn retrieve<'a>(iter: &mut YarnValueWrapperIter<'a>) -> Self::Item<'a>;
#[doc(hidden)]
fn parameter_types() -> Vec<TypeId>;
}
pub type YarnFnParamItem<'a, P> = <P as YarnFnParam>::Item<'a>;
impl<T: YarnFnParam + 'static> YarnFnParam for Option<T> {
type Item<'new> = Option<T::Item<'new>>;
type Optionality = Optional;
fn retrieve<'a>(iter: &mut YarnValueWrapperIter<'a>) -> Self::Item<'a> {
if iter.peek().is_some() {
Some(T::retrieve(iter))
} else {
None
}
}
fn parameter_types() -> Vec<TypeId> {
vec![TypeId::of::<Option<T>>()]
}
}
macro_rules! impl_yarn_fn_param_tuple {
($($param: ident),*) => {
#[allow(non_snake_case)]
impl<$($param,)*> YarnFnParam for ($($param,)*)
where $($param: YarnFnParam + 'static,)*
($(<$param as YarnFnParam>::Optionality,)*): AllowedOptionalityChain
{
type Item<'new> = ($($param::Item<'new>,)*);
type Optionality = <($(<$param as YarnFnParam>::Optionality,)*) as AllowedOptionalityChain>::Last;
#[allow(unused_variables, clippy::unused_unit)] fn retrieve<'a>(iter: &mut YarnValueWrapperIter<'a>) -> Self::Item<'a> {
($($param::retrieve(iter),)*)
}
fn parameter_types() -> Vec<TypeId> {
vec![$(TypeId::of::<$param>()),*]
}
}
};
}
all_tuples!(impl_yarn_fn_param_tuple, 0, 16, P);
struct ResRef<'a, T>
where
T: TryFrom<YarnValue> + 'static,
<T as TryFrom<YarnValue>>::Error: Display,
{
value: &'a T,
phantom_data: PhantomData<T>,
}
impl<T> YarnFnParam for ResRef<'_, T>
where
T: TryFrom<YarnValue> + 'static,
<T as TryFrom<YarnValue>>::Error: Display,
{
type Item<'new> = ResRef<'new, T>;
type Optionality = Required;
fn retrieve<'a>(iter: &mut YarnValueWrapperIter<'a>) -> Self::Item<'a> {
let value = iter.next().expect("Passed too few arguments to YarnFn");
value.convert::<T>();
let converted = value.converted.as_ref().unwrap();
let value = converted.downcast_ref::<T>().unwrap();
ResRef {
value,
phantom_data: PhantomData,
}
}
fn parameter_types() -> Vec<TypeId> {
vec![TypeId::of::<&T>()]
}
}
struct ResRefBorrow<'a, T, U>
where
T: TryFrom<YarnValue> + 'static,
<T as TryFrom<YarnValue>>::Error: Display,
T: Borrow<U>,
U: ?Sized + 'static,
{
value: &'a U,
phantom_data: PhantomData<T>,
}
impl<T, U> YarnFnParam for ResRefBorrow<'_, T, U>
where
T: TryFrom<YarnValue> + 'static,
<T as TryFrom<YarnValue>>::Error: Display,
T: Borrow<U>,
U: ?Sized + 'static,
{
type Item<'new> = ResRefBorrow<'new, T, U>;
type Optionality = Required;
fn retrieve<'a>(iter: &mut YarnValueWrapperIter<'a>) -> Self::Item<'a> {
let value = iter.next().expect("Passed too few arguments to YarnFn");
value.convert::<T>();
let converted = value.converted.as_ref().unwrap();
let value = converted.downcast_ref::<T>().unwrap();
ResRefBorrow {
value: value.borrow(),
phantom_data: PhantomData,
}
}
fn parameter_types() -> Vec<TypeId> {
vec![TypeId::of::<&U>()]
}
}
struct ResOwned<T>
where
T: TryFrom<YarnValue> + 'static,
<T as TryFrom<YarnValue>>::Error: Display,
{
value: T,
}
impl<T> YarnFnParam for ResOwned<T>
where
T: TryFrom<YarnValue> + 'static,
<T as TryFrom<YarnValue>>::Error: Display,
{
type Item<'new> = ResOwned<T>;
type Optionality = Required;
fn retrieve<'a>(iter: &mut YarnValueWrapperIter<'a>) -> Self::Item<'a> {
let value = iter.next().expect("Passed too few arguments to YarnFn");
value.convert::<T>();
let converted = value.converted.take().unwrap();
let value = *converted.downcast::<T>().unwrap();
ResOwned { value }
}
fn parameter_types() -> Vec<TypeId> {
vec![TypeId::of::<T>()]
}
}
macro_rules! impl_yarn_fn_param {
([$($referenced:ty $(=> $owned:ty)?),*]: YarnFnParam) => {
$(
impl_yarn_fn_param_inner!{
$referenced $(=> $owned)?: YarnFnParam
}
)*
}
}
macro_rules! impl_yarn_fn_param_inner {
($referenced:ty: YarnFnParam) => {
impl YarnFnParam for &$referenced {
type Item<'new> = &'new $referenced;
type Optionality = Required;
fn retrieve<'a>(iter: &mut YarnValueWrapperIter<'a>) -> Self::Item<'a> {
ResRef::<$referenced>::retrieve(iter).value
}
fn parameter_types() -> Vec<TypeId> {
vec![TypeId::of::<&$referenced>()]
}
}
impl YarnFnParam for $referenced {
type Item<'new> = $referenced;
type Optionality = Required;
fn retrieve<'a>(iter: &mut YarnValueWrapperIter<'a>) -> Self::Item<'a> {
ResOwned::<$referenced>::retrieve(iter).value
}
fn parameter_types() -> Vec<TypeId> {
vec![TypeId::of::<$referenced>()]
}
}
};
($referenced:ty => $owned:ty: YarnFnParam) => {
impl YarnFnParam for &$referenced {
type Item<'new> = &'new $referenced;
type Optionality = Required;
fn retrieve<'a>(iter: &mut YarnValueWrapperIter<'a>) -> Self::Item<'a> {
ResRefBorrow::<$owned, $referenced>::retrieve(iter).value
}
fn parameter_types() -> Vec<TypeId> {
vec![TypeId::of::<&$referenced>()]
}
}
impl YarnFnParam for &$owned {
type Item<'new> = &'new $owned;
type Optionality = Required;
fn retrieve<'a>(iter: &mut YarnValueWrapperIter<'a>) -> Self::Item<'a> {
ResRef::<$owned>::retrieve(iter).value
}
fn parameter_types() -> Vec<TypeId> {
vec![TypeId::of::<&$owned>()]
}
}
impl YarnFnParam for $owned {
type Item<'new> = $owned;
type Optionality = Required;
fn retrieve<'a>(iter: &mut YarnValueWrapperIter<'a>) -> Self::Item<'a> {
ResOwned::<$owned>::retrieve(iter).value
}
fn parameter_types() -> Vec<TypeId> {
vec![TypeId::of::<$owned>()]
}
}
};
}
impl_yarn_fn_param! {
[str => String, YarnValue, bool, f32, f64, i8, i16, i32, i64, i128, u8, u16, u32, u64, u128, usize, isize]: YarnFnParam
}