use std::borrow::Cow;
use std::fmt::Display;
use std::fmt::Write;
use std::ops::Deref;
use chrono::{DateTime, Datelike, Timelike};
use polars::datatypes::{AnyValue, TimeUnit};
use crate::misc::config::config;
#[derive(Clone)]
pub struct AnyValueFormatter {
buf: String,
fp_prec: Option<usize>,
}
impl AnyValueFormatter {
pub fn new(fp_precision: Option<usize>) -> Self {
Self {
buf: Default::default(),
fp_prec: fp_precision,
}
}
pub fn into_single_line(mut self, value: AnyValue) -> Cow<'static, str> {
match self.to_single_line(value) {
Formatted::Static(s) => Cow::Borrowed(s),
Formatted::Buffer(_) => Cow::Owned(self.buf),
}
}
pub fn to_single_line<'a>(&'a mut self, value: AnyValue) -> Formatted<'a> {
let fp_prec = self.fp_prec;
match value {
AnyValue::Null => Formatted::Static(""),
AnyValue::Boolean(b) => Formatted::Static(bool(b)),
AnyValue::String(s) => {
Formatted::Buffer(copy_chars(&mut self.buf, untabbed(first_line(s.chars()))))
}
AnyValue::StringOwned(s) => {
Formatted::Buffer(copy_chars(&mut self.buf, untabbed(first_line(s.chars()))))
}
AnyValue::UInt8(u) => Formatted::Buffer(display(&mut self.buf, u)),
AnyValue::UInt16(u) => Formatted::Buffer(display(&mut self.buf, u)),
AnyValue::UInt32(u) => Formatted::Buffer(display(&mut self.buf, u)),
AnyValue::UInt64(u) => Formatted::Buffer(display(&mut self.buf, u)),
AnyValue::UInt128(u) => Formatted::Buffer(display(&mut self.buf, u)),
AnyValue::Int8(i) => Formatted::Buffer(display(&mut self.buf, i)),
AnyValue::Int16(i) => Formatted::Buffer(display(&mut self.buf, i)),
AnyValue::Int32(i) => Formatted::Buffer(display(&mut self.buf, i)),
AnyValue::Int64(i) => Formatted::Buffer(display(&mut self.buf, i)),
AnyValue::Int128(i) => Formatted::Buffer(display(&mut self.buf, i)),
AnyValue::Float16(f) => {
Formatted::Buffer(display_with_precision(&mut self.buf, fp_prec, f))
}
AnyValue::Float32(f) => {
Formatted::Buffer(display_with_precision(&mut self.buf, fp_prec, f))
}
AnyValue::Float64(f) => {
Formatted::Buffer(display_with_precision(&mut self.buf, fp_prec, f))
}
AnyValue::Date(d) => Formatted::Buffer(date(&mut self.buf, d)),
AnyValue::Datetime(t, unit, _) | AnyValue::DatetimeOwned(t, unit, _) => {
Formatted::Buffer(datetime(&mut self.buf, t, unit))
}
AnyValue::Duration(_, _) => Formatted::Buffer(display(&mut self.buf, value)),
AnyValue::Time(t) => Formatted::Buffer(time(&mut self.buf, t)),
AnyValue::Categorical(cat, map) | AnyValue::Enum(cat, map) => {
if let Some(s) = map.cat_to_str(cat) {
Formatted::Buffer(copy_chars(&mut self.buf, untabbed(first_line(s.chars()))))
} else {
Formatted::Static("")
}
}
AnyValue::CategoricalOwned(cat, map) | AnyValue::EnumOwned(cat, map) => {
if let Some(s) = map.cat_to_str(cat) {
Formatted::Buffer(copy_chars(&mut self.buf, untabbed(first_line(s.chars()))))
} else {
Formatted::Static("")
}
}
AnyValue::List(series) => Formatted::Buffer(display(
&mut self.buf,
format_args!("[{} items]", series.len()),
)),
AnyValue::Array(_, n) => {
Formatted::Buffer(display(&mut self.buf, format_args!("[{n} items]")))
}
AnyValue::Struct(_, _, fields) => Formatted::Buffer(display(
&mut self.buf,
format_args!("{{{} fields}}", fields.len()),
)),
AnyValue::StructOwned(st) => Formatted::Buffer(display(
&mut self.buf,
format_args!("{{{} fields}}", st.0.len()),
)),
AnyValue::Binary(items) => Formatted::Buffer(display(
&mut self.buf,
format_args!("[{} bytes]", items.len()),
)),
AnyValue::BinaryOwned(items) => Formatted::Buffer(display(
&mut self.buf,
format_args!("[{} bytes]", items.len()),
)),
AnyValue::Decimal(_, _, _) => Formatted::Buffer(display(&mut self.buf, value)),
}
}
pub fn into_multi_line(mut self, value: AnyValue) -> Cow<'static, str> {
match self.to_multi_line(value) {
Formatted::Static(s) => Cow::Borrowed(s),
Formatted::Buffer(_) => Cow::Owned(self.buf),
}
}
pub fn to_multi_line<'a>(&'a mut self, value: AnyValue) -> Formatted<'a> {
let fp_prec = self.fp_prec;
match value {
AnyValue::Null => Formatted::Static(""),
AnyValue::Boolean(b) => Formatted::Static(bool(b)),
AnyValue::String(s) => {
Formatted::Buffer(copy_chars(&mut self.buf, untabbed(s.chars())))
}
AnyValue::StringOwned(s) => {
Formatted::Buffer(copy_chars(&mut self.buf, untabbed(s.chars())))
}
AnyValue::UInt8(u) => Formatted::Buffer(display(&mut self.buf, u)),
AnyValue::UInt16(u) => Formatted::Buffer(display(&mut self.buf, u)),
AnyValue::UInt32(u) => Formatted::Buffer(display(&mut self.buf, u)),
AnyValue::UInt64(u) => Formatted::Buffer(display(&mut self.buf, u)),
AnyValue::UInt128(u) => Formatted::Buffer(display(&mut self.buf, u)),
AnyValue::Int8(i) => Formatted::Buffer(display(&mut self.buf, i)),
AnyValue::Int16(i) => Formatted::Buffer(display(&mut self.buf, i)),
AnyValue::Int32(i) => Formatted::Buffer(display(&mut self.buf, i)),
AnyValue::Int64(i) => Formatted::Buffer(display(&mut self.buf, i)),
AnyValue::Int128(i) => Formatted::Buffer(display(&mut self.buf, i)),
AnyValue::Float16(f) => {
Formatted::Buffer(display_with_precision(&mut self.buf, fp_prec, f))
}
AnyValue::Float32(f) => {
Formatted::Buffer(display_with_precision(&mut self.buf, fp_prec, f))
}
AnyValue::Float64(f) => {
Formatted::Buffer(display_with_precision(&mut self.buf, fp_prec, f))
}
AnyValue::Date(d) => Formatted::Buffer(date(&mut self.buf, d)),
AnyValue::Datetime(t, unit, _) | AnyValue::DatetimeOwned(t, unit, _) => {
Formatted::Buffer(datetime(&mut self.buf, t, unit))
}
AnyValue::Duration(_, _) => Formatted::Buffer(display(&mut self.buf, value)),
AnyValue::Time(t) => Formatted::Buffer(time(&mut self.buf, t)),
AnyValue::Categorical(cat, map) | AnyValue::Enum(cat, map) => {
if let Some(s) = map.cat_to_str(cat) {
Formatted::Buffer(copy_chars(&mut self.buf, untabbed(s.chars())))
} else {
Formatted::Static("")
}
}
AnyValue::CategoricalOwned(cat, map) | AnyValue::EnumOwned(cat, map) => {
if let Some(s) = map.cat_to_str(cat) {
Formatted::Buffer(copy_chars(&mut self.buf, untabbed(s.chars())))
} else {
Formatted::Static("")
}
}
AnyValue::Binary(b) => Formatted::Buffer(bytes(&mut self.buf, b)),
AnyValue::BinaryOwned(b) => Formatted::Buffer(bytes(&mut self.buf, &b)),
AnyValue::Decimal(_, _, _) => Formatted::Buffer(display(&mut self.buf, value)),
av => Formatted::Buffer(display(&mut self.buf, av)),
}
}
}
impl Default for AnyValueFormatter {
fn default() -> Self {
Self {
buf: Default::default(),
fp_prec: config().fp_precision(),
}
}
}
pub enum Formatted<'a> {
Static(&'static str),
Buffer(&'a str),
}
impl<'a> Formatted<'a> {
pub fn into_string(self) -> String {
match self {
Formatted::Static(s) => s.to_owned(),
Formatted::Buffer(s) => s.to_owned(),
}
}
}
impl Deref for Formatted<'_> {
type Target = str;
fn deref(&self) -> &Self::Target {
match self {
Formatted::Static(s) => s,
Formatted::Buffer(s) => s,
}
}
}
#[inline]
fn display(buf: &mut String, value: impl Display) -> &str {
buf.clear();
let _ = write!(buf, "{value}");
buf
}
#[inline]
fn display_with_precision(buf: &mut String, fp_prec: Option<usize>, value: impl Display) -> &str {
if let Some(precision) = fp_prec {
buf.clear();
let _ = write!(buf, "{value:.precision$}");
buf
} else {
display(buf, value)
}
}
#[inline]
fn copy_chars(buf: &mut String, chars: impl Iterator<Item = char>) -> &str {
buf.clear();
buf.extend(chars);
buf
}
#[inline]
fn bool(b: bool) -> &'static str {
if b { "true" } else { "false" }
}
#[inline]
fn date(buf: &mut String, days: i32) -> &str {
let date = DateTime::from_timestamp(i64::from(days) * 86_400, 0)
.unwrap_or_default()
.date_naive();
buf.clear();
let _ = write!(
buf,
"{:04}-{:02}-{:02}",
date.year(),
date.month(),
date.day()
);
buf
}
#[inline]
fn time(buf: &mut String, nanos: i64) -> &str {
let seconds = nanos / 1_000_000_000;
buf.clear();
let _ = write!(
buf,
"{:02}:{:02}:{:02}",
seconds / 3_600,
seconds / 60 % 60,
seconds % 60
);
buf
}
#[inline]
fn datetime(buf: &mut String, value: i64, unit: TimeUnit) -> &str {
let datetime = match unit {
TimeUnit::Nanoseconds => DateTime::from_timestamp_nanos(value),
TimeUnit::Microseconds => DateTime::from_timestamp_micros(value).unwrap_or_default(),
TimeUnit::Milliseconds => DateTime::from_timestamp_millis(value).unwrap_or_default(),
};
buf.clear();
let _ = write!(
buf,
"{:04}-{:02}-{:02} {:02}:{:02}:{:02}",
datetime.year(),
datetime.month(),
datetime.day(),
datetime.hour(),
datetime.minute(),
datetime.second()
);
buf
}
#[inline]
fn bytes<'a>(buf: &'a mut String, b: &[u8]) -> &'a str {
if let Some(b) = b.first() {
let _ = write!(buf, "{b:02x}");
}
if b.len() > 1 {
for chunk in b[1..].chunks(8) {
for b in chunk {
let _ = write!(buf, " {b:02x}");
}
}
}
buf
}
fn first_line(chars: impl Iterator<Item = char>) -> impl Iterator<Item = char> {
let mut chars = chars.peekable();
let mut done = false;
std::iter::from_fn(move || {
if done {
return None;
}
let c = chars.next()?;
match c {
'\n' => {
done = true;
None
}
'\r' if chars.peek() == Some(&'\n') => {
done = true;
None
}
c => Some(c),
}
})
}
fn untabbed(chars: impl Iterator<Item = char>) -> impl Iterator<Item = char> {
chars.map(|c| match c {
'\t' => ' ',
c => c,
})
}