#![allow(unused)]
use polars_core::error::{PolarsResult, polars_bail};
use polars_core::schema::*;
use polars_utils::arena::{Arena, Node};
use polars_utils::pl_str::PlSmallStr;
use super::{AExpr, aexpr_to_leaf_names_iter};
use crate::plans::visitor::{AexprNode, RewriteRecursion, RewritingVisitor, TreeWalker};
use crate::plans::{ExprIR, OutputName};
#[derive(Debug, Clone, PartialEq, Copy)]
#[repr(u8)]
pub(crate) enum ExprOrigin {
None = 0b00,
Left = 0b10,
Right = 0b01,
#[expect(unused)]
Both = 0b11,
}
impl ExprOrigin {
pub(crate) fn get_expr_origin(
root: Node,
expr_arena: &Arena<AExpr>,
left_schema: &Schema,
right_schema: &Schema,
suffix: &str,
is_coalesced_to_right: Option<&dyn Fn(&str) -> bool>,
) -> PolarsResult<ExprOrigin> {
aexpr_to_leaf_names_iter(root, expr_arena).try_fold(
ExprOrigin::None,
|acc_origin, column_name| {
Ok(acc_origin
| Self::get_column_origin(
column_name,
left_schema,
right_schema,
suffix,
is_coalesced_to_right,
)?)
},
)
}
pub(crate) fn get_column_origin(
column_name: &str,
left_schema: &Schema,
right_schema: &Schema,
suffix: &str,
is_coalesced_to_right: Option<&dyn Fn(&str) -> bool>,
) -> PolarsResult<ExprOrigin> {
Ok(
if left_schema.contains(column_name)
&& !is_coalesced_to_right.is_some_and(|f| f(column_name))
{
ExprOrigin::Left
} else if right_schema.contains(column_name)
|| column_name
.strip_suffix(suffix)
.is_some_and(|x| right_schema.contains(x))
{
ExprOrigin::Right
} else {
polars_bail!(ColumnNotFound: "{column_name}")
},
)
}
}
impl std::ops::BitOr for ExprOrigin {
type Output = ExprOrigin;
fn bitor(self, rhs: Self) -> Self::Output {
unsafe { std::mem::transmute::<u8, ExprOrigin>(self as u8 | rhs as u8) }
}
}
impl std::ops::BitOrAssign for ExprOrigin {
fn bitor_assign(&mut self, rhs: Self) {
*self = *self | rhs;
}
}
pub(super) fn remove_suffix<'a>(
expr: &mut ExprIR,
expr_arena: &mut Arena<AExpr>,
schema_rhs: &'a Schema,
suffix: &'a str,
) {
let schema = schema_rhs;
let node = AexprNode::new(expr.node())
.rewrite(&mut RemoveSuffix { schema, suffix }, expr_arena)
.unwrap()
.node();
expr.set_node(node);
if let OutputName::ColumnLhs(colname) = expr.output_name_inner() {
if colname.ends_with(suffix) && !schema.contains(colname.as_str()) {
let name = PlSmallStr::from(&colname[..colname.len() - suffix.len()]);
expr.set_columnlhs(name);
}
}
struct RemoveSuffix<'a> {
schema: &'a Schema,
suffix: &'a str,
}
impl RewritingVisitor for RemoveSuffix<'_> {
type Node = AexprNode;
type Arena = Arena<AExpr>;
fn pre_visit(
&mut self,
node: &Self::Node,
arena: &mut Self::Arena,
) -> polars_core::prelude::PolarsResult<crate::prelude::visitor::RewriteRecursion> {
let AExpr::Column(colname) = arena.get(node.node()) else {
return Ok(RewriteRecursion::NoMutateAndContinue);
};
if !colname.ends_with(self.suffix) || self.schema.contains(colname.as_str()) {
return Ok(RewriteRecursion::NoMutateAndContinue);
}
Ok(RewriteRecursion::MutateAndContinue)
}
fn mutate(
&mut self,
node: Self::Node,
arena: &mut Self::Arena,
) -> polars_core::prelude::PolarsResult<Self::Node> {
let AExpr::Column(colname) = arena.get(node.node()) else {
unreachable!();
};
Ok(AexprNode::new(arena.add(AExpr::Column(PlSmallStr::from(
&colname[..colname.len() - self.suffix.len()],
)))))
}
}
}