qroissant/crates/qroissant-core/src/protocol.rs

373 lines
10 KiB
Rust

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<Attribute> 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<i8> for Attribute {
type Error = CoreError;
fn try_from(value: i8) -> CoreResult<Self> {
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<usize> {
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<Primitive>,
pub shape: Shape,
pub attribute: Option<Attribute>,
pub sorted: Option<bool>,
}
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<Primitive> {
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<TypeCode> for i8 {
fn from(value: TypeCode) -> Self {
value as i8
}
}
impl TryFrom<i8> for TypeCode {
type Error = CoreError;
fn try_from(value: i8) -> CoreResult<Self> {
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);
}
}