use std::ops::Deref;
use either::Either;
use gazebo::prelude::*;
use crate::values::{list::List, tuple::Tuple, Value, ValueError};
pub trait UnpackValue<'v>: Sized {
fn expected() -> String;
fn unpack_value(value: Value<'v>) -> Option<Self>;
fn unpack_param(value: Value<'v>) -> anyhow::Result<Self> {
Self::unpack_value(value).ok_or_else(|| {
ValueError::IncorrectParameterTypeWithExpected(
Self::expected(),
value.get_type().to_owned(),
)
.into()
})
}
fn unpack_named_param(value: Value<'v>, param_name: &str) -> anyhow::Result<Self> {
Self::unpack_value(value).ok_or_else(|| {
ValueError::IncorrectParameterTypeNamedWithExpected(
param_name.to_owned(),
Self::expected(),
value.get_type().to_owned(),
)
.into()
})
}
}
impl<'v> UnpackValue<'v> for Value<'v> {
fn expected() -> String {
"Value".to_owned()
}
fn unpack_value(value: Value<'v>) -> Option<Self> {
Some(value)
}
}
#[derive(Debug, Copy, Clone, Dupe)]
pub struct ValueOf<'v, T: UnpackValue<'v>> {
pub value: Value<'v>,
pub typed: T,
}
impl<'v, T: UnpackValue<'v>> Deref for ValueOf<'v, T> {
type Target = Value<'v>;
fn deref(&self) -> &Self::Target {
&self.value
}
}
impl<'v, T: UnpackValue<'v>> UnpackValue<'v> for ValueOf<'v, T> {
fn expected() -> String {
T::expected()
}
fn unpack_value(value: Value<'v>) -> Option<Self> {
let typed = T::unpack_value(value)?;
Some(Self { value, typed })
}
}
impl<'v, TLeft: UnpackValue<'v>, TRight: UnpackValue<'v>> UnpackValue<'v>
for Either<TLeft, TRight>
{
fn expected() -> String {
format!("either {} or {}", TLeft::expected(), TRight::expected())
}
fn unpack_value(value: Value<'v>) -> Option<Self> {
if let Some(left) = TLeft::unpack_value(value) {
Some(Self::Left(left))
} else {
TRight::unpack_value(value).map(Self::Right)
}
}
}
impl<'v, T: UnpackValue<'v>> UnpackValue<'v> for Vec<T> {
fn expected() -> String {
format!("list or tuple of {}", T::expected())
}
fn unpack_value(value: Value<'v>) -> Option<Self> {
if let Some(o) = List::from_value(value) {
o.iter().map(T::unpack_value).collect::<Option<Vec<_>>>()
} else if let Some(o) = Tuple::from_value(value) {
o.iter().map(T::unpack_value).collect::<Option<Vec<_>>>()
} else {
None
}
}
}