use std::fmt::{self, Display};
use std::str::FromStr;
use arrow::compute::CastOptions;
use arrow::util::display::{DurationFormat, FormatOptions};
use crate::config::{ConfigField, Visit};
use crate::error::{DataFusionError, Result};
pub const DEFAULT_FORMAT_OPTIONS: FormatOptions<'static> =
FormatOptions::new().with_duration_format(DurationFormat::Pretty);
pub const DEFAULT_CAST_OPTIONS: CastOptions<'static> = CastOptions {
safe: false,
format_options: DEFAULT_FORMAT_OPTIONS,
};
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum ExplainFormat {
Indent,
Tree,
PostgresJSON,
Graphviz,
}
impl FromStr for ExplainFormat {
type Err = DataFusionError;
fn from_str(format: &str) -> Result<Self, Self::Err> {
match format.to_lowercase().as_str() {
"indent" => Ok(ExplainFormat::Indent),
"tree" => Ok(ExplainFormat::Tree),
"pgjson" => Ok(ExplainFormat::PostgresJSON),
"graphviz" => Ok(ExplainFormat::Graphviz),
_ => Err(DataFusionError::Configuration(format!(
"Invalid explain format. Expected 'indent', 'tree', 'pgjson' or 'graphviz'. Got '{format}'"
))),
}
}
}
impl Display for ExplainFormat {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let s = match self {
ExplainFormat::Indent => "indent",
ExplainFormat::Tree => "tree",
ExplainFormat::PostgresJSON => "pgjson",
ExplainFormat::Graphviz => "graphviz",
};
write!(f, "{s}")
}
}
impl ConfigField for ExplainFormat {
fn visit<V: Visit>(&self, v: &mut V, key: &str, description: &'static str) {
v.some(key, self, description)
}
fn set(&mut self, _: &str, value: &str) -> Result<()> {
*self = ExplainFormat::from_str(value)?;
Ok(())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum MetricType {
Summary,
Dev,
}
impl MetricType {
pub fn included_types(self) -> Vec<MetricType> {
match self {
MetricType::Summary => vec![MetricType::Summary],
MetricType::Dev => vec![MetricType::Summary, MetricType::Dev],
}
}
}
impl FromStr for MetricType {
type Err = DataFusionError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s.trim().to_lowercase().as_str() {
"summary" => Ok(Self::Summary),
"dev" => Ok(Self::Dev),
other => Err(DataFusionError::Configuration(format!(
"Invalid explain analyze level. Expected 'summary' or 'dev'. Got '{other}'"
))),
}
}
}
impl Display for MetricType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Summary => write!(f, "summary"),
Self::Dev => write!(f, "dev"),
}
}
}
impl ConfigField for MetricType {
fn visit<V: Visit>(&self, v: &mut V, key: &str, description: &'static str) {
v.some(key, self, description)
}
fn set(&mut self, _: &str, value: &str) -> Result<()> {
*self = MetricType::from_str(value)?;
Ok(())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum MetricCategory {
Rows,
Bytes,
Timing,
Uncategorized,
}
impl FromStr for MetricCategory {
type Err = DataFusionError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s.trim().to_lowercase().as_str() {
"rows" => Ok(Self::Rows),
"bytes" => Ok(Self::Bytes),
"timing" => Ok(Self::Timing),
"uncategorized" => Ok(Self::Uncategorized),
other => Err(DataFusionError::Configuration(format!(
"Invalid metric category '{other}'. \
Expected 'rows', 'bytes', 'timing', or 'uncategorized'."
))),
}
}
}
impl Display for MetricCategory {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Rows => write!(f, "rows"),
Self::Bytes => write!(f, "bytes"),
Self::Timing => write!(f, "timing"),
Self::Uncategorized => write!(f, "uncategorized"),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)]
pub enum ExplainAnalyzeCategories {
#[default]
All,
Only(Vec<MetricCategory>),
}
impl FromStr for ExplainAnalyzeCategories {
type Err = DataFusionError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let s = s.trim().to_lowercase();
match s.as_str() {
"all" => Ok(Self::All),
"none" => Ok(Self::Only(vec![])),
other => {
let mut cats = Vec::new();
for part in other.split(',') {
cats.push(part.trim().parse::<MetricCategory>()?);
}
cats.dedup();
Ok(Self::Only(cats))
}
}
}
}
impl Display for ExplainAnalyzeCategories {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::All => write!(f, "all"),
Self::Only(cats) if cats.is_empty() => write!(f, "none"),
Self::Only(cats) => {
let mut first = true;
for cat in cats {
if !first {
write!(f, ",")?;
}
first = false;
write!(f, "{cat}")?;
}
Ok(())
}
}
}
}
impl ConfigField for ExplainAnalyzeCategories {
fn visit<V: Visit>(&self, v: &mut V, key: &str, description: &'static str) {
v.some(key, self, description)
}
fn set(&mut self, _: &str, value: &str) -> Result<()> {
*self = ExplainAnalyzeCategories::from_str(value)?;
Ok(())
}
}