starlark/values/layout/
complex.rs1use std::convert::Infallible;
19use std::fmt;
20use std::marker::PhantomData;
21
22use allocative::Allocative;
23use dupe::Clone_;
24use dupe::Copy_;
25use dupe::Dupe_;
26use either::Either;
27use starlark_syntax::value_error;
28
29use crate::typing::Ty;
30use crate::values::type_repr::StarlarkTypeRepr;
31use crate::values::AllocValue;
32use crate::values::ComplexValue;
33use crate::values::Freeze;
34use crate::values::FreezeError;
35use crate::values::FreezeResult;
36use crate::values::Freezer;
37use crate::values::FrozenValueTyped;
38use crate::values::StarlarkValue;
39use crate::values::Trace;
40use crate::values::Tracer;
41use crate::values::UnpackValue;
42use crate::values::Value;
43use crate::values::ValueLike;
44use crate::values::ValueTyped;
45
46#[derive(Copy_, Clone_, Dupe_, Allocative)]
48#[allocative(skip)] pub struct ValueTypedComplex<'v, T>(Value<'v>, PhantomData<T>)
50where
51 T: ComplexValue<'v>,
52 T::Frozen: StarlarkValue<'static>;
53
54impl<'v, T> ValueTypedComplex<'v, T>
55where
56 T: ComplexValue<'v>,
57 T::Frozen: StarlarkValue<'static>,
58{
59 pub fn new(value: Value<'v>) -> Option<Self> {
61 if value.downcast_ref::<T>().is_some()
62 || unsafe { value.cast_lifetime() }
63 .downcast_ref::<T::Frozen>()
64 .is_some()
65 {
66 Some(ValueTypedComplex(value, PhantomData))
67 } else {
68 None
69 }
70 }
71
72 pub fn new_err(value: Value<'v>) -> anyhow::Result<Self> {
74 match Self::new(value) {
75 Some(v) => Ok(v),
76 None => Err(value_error!(
77 "Expected value of type `{}`, got: `{}`",
78 T::TYPE,
79 value.to_string_for_type_error()
80 )
81 .into_anyhow()),
82 }
83 }
84
85 #[inline]
87 pub fn to_value(self) -> Value<'v> {
88 self.0
89 }
90
91 #[inline]
93 pub fn unpack(self) -> Either<&'v T, &'v T::Frozen> {
94 if let Some(v) = self.0.downcast_ref::<T>() {
95 Either::Left(v)
96 } else if let Some(v) =
97 unsafe { self.0.to_value().cast_lifetime() }.downcast_ref::<T::Frozen>()
98 {
99 Either::Right(v)
100 } else {
101 unreachable!("validated at construction")
102 }
103 }
104}
105
106impl<'v, T> StarlarkTypeRepr for ValueTypedComplex<'v, T>
107where
108 T: ComplexValue<'v>,
109 T::Frozen: StarlarkValue<'static>,
110{
111 type Canonical = <T as StarlarkTypeRepr>::Canonical;
112
113 fn starlark_type_repr() -> Ty {
114 T::starlark_type_repr()
115 }
116}
117
118impl<'v, T> AllocValue<'v> for ValueTypedComplex<'v, T>
119where
120 T: ComplexValue<'v>,
121 T::Frozen: StarlarkValue<'static>,
122{
123 #[inline]
124 fn alloc_value(self, _heap: &'v crate::values::Heap) -> Value<'v> {
125 self.0
126 }
127}
128
129impl<'v, T> UnpackValue<'v> for ValueTypedComplex<'v, T>
130where
131 T: ComplexValue<'v>,
132 T::Frozen: StarlarkValue<'static>,
133{
134 type Error = Infallible;
135
136 fn unpack_value_impl(value: Value<'v>) -> Result<Option<Self>, Self::Error> {
137 Ok(Self::new(value))
138 }
139}
140
141impl<'v, T> From<ValueTyped<'v, T>> for ValueTypedComplex<'v, T>
142where
143 T: ComplexValue<'v>,
144 T::Frozen: StarlarkValue<'static>,
145{
146 fn from(t: ValueTyped<'v, T>) -> Self {
147 Self(t.to_value(), PhantomData)
148 }
149}
150
151impl<'v, T> fmt::Debug for ValueTypedComplex<'v, T>
152where
153 T: ComplexValue<'v>,
154 T::Frozen: StarlarkValue<'static>,
155{
156 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
157 f.debug_tuple("ValueTypedComplex").field(&self.0).finish()
158 }
159}
160
161unsafe impl<'v, T> Trace<'v> for ValueTypedComplex<'v, T>
162where
163 T: ComplexValue<'v>,
164 T::Frozen: StarlarkValue<'static>,
165{
166 fn trace(&mut self, tracer: &Tracer<'v>) {
167 tracer.trace(&mut self.0);
168 debug_assert!(Self::new(self.0).is_some());
170 }
171}
172
173impl<'v, T> Freeze for ValueTypedComplex<'v, T>
174where
175 T: ComplexValue<'v>,
176 T::Frozen: StarlarkValue<'static>,
177{
178 type Frozen = FrozenValueTyped<'static, T::Frozen>;
179
180 fn freeze(self, freezer: &Freezer) -> FreezeResult<Self::Frozen> {
181 FrozenValueTyped::new_err(self.0.freeze(freezer)?)
182 .map_err(|e| FreezeError::new(format!("{e}")))
183 }
184}
185
186#[cfg(test)]
187mod tests {
188 use anyhow::Context;
189 use either::Either;
190 use starlark_derive::starlark_module;
191
192 use crate as starlark;
193 use crate::assert::Assert;
194 use crate::const_frozen_string;
195 use crate::environment::GlobalsBuilder;
196 use crate::tests::util::TestComplexValue;
197 use crate::values::layout::complex::ValueTypedComplex;
198 use crate::values::Value;
199
200 #[starlark_module]
201 fn test_module(globals: &mut GlobalsBuilder) {
202 fn test_unpack<'v>(
203 v: ValueTypedComplex<'v, TestComplexValue<Value<'v>>>,
204 ) -> anyhow::Result<&'v str> {
205 Ok(match v.unpack() {
206 Either::Left(v) => v.0.unpack_str().context("not a string")?,
207 Either::Right(v) => v.0.to_value().unpack_str().context("not a string")?,
208 })
209 }
210 }
211
212 #[test]
213 fn test_unpack() {
214 let mut a = Assert::new();
215 a.globals_add(test_module);
216 a.setup_eval(|eval| {
217 let s = eval.heap().alloc("test1");
218 let x = eval.heap().alloc(TestComplexValue(s));
219 let y = eval.frozen_heap().alloc(TestComplexValue(
220 const_frozen_string!("test2").to_frozen_value(),
221 ));
222 eval.module().set("x", x);
223 eval.module().set("y", y.to_value());
224 });
225 a.eq("'test1'", "test_unpack(x)");
226 a.eq("'test2'", "test_unpack(y)");
227 }
228}