Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
pola-rs
GitHub Repository: pola-rs/polars
Path: blob/main/crates/polars-plan/src/dsl/selector.rs
8424 views
1
use std::fmt;
2
use std::ops::{
3
BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, Not, Sub, SubAssign,
4
};
5
6
#[cfg(feature = "serde")]
7
use serde::{Deserialize, Serialize};
8
9
use super::*;
10
11
#[cfg(feature = "dsl-schema")]
12
impl schemars::JsonSchema for TimeUnitSet {
13
fn schema_name() -> std::borrow::Cow<'static, str> {
14
"TimeUnitSet".into()
15
}
16
17
fn schema_id() -> std::borrow::Cow<'static, str> {
18
std::borrow::Cow::Borrowed(concat!(module_path!(), "::", "TimeUnitSet"))
19
}
20
21
fn json_schema(_generator: &mut schemars::SchemaGenerator) -> schemars::Schema {
22
use schemars::json_schema;
23
use serde_json::{Map, Value};
24
25
// Add a map of flag names and bit patterns to detect schema changes
26
let name_to_bits: Map<String, Value> = Self::all()
27
.iter_names()
28
.map(|(name, flag)| (name.to_owned(), flag.bits().into()))
29
.collect();
30
31
json_schema!({
32
"type": "string",
33
"format": "bitflags",
34
"bitflags": name_to_bits
35
})
36
}
37
}
38
39
#[derive(Clone, Hash, PartialEq, Eq, Debug)]
40
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
41
#[cfg_attr(feature = "dsl-schema", derive(schemars::JsonSchema))]
42
pub enum TimeZoneSet {
43
Any,
44
AnySet,
45
Unset,
46
UnsetOrAnyOf(Arc<[TimeZone]>),
47
AnyOf(Arc<[TimeZone]>),
48
}
49
50
bitflags::bitflags! {
51
#[repr(transparent)]
52
#[derive(Clone, Copy, Hash, PartialEq, Eq, Debug)]
53
#[cfg_attr(
54
feature = "serde",
55
derive(serde::Serialize, serde::Deserialize)
56
)]
57
pub struct TimeUnitSet: u8 {
58
const NANO_SECONDS = 0x01;
59
const MICRO_SECONDS = 0x02;
60
const MILLI_SECONDS = 0x04;
61
}
62
}
63
64
impl From<TimeUnit> for TimeUnitSet {
65
fn from(value: TimeUnit) -> Self {
66
match value {
67
TimeUnit::Nanoseconds => TimeUnitSet::NANO_SECONDS,
68
TimeUnit::Microseconds => TimeUnitSet::MICRO_SECONDS,
69
TimeUnit::Milliseconds => TimeUnitSet::MILLI_SECONDS,
70
}
71
}
72
}
73
74
impl fmt::Display for TimeUnitSet {
75
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
76
if self.is_all() {
77
f.write_str("*")?;
78
} else {
79
if self.bits().count_ones() != 1 {
80
f.write_str("[")?;
81
}
82
83
if self.contains(TimeUnitSet::NANO_SECONDS) {
84
f.write_str("'ns'")?;
85
if self.intersects(TimeUnitSet::MICRO_SECONDS | TimeUnitSet::MILLI_SECONDS) {
86
f.write_str(", ")?;
87
}
88
}
89
if self.contains(TimeUnitSet::MICRO_SECONDS) {
90
f.write_str("'us'")?;
91
if self.contains(TimeUnitSet::MILLI_SECONDS) {
92
f.write_str(", ")?;
93
}
94
}
95
if self.contains(TimeUnitSet::MILLI_SECONDS) {
96
f.write_str("'ms'")?;
97
}
98
99
if self.bits().count_ones() != 1 {
100
f.write_str("]")?;
101
}
102
}
103
104
Ok(())
105
}
106
}
107
108
#[derive(Clone, PartialEq, Hash, Debug, Eq)]
109
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
110
#[cfg_attr(feature = "dsl-schema", derive(schemars::JsonSchema))]
111
pub enum DataTypeSelector {
112
Union(Arc<DataTypeSelector>, Arc<DataTypeSelector>),
113
Difference(Arc<DataTypeSelector>, Arc<DataTypeSelector>),
114
ExclusiveOr(Arc<DataTypeSelector>, Arc<DataTypeSelector>),
115
Intersect(Arc<DataTypeSelector>, Arc<DataTypeSelector>),
116
117
Wildcard,
118
Empty,
119
120
AnyOf(Arc<[DataType]>),
121
122
Integer,
123
UnsignedInteger,
124
SignedInteger,
125
Float,
126
127
Enum,
128
Categorical,
129
130
Nested,
131
List(Option<Arc<DataTypeSelector>>),
132
Array(Option<Arc<DataTypeSelector>>, Option<usize>),
133
Struct,
134
135
Decimal,
136
Numeric,
137
Temporal,
138
/// Selector for `DataType::Datetime` with optional matching on TimeUnit and TimeZone.
139
Datetime(TimeUnitSet, TimeZoneSet),
140
/// Selector for `DataType::Duration` with optional matching on TimeUnit.
141
Duration(TimeUnitSet),
142
Object,
143
}
144
145
#[derive(Clone, PartialEq, Hash, Debug, Eq)]
146
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
147
#[cfg_attr(feature = "dsl-schema", derive(schemars::JsonSchema))]
148
pub enum Selector {
149
Union(Arc<Selector>, Arc<Selector>),
150
Difference(Arc<Selector>, Arc<Selector>),
151
ExclusiveOr(Arc<Selector>, Arc<Selector>),
152
Intersect(Arc<Selector>, Arc<Selector>),
153
154
// Leaf nodes
155
156
// These 2 return their inputs in given order not in schema order.
157
ByName {
158
names: Arc<[PlSmallStr]>,
159
strict: bool,
160
},
161
ByIndex {
162
indices: Arc<[i64]>,
163
strict: bool,
164
},
165
166
Matches(PlSmallStr),
167
ByDType(DataTypeSelector),
168
169
Wildcard,
170
Empty,
171
}
172
173
fn dtype_selector(
174
schema: &Schema,
175
ignored_columns: &PlHashSet<PlSmallStr>,
176
f: impl Fn(&DataType) -> bool,
177
) -> PlIndexSet<PlSmallStr> {
178
PlIndexSet::from_iter(
179
schema
180
.iter()
181
.filter(|(name, dtype)| !ignored_columns.contains(*name) && f(dtype))
182
.map(|(name, _)| name.clone()),
183
)
184
}
185
186
impl Selector {
187
/// Turns the selector into an ordered set of selected columns from the schema.
188
///
189
/// - The order of the columns corresponds to the order in the schema.
190
/// - Column names in `ignored_columns` are only used if they are explicitly mentioned by a
191
/// `ByName` or `Nth`.
192
pub fn into_columns(
193
&self,
194
schema: &Schema,
195
ignored_columns: &PlHashSet<PlSmallStr>,
196
) -> PolarsResult<PlIndexSet<PlSmallStr>> {
197
let out = match self {
198
Self::Union(lhs, rhs) => {
199
let mut lhs = lhs.into_columns(schema, ignored_columns)?;
200
let rhs = rhs.into_columns(schema, ignored_columns)?;
201
lhs.extend(rhs);
202
sort_schema_order(&mut lhs, schema);
203
lhs
204
},
205
Self::Difference(lhs, rhs) => {
206
let mut lhs = lhs.into_columns(schema, ignored_columns)?;
207
let rhs = rhs.into_columns(schema, ignored_columns)?;
208
lhs.retain(|n| !rhs.contains(n));
209
sort_schema_order(&mut lhs, schema);
210
lhs
211
},
212
Self::ExclusiveOr(lhs, rhs) => {
213
let lhs = lhs.into_columns(schema, ignored_columns)?;
214
let rhs = rhs.into_columns(schema, ignored_columns)?;
215
let mut out = PlIndexSet::with_capacity(lhs.len() + rhs.len());
216
out.extend(lhs.iter().filter(|n| !rhs.contains(*n)).cloned());
217
out.extend(rhs.into_iter().filter(|n| !lhs.contains(n)));
218
sort_schema_order(&mut out, schema);
219
out
220
},
221
Self::Intersect(lhs, rhs) => {
222
let mut lhs = lhs.into_columns(schema, ignored_columns)?;
223
let rhs = rhs.into_columns(schema, ignored_columns)?;
224
lhs.retain(|n| rhs.contains(n));
225
sort_schema_order(&mut lhs, schema);
226
lhs
227
},
228
229
Self::ByDType(dts) => dts.into_columns(schema, ignored_columns)?,
230
Self::ByName { names, strict } => {
231
let mut out = PlIndexSet::with_capacity(names.len());
232
for name in names.iter() {
233
if schema.contains(name) {
234
out.insert(name.clone());
235
} else if *strict {
236
polars_bail!(col_not_found = name);
237
}
238
}
239
out
240
},
241
Self::ByIndex { indices, strict } => {
242
let mut out = PlIndexSet::with_capacity(indices.len());
243
let mut set = PlIndexSet::with_capacity(indices.len());
244
for &idx in indices.iter() {
245
let Some(idx) = idx.negative_to_usize(schema.len()) else {
246
polars_ensure!(!strict, ColumnNotFound: "cannot get the {idx}-th column when schema has {} columns", schema.len());
247
continue;
248
};
249
let (name, _) = schema.get_at_index(idx).unwrap();
250
if !set.insert(idx) {
251
polars_bail!(Duplicate: "duplicate column name {name}");
252
}
253
out.insert(name.clone());
254
}
255
out
256
},
257
Self::Matches(regex_str) => {
258
let re = polars_utils::regex_cache::compile_regex(regex_str).map_err(
259
|_| polars_err!(InvalidOperation: "invalid regex in selector '{regex_str}'"),
260
)?;
261
PlIndexSet::from_iter(
262
schema
263
.iter_names()
264
.filter(|name| !ignored_columns.contains(*name) && re.is_match(name))
265
.cloned(),
266
)
267
},
268
Self::Wildcard => PlIndexSet::from_iter(
269
schema
270
.iter_names()
271
.filter(|name| !ignored_columns.contains(*name))
272
.cloned(),
273
),
274
Self::Empty => Default::default(),
275
};
276
Ok(out)
277
}
278
279
pub fn as_expr(self) -> Expr {
280
self.into()
281
}
282
283
pub fn to_dtype_selector(&self) -> Option<DataTypeSelector> {
284
use DataTypeSelector as DS;
285
match self {
286
Self::Union(l, r) => Some(DS::Union(
287
Arc::new(l.to_dtype_selector()?),
288
Arc::new(r.to_dtype_selector()?),
289
)),
290
Self::Difference(l, r) => Some(DS::Difference(
291
Arc::new(l.to_dtype_selector()?),
292
Arc::new(r.to_dtype_selector()?),
293
)),
294
Self::ExclusiveOr(l, r) => Some(DS::ExclusiveOr(
295
Arc::new(l.to_dtype_selector()?),
296
Arc::new(r.to_dtype_selector()?),
297
)),
298
Self::Intersect(l, r) => Some(DS::ExclusiveOr(
299
Arc::new(l.to_dtype_selector()?),
300
Arc::new(r.to_dtype_selector()?),
301
)),
302
Self::Wildcard => Some(DS::Wildcard),
303
Self::Empty => Some(DS::Empty),
304
305
Self::ByDType(dts) => Some(dts.clone()),
306
307
Self::ByName { .. } | Self::ByIndex { .. } | Self::Matches(_) => None,
308
}
309
}
310
311
/// Exclude a column from a wildcard/regex selection.
312
///
313
/// You may also use regexes in the exclude as long as they start with `^` and end with `$`.
314
pub fn exclude_cols(self, columns: impl IntoVec<PlSmallStr>) -> Self {
315
self - functions::cols(columns.into_vec())
316
}
317
318
pub fn exclude_dtype<D: AsRef<[DataType]>>(self, dtypes: D) -> Self {
319
self - DataTypeSelector::AnyOf(dtypes.as_ref().into()).as_selector()
320
}
321
}
322
323
fn list_matches(inner_dts: Option<&DataTypeSelector>, dtype: &DataType) -> bool {
324
matches!(dtype, DataType::List(inner) if inner_dts.is_none_or(|dts| dts.matches(inner.as_ref())))
325
}
326
327
fn array_matches(
328
inner_dts: Option<&DataTypeSelector>,
329
swidth: Option<usize>,
330
dtype: &DataType,
331
) -> bool {
332
#[cfg(feature = "dtype-array")]
333
{
334
matches!(dtype, DataType::Array(inner, width) if inner_dts.is_none_or(|dts| dts.matches(inner.as_ref())) && swidth.is_none_or(|w| w == *width))
335
}
336
337
#[cfg(not(feature = "dtype-array"))]
338
{
339
false
340
}
341
}
342
343
fn datetime_matches(stu: TimeUnitSet, stz: &TimeZoneSet, dtype: &DataType) -> bool {
344
let DataType::Datetime(tu, tz) = dtype else {
345
return false;
346
};
347
348
if !stu.contains(TimeUnitSet::from(*tu)) {
349
return false;
350
}
351
352
use TimeZoneSet as TZS;
353
match (stz, tz) {
354
(TZS::Any, _)
355
| (TZS::Unset, None)
356
| (TZS::UnsetOrAnyOf(_), None)
357
| (TZS::AnySet, Some(_)) => true,
358
(TZS::AnyOf(stz) | TZS::UnsetOrAnyOf(stz), Some(tz)) => stz.contains(tz),
359
_ => false,
360
}
361
}
362
363
fn sort_schema_order(set: &mut PlIndexSet<PlSmallStr>, schema: &Schema) {
364
set.sort_unstable_by(|l, r| {
365
schema
366
.index_of(l)
367
.unwrap()
368
.cmp(&schema.index_of(r).unwrap())
369
})
370
}
371
372
fn duration_matches(stu: TimeUnitSet, dtype: &DataType) -> bool {
373
matches!(dtype, DataType::Duration(tu) if stu.contains(TimeUnitSet::from(*tu)))
374
}
375
376
impl DataTypeSelector {
377
pub fn matches(&self, dtype: &DataType) -> bool {
378
match self {
379
Self::Union(lhs, rhs) => lhs.matches(dtype) || rhs.matches(dtype),
380
Self::Difference(lhs, rhs) => lhs.matches(dtype) && !rhs.matches(dtype),
381
Self::ExclusiveOr(lhs, rhs) => lhs.matches(dtype) ^ rhs.matches(dtype),
382
Self::Intersect(lhs, rhs) => lhs.matches(dtype) && rhs.matches(dtype),
383
Self::Wildcard => true,
384
Self::Empty => false,
385
Self::AnyOf(dtypes) => dtypes.iter().any(|dt| dt == dtype),
386
Self::Integer => dtype.is_integer(),
387
Self::UnsignedInteger => dtype.is_unsigned_integer(),
388
Self::SignedInteger => dtype.is_signed_integer(),
389
Self::Float => dtype.is_float(),
390
Self::Enum => dtype.is_enum(),
391
Self::Categorical => dtype.is_categorical(),
392
Self::Nested => dtype.is_nested(),
393
Self::List(inner_dts) => list_matches(inner_dts.as_deref(), dtype),
394
Self::Array(inner_dts, swidth) => array_matches(inner_dts.as_deref(), *swidth, dtype),
395
Self::Struct => dtype.is_struct(),
396
Self::Decimal => dtype.is_decimal(),
397
Self::Numeric => dtype.is_numeric(),
398
Self::Temporal => dtype.is_temporal(),
399
Self::Datetime(stu, stz) => datetime_matches(*stu, stz, dtype),
400
Self::Duration(stu) => duration_matches(*stu, dtype),
401
Self::Object => dtype.is_object(),
402
}
403
}
404
405
#[allow(clippy::wrong_self_convention)]
406
fn into_columns(
407
&self,
408
schema: &Schema,
409
ignored_columns: &PlHashSet<PlSmallStr>,
410
) -> PolarsResult<PlIndexSet<PlSmallStr>> {
411
Ok(match self {
412
Self::Union(lhs, rhs) => {
413
let mut lhs = lhs.into_columns(schema, ignored_columns)?;
414
let rhs = rhs.into_columns(schema, ignored_columns)?;
415
lhs.extend(rhs);
416
sort_schema_order(&mut lhs, schema);
417
lhs
418
},
419
Self::Difference(lhs, rhs) => {
420
let mut lhs = lhs.into_columns(schema, ignored_columns)?;
421
let rhs = rhs.into_columns(schema, ignored_columns)?;
422
lhs.retain(|n| !rhs.contains(n));
423
sort_schema_order(&mut lhs, schema);
424
lhs
425
},
426
Self::ExclusiveOr(lhs, rhs) => {
427
let lhs = lhs.into_columns(schema, ignored_columns)?;
428
let rhs = rhs.into_columns(schema, ignored_columns)?;
429
let mut out = PlIndexSet::with_capacity(lhs.len() + rhs.len());
430
out.extend(lhs.iter().filter(|n| !rhs.contains(*n)).cloned());
431
out.extend(rhs.into_iter().filter(|n| !lhs.contains(n)));
432
sort_schema_order(&mut out, schema);
433
out
434
},
435
Self::Intersect(lhs, rhs) => {
436
let mut lhs = lhs.into_columns(schema, ignored_columns)?;
437
let rhs = rhs.into_columns(schema, ignored_columns)?;
438
lhs.retain(|n| rhs.contains(n));
439
sort_schema_order(&mut lhs, schema);
440
lhs
441
},
442
Self::Wildcard => schema
443
.iter_names()
444
.filter(|n| ignored_columns.contains(*n))
445
.cloned()
446
.collect(),
447
Self::Empty => Default::default(),
448
Self::AnyOf(dtypes) => {
449
let dtypes = PlHashSet::from_iter(dtypes.iter().cloned());
450
dtype_selector(schema, ignored_columns, |dtype| dtypes.contains(dtype))
451
},
452
Self::Integer => dtype_selector(schema, ignored_columns, |dtype| dtype.is_integer()),
453
Self::UnsignedInteger => {
454
dtype_selector(schema, ignored_columns, |dtype| dtype.is_unsigned_integer())
455
},
456
Self::SignedInteger => {
457
dtype_selector(schema, ignored_columns, |dtype| dtype.is_signed_integer())
458
},
459
Self::Float => dtype_selector(schema, ignored_columns, |dtype| dtype.is_float()),
460
Self::Enum => dtype_selector(schema, ignored_columns, |dtype| dtype.is_enum()),
461
Self::Categorical => {
462
dtype_selector(schema, ignored_columns, |dtype| dtype.is_categorical())
463
},
464
Self::Nested => dtype_selector(schema, ignored_columns, |dtype| dtype.is_nested()),
465
Self::List(inner_dts) => dtype_selector(schema, ignored_columns, |dtype| {
466
list_matches(inner_dts.as_deref(), dtype)
467
}),
468
Self::Array(inner_dts, swidth) => dtype_selector(schema, ignored_columns, |dtype| {
469
array_matches(inner_dts.as_deref(), *swidth, dtype)
470
}),
471
Self::Struct => dtype_selector(schema, ignored_columns, |dtype| dtype.is_struct()),
472
Self::Decimal => dtype_selector(schema, ignored_columns, |dtype| dtype.is_decimal()),
473
Self::Numeric => dtype_selector(schema, ignored_columns, |dtype| dtype.is_numeric()),
474
Self::Temporal => dtype_selector(schema, ignored_columns, |dtype| dtype.is_temporal()),
475
Self::Datetime(stu, stz) => dtype_selector(schema, ignored_columns, |dtype| {
476
datetime_matches(*stu, stz, dtype)
477
}),
478
Self::Duration(stu) => dtype_selector(schema, ignored_columns, |dtype| {
479
duration_matches(*stu, dtype)
480
}),
481
Self::Object => dtype_selector(schema, ignored_columns, |dtype| dtype.is_object()),
482
})
483
}
484
485
pub fn as_selector(self) -> Selector {
486
Selector::ByDType(self)
487
}
488
}
489
490
impl BitOr for Selector {
491
type Output = Self;
492
fn bitor(self, rhs: Self) -> Self::Output {
493
Selector::Union(Arc::new(self), Arc::new(rhs))
494
}
495
}
496
497
impl BitOrAssign for Selector {
498
fn bitor_assign(&mut self, rhs: Self) {
499
*self = Selector::Union(
500
Arc::new(std::mem::replace(self, Self::Empty)),
501
Arc::new(rhs),
502
)
503
}
504
}
505
506
impl BitAnd for Selector {
507
type Output = Self;
508
fn bitand(self, rhs: Self) -> Self::Output {
509
Selector::Intersect(Arc::new(self), Arc::new(rhs))
510
}
511
}
512
513
impl BitAndAssign for Selector {
514
fn bitand_assign(&mut self, rhs: Self) {
515
*self = Selector::Intersect(
516
Arc::new(std::mem::replace(self, Self::Empty)),
517
Arc::new(rhs),
518
)
519
}
520
}
521
522
impl BitXor for Selector {
523
type Output = Self;
524
fn bitxor(self, rhs: Self) -> Self::Output {
525
Selector::ExclusiveOr(Arc::new(self), Arc::new(rhs))
526
}
527
}
528
529
impl BitXorAssign for Selector {
530
fn bitxor_assign(&mut self, rhs: Self) {
531
*self = Selector::ExclusiveOr(
532
Arc::new(std::mem::replace(self, Self::Empty)),
533
Arc::new(rhs),
534
)
535
}
536
}
537
538
impl Sub for Selector {
539
type Output = Self;
540
fn sub(self, rhs: Self) -> Self::Output {
541
Selector::Difference(Arc::new(self), Arc::new(rhs))
542
}
543
}
544
545
impl SubAssign for Selector {
546
fn sub_assign(&mut self, rhs: Self) {
547
*self = Selector::Difference(
548
Arc::new(std::mem::replace(self, Self::Empty)),
549
Arc::new(rhs),
550
)
551
}
552
}
553
554
impl Not for Selector {
555
type Output = Self;
556
fn not(self) -> Self::Output {
557
Self::Wildcard - self
558
}
559
}
560
561
impl BitOr for DataTypeSelector {
562
type Output = Self;
563
fn bitor(self, rhs: Self) -> Self::Output {
564
Self::Union(Arc::new(self), Arc::new(rhs))
565
}
566
}
567
568
impl BitOrAssign for DataTypeSelector {
569
fn bitor_assign(&mut self, rhs: Self) {
570
*self = Self::Union(
571
Arc::new(std::mem::replace(self, Self::Empty)),
572
Arc::new(rhs),
573
)
574
}
575
}
576
577
impl BitAnd for DataTypeSelector {
578
type Output = Self;
579
fn bitand(self, rhs: Self) -> Self::Output {
580
Self::Intersect(Arc::new(self), Arc::new(rhs))
581
}
582
}
583
584
impl BitAndAssign for DataTypeSelector {
585
fn bitand_assign(&mut self, rhs: Self) {
586
*self = Self::Intersect(
587
Arc::new(std::mem::replace(self, Self::Empty)),
588
Arc::new(rhs),
589
)
590
}
591
}
592
593
impl BitXor for DataTypeSelector {
594
type Output = Self;
595
fn bitxor(self, rhs: Self) -> Self::Output {
596
Self::ExclusiveOr(Arc::new(self), Arc::new(rhs))
597
}
598
}
599
600
impl BitXorAssign for DataTypeSelector {
601
fn bitxor_assign(&mut self, rhs: Self) {
602
*self = Self::ExclusiveOr(
603
Arc::new(std::mem::replace(self, Self::Empty)),
604
Arc::new(rhs),
605
)
606
}
607
}
608
609
impl Sub for DataTypeSelector {
610
type Output = Self;
611
fn sub(self, rhs: Self) -> Self::Output {
612
Self::Difference(Arc::new(self), Arc::new(rhs))
613
}
614
}
615
616
impl SubAssign for DataTypeSelector {
617
fn sub_assign(&mut self, rhs: Self) {
618
*self = Self::Difference(
619
Arc::new(std::mem::replace(self, Self::Empty)),
620
Arc::new(rhs),
621
)
622
}
623
}
624
625
impl Not for DataTypeSelector {
626
type Output = Self;
627
fn not(self) -> Self::Output {
628
Self::Wildcard - self
629
}
630
}
631
632
impl From<Selector> for Expr {
633
fn from(value: Selector) -> Self {
634
Expr::Selector(value)
635
}
636
}
637
638
impl fmt::Display for Selector {
639
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
640
match self {
641
Self::Union(left, right) => write!(f, "[{left} | {right}]"),
642
Self::Difference(left, right) => write!(f, "[{left} - {right}]"),
643
Self::ExclusiveOr(left, right) => write!(f, "[{left} ^ {right}]"),
644
Self::Intersect(left, right) => write!(f, "[{left} & {right}]"),
645
646
Self::ByDType(dst) => fmt::Display::fmt(dst, f),
647
Self::ByName { names, strict } => {
648
f.write_str("cs.by_name(")?;
649
650
for e in names.iter() {
651
write!(f, "'{e}', ")?;
652
}
653
654
write!(f, "require_all={strict})")
655
},
656
Self::ByIndex { indices, strict } if indices.as_ref() == [0] => {
657
write!(f, "cs.first(require={strict})")
658
},
659
Self::ByIndex { indices, strict } if indices.as_ref() == [-1] => {
660
write!(f, "cs.last(require={strict})")
661
},
662
Self::ByIndex { indices, strict } if indices.len() == 1 => {
663
write!(f, "cs.nth({}, require_all={strict})", indices[0])
664
},
665
Self::ByIndex { indices, strict } => {
666
write!(
667
f,
668
"cs.by_index({:?}, require_all={strict})",
669
indices.as_ref()
670
)
671
},
672
Self::Matches(s) => write!(f, "cs.matches(\"{s}\")"),
673
Self::Wildcard => f.write_str("cs.all()"),
674
Self::Empty => f.write_str("cs.empty()"),
675
}
676
}
677
}
678
679
impl fmt::Display for DataTypeSelector {
680
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
681
match self {
682
Self::Union(left, right) => write!(f, "[{left} | {right}]"),
683
Self::Difference(left, right) => write!(f, "[{left} - {right}]"),
684
Self::ExclusiveOr(left, right) => write!(f, "[{left} ^ {right}]"),
685
Self::Intersect(left, right) => write!(f, "[{left} & {right}]"),
686
687
Self::Float => f.write_str("cs.float()"),
688
Self::Integer => f.write_str("cs.integer()"),
689
Self::SignedInteger => f.write_str("cs.signed_integer()"),
690
Self::UnsignedInteger => f.write_str("cs.unsigned_integer()"),
691
692
Self::Enum => f.write_str("cs.enum()"),
693
Self::Categorical => f.write_str("cs.categorical()"),
694
695
Self::Nested => f.write_str("cs.nested()"),
696
Self::List(inner_dst) => {
697
f.write_str("cs.list(")?;
698
if let Some(inner_dst) = inner_dst {
699
fmt::Display::fmt(inner_dst.as_ref(), f)?;
700
}
701
f.write_str(")")
702
},
703
Self::Array(inner_dst, swidth) => {
704
f.write_str("cs.list(")?;
705
if let Some(inner_dst) = inner_dst {
706
fmt::Display::fmt(inner_dst.as_ref(), f)?;
707
}
708
f.write_str(", width=")?;
709
match swidth {
710
None => f.write_str("*")?,
711
Some(swidth) => write!(f, "{swidth}")?,
712
}
713
f.write_str(")")
714
},
715
Self::Struct => f.write_str("cs.struct()"),
716
717
Self::Numeric => f.write_str("cs.numeric()"),
718
Self::Decimal => f.write_str("cs.decimal()"),
719
Self::Temporal => f.write_str("cs.temporal()"),
720
Self::Datetime(tu, tz) => {
721
write!(f, "cs.datetime(time_unit={tu}, time_zone=")?;
722
use TimeZoneSet as TZS;
723
match tz {
724
TZS::Any => f.write_str("*")?,
725
TZS::AnySet => f.write_str("*set")?,
726
TZS::Unset => f.write_str("None")?,
727
TZS::UnsetOrAnyOf(tz) => {
728
f.write_str("[None")?;
729
for e in tz.iter() {
730
write!(f, ", '{e}'")?;
731
}
732
f.write_str("]")?;
733
},
734
TZS::AnyOf(tz) => {
735
f.write_str("[")?;
736
if let Some(e) = tz.first() {
737
write!(f, "'{e}'")?;
738
for e in &tz[1..] {
739
write!(f, ", '{e}'")?;
740
}
741
}
742
f.write_str("]")?;
743
},
744
}
745
f.write_str(")")
746
},
747
Self::Duration(tu) => {
748
write!(f, "cs.duration(time_unit={tu})")
749
},
750
Self::Object => f.write_str("cs.object()"),
751
752
Self::AnyOf(dtypes) => {
753
use DataType as D;
754
match dtypes.as_ref() {
755
[D::Boolean] => f.write_str("cs.boolean()"),
756
[D::Binary] => f.write_str("cs.binary()"),
757
[D::Time] => f.write_str("cs.time()"),
758
[D::Date] => f.write_str("cs.date()"),
759
[D::String] => f.write_str("cs.string()"),
760
_ => write!(f, "cs.by_dtype({dtypes:?})"),
761
}
762
},
763
764
Self::Wildcard => f.write_str("cs.all()"),
765
Self::Empty => f.write_str("cs.empty()"),
766
}
767
}
768
}
769
770
impl fmt::Display for Excluded {
771
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
772
match self {
773
Excluded::Name(name) => write!(f, "\"{name}\""),
774
Excluded::Dtype(dtype) => fmt::Display::fmt(dtype, f),
775
}
776
}
777
}
778
779