@@ -8,20 +8,25 @@ use std::sync::Arc;
88use vortex_buffer:: BufferString ;
99use vortex_buffer:: ByteBuffer ;
1010use vortex_error:: VortexExpect ;
11+ use vortex_error:: VortexResult ;
12+ use vortex_error:: vortex_ensure_eq;
13+ use vortex_error:: vortex_err;
1114use vortex_error:: vortex_panic;
1215
1316use crate :: dtype:: DType ;
1417use crate :: dtype:: DecimalDType ;
1518use crate :: dtype:: NativePType ;
1619use crate :: dtype:: Nullability ;
1720use crate :: dtype:: PType ;
21+ use crate :: dtype:: UnionVariants ;
1822use crate :: dtype:: extension:: ExtDType ;
1923use crate :: dtype:: extension:: ExtDTypeRef ;
2024use crate :: dtype:: extension:: ExtVTable ;
2125use crate :: scalar:: DecimalValue ;
2226use crate :: scalar:: PValue ;
2327use crate :: scalar:: Scalar ;
2428use crate :: scalar:: ScalarValue ;
29+ use crate :: scalar:: UnionValue ;
2530
2631// TODO(connor): Really, we want `try_` constructors that return errors instead of just panic.
2732impl Scalar {
@@ -190,6 +195,42 @@ impl Scalar {
190195 . vortex_expect ( "unable to construct an extension `Scalar`" )
191196 }
192197
198+ /// Creates a union scalar from a type ID and child scalar.
199+ ///
200+ /// A null child is normalized to a null union scalar, so null union scalars do not retain a
201+ /// selected type ID. A type ID is not part of a null union scalar's logical identity and is not
202+ /// preserved across serialization or other round trips. The type ID and exact child dtype are
203+ /// still validated before normalization.
204+ ///
205+ /// # Errors
206+ ///
207+ /// Returns an error if the type ID is not present in `variants` or if the child scalar's dtype
208+ /// does not exactly match the selected variant dtype.
209+ pub fn union ( variants : UnionVariants , type_id : i8 , child : Scalar ) -> VortexResult < Self > {
210+ let child_index = variants. tag_to_child_index ( type_id) . ok_or_else ( || {
211+ vortex_err ! (
212+ "union type ID {type_id} is not present in {:?}" ,
213+ variants. type_ids( )
214+ )
215+ } ) ?;
216+
217+ let expected_dtype = variants
218+ . variant_by_index ( child_index)
219+ . vortex_expect ( "type ID resolved to a valid child index" ) ;
220+
221+ vortex_ensure_eq ! (
222+ child. dtype( ) ,
223+ & expected_dtype,
224+ "union type ID {type_id} selects child dtype {expected_dtype}, got {}" ,
225+ child. dtype( )
226+ ) ;
227+
228+ let ( _, child_value) = child. into_parts ( ) ;
229+ let value = child_value. map ( |value| ScalarValue :: Union ( UnionValue :: new ( type_id, value) ) ) ;
230+
231+ Self :: try_new ( DType :: Union ( variants) , value)
232+ }
233+
193234 /// Creates a new variant scalar from a row-specific nested scalar.
194235 ///
195236 /// Use [`Scalar::null(DType::Variant(Nullability::Nullable))`][Scalar::null] for a top-level
0 commit comments