@@ -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,38 @@ 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. The type ID and exact child dtype are still validated before that
202+ /// normalization.
203+ ///
204+ /// # Errors
205+ ///
206+ /// Returns an error if the type ID is not present in `variants` or if the child scalar's dtype
207+ /// does not exactly match the selected variant dtype.
208+ pub fn union ( variants : UnionVariants , type_id : i8 , child : Scalar ) -> VortexResult < Self > {
209+ let child_index = variants. tag_to_child_index ( type_id) . ok_or_else ( || {
210+ vortex_err ! (
211+ "union type ID {type_id} is not present in {:?}" ,
212+ variants. type_ids( )
213+ )
214+ } ) ?;
215+ let expected_dtype = variants
216+ . variant_by_index ( child_index)
217+ . vortex_expect ( "type ID resolved to a valid child index" ) ;
218+ vortex_ensure_eq ! (
219+ child. dtype( ) ,
220+ & expected_dtype,
221+ "union type ID {type_id} selects child dtype {expected_dtype}, got {}" ,
222+ child. dtype( )
223+ ) ;
224+
225+ let ( _, child_value) = child. into_parts ( ) ;
226+ let value = child_value. map ( |value| ScalarValue :: Union ( UnionValue :: new ( type_id, value) ) ) ;
227+ Self :: try_new ( DType :: Union ( variants) , value)
228+ }
229+
193230 /// Creates a new variant scalar from a row-specific nested scalar.
194231 ///
195232 /// Use [`Scalar::null(DType::Variant(Nullability::Nullable))`][Scalar::null] for a top-level
0 commit comments