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}