use std::fmt;
use std::fmt::Display;
use std::ops::Deref;
use allocative::Allocative;
use dupe::Clone_;
use dupe::Dupe;
use dupe::Dupe_;
use crate::cast::transmute;
use crate::typing::Ty;
use crate::values::none::NoneType;
use crate::values::owned_frozen_ref::OwnedFrozenRef;
use crate::values::type_repr::StarlarkTypeRepr;
use crate::values::AllocFrozenValue;
use crate::values::FrozenHeap;
use crate::values::FrozenHeapRef;
use crate::values::FrozenValue;
use crate::values::FrozenValueTyped;
use crate::values::OwnedRefFrozenRef;
use crate::values::StarlarkValue;
use crate::values::Value;
#[derive(Debug, thiserror::Error)]
enum OwnedError {
#[error("Expected value of type `{0}` but got `{1}`")]
WrongType(&'static str, String),
}
#[derive(Debug, Clone, Dupe, Allocative)]
pub struct OwnedFrozenValue {
owner: FrozenHeapRef,
value: FrozenValue,
}
impl Default for OwnedFrozenValue {
fn default() -> Self {
OwnedFrozenValue::alloc(NoneType)
}
}
impl Display for OwnedFrozenValue {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
Display::fmt(&self.value, f)
}
}
impl StarlarkTypeRepr for OwnedFrozenValue {
type Canonical = <FrozenValue as StarlarkTypeRepr>::Canonical;
fn starlark_type_repr() -> Ty {
FrozenValue::starlark_type_repr()
}
}
impl AllocFrozenValue for OwnedFrozenValue {
fn alloc_frozen_value(self, heap: &FrozenHeap) -> FrozenValue {
unsafe { self.owned_frozen_value(heap) }
}
}
impl OwnedFrozenValue {
pub unsafe fn new(owner: FrozenHeapRef, value: FrozenValue) -> Self {
Self { owner, value }
}
pub fn alloc(x: impl AllocFrozenValue) -> Self {
let heap = FrozenHeap::new();
let val = heap.alloc(x);
unsafe { Self::new(heap.into_ref(), val) }
}
pub fn unpack_bool(&self) -> Option<bool> {
self.value.unpack_bool()
}
pub fn unpack_i32(&self) -> Option<i32> {
self.value.unpack_i32()
}
pub fn unpack_str(&self) -> Option<&str> {
self.value.unpack_str()
}
pub fn downcast<T: StarlarkValue<'static>>(self) -> Result<OwnedFrozenValueTyped<T>, Self> {
match FrozenValueTyped::new(self.value) {
Some(typed) => Ok(OwnedFrozenValueTyped {
owner: self.owner,
value: typed,
}),
None => Err(self),
}
}
pub fn downcast_starlark<T: StarlarkValue<'static>>(
self,
) -> crate::Result<OwnedFrozenValueTyped<T>> {
match self.downcast() {
Ok(v) => Ok(v),
Err(this) => Err(crate::Error::new_value(OwnedError::WrongType(
T::TYPE,
this.value.to_value().to_string_for_type_error(),
))),
}
}
pub fn value<'v>(&'v self) -> Value<'v> {
Value::new_frozen(self.value)
}
pub fn owned_value<'v>(&self, heap: &'v FrozenHeap) -> Value<'v> {
unsafe { self.owned_frozen_value(heap).to_value() }
}
pub fn map(&self, f: impl FnOnce(FrozenValue) -> FrozenValue) -> Self {
Self {
owner: self.owner.dupe(),
value: f(self.value),
}
}
pub fn try_map<E>(
&self,
f: impl FnOnce(FrozenValue) -> Result<FrozenValue, E>,
) -> Result<Self, E> {
Ok(Self {
owner: self.owner.dupe(),
value: f(self.value)?,
})
}
pub fn owner(&self) -> &FrozenHeapRef {
&self.owner
}
pub unsafe fn unchecked_frozen_value(&self) -> FrozenValue {
self.value
}
pub unsafe fn owned_frozen_value(&self, heap: &FrozenHeap) -> FrozenValue {
heap.add_reference(&self.owner);
self.value
}
}
#[derive(Debug, Clone_, Dupe_, Allocative)]
pub struct OwnedFrozenValueTyped<T: StarlarkValue<'static>> {
owner: FrozenHeapRef,
value: FrozenValueTyped<'static, T>,
}
impl<T: StarlarkValue<'static>> Deref for OwnedFrozenValueTyped<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
self.value.as_ref()
}
}
impl<T: StarlarkValue<'static>> OwnedFrozenValueTyped<T> {
pub unsafe fn new(owner: FrozenHeapRef, value: FrozenValueTyped<'static, T>) -> Self {
Self { owner, value }
}
pub unsafe fn to_frozen_value(&self) -> FrozenValue {
self.value.to_frozen_value()
}
pub fn to_value<'v>(&'v self) -> Value<'v> {
unsafe { self.to_frozen_value().to_value() }
}
pub fn to_owned_frozen_value(&self) -> OwnedFrozenValue {
OwnedFrozenValue {
owner: self.owner.dupe(),
value: self.value.to_frozen_value(),
}
}
pub fn as_owned_ref_frozen_ref(&self) -> OwnedRefFrozenRef<'_, T> {
unsafe { OwnedRefFrozenRef::new_unchecked(self.value.as_ref(), &self.owner) }
}
pub fn into_owned_frozen_ref(self) -> OwnedFrozenRef<T> {
unsafe { OwnedFrozenRef::new_unchecked(self.value.as_ref(), self.owner) }
}
pub fn owner(&self) -> &FrozenHeapRef {
&self.owner
}
pub fn as_ref(&self) -> &T {
self.value.as_ref()
}
pub unsafe fn value_typed(&self) -> FrozenValueTyped<'static, T> {
self.value
}
pub fn owned_frozen_value_typed(&self, heap: &FrozenHeap) -> FrozenValueTyped<'static, T> {
heap.add_reference(&self.owner);
self.value
}
pub fn owned_frozen_value(&self, heap: &FrozenHeap) -> FrozenValue {
self.owned_frozen_value_typed(heap).to_frozen_value()
}
pub fn owned_value<'v>(&self, heap: &'v FrozenHeap) -> Value<'v> {
self.owned_frozen_value(heap).to_value()
}
pub fn owned_as_ref<'v>(&self, heap: &'v FrozenHeap) -> &'v T {
self.owned_value(heap);
unsafe { transmute!(&T, &T, self.as_ref()) }
}
pub fn map<U: StarlarkValue<'static>>(
&self,
f: impl FnOnce(FrozenValueTyped<T>) -> FrozenValueTyped<U>,
) -> OwnedFrozenValueTyped<U> {
OwnedFrozenValueTyped {
owner: self.owner.dupe(),
value: f(self.value),
}
}
pub fn try_map<U: StarlarkValue<'static>, E>(
&self,
f: impl FnOnce(FrozenValueTyped<T>) -> Result<FrozenValueTyped<U>, E>,
) -> Result<OwnedFrozenValueTyped<U>, E> {
Ok(OwnedFrozenValueTyped {
owner: self.owner.dupe(),
value: f(self.value)?,
})
}
pub fn maybe_map<U: StarlarkValue<'static>>(
&self,
f: impl FnOnce(FrozenValueTyped<T>) -> Option<FrozenValueTyped<U>>,
) -> Option<OwnedFrozenValueTyped<U>> {
Some(OwnedFrozenValueTyped {
owner: self.owner.dupe(),
value: f(self.value)?,
})
}
}