word_tally/options/
sort.rs

1//! Sorting options for word tallying results.
2
3use core::{
4    cmp::Reverse,
5    fmt::{self, Display, Formatter},
6};
7
8use clap::ValueEnum;
9use rayon::slice::ParallelSliceMut;
10use serde::{Deserialize, Serialize};
11
12use crate::{Count, Io, Word};
13
14/// Sort order by count.
15///
16/// # Examples
17///
18/// ```
19/// use word_tally::Sort;
20///
21/// assert_eq!(Sort::default(), Sort::Desc);
22/// assert_eq!(Sort::Desc.to_string(), "desc");
23/// assert_eq!(Sort::Asc.to_string(), "asc");
24/// assert_eq!(Sort::Unsorted.to_string(), "unsorted");
25/// ```
26#[derive(
27    Clone,
28    Copy,
29    Debug,
30    Default,
31    PartialEq,
32    Eq,
33    PartialOrd,
34    Ord,
35    Hash,
36    Serialize,
37    Deserialize,
38    ValueEnum,
39)]
40#[serde(rename_all = "camelCase")]
41pub enum Sort {
42    /// Sort by count descending.
43    #[default]
44    Desc,
45    /// Sort by count ascending.
46    Asc,
47    /// No sorting applied.
48    Unsorted,
49}
50
51impl Display for Sort {
52    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
53        match self {
54            Self::Desc => write!(f, "desc"),
55            Self::Asc => write!(f, "asc"),
56            Self::Unsorted => write!(f, "unsorted"),
57        }
58    }
59}
60
61impl Sort {
62    /// Returns true if sorting should be applied.
63    #[must_use]
64    pub const fn should_sort(&self) -> bool {
65        !matches!(self, Self::Unsorted)
66    }
67
68    /// Returns true if sorting should be in descending order.
69    #[must_use]
70    pub const fn is_descending(&self) -> bool {
71        matches!(self, Self::Desc)
72    }
73
74    /// Sorts the tally in place based on the sort order and I/O mode.
75    pub fn apply(&self, tally: &mut [(Word, Count)], io: Io) {
76        match (self, io) {
77            // No sorting
78            (Self::Unsorted, _) => {}
79
80            // Sequential unstable sorting
81            (Self::Desc, Io::Stream) => {
82                tally.sort_unstable_by_key(|&(_, count)| Reverse(count));
83            }
84            (Self::Asc, Io::Stream) => {
85                tally.sort_unstable_by_key(|&(_, count)| count);
86            }
87
88            // Parallel unstable sorting
89            (
90                Self::Desc,
91                Io::Auto
92                | Io::ParallelStream
93                | Io::ParallelInMemory
94                | Io::ParallelMmap
95                | Io::ParallelBytes,
96            ) => {
97                tally.par_sort_unstable_by_key(|&(_, count)| Reverse(count));
98            }
99            (
100                Self::Asc,
101                Io::Auto
102                | Io::ParallelStream
103                | Io::ParallelInMemory
104                | Io::ParallelMmap
105                | Io::ParallelBytes,
106            ) => {
107                tally.par_sort_unstable_by_key(|&(_, count)| count);
108            }
109        }
110    }
111}