use std::{
ops::{Add, Mul},
sync::Arc,
};
use arrow::array::{
ArrayRef, Time32MillisecondBuilder, Time64MicrosecondBuilder, Time64NanosecondBuilder,
};
use atoi::FromRadix10;
use odbc_api::{
buffers::{AnySlice, BufferDesc},
sys::Time,
};
use super::{MappingError, ReadStrategy};
pub fn seconds_since_midnight(time: &Time) -> i32 {
(time.hour as i32 * 60 + time.minute as i32) * 60 + time.second as i32
}
pub struct TimeMsI32;
impl ReadStrategy for TimeMsI32 {
fn buffer_desc(&self) -> BufferDesc {
BufferDesc::Text {
max_str_len: 12,
}
}
fn fill_arrow_array(&self, column_view: AnySlice) -> Result<ArrayRef, MappingError> {
let view = column_view.as_text_view().unwrap();
let mut builder = Time32MillisecondBuilder::new();
for opt in view.iter() {
if let Some(text) = opt {
let num = ticks_since_midnights_from_text::<i32>(text, 3);
builder.append_value(num);
} else {
builder.append_null();
}
}
Ok(Arc::new(builder.finish()))
}
}
pub struct TimeUsI64;
impl ReadStrategy for TimeUsI64 {
fn buffer_desc(&self) -> BufferDesc {
BufferDesc::Text {
max_str_len: 15,
}
}
fn fill_arrow_array(&self, column_view: AnySlice) -> Result<ArrayRef, MappingError> {
let view = column_view.as_text_view().unwrap();
let mut builder = Time64MicrosecondBuilder::new();
for opt in view.iter() {
if let Some(text) = opt {
let num = ticks_since_midnights_from_text::<i64>(text, 6);
builder.append_value(num);
} else {
builder.append_null();
}
}
Ok(Arc::new(builder.finish()))
}
}
pub struct TimeNsI64;
impl ReadStrategy for TimeNsI64 {
fn buffer_desc(&self) -> BufferDesc {
BufferDesc::Text {
max_str_len: 18,
}
}
fn fill_arrow_array(&self, column_view: AnySlice) -> Result<ArrayRef, MappingError> {
let view = column_view.as_text_view().unwrap();
let mut builder = Time64NanosecondBuilder::new();
for opt in view.iter() {
if let Some(text) = opt {
let num = ticks_since_midnights_from_text::<i64>(text, 9);
builder.append_value(num);
} else {
builder.append_null();
}
}
Ok(Arc::new(builder.finish()))
}
}
fn ticks_since_midnights_from_text<I>(text: &[u8], precision: u32) -> I
where
I: Tick,
{
let (hours, hours_digits) = I::from_radix_10(&text[0..2]);
debug_assert_eq!(2, hours_digits);
debug_assert_eq!(b':', text[2]);
let (min, min_digits) = I::from_radix_10(&text[3..5]);
debug_assert_eq!(2, min_digits);
debug_assert_eq!(b':', text[5]);
let (sec, sec_digits) = I::from_radix_10(&text[6..8]);
debug_assert_eq!(2, sec_digits);
let (frac, frac_digits) = if text.len() > 9 {
I::from_radix_10(&text[9..])
} else {
(I::ZERO, 0)
};
let frac = frac * I::TEN.pow(precision - frac_digits as u32);
((hours * I::SIXTY + min) * I::SIXTY + sec) * I::TEN.pow(precision) + frac
}
trait Tick: FromRadix10 + Mul<Output = Self> + Add<Output = Self> {
const ZERO: Self;
const TEN: Self;
const SIXTY: Self;
fn pow(self, exp: u32) -> Self;
}
impl Tick for i32 {
const ZERO: Self = 0;
const TEN: Self = 10;
const SIXTY: Self = 60;
fn pow(self, exp: u32) -> Self {
self.pow(exp)
}
}
impl Tick for i64 {
const ZERO: Self = 0;
const TEN: Self = 10;
const SIXTY: Self = 60;
fn pow(self, exp: u32) -> Self {
self.pow(exp)
}
}