use std::collections::HashSet;
use std::fmt;
use indexmap::IndexMap;
use substrait::proto;
use substrait::proto::expression::field_reference::ReferenceType;
use substrait::proto::expression::literal::LiteralType;
use substrait::proto::expression::{RexType, reference_segment};
use super::ExtensionError;
use crate::textify::expressions::Reference;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum AddendumKind {
Enhancement,
Optimization,
ExtensionTable,
}
impl AddendumKind {
pub(crate) fn prefix(self) -> &'static str {
match self {
AddendumKind::Enhancement => "Enh",
AddendumKind::Optimization => "Opt",
AddendumKind::ExtensionTable => "Ext",
}
}
}
#[derive(Debug, Clone)]
pub struct Expr(Box<proto::Expression>);
impl Expr {
pub fn field(index: i32) -> Self {
Reference(index).into()
}
pub fn as_proto(&self) -> &proto::Expression {
self.0.as_ref()
}
pub fn to_proto(&self) -> proto::Expression {
self.as_proto().clone()
}
pub fn as_direct_reference(&self) -> Option<i32> {
let Some(RexType::Selection(field_ref)) = self.as_proto().rex_type.as_ref() else {
return None;
};
let Some(ReferenceType::DirectReference(segment)) = field_ref.reference_type.as_ref()
else {
return None;
};
let Some(reference_segment::ReferenceType::StructField(field)) =
segment.reference_type.as_ref()
else {
return None;
};
if field.child.is_some() {
return None;
}
Some(field.field)
}
}
impl From<proto::Expression> for Expr {
fn from(expr: proto::Expression) -> Self {
Expr(Box::new(expr))
}
}
impl From<proto::expression::Literal> for Expr {
fn from(literal: proto::expression::Literal) -> Self {
proto::Expression {
rex_type: Some(RexType::Literal(literal)),
}
.into()
}
}
impl From<Reference> for Expr {
fn from(reference: Reference) -> Self {
proto::Expression::from(reference).into()
}
}
impl From<Expr> for proto::Expression {
fn from(expr: Expr) -> Self {
*expr.0
}
}
impl From<i64> for Expr {
fn from(value: i64) -> Self {
proto::expression::Literal {
literal_type: Some(LiteralType::I64(value)),
nullable: false,
type_variation_reference: 0,
}
.into()
}
}
impl From<f64> for Expr {
fn from(value: f64) -> Self {
proto::expression::Literal {
literal_type: Some(LiteralType::Fp64(value)),
nullable: false,
type_variation_reference: 0,
}
.into()
}
}
impl From<bool> for Expr {
fn from(value: bool) -> Self {
proto::expression::Literal {
literal_type: Some(LiteralType::Boolean(value)),
nullable: false,
type_variation_reference: 0,
}
.into()
}
}
impl From<String> for Expr {
fn from(value: String) -> Self {
proto::expression::Literal {
literal_type: Some(LiteralType::String(value)),
nullable: false,
type_variation_reference: 0,
}
.into()
}
}
impl From<&str> for Expr {
fn from(value: &str) -> Self {
value.to_string().into()
}
}
#[derive(Debug, Clone, Default)]
pub struct ExtensionArgs {
pub positional: Vec<ExtensionValue>,
pub named: IndexMap<String, ExtensionValue>,
pub output_columns: Vec<ExtensionColumn>,
}
pub struct ArgsExtractor<'a> {
args: &'a ExtensionArgs,
consumed: HashSet<&'a str>,
checked: bool,
}
impl<'a> ArgsExtractor<'a> {
pub fn new(args: &'a ExtensionArgs) -> Self {
Self {
args,
consumed: HashSet::new(),
checked: false,
}
}
pub fn get_named_arg(&mut self, name: &str) -> Option<&'a ExtensionValue> {
match self.args.named.get_key_value(name) {
Some((k, value)) => {
self.consumed.insert(k);
Some(value)
}
None => None,
}
}
pub fn expect_named_arg<T>(&mut self, name: &str) -> Result<T, ExtensionError>
where
T: TryFrom<&'a ExtensionValue>,
T::Error: Into<ExtensionError>,
{
match self.get_named_arg(name) {
Some(value) => T::try_from(value).map_err(Into::into),
None => Err(ExtensionError::MissingArgument {
name: name.to_string(),
}),
}
}
pub fn get_named_or<T>(&mut self, name: &str, default: T) -> Result<T, ExtensionError>
where
T: TryFrom<&'a ExtensionValue>,
T::Error: Into<ExtensionError>,
{
match self.get_named_arg(name) {
Some(value) => T::try_from(value).map_err(Into::into),
None => Ok(default),
}
}
pub fn check_exhausted(&mut self) -> Result<(), ExtensionError> {
self.checked = true;
let mut unknown_args = Vec::new();
for name in self.args.named.keys() {
if !self.consumed.contains(name.as_str()) {
unknown_args.push(name.as_str());
}
}
if unknown_args.is_empty() {
Ok(())
} else {
unknown_args.sort();
Err(ExtensionError::InvalidArgument(format!(
"Unknown named arguments: {}",
unknown_args.join(", ")
)))
}
}
}
impl Drop for ArgsExtractor<'_> {
fn drop(&mut self) {
if self.checked || std::thread::panicking() {
return;
}
debug_assert!(
false,
"ArgsExtractor dropped without calling check_exhausted()"
);
}
}
#[derive(Debug, Clone)]
pub struct TupleValue(Vec<ExtensionValue>);
impl TupleValue {
pub fn len(&self) -> usize {
self.0.len()
}
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
pub fn iter(&self) -> std::slice::Iter<'_, ExtensionValue> {
self.0.iter()
}
}
impl<'a> IntoIterator for &'a TupleValue {
type Item = &'a ExtensionValue;
type IntoIter = std::slice::Iter<'a, ExtensionValue>;
fn into_iter(self) -> Self::IntoIter {
self.0.iter()
}
}
impl IntoIterator for TupleValue {
type Item = ExtensionValue;
type IntoIter = std::vec::IntoIter<ExtensionValue>;
fn into_iter(self) -> Self::IntoIter {
self.0.into_iter()
}
}
impl FromIterator<ExtensionValue> for TupleValue {
fn from_iter<I: IntoIterator<Item = ExtensionValue>>(iter: I) -> Self {
TupleValue(iter.into_iter().collect())
}
}
impl From<Vec<ExtensionValue>> for TupleValue {
fn from(items: Vec<ExtensionValue>) -> Self {
TupleValue(items)
}
}
#[derive(Debug, Clone)]
pub enum ExtensionValue {
String(String),
Integer(i64),
Float(f64),
Boolean(bool),
Expr(Expr),
Enum(String),
Tuple(TupleValue),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ExtensionValueKind {
String,
Integer,
Float,
Boolean,
Reference,
Enum,
Tuple,
Expression,
}
impl fmt::Display for ExtensionValueKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ExtensionValueKind::String => write!(f, "string"),
ExtensionValueKind::Integer => write!(f, "integer"),
ExtensionValueKind::Float => write!(f, "float"),
ExtensionValueKind::Boolean => write!(f, "boolean"),
ExtensionValueKind::Reference => write!(f, "reference"),
ExtensionValueKind::Enum => write!(f, "enum"),
ExtensionValueKind::Tuple => write!(f, "tuple"),
ExtensionValueKind::Expression => write!(f, "expression"),
}
}
}
impl ExtensionValue {
pub fn kind(&self) -> ExtensionValueKind {
match self {
ExtensionValue::String(_) => ExtensionValueKind::String,
ExtensionValue::Integer(_) => ExtensionValueKind::Integer,
ExtensionValue::Float(_) => ExtensionValueKind::Float,
ExtensionValue::Boolean(_) => ExtensionValueKind::Boolean,
ExtensionValue::Expr(_) => ExtensionValueKind::Expression,
ExtensionValue::Enum(_) => ExtensionValueKind::Enum,
ExtensionValue::Tuple(_) => ExtensionValueKind::Tuple,
}
}
}
impl From<Expr> for ExtensionValue {
fn from(expr: Expr) -> Self {
ExtensionValue::Expr(expr)
}
}
impl From<proto::Expression> for ExtensionValue {
fn from(expr: proto::Expression) -> Self {
Expr::from(expr).into()
}
}
impl From<proto::expression::Literal> for ExtensionValue {
fn from(literal: proto::expression::Literal) -> Self {
Expr::from(literal).into()
}
}
impl From<Reference> for ExtensionValue {
fn from(reference: Reference) -> Self {
Expr::from(reference).into()
}
}
impl From<i64> for ExtensionValue {
fn from(value: i64) -> Self {
ExtensionValue::Integer(value)
}
}
impl From<f64> for ExtensionValue {
fn from(value: f64) -> Self {
ExtensionValue::Float(value)
}
}
impl From<bool> for ExtensionValue {
fn from(value: bool) -> Self {
ExtensionValue::Boolean(value)
}
}
impl From<String> for ExtensionValue {
fn from(value: String) -> Self {
ExtensionValue::String(value)
}
}
impl From<&str> for ExtensionValue {
fn from(value: &str) -> Self {
ExtensionValue::String(value.to_string())
}
}
fn invalid_type(expected: ExtensionValueKind, actual: &ExtensionValue) -> ExtensionError {
ExtensionError::InvalidArgumentType {
expected,
actual: actual.kind(),
}
}
impl<'a> TryFrom<&'a ExtensionValue> for &'a str {
type Error = ExtensionError;
fn try_from(value: &'a ExtensionValue) -> Result<&'a str, Self::Error> {
match value {
ExtensionValue::String(s) => Ok(s),
v => Err(invalid_type(ExtensionValueKind::String, v)),
}
}
}
impl TryFrom<ExtensionValue> for String {
type Error = ExtensionError;
fn try_from(value: ExtensionValue) -> Result<String, Self::Error> {
<&str>::try_from(&value).map(ToOwned::to_owned)
}
}
pub struct EnumValue(pub String);
impl<'a> TryFrom<&'a ExtensionValue> for EnumValue {
type Error = ExtensionError;
fn try_from(value: &'a ExtensionValue) -> Result<EnumValue, Self::Error> {
match value {
ExtensionValue::Enum(s) => Ok(EnumValue(s.clone())),
v => Err(invalid_type(ExtensionValueKind::Enum, v)),
}
}
}
impl<'a> TryFrom<&'a ExtensionValue> for &'a TupleValue {
type Error = ExtensionError;
fn try_from(value: &'a ExtensionValue) -> Result<&'a TupleValue, Self::Error> {
match value {
ExtensionValue::Tuple(tv) => Ok(tv),
v => Err(invalid_type(ExtensionValueKind::Tuple, v)),
}
}
}
impl TryFrom<&ExtensionValue> for i64 {
type Error = ExtensionError;
fn try_from(value: &ExtensionValue) -> Result<i64, Self::Error> {
match value {
ExtensionValue::Integer(i) => Ok(*i),
v => Err(invalid_type(ExtensionValueKind::Integer, v)),
}
}
}
impl TryFrom<&ExtensionValue> for f64 {
type Error = ExtensionError;
fn try_from(value: &ExtensionValue) -> Result<f64, Self::Error> {
match value {
ExtensionValue::Float(f) => Ok(*f),
v => Err(invalid_type(ExtensionValueKind::Float, v)),
}
}
}
impl TryFrom<&ExtensionValue> for bool {
type Error = ExtensionError;
fn try_from(value: &ExtensionValue) -> Result<bool, Self::Error> {
match value {
ExtensionValue::Boolean(b) => Ok(*b),
v => Err(invalid_type(ExtensionValueKind::Boolean, v)),
}
}
}
impl TryFrom<&ExtensionValue> for Reference {
type Error = ExtensionError;
fn try_from(value: &ExtensionValue) -> Result<Reference, Self::Error> {
match value {
ExtensionValue::Expr(expr) => expr
.as_direct_reference()
.map(Reference)
.ok_or_else(|| invalid_type(ExtensionValueKind::Reference, value)),
v => Err(invalid_type(ExtensionValueKind::Reference, v)),
}
}
}
impl TryFrom<&ExtensionValue> for Expr {
type Error = ExtensionError;
fn try_from(value: &ExtensionValue) -> Result<Expr, Self::Error> {
match value {
ExtensionValue::Expr(e) => Ok(e.clone()),
ExtensionValue::Integer(i) => Ok(Expr::from(*i)),
ExtensionValue::Float(f) => Ok(Expr::from(*f)),
ExtensionValue::String(s) => Ok(Expr::from(s.as_str())),
ExtensionValue::Boolean(b) => Ok(Expr::from(*b)),
v => Err(invalid_type(ExtensionValueKind::Expression, v)),
}
}
}
#[derive(Debug, Clone)]
pub enum ExtensionColumn {
Named {
name: String,
r#type: proto::Type,
},
Expr(Expr),
}
impl ExtensionColumn {
pub fn field(index: i32) -> Self {
Self::Expr(Expr::field(index))
}
}
impl ExtensionArgs {
pub fn push<T>(&mut self, value: T)
where
T: Into<ExtensionValue>,
{
self.positional.push(value.into());
}
pub fn insert<K, V>(&mut self, name: K, value: V) -> Option<ExtensionValue>
where
K: Into<String>,
V: Into<ExtensionValue>,
{
self.named.insert(name.into(), value.into())
}
pub fn extractor(&self) -> ArgsExtractor<'_> {
ArgsExtractor::new(self)
}
}