use crate::error::CoreError; use crate::error::CoreResult; /// q attribute attached to vectors, lists, and tables. #[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] pub enum Attribute { #[default] None, Sorted, Unique, Parted, Grouped, } impl From for i8 { fn from(value: Attribute) -> Self { match value { Attribute::None => 0, Attribute::Sorted => 1, Attribute::Unique => 2, Attribute::Parted => 3, Attribute::Grouped => 4, } } } impl TryFrom for Attribute { type Error = CoreError; fn try_from(value: i8) -> CoreResult { match value { 0 => Ok(Self::None), 1 => Ok(Self::Sorted), 2 => Ok(Self::Unique), 3 => Ok(Self::Parted), 4 => Ok(Self::Grouped), _ => Err(CoreError::InvalidAttribute(value)), } } } /// q primitive domain shared by atoms and homogeneous vectors. #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum Primitive { Boolean, Guid, Byte, Short, Int, Long, Real, Float, Char, Symbol, Timestamp, Month, Date, Datetime, Timespan, Minute, Second, Time, Mixed, } impl Primitive { /// Fixed-width byte width for primitives that have one on the wire. pub fn width(self) -> Option { match self { Self::Boolean | Self::Byte | Self::Char => Some(1), Self::Short => Some(2), Self::Int | Self::Real | Self::Month | Self::Date | Self::Minute | Self::Second | Self::Time => Some(4), Self::Long | Self::Float | Self::Timestamp | Self::Datetime | Self::Timespan => Some(8), Self::Guid => Some(16), Self::Symbol | Self::Mixed => None, } } } /// Top-level q structural shape. #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum Shape { Atom, Vector, List, Dictionary, Table, UnaryPrimitive, Error, } /// Complete q type descriptor for a decoded value. #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct ValueType { pub primitive: Option, pub shape: Shape, pub attribute: Option, pub sorted: Option, } impl ValueType { pub fn atom(primitive: Primitive) -> Self { Self { primitive: Some(primitive), shape: Shape::Atom, attribute: None, sorted: None, } } pub fn vector(primitive: Primitive, attribute: Attribute) -> Self { Self { primitive: Some(primitive), shape: Shape::Vector, attribute: Some(attribute), sorted: None, } } pub fn list(attribute: Attribute) -> Self { Self { primitive: Some(Primitive::Mixed), shape: Shape::List, attribute: Some(attribute), sorted: None, } } pub fn dictionary(sorted: bool) -> Self { Self { primitive: None, shape: Shape::Dictionary, attribute: None, sorted: Some(sorted), } } pub fn table(attribute: Attribute) -> Self { Self { primitive: None, shape: Shape::Table, attribute: Some(attribute), sorted: None, } } pub fn unary_primitive() -> Self { Self { primitive: None, shape: Shape::UnaryPrimitive, attribute: None, sorted: None, } } } /// Raw q IPC type code. #[repr(i8)] #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum TypeCode { GeneralList = 0, BooleanVector = 1, GuidVector = 2, ByteVector = 4, ShortVector = 5, IntVector = 6, LongVector = 7, RealVector = 8, FloatVector = 9, CharVector = 10, SymbolVector = 11, TimestampVector = 12, MonthVector = 13, DateVector = 14, DatetimeVector = 15, TimespanVector = 16, MinuteVector = 17, SecondVector = 18, TimeVector = 19, Table = 98, Dictionary = 99, UnaryPrimitive = 101, SortedDictionary = 127, BooleanAtom = -1, GuidAtom = -2, ByteAtom = -4, ShortAtom = -5, IntAtom = -6, LongAtom = -7, RealAtom = -8, FloatAtom = -9, CharAtom = -10, SymbolAtom = -11, TimestampAtom = -12, MonthAtom = -13, DateAtom = -14, DatetimeAtom = -15, TimespanAtom = -16, MinuteAtom = -17, SecondAtom = -18, TimeAtom = -19, ErrorCode = -128, } impl TypeCode { pub fn primitive(self) -> Option { match self { Self::BooleanAtom | Self::BooleanVector => Some(Primitive::Boolean), Self::GuidAtom | Self::GuidVector => Some(Primitive::Guid), Self::ByteAtom | Self::ByteVector => Some(Primitive::Byte), Self::ShortAtom | Self::ShortVector => Some(Primitive::Short), Self::IntAtom | Self::IntVector => Some(Primitive::Int), Self::LongAtom | Self::LongVector => Some(Primitive::Long), Self::RealAtom | Self::RealVector => Some(Primitive::Real), Self::FloatAtom | Self::FloatVector => Some(Primitive::Float), Self::CharAtom | Self::CharVector => Some(Primitive::Char), Self::SymbolAtom | Self::SymbolVector => Some(Primitive::Symbol), Self::TimestampAtom | Self::TimestampVector => Some(Primitive::Timestamp), Self::MonthAtom | Self::MonthVector => Some(Primitive::Month), Self::DateAtom | Self::DateVector => Some(Primitive::Date), Self::DatetimeAtom | Self::DatetimeVector => Some(Primitive::Datetime), Self::TimespanAtom | Self::TimespanVector => Some(Primitive::Timespan), Self::MinuteAtom | Self::MinuteVector => Some(Primitive::Minute), Self::SecondAtom | Self::SecondVector => Some(Primitive::Second), Self::TimeAtom | Self::TimeVector => Some(Primitive::Time), Self::GeneralList | Self::Table | Self::Dictionary | Self::UnaryPrimitive | Self::SortedDictionary | Self::ErrorCode => None, } } pub fn shape(self) -> Shape { match self { Self::BooleanAtom | Self::GuidAtom | Self::ByteAtom | Self::ShortAtom | Self::IntAtom | Self::LongAtom | Self::RealAtom | Self::FloatAtom | Self::CharAtom | Self::SymbolAtom | Self::TimestampAtom | Self::MonthAtom | Self::DateAtom | Self::DatetimeAtom | Self::TimespanAtom | Self::MinuteAtom | Self::SecondAtom | Self::TimeAtom => Shape::Atom, Self::BooleanVector | Self::GuidVector | Self::ByteVector | Self::ShortVector | Self::IntVector | Self::LongVector | Self::RealVector | Self::FloatVector | Self::CharVector | Self::SymbolVector | Self::TimestampVector | Self::MonthVector | Self::DateVector | Self::DatetimeVector | Self::TimespanVector | Self::MinuteVector | Self::SecondVector | Self::TimeVector => Shape::Vector, Self::GeneralList => Shape::List, Self::Dictionary | Self::SortedDictionary => Shape::Dictionary, Self::Table => Shape::Table, Self::UnaryPrimitive => Shape::UnaryPrimitive, Self::ErrorCode => Shape::Error, } } } impl From for i8 { fn from(value: TypeCode) -> Self { value as i8 } } impl TryFrom for TypeCode { type Error = CoreError; fn try_from(value: i8) -> CoreResult { match value { 0 => Ok(Self::GeneralList), 1 => Ok(Self::BooleanVector), 2 => Ok(Self::GuidVector), 4 => Ok(Self::ByteVector), 5 => Ok(Self::ShortVector), 6 => Ok(Self::IntVector), 7 => Ok(Self::LongVector), 8 => Ok(Self::RealVector), 9 => Ok(Self::FloatVector), 10 => Ok(Self::CharVector), 11 => Ok(Self::SymbolVector), 12 => Ok(Self::TimestampVector), 13 => Ok(Self::MonthVector), 14 => Ok(Self::DateVector), 15 => Ok(Self::DatetimeVector), 16 => Ok(Self::TimespanVector), 17 => Ok(Self::MinuteVector), 18 => Ok(Self::SecondVector), 19 => Ok(Self::TimeVector), 98 => Ok(Self::Table), 99 => Ok(Self::Dictionary), 101 => Ok(Self::UnaryPrimitive), 127 => Ok(Self::SortedDictionary), -1 => Ok(Self::BooleanAtom), -2 => Ok(Self::GuidAtom), -4 => Ok(Self::ByteAtom), -5 => Ok(Self::ShortAtom), -6 => Ok(Self::IntAtom), -7 => Ok(Self::LongAtom), -8 => Ok(Self::RealAtom), -9 => Ok(Self::FloatAtom), -10 => Ok(Self::CharAtom), -11 => Ok(Self::SymbolAtom), -12 => Ok(Self::TimestampAtom), -13 => Ok(Self::MonthAtom), -14 => Ok(Self::DateAtom), -15 => Ok(Self::DatetimeAtom), -16 => Ok(Self::TimespanAtom), -17 => Ok(Self::MinuteAtom), -18 => Ok(Self::SecondAtom), -19 => Ok(Self::TimeAtom), -128 => Ok(Self::ErrorCode), _ => Err(CoreError::InvalidTypeCode(value)), } } } #[cfg(test)] mod tests { use super::*; #[test] fn attribute_round_trips() { assert_eq!(Attribute::try_from(0).unwrap(), Attribute::None); assert_eq!(Attribute::try_from(4).unwrap(), Attribute::Grouped); assert!(matches!( Attribute::try_from(9), Err(CoreError::InvalidAttribute(9)) )); } #[test] fn type_code_maps_to_expected_shape_and_primitive() { let atom = TypeCode::IntAtom; let vector = TypeCode::SymbolVector; let list = TypeCode::GeneralList; assert_eq!(atom.shape(), Shape::Atom); assert_eq!(atom.primitive(), Some(Primitive::Int)); assert_eq!(vector.shape(), Shape::Vector); assert_eq!(vector.primitive(), Some(Primitive::Symbol)); assert_eq!(list.shape(), Shape::List); assert_eq!(list.primitive(), None); } }