use std::fmt;
use std::fmt::Display;
use allocative::Allocative;
use derivative::Derivative;
use dupe::Dupe;
use either::Either;
use starlark_derive::starlark_module;
use starlark_derive::starlark_value;
use starlark_derive::Coerce;
use starlark_derive::Freeze;
use starlark_derive::Trace;
use starlark_map::StarlarkHasher;
use crate as starlark;
use crate::__derive_refs::serde;
use crate::any::ProvidesStaticType;
use crate::environment::Methods;
use crate::environment::MethodsBuilder;
use crate::environment::MethodsStatic;
use crate::starlark_complex_value;
use crate::starlark_complex_values;
use crate::typing::Ty;
use crate::values::enumeration::enum_type::EnumType;
use crate::values::enumeration::enum_type::FrozenEnumType;
use crate::values::types::type_instance_id::TypeInstanceId;
use crate::values::StarlarkValue;
use crate::values::Value;
use crate::values::ValueLike;
#[derive(
Clone,
Derivative,
Trace,
Coerce,
Freeze,
ProvidesStaticType,
Allocative
)]
#[repr(C)]
#[derivative(Debug)]
pub struct EnumValueGen<V> {
#[derivative(Debug = "ignore")]
pub(crate) typ: V, pub(crate) value: V, pub(crate) index: i32, pub(crate) id: TypeInstanceId,
}
impl<'v, V: ValueLike<'v> + 'v> Display for EnumValueGen<V> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let ty_enum_data = match self.get_enum_type() {
Either::Left(x) => x.ty_enum_data(),
Either::Right(x) => x.ty_enum_data(),
};
match ty_enum_data {
Some(ty_enum_data) => {
{
write!(f, "{}", &ty_enum_data.name)?;
write!(f, "(")?;
Display::fmt(&self.value, f)?;
write!(f, ")")?
};
Ok(())
}
None => {
{
write!(f, "enum()(")?;
Display::fmt(&self.value, f)?;
write!(f, ")")?
};
Ok(())
}
}
}
}
starlark_complex_values!(EnumType);
starlark_complex_value!(pub EnumValue);
impl<'v, V: ValueLike<'v>> EnumValueGen<V> {
pub const TYPE: &'static str = "enum";
fn get_enum_type(&self) -> Either<&'v EnumType<'v>, &'v FrozenEnumType> {
EnumType::from_value(self.typ.to_value()).unwrap()
}
}
#[starlark_value(type = EnumValue::TYPE)]
impl<'v, V: ValueLike<'v> + 'v> StarlarkValue<'v> for EnumValueGen<V>
where
Self: ProvidesStaticType<'v>,
{
fn matches_type(&self, ty: &str) -> bool {
if ty == EnumValue::TYPE {
return true;
}
let ty_enum_data = match self.get_enum_type() {
Either::Left(x) => x.ty_enum_data(),
Either::Right(x) => x.ty_enum_data(),
};
match ty_enum_data {
Some(ty_enum_data) => ty_enum_data.name == ty,
None => false,
}
}
fn write_hash(&self, hasher: &mut StarlarkHasher) -> crate::Result<()> {
self.value.write_hash(hasher)
}
fn get_methods() -> Option<&'static Methods>
where
Self: Sized,
{
static RES: MethodsStatic = MethodsStatic::new();
RES.methods(enum_value_methods)
}
fn typechecker_ty(&self) -> Option<Ty> {
let ty_enum_type = match self.get_enum_type() {
Either::Left(x) => x.ty_enum_data()?,
Either::Right(x) => x.ty_enum_data()?,
};
Some(ty_enum_type.ty_enum_value.dupe())
}
}
impl<'v, V: ValueLike<'v>> serde::Serialize for EnumValueGen<V> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
self.value.serialize(serializer)
}
}
#[starlark_module]
fn enum_value_methods(methods: &mut MethodsBuilder) {
#[starlark(attribute)]
fn index(this: &EnumValue) -> anyhow::Result<i32> {
Ok(this.index)
}
#[starlark(attribute)]
fn value<'v>(this: &EnumValue<'v>) -> anyhow::Result<Value<'v>> {
Ok(this.value.to_value())
}
}