use std::convert::Infallible;
use std::fmt::Debug;
use anyhow::Context;
use either::Either;
use starlark_syntax::StarlarkResultExt;
use crate::typing::Ty;
use crate::values::type_repr::StarlarkTypeRepr;
use crate::values::Value;
pub trait UnpackValueError: Debug + Send + Sync + 'static {
fn into_error(this: Self) -> crate::Error;
}
impl UnpackValueError for crate::Error {
#[cold]
fn into_error(this: Self) -> crate::Error {
this
}
}
impl UnpackValueError for anyhow::Error {
#[cold]
fn into_error(this: Self) -> crate::Error {
crate::Error::new_value(this)
}
}
impl UnpackValueError for Infallible {
#[cold]
fn into_error(this: Self) -> crate::Error {
match this {}
}
}
impl<A: UnpackValueError, B: UnpackValueError> UnpackValueError for Either<A, B> {
#[cold]
fn into_error(this: Self) -> crate::Error {
match this {
Either::Left(a) => UnpackValueError::into_error(a),
Either::Right(b) => UnpackValueError::into_error(b),
}
}
}
pub trait UnpackValueErrorInfallible: UnpackValueError {
fn into_infallible(this: Self) -> !;
}
impl UnpackValueErrorInfallible for Infallible {
fn into_infallible(this: Self) -> ! {
match this {}
}
}
impl<A: UnpackValueErrorInfallible, B: UnpackValueErrorInfallible> UnpackValueErrorInfallible
for Either<A, B>
{
fn into_infallible(this: Self) -> ! {
match this {
Either::Left(a) => UnpackValueErrorInfallible::into_infallible(a),
Either::Right(b) => UnpackValueErrorInfallible::into_infallible(b),
}
}
}
pub trait UnpackValue<'v>: Sized + StarlarkTypeRepr {
type Error: UnpackValueError;
fn unpack_value_impl(value: Value<'v>) -> Result<Option<Self>, Self::Error>;
fn unpack_value(value: Value<'v>) -> Result<Option<Self>, crate::Error> {
Self::unpack_value_impl(value).map_err(Self::Error::into_error)
}
fn unpack_value_opt(value: Value<'v>) -> Option<Self>
where
Self::Error: UnpackValueErrorInfallible,
{
match Self::unpack_value_impl(value) {
Ok(x) => x,
Err(e) => Self::Error::into_infallible(e),
}
}
#[inline]
fn unpack_value_err(value: Value<'v>) -> anyhow::Result<Self> {
#[cold]
fn error<'v>(value: Value<'v>, ty: fn() -> Ty) -> anyhow::Error {
#[derive(thiserror::Error, Debug)]
#[error("Expected `{0}`, but got `{1}`")]
struct IncorrectType(Ty, String);
crate::Error::new_value(IncorrectType(ty(), value.to_string_for_type_error()))
.into_anyhow()
}
Self::unpack_value(value)
.into_anyhow_result()?
.ok_or_else(|| error(value, Self::starlark_type_repr))
}
#[inline]
fn unpack_param(value: Value<'v>) -> anyhow::Result<Self> {
#[cold]
fn error<'v>(value: Value<'v>, ty: fn() -> Ty) -> anyhow::Error {
#[derive(thiserror::Error, Debug)]
#[error("Type of parameters mismatch, expected `{0}`, actual `{1}`")]
struct IncorrectParameterTypeWithExpected(Ty, String);
crate::Error::new_value(IncorrectParameterTypeWithExpected(
ty(),
value.to_string_for_type_error(),
))
.into_anyhow()
}
Self::unpack_value(value)
.into_anyhow_result()?
.ok_or_else(|| error(value, Self::starlark_type_repr))
}
#[inline]
fn unpack_named_param(value: Value<'v>, param_name: &str) -> anyhow::Result<Self> {
#[cold]
fn error<'v>(value: Value<'v>, param_name: &str, ty: fn() -> Ty) -> anyhow::Error {
#[derive(thiserror::Error, Debug)]
#[error("Type of parameter `{0}` doesn't match, expected `{1}`, actual `{2}`")]
struct IncorrectParameterTypeNamedWithExpected(String, Ty, String);
crate::Error::new_value(IncorrectParameterTypeNamedWithExpected(
param_name.to_owned(),
ty(),
value.to_string_for_type_error(),
))
.into_anyhow()
}
Self::unpack_value(value)
.into_anyhow_result()
.with_context(|| {
format!(
"Error unpacking value for parameter `{}` of type `{}",
param_name,
Self::starlark_type_repr()
)
})?
.ok_or_else(|| error(value, param_name, Self::starlark_type_repr))
}
}
impl<'v> UnpackValue<'v> for Value<'v> {
type Error = Infallible;
fn unpack_value_impl(value: Value<'v>) -> Result<Option<Self>, Self::Error> {
Ok(Some(value))
}
}
impl<'v, TLeft: UnpackValue<'v>, TRight: UnpackValue<'v>> UnpackValue<'v>
for Either<TLeft, TRight>
{
type Error = Either<TLeft::Error, TRight::Error>;
fn unpack_value_impl(value: Value<'v>) -> Result<Option<Self>, Self::Error> {
if let Some(left) = TLeft::unpack_value_impl(value).map_err(Either::Left)? {
Ok(Some(Self::Left(left)))
} else {
Ok(TRight::unpack_value_impl(value)
.map_err(Either::Right)?
.map(Self::Right))
}
}
}