diff --git a/vortex-scalar/src/display.rs b/vortex-scalar/src/display.rs index 39d20e28c8..9f0f9fb6f6 100644 --- a/vortex-scalar/src/display.rs +++ b/vortex-scalar/src/display.rs @@ -6,6 +6,7 @@ use vortex_dtype::{match_each_native_ptype, DType}; use crate::binary::BinaryScalar; use crate::bool::BoolScalar; use crate::primitive::PrimitiveScalar; +use crate::struct_::StructScalar; use crate::utf8::Utf8Scalar; use crate::Scalar; @@ -50,7 +51,29 @@ impl Display for Scalar { } } } - DType::Struct(..) => todo!(), + DType::Struct(dtype, _) => { + let v = StructScalar::try_from(self).map_err(|x| { + panic!("{}", x); + // std::fmt::Error + })?; + + if v.is_null() { + write!(f, "null") + } else { + write!(f, "{{")?; + let formatted_fields = dtype + .names() + .iter() + .enumerate() + .map(|(idx, name)| match v.field_by_idx(idx) { + None => format!("{name}:null"), + Some(val) => format!("{name}:{val}"), + }) + .format(","); + write!(f, "{}", formatted_fields)?; + write!(f, "}}") + } + } DType::List(..) => todo!(), DType::Extension(..) => todo!(), } @@ -59,11 +82,13 @@ impl Display for Scalar { #[cfg(test)] mod tests { + use std::sync::Arc; + use vortex_buffer::Buffer; use vortex_dtype::Nullability::{NonNullable, Nullable}; - use vortex_dtype::{DType, PType}; + use vortex_dtype::{DType, PType, StructDType}; - use crate::Scalar; + use crate::{PValue, Scalar, ScalarValue}; #[test] fn display_bool() { @@ -109,4 +134,83 @@ mod tests { ); assert_eq!(format!("{}", Scalar::null(DType::Binary(Nullable))), "null"); } + + #[test] + fn display_empty_struct() { + fn dtype() -> DType { + DType::Struct(StructDType::new(Arc::new([]), vec![]), Nullable) + } + + assert_eq!(format!("{}", Scalar::null(dtype())), "null"); + + assert_eq!(format!("{}", Scalar::r#struct(dtype(), vec![])), "{}"); + } + + #[test] + fn display_one_field_struct() { + fn dtype() -> DType { + DType::Struct( + StructDType::new( + Arc::new([Arc::from("foo")]), + vec![DType::Primitive(PType::U32, Nullable)], + ), + Nullable, + ) + } + + assert_eq!(format!("{}", Scalar::null(dtype())), "null"); + + assert_eq!( + format!( + "{}", + Scalar::r#struct(dtype(), vec![ScalarValue::Primitive(PValue::U32(32))]) + ), + "{foo:32}" + ); + } + + #[test] + fn display_two_field_struct() { + fn dtype() -> DType { + DType::Struct( + StructDType::new( + Arc::new([Arc::from("foo"), Arc::from("bar")]), + vec![ + DType::Bool(Nullable), + DType::Primitive(PType::U32, Nullable), + ], + ), + Nullable, + ) + } + + assert_eq!(format!("{}", Scalar::null(dtype())), "null"); + + assert_eq!( + format!("{}", Scalar::r#struct(dtype(), vec![])), + "{foo:null,bar:null}" + ); + + assert_eq!( + format!( + "{}", + Scalar::r#struct(dtype(), vec![ScalarValue::Bool(true)]) + ), + "{foo:true,bar:null}" + ); + + assert_eq!( + format!( + "{}", + Scalar::r#struct( + dtype(), + vec![ + ScalarValue::Bool(true), + ScalarValue::Primitive(PValue::U32(32)) + ] + ) + ), + "{foo:true,bar:32}" + ); + } } diff --git a/vortex-scalar/src/struct_.rs b/vortex-scalar/src/struct_.rs index 16144de6a8..8da6766eed 100644 --- a/vortex-scalar/src/struct_.rs +++ b/vortex-scalar/src/struct_.rs @@ -17,6 +17,10 @@ impl<'a> StructScalar<'a> { self.dtype } + pub fn is_null(&self) -> bool { + return self.fields.is_none(); + } + pub fn field_by_idx(&self, idx: usize) -> Option { let DType::Struct(st, _) = self.dtype() else { unreachable!() @@ -56,7 +60,7 @@ impl<'a> TryFrom<&'a Scalar> for StructScalar<'a> { type Error = VortexError; fn try_from(value: &'a Scalar) -> Result { - if matches!(value.dtype(), DType::Struct(..)) { + if !matches!(value.dtype(), DType::Struct(..)) { vortex_bail!("Expected struct scalar, found {}", value.dtype()) } Ok(Self { diff --git a/vortex-scalar/src/value.rs b/vortex-scalar/src/value.rs index bcf12d38d1..78a4613c28 100644 --- a/vortex-scalar/src/value.rs +++ b/vortex-scalar/src/value.rs @@ -61,6 +61,7 @@ impl ScalarValue { pub fn as_list(&self) -> VortexResult>> { match self { + Self::Null => Ok(None), Self::List(l) => Ok(Some(l)), _ => Err(vortex_err!("Expected a list scalar, found {:?}", self)), }