polars_plan/dsl/
meta.rs

1use std::fmt::Display;
2use std::ops::BitAnd;
3
4use super::*;
5use crate::plans::conversion::is_regex_projection;
6use crate::plans::ir::tree_format::TreeFmtVisitor;
7use crate::plans::visitor::{AexprNode, TreeWalker};
8use crate::prelude::tree_format::TreeFmtVisitorDisplay;
9
10/// Specialized expressions for Categorical dtypes.
11pub struct MetaNameSpace(pub(crate) Expr);
12
13impl MetaNameSpace {
14    /// Pop latest expression and return the input(s) of the popped expression.
15    pub fn pop(self) -> PolarsResult<Vec<Expr>> {
16        let mut arena = Arena::with_capacity(8);
17        let node = to_aexpr(self.0, &mut arena)?;
18        let ae = arena.get(node);
19        let mut inputs = Vec::with_capacity(2);
20        ae.inputs_rev(&mut inputs);
21        Ok(inputs
22            .iter()
23            .map(|node| node_to_expr(*node, &arena))
24            .collect())
25    }
26
27    /// Get the root column names.
28    pub fn root_names(&self) -> Vec<PlSmallStr> {
29        expr_to_leaf_column_names(&self.0)
30    }
31
32    /// A projection that only takes a column or a column + alias.
33    pub fn is_simple_projection(&self) -> bool {
34        let mut arena = Arena::with_capacity(8);
35        to_aexpr(self.0.clone(), &mut arena)
36            .map(|node| aexpr_is_simple_projection(node, &arena))
37            .unwrap_or(false)
38    }
39
40    /// Get the output name of this expression.
41    pub fn output_name(&self) -> PolarsResult<PlSmallStr> {
42        expr_output_name(&self.0)
43    }
44
45    /// Undo any renaming operation like `alias`, `keep_name`.
46    pub fn undo_aliases(self) -> Expr {
47        self.0.map_expr(|e| match e {
48            Expr::Alias(input, _)
49            | Expr::KeepName(input)
50            | Expr::RenameAlias { expr: input, .. } => Arc::unwrap_or_clone(input),
51            e => e,
52        })
53    }
54
55    /// Indicate if this expression expands to multiple expressions.
56    pub fn has_multiple_outputs(&self) -> bool {
57        self.0.into_iter().any(|e| match e {
58            Expr::Selector(_) | Expr::Wildcard | Expr::Columns(_) | Expr::DtypeColumn(_) => true,
59            Expr::IndexColumn(idxs) => idxs.len() > 1,
60            Expr::Column(name) => is_regex_projection(name),
61            _ => false,
62        })
63    }
64
65    /// Indicate if this expression is a basic (non-regex) column.
66    pub fn is_column(&self) -> bool {
67        match &self.0 {
68            Expr::Column(name) => !is_regex_projection(name),
69            _ => false,
70        }
71    }
72
73    /// Indicate if this expression only selects columns; the presence of any
74    /// transform operations will cause the check to return `false`, though
75    /// aliasing of the selected columns is optionally allowed.
76    pub fn is_column_selection(&self, allow_aliasing: bool) -> bool {
77        self.0.into_iter().all(|e| match e {
78            Expr::Column(_)
79            | Expr::Columns(_)
80            | Expr::DtypeColumn(_)
81            | Expr::Exclude(_, _)
82            | Expr::Nth(_)
83            | Expr::IndexColumn(_)
84            | Expr::Selector(_)
85            | Expr::Wildcard => true,
86            Expr::Alias(_, _) | Expr::KeepName(_) | Expr::RenameAlias { .. } => allow_aliasing,
87            _ => false,
88        })
89    }
90
91    /// Indicate if this expression represents a literal value (optionally aliased).
92    pub fn is_literal(&self, allow_aliasing: bool) -> bool {
93        self.0.into_iter().all(|e| match e {
94            Expr::Literal(_) => true,
95            Expr::Alias(_, _) => allow_aliasing,
96            Expr::Cast {
97                expr,
98                dtype: DataType::Datetime(_, _),
99                options: CastOptions::Strict,
100            } if matches!(&**expr, Expr::Literal(LiteralValue::DateTime(_, _, _))) => true,
101            _ => false,
102        })
103    }
104
105    /// Indicate if this expression expands to multiple expressions with regex expansion.
106    pub fn is_regex_projection(&self) -> bool {
107        self.0.into_iter().any(|e| match e {
108            Expr::Column(name) => is_regex_projection(name),
109            _ => false,
110        })
111    }
112
113    pub fn _selector_add(self, other: Expr) -> PolarsResult<Expr> {
114        if let Expr::Selector(mut s) = self.0 {
115            if let Expr::Selector(s_other) = other {
116                s = s + s_other;
117            } else {
118                s = s + Selector::Root(Box::new(other))
119            }
120            Ok(Expr::Selector(s))
121        } else {
122            polars_bail!(ComputeError: "expected selector, got {:?}", self.0)
123        }
124    }
125
126    pub fn _selector_and(self, other: Expr) -> PolarsResult<Expr> {
127        if let Expr::Selector(mut s) = self.0 {
128            if let Expr::Selector(s_other) = other {
129                s = s.bitand(s_other);
130            } else {
131                s = s.bitand(Selector::Root(Box::new(other)))
132            }
133            Ok(Expr::Selector(s))
134        } else {
135            polars_bail!(ComputeError: "expected selector, got {:?}", self.0)
136        }
137    }
138
139    pub fn _selector_sub(self, other: Expr) -> PolarsResult<Expr> {
140        if let Expr::Selector(mut s) = self.0 {
141            if let Expr::Selector(s_other) = other {
142                s = s - s_other;
143            } else {
144                s = s - Selector::Root(Box::new(other))
145            }
146            Ok(Expr::Selector(s))
147        } else {
148            polars_bail!(ComputeError: "expected selector, got {:?}", self.0)
149        }
150    }
151
152    pub fn _selector_xor(self, other: Expr) -> PolarsResult<Expr> {
153        if let Expr::Selector(mut s) = self.0 {
154            if let Expr::Selector(s_other) = other {
155                s = s ^ s_other;
156            } else {
157                s = s ^ Selector::Root(Box::new(other))
158            }
159            Ok(Expr::Selector(s))
160        } else {
161            polars_bail!(ComputeError: "expected selector, got {:?}", self.0)
162        }
163    }
164
165    pub fn _into_selector(self) -> Expr {
166        if let Expr::Selector(_) = self.0 {
167            self.0
168        } else {
169            Expr::Selector(Selector::new(self.0))
170        }
171    }
172
173    /// Get a hold to an implementor of the `Display` trait that will format as
174    /// the expression as a tree
175    pub fn into_tree_formatter(self, display_as_dot: bool) -> PolarsResult<impl Display> {
176        let mut arena = Default::default();
177        let node = to_aexpr(self.0, &mut arena)?;
178        let mut visitor = TreeFmtVisitor::default();
179        if display_as_dot {
180            visitor.display = TreeFmtVisitorDisplay::DisplayDot;
181        }
182
183        AexprNode::new(node).visit(&mut visitor, &arena)?;
184
185        Ok(visitor)
186    }
187}