polars_view/sort.rs
1//! Defines the representation of sorting criteria for the table.
2//! This module contains the core types for managing single and multiple sort column states.
3
4use std::fmt::Debug;
5
6/// Represents a single criterion for sorting.
7/// Used within `DataFrameContainer` to store the cumulative sort order as `Vec<SortBy>`.
8/// The order of criteria in the Vec determines sort precedence.
9#[derive(Debug, Clone, Eq, PartialEq)]
10pub struct SortBy {
11 /// The name of the column to sort by.
12 pub column_name: String,
13 /// The sort direction. `true` for ascending, `false` for descending.
14 pub ascending: bool,
15 /// How nulls should be ordered. `true` to place nulls last, `false` for first.
16 pub nulls_last: bool,
17}
18
19/// Represents the *interaction* state for sorting a specific column header in the UI.
20///
21/// This enum manages the click cycle:
22/// NotSorted -> DescNullsFirst -> AscNullsFirst -> DescNullsLast -> AscNullsLast -> NotSorted.
23/// The actual applied cumulative sort state (`Vec<SortBy>`) is stored and managed separately
24/// in `DataFrameContainer`.
25#[derive(Debug, Clone, Eq, PartialEq)]
26pub enum HeaderSortState {
27 /// Column is not part of the current sort criteria list.
28 NotSorted,
29 /// Column sorted descending, nulls appear first.
30 DescendingNullsFirst, // State 1
31 /// Column sorted ascending, nulls appear first.
32 AscendingNullsFirst, // State 2
33 /// Column sorted descending, nulls appear last.
34 DescendingNullsLast, // State 3
35 /// Column sorted ascending, nulls appear last.
36 AscendingNullsLast, // State 4
37}
38
39impl HeaderSortState {
40 /// Calculates the next interaction state in the UI cycle for a header click.
41 ///
42 /// The cycle progresses as follows:
43 /// 1. `NotSorted` -> `DescendingNullsFirst`
44 /// 2. `DescendingNullsFirst` -> `AscendingNullsFirst`
45 /// 3. `AscendingNullsFirst` -> `DescendingNullsLast`
46 /// 4. `DescendingNullsLast` -> `AscendingNullsLast`
47 /// 5. `AscendingNullsLast` -> `NotSorted` (removes the sort for this column)
48 ///
49 /// Called by `container.rs::render_table_header` when a click is detected.
50 ///
51 /// ### Returns
52 /// The next `HeaderSortState` in the cycle.
53 pub fn cycle_next(&self) -> Self {
54 match self {
55 HeaderSortState::NotSorted => HeaderSortState::DescendingNullsFirst,
56 HeaderSortState::DescendingNullsFirst => HeaderSortState::AscendingNullsFirst,
57 HeaderSortState::AscendingNullsFirst => HeaderSortState::DescendingNullsLast,
58 HeaderSortState::DescendingNullsLast => HeaderSortState::AscendingNullsLast,
59 HeaderSortState::AscendingNullsLast => HeaderSortState::NotSorted,
60 }
61 }
62
63 /// Returns a Unicode icon visually representing the interaction state.
64 /// Optionally includes the sort precedence index (1-based) if the column is sorted.
65 /// Uses different symbols to distinguish nulls placement.
66 ///
67 /// Used by the `SortableHeaderRenderer` trait implementation in `traits.rs`
68 /// to display feedback in the table header (e.g., "1▼", "2▲", "3▽", "4△", "↕").
69 ///
70 /// ### Arguments
71 /// * `index`: `Option<usize>` - The 0-based index representing the sort precedence. `None` if not sorted.
72 ///
73 /// ### Returns
74 /// A `String` containing the icon and optional index number.
75 pub fn get_icon(&self, index: Option<usize>) -> String {
76 let base_icon = match self {
77 // Nulls First States
78 HeaderSortState::DescendingNullsFirst => "⏷", // U+23F7 (Down arrow)
79 HeaderSortState::AscendingNullsFirst => "⏶", // U+23F6 (Up arrow)
80
81 // Nulls Last States
82 HeaderSortState::DescendingNullsLast => "⬇",
83 HeaderSortState::AscendingNullsLast => "⬆",
84
85 // Unsorted State
86 HeaderSortState::NotSorted => "↕", // U+2195 UP DOWN ARROW
87 };
88
89 match index {
90 Some(idx) => format!("{}{}", idx + 1, base_icon),
91 None => base_icon.to_string(),
92 }
93 }
94}