ord_by_set/
order.rs

1use core::cmp::Ordering;
2
3/// A trait representing the capability of taking two items and ordering them.
4///
5/// An orderer *is* allowed to have two "equal" values which are not actually equal,
6/// but can be considered loosely equal. This is similar to javascript's `==` operator,
7/// while [`Ord`] would be equivelant to javascript's `===` operator.
8///
9/// For example, if you had an enum which allowed both strings and numbers:
10///
11/// ```
12/// enum Val {
13///     String(String),
14///     Num(i32),
15/// }
16/// ```
17///
18/// You *could* allow `Val::String("3")` to be loosely equivelant to `Val::Num(3)`, while still
19/// having them be distinct values. Then if the following operation is performed:
20///
21/// ```
22/// # enum Val {
23/// #     String(String),
24/// #     Num(i32),
25/// # }
26/// #
27/// use ord_by_set::{OrdBySet, Order};
28/// use std::cmp::Ordering;
29///
30/// #[derive(Default)]
31/// struct LooseOrder;
32///
33/// impl Order<Val> for LooseOrder {
34///     fn order_of(&self, left: &Val, right: &Val) -> Ordering {
35///         match (left, right) {
36///             (Val::String(left), Val::String(right)) => left.cmp(right),
37///             (Val::Num(left), Val::Num(right)) => left.cmp(right),
38///
39///             (Val::String(left), Val::Num(right)) => left.parse::<i32>()
40///                 .unwrap()
41///                 .cmp(right),
42///             (Val::Num(left), Val::String(right)) => left.cmp(&right.parse::<i32>().unwrap()),
43///         }
44///     }
45/// }
46///
47/// let totally_numbers = [
48///     Val::Num(100),
49///     Val::String("70".into()),
50///     Val::Num(70),
51///     Val::String("30".into()),
52/// ];
53/// let ord = OrdBySet::new_with_order(LooseOrder).with_items(totally_numbers);
54///
55/// assert!(matches!(
56///     ord.get(&Val::Num(70)),
57///     Some([Val::Num(70), Val::String(num)] | [Val::String(num), Val::Num(70)])
58///         if num == "70"
59/// ));
60/// ```
61///
62/// ### Specification
63///
64/// The following behaviors must hold true in a proper `Order<T>` implementation:
65///
66/// * Exactly one of `a < b`, `a > b`, or `a == b` is true.
67/// * LessThan, Equals, and GreaterThan are all transitive. Which is to say that
68/// `a == b` and `b == c` implies `a == c`.
69///
70/// The easiest way to think about this is that `Order<T>` is a proper implementation of
71/// [`Ord`] for a subset of the type `T`, albeit with possibly alternate behavior to that
72/// of T's [`Ord`] itself, if such an implementation exists.
73///
74/// Failure to uphold this contract will result in unspecified (albeit safe/sound in the
75/// context of Rust's safety guarantees) behavior by [`OrdBySet`].
76///
77/// While not strictly required for a valid `Order` implementation, if you wish to use
78/// the `*_specific` methods in `OrdBySet<T>`, then it is expected that the set of
79/// comparison pairs (left, right) will return `Ordering::Equal` for all values of
80/// `left` and `right` where `PartialEq::eq(left, right)` returns `true`.
81///
82/// That is to say, the set of comparison pairs which are equivelant under `Order<T>`
83/// should be a superset of those equal under `PartialEq` if you choose to use those
84/// methods.
85pub trait Order<T> {
86    /// Takes two items and compares them, returning if the first is less than, equal to,
87    /// or greater than, the latter.
88    fn order_of(&self, left: &T, right: &T) -> Ordering;
89
90    /// Takes a slice of items and sorts them using the given order
91    fn sort_slice(&self, items: &mut [T]) {
92        items.sort_by(|left, right| self.order_of(&left, &right));
93    }
94}
95
96/// An ordering implementation that just defers to [`Ord`]
97#[derive(Default)]
98pub struct FullOrd;
99
100impl<T: Ord> Order<T> for FullOrd {
101    fn order_of(&self, left: &T, right: &T) -> Ordering {
102        left.cmp(right)
103    }
104}
105
106/// An implementation for closures to allow for ad-hoc ordering
107impl<T, OrderFn> Order<T> for OrderFn
108where
109    OrderFn: Fn(&T, &T) -> Ordering,
110{
111    fn order_of(&self, left: &T, right: &T) -> Ordering {
112        self(left, right)
113    }
114}